国产高清吹潮免费视频,老熟女@tubeumtv,粉嫩av一区二区三区免费观看,亚洲国产成人精品青青草原

二維碼
企資網(wǎng)

掃一掃關(guān)注

當(dāng)前位置: 首頁(yè) » 企資頭條 » 專題 » 正文

從使用到自己實(shí)現(xiàn)簡(jiǎn)單Vue_Router看這個(gè)

放大字體  縮小字體 發(fā)布日期:2021-09-14 07:13:59    作者:高雨凡    瀏覽次數(shù):47
導(dǎo)讀

Vue Router 基礎(chǔ)讓我們先來(lái)了解下Vue Router的簡(jiǎn)單使用吧,先了解怎么使用,之后再去想辦法怎么去實(shí)現(xiàn)1.簡(jiǎn)介路由:本質(zhì)上是一種對(duì)應(yīng)關(guān)系分類分為前端路由和后端路由后端路由比如node.js 的路由是 URL的請(qǐng)求地址和服

Vue Router 基礎(chǔ)

讓我們先來(lái)了解下Vue Router的簡(jiǎn)單使用吧,先了解怎么使用,之后再去想辦法怎么去實(shí)現(xiàn)

1.簡(jiǎn)介

路由:本質(zhì)上是一種對(duì)應(yīng)關(guān)系

分類分為前端路由和后端路由

后端路由

比如node.js 的路由是 URL的請(qǐng)求地址和服務(wù)器上面的資源對(duì)應(yīng),根據(jù)不同的請(qǐng)求地址返回不同的資源

前端路由

在SPA(單頁(yè)應(yīng)用)中根據(jù)用戶所觸發(fā)的事件改變了URL 在無(wú)需刷新的前提下 顯示不同的頁(yè)面內(nèi)容,比如等下就要講的Vue Router

2.Vue-Router最基礎(chǔ)的使用步驟

2.1.引入Vue-Router文件

<!-- 使用vue router前提 vue 必不可少 --><script src=nnzzn/skin/m04blueskin/image/nopic.gif 引入vue-router文件 --><script src=nnzzn/skin/m04blueskin/image/nopic.gif>

2.2.在頁(yè)面上添加 router-link 和 router-view

<!-- 添加路由 --><!-- 會(huì)被渲染為 <a href="#/home"></a> --><router-link to="/home">Home</router-link><router-link to="/login">Login</router-link><!-- 展示路由的內(nèi)容 --><router-view></router-view>

2.3.創(chuàng)建路由組件

//創(chuàng)建路由組件const home = { template: `<div>歡迎來(lái)到{{name}}</div>`,  data() {   return {    name: '首頁(yè)',   }  },}const login = { template: `  <div>歡迎來(lái)到登錄頁(yè)</div>`,}

2.4.配置路由規(guī)則

// 配置路由規(guī)則const router = new VueRouter({ routes: [  //每一個(gè)路由規(guī)則都是一個(gè)對(duì)象  //path 路由的 hash地址  //component 路由的所展示的組件  {   path: '/',   // 當(dāng)訪問(wèn) '/'的時(shí)候 路由重定向 到新的地址 '/home'   redirect: '/home',   },        {     path: '/home',     component: home,    },    {     path: '/login',     component: login,    },   ],})

2.5.掛載路由

let vm = new Vue({ el: '#app', data: {}, methods: {},  // 掛載到vue 上面 router, })

3.嵌套路由

這里的嵌套路由是基于上面的例子繼續(xù)寫的

3.1.在路由里面添加 子路由鏈接和 占位符

//創(chuàng)建路由組件const home = {    template: `    <div>    歡迎來(lái)到首頁(yè)    <br>    <!-- 子路由鏈接 -->    <router-link to="/tab1">Tab1</router-link>    <router-link to="/tab2">Tab2</router-link>    <!-- 子路由展示 -->    <router-view></router-view>    </div>}復(fù)制代碼

3.2.添加路由組件

// 創(chuàng)建兩個(gè)子路由組件const tab1 = {    template: `    <div>    子路由1    </div>    `,}const tab2 = {    template: `    <div>    子路由2    </div>    `,}復(fù)制代碼

3.3.配置路由規(guī)則

// 配置路由規(guī)則const router = new VueRouter({    routes: [        {            path: '/home',            component: home,            //children 表示子路由規(guī)則            children: [                { path: '/tab1', component: tab1 },                { path: '/tab2', component: tab2 },            ],        },    ],})復(fù)制代碼

4.動(dòng)態(tài)路由

path屬性加上/:id 使用route對(duì)象的params.id獲取動(dòng)態(tài)參數(shù)

比如現(xiàn)在有這么多個(gè)路由,如果自己也配置多個(gè)路由,豈不是有點(diǎn)。。。多余

<div id="app">    <!-- 添加路由 -->    <!-- 會(huì)被渲染為 <a href="#/home"></a> -->    <router-link to="/goods/1">goods1</router-link>    <router-link to="/goods/2">goods2</router-link>    <router-link to="/goods/3">goods3</router-link>    <router-link to="/goods/4">goods4</router-link>    <!-- 展示路由的內(nèi)容 -->    <router-view></router-view></div>

然后這里就可以使用 動(dòng)態(tài)路由來(lái)解決

<script>    //創(chuàng)建路由組件    const goods = {        // this.$route.parms.id 可以省略 this        template: `        <div>歡迎來(lái)到商品 {{$route.params.id}}頁(yè)</div>        `,        }    // 配置路由規(guī)則    const router = new VueRouter({        routes: [            {                // 加上`/:id`                path: '/goods/:id',                component: goods,            },        ],    })    let vm = new Vue({        el: '#app',        data: {},        methods: {},        // 掛載到vue 上面        router,    })</script>

最后提一下還可以用query進(jìn)行傳參.

// 比如<router-link to="/goods?id=1">goods</router-link>復(fù)制代碼

然后使用this.$route.query.id就可以在路由組件中獲取到id

添加動(dòng)態(tài)路由

使用 this.$router.addRoutes([]) 可以添加動(dòng)態(tài)路由,里面?zhèn)鬟f是一個(gè)數(shù)組 和 routes里面一樣

5.路由傳參

我們可以使用 props 進(jìn)行傳值

為啥要用 props 進(jìn)行傳值,route不香了嗎,確實(shí)route 不夠靈活

props 值有三種情況

5.1.布爾值類型

//創(chuàng)建路由組件const goods = {    // 使用props接收    props: ['id'],    template: `    <div>歡迎來(lái)到商品 {{id}}頁(yè)</div>    `,}// 配置路由規(guī)則const router = new VueRouter({    routes: [        {            path: '/goods/:id',            component: goods,            //props為true, route.params將會(huì)被設(shè)置為組件屬性            props: true,        },    ],})復(fù)制代碼

5.2.對(duì)象類型

但是這里就獲取不到 id 了,會(huì)報(bào)錯(cuò)

這里的id 需要 $route.params.id 獲取

const goods = {    // 使用props接收    props: ['name', 'info', 'id'],    // 這里的 id 是獲取不到的    template: `    <div>{{info}}來(lái)到{{name}} {{id}}頁(yè)</div>    `,}// 配置路由規(guī)則const router = new VueRouter({    routes: [        {            path: '/goods/:id',            component: goods,            //props為對(duì)象 就會(huì)把這個(gè)對(duì)象傳遞的路由組件            //路由組件使用props接收            props: {                name: '商品',                info: '歡迎',            },        },    ],})復(fù)制代碼

5.3.函數(shù)

const goods = {    // 使用props接收    props: ['name', 'info', 'id'],    template: `    <div>{{info}}來(lái)到{{name}} {{id}}頁(yè)</div>    `,}// 配置路由規(guī)則const router = new VueRouter({    routes: [        {            path: '/goods/:id',            component: goods,            //prop是一個(gè)函數(shù)的話 就可以組合傳值            props: (route) => {                return {                    name: '商品',                    info: '歡迎',                    id: route.params.id,                }            },        },    ],})復(fù)制代碼

6.route 和 router

在上面提到了route 那么和 router有什么區(qū)別呢

  • route為當(dāng)前router跳轉(zhuǎn)對(duì)象里面可以獲取path,params,hash,query,fullPath,matched,name
  • router為VueRouter實(shí)例用 new VueRouter創(chuàng)建的實(shí)例,想要導(dǎo)航到不同URL,則使用router.push方法
  • routes是router路由實(shí)例用來(lái)配置路由對(duì)象(順帶提一下)

    7.命名路由

    路由組件

    //創(chuàng)建路由組件const goods = {    // 使用props接收    props: ['id'],    template: `    <div>商品{{id}}頁(yè)</div>    `,}復(fù)制代碼

    路由配置

    //配置路由const router = new VueRouter({    routes: [        {            path: '/goods/:id',            // 命名路由            name: 'goods',            component: goods,        },    ],})復(fù)制代碼

    綁定 :to 通過(guò)name找到定義的路由 還可以使用 params 傳遞參數(shù)

    <router-link :to="{name: 'goods', params: { id: 1 } }">goods1</router-link><!-- 展示路由的內(nèi)容 --><router-view></router-view>復(fù)制代碼

    8.編程式導(dǎo)航

    8.1.聲明式導(dǎo)航

    既然提到了編程式導(dǎo)航,那么先簡(jiǎn)單說(shuō)下聲明式導(dǎo)航

    上面所展示的都是聲明是導(dǎo)航 比如router-link

    <router-link to="/goods/1">goods1</router-link>

    還有a標(biāo)簽

    <a href="#/goods/1">goods1</a>

    8.2.編程式導(dǎo)航

    使用javascript來(lái)控制路由跳轉(zhuǎn)

    在普通的網(wǎng)頁(yè)中使用 loaction.href window.open 等等進(jìn)行跳轉(zhuǎn)

    現(xiàn)在我要介紹的是Vue Router中的編程式導(dǎo)航

    我們平時(shí)都是用router.push() **router.go(n)**方法進(jìn)行跳轉(zhuǎn)

    //字符串this.$router.push('/home')//對(duì)象this.$ruter.push({path:'/home'})//比如這個(gè) /goods?id=1this.$router.push({path:'/goods',query:{id:'1'}})//命名路由 /goods/1this.$router.push({name:'goods',params:{id:1}})//后退this.$router.go(-1)復(fù)制代碼

    9.路由守衛(wèi)

    9.1.全局守衛(wèi)

    router.beforeEach 全局守衛(wèi) 對(duì)所有的路由都起作用

    router.beforeEach((to, from, next) => {       next();//使用時(shí),千萬(wàn)不能漏寫next!!!    }).catch(()=>{      //跳轉(zhuǎn)失敗頁(yè)面      next({ path: '/error', replace: true, query: { back: false }}    )})復(fù)制代碼

    全局的守衛(wèi)的三個(gè)參數(shù)

    to: 即將要進(jìn)入的目標(biāo) 路由對(duì)象

    from: 當(dāng)前導(dǎo)航正要離開 路由對(duì)象

    next: 參數(shù)不同做的事也不同

    next() 直接進(jìn)入下一個(gè)鉤子

    next(false) 停止當(dāng)前導(dǎo)航

    next('/路徑') 跳轉(zhuǎn)到path路由地址 當(dāng)然這里面也可以寫成對(duì)象形式 next({path : '/路徑'}) next(error): 如果傳入?yún)?shù)是一個(gè) Error 實(shí)例,則導(dǎo)航會(huì)被終止且該錯(cuò)誤會(huì)被傳遞給 router.onError()

    9.2.路由獨(dú)享的守衛(wèi)

    beforeEnter 路由對(duì)象獨(dú)享的守衛(wèi)寫在routes里面

    const router = new VueRouter({  routes: [    {      path: '/goods',      component: goods,      beforeEnter: (to, from, next) => {        // 一樣的用法      }    }  ]})復(fù)制代碼

    9.3.組件內(nèi)的守衛(wèi)(了解)

    組件內(nèi)的守衛(wèi) 寫在組件內(nèi)部 下面是官方介紹

  • beforeRouteEnter 進(jìn)入路由前,組件還沒(méi)有被實(shí)例化所以這里無(wú)法獲取到this
  • beforeRouteUpdate (2.2) 這個(gè)階段可以獲取this,在路由復(fù)用同一個(gè)組件時(shí)觸發(fā)
  • beforeRouteLeave 這個(gè)階段可以獲取this,當(dāng)離開組件對(duì)應(yīng)的路由時(shí),此時(shí)可以用來(lái)保存數(shù)據(jù),或數(shù)據(jù)初始化,或關(guān)閉定時(shí)器等等
    const goods = {  template: `<div>goods</div>`,  beforeRouteEnter (to, from, next) {    // 具體邏輯  },  beforeRouteUpdate (to, from, next) {    // 具體邏輯  },  beforeRouteLeave (to, from, next) {    // 具體邏輯  }}復(fù)制代碼

    10.組件緩存keep-alive

    頁(yè)面重新加載會(huì)重新渲染頁(yè)面比如回退的時(shí)候等等,我們有的組件它不是一個(gè)活動(dòng)的(數(shù)據(jù)不變)不希望它被重新渲染,所以這里就可以使用 <keep-alive> </keep-alive> 包裹起來(lái),這樣就不會(huì)觸發(fā)created鉤子

    應(yīng)用場(chǎng)景:獲取一個(gè)商品的詳情然后回退在前進(jìn)的時(shí)候就使用緩存,提高性能

    10.1.不使用 keep-alive例子

    這里home 組件在created進(jìn)行打印當(dāng)前的時(shí)間

    <div id="app">    <router-link to="/home">home</router-link><router-link to="/login">login</router-link><router-view></router-view></div>復(fù)制代碼
    <script>      const login = {        template: `        <div>Login</div>        `,      }      const home = {        template: `        <div>Home</div>        `,        created() {          console.log(new Date())        },      }      const router = new VueRouter({        routes: [          {            path: '/',            redirect: '/home',          },          {            path: '/home',            component: home,          },          {            path: '/login',            component: login,          },        ],      })      let vm = new Vue({        el: '#app',        data: {},        methods: {},        router,      })  </script>復(fù)制代碼

    如上,每切換home 的路由 組件就會(huì)重新渲染,打印當(dāng)前的時(shí)間

    如果使用 keep-alive 會(huì)有什么效果呢

    10.2.使用keep-alive

    這里只需簡(jiǎn)單的包裹起來(lái)就行了

    <div id="app">    <router-link to="/home">home</router-link>    <router-link to="/login">login</router-link>    <keep-alive>        <router-view></router-view>    </keep-alive></div>復(fù)制代碼

    可以看到的是只打印一次,說(shuō)明切換了路由它并沒(méi)有重新渲染組件

    當(dāng)然可以在 組件內(nèi)取個(gè)name名字 keep-alive 標(biāo)簽里面添加 include 屬性就可以對(duì)相應(yīng)的組件進(jìn)行緩存

    const login = {    name: login,    template: `    <div>Login</div>    `,}const home = {    name: home,    template: `    <div>Home</div>    `,    created() {    console.log(new Date())    },}復(fù)制代碼
    <div id="app">    <router-link to="/home">home</router-link>    <router-link to="/login">login</router-link>    <keep-alive include="login,home">        <router-view></router-view>    </keep-alive></div>復(fù)制代碼

    10.3.activated 和 deactivated

    keep-alive 生命周期執(zhí)行順序

    第一次訪問(wèn)路由時(shí):

  • created-->mounted -->activated
  • deactivated在退出后觸發(fā)

    以后進(jìn)入只會(huì)觸發(fā) activated

    11.hash 和 history 模式

    11.1.hash模式

    在vue-router中默認(rèn)使用的是 hash 模式

    hash是url中的錨點(diǎn)就是**#,通過(guò)錨點(diǎn)作為路由地址,我們通常改變的是改變#**后面部分,實(shí)現(xiàn)瀏覽器渲染指定的組件.,錨點(diǎn)發(fā)生改變會(huì)觸發(fā) onhashchange 事件

    11.2.history模式

    history 模式就是平時(shí)正常的地址,使用方面需要服務(wù)器支持

    如果訪問(wèn)的路徑資源沒(méi)有 直接就是 404

    在HTML5后新增了兩個(gè)API

    pushState(): IE10后支持

    replaceState()

    在vue-router中如果要使用 history 模式需要指定

    const router = new VueRouter({  mode: 'history'})復(fù)制代碼

    實(shí)現(xiàn)一個(gè)基礎(chǔ) Vue Router

    復(fù)習(xí)上面的路由的基礎(chǔ)那么我們不如來(lái)寫個(gè)Vue Router吧

    實(shí)現(xiàn)的這個(gè) Vue Router是基于 history模式

    所有的步驟都放到代碼的注釋中,每一行都寫個(gè)注釋

    這個(gè)簡(jiǎn)單的沒(méi)有按照Vue Router源碼來(lái)寫主要是一些基礎(chǔ)功能的實(shí)現(xiàn)

    為后面的按照源碼寫打基礎(chǔ)

    1.注冊(cè)全局Vue Router

    首先就是先注冊(cè)自己的 Vue Router

    判斷是否注冊(cè)了組件

    在Vue實(shí)例創(chuàng)建完成進(jìn)行注冊(cè)

    // 保存一個(gè)全局變量 Vuelet _Vue = null// 默認(rèn)導(dǎo)出自己寫的 VueRouterexport default class MyVueRouter {  // 實(shí)現(xiàn)install 注冊(cè) MyVueRouter vue提供install可供我們開發(fā)新的插件及全局注冊(cè)組件等  // 把Vue傳進(jìn)去  static install(Vue) {    // 定義一個(gè)標(biāo)識(shí)判斷是否注冊(cè)了 MyVueRouter ,注冊(cè)了就不用下一步了    if (MyVueRouter.install.installed) return    // 沒(méi)有就進(jìn)行下面的,把標(biāo)識(shí)改變true    MyVueRouter.install.installed = true    // 把全局變量 _Vue 保存    _Vue = Vue    // 為了獲取Vue中的this執(zhí)行這里使用 混入    _Vue.mixin({      // 在Vue實(shí)例創(chuàng)建好的時(shí)候進(jìn)行操做      beforeCreate() {        // 判斷是否是實(shí)例創(chuàng)建還是組件創(chuàng)建 ,可以判斷是否掛載 了router        if (this.$options.router) {          // 把router注冊(cè)到 _Vue上          _Vue.prototype.$router = this.$options.router        }      },    })  }}復(fù)制代碼

    2.實(shí)現(xiàn) 構(gòu)造方法

    optoins 保存?zhèn)魅氲囊?guī)則

    routerMap 確定地址和組件的關(guān)系

    current 表示當(dāng)前的地址是響應(yīng)式的之后渲染組件和它相關(guān)

    export default class MyVueRouter {  ...  //實(shí)現(xiàn)構(gòu)造  constructor(optoins) {    // 這個(gè)保存的是  routes    this.optoins = optoins    // routerMap 保存路由和 組件之間的關(guān)系    this.routerMap = {}    // 用來(lái)記錄數(shù)據(jù) 這里面的數(shù)據(jù)都是 響應(yīng)式    this.data = _Vue.observable({      // 當(dāng)前路由的地址      current: '/',    })  }}復(fù)制代碼

    3.解析路由規(guī)則

    傳入的路由規(guī)則拿到一個(gè)對(duì)象里 地址 和 組件一一匹配

    export default class MyVueRouter {  ...  // 解析路由規(guī)則  createRouterMap() {    // 把之前構(gòu)造函數(shù)的中的傳入的 routes 規(guī)則進(jìn)行遍歷    this.optoins.routes.forEach((item) => {      // 把路由 和 組件的對(duì)應(yīng)關(guān)系添加到 routerMap中      this.routerMap[item.path] = itemponent    })  }}復(fù)制代碼

    4.實(shí)現(xiàn) router-link 組件

    router-link就是頁(yè)面上所展示的路由鏈接

    因?yàn)橐话闶褂玫幕径际沁\(yùn)行版的Vue 所以自己把組件轉(zhuǎn)為 虛擬DOM

    還有就是鏈接會(huì)刷新的問(wèn)題

    自己寫個(gè)函數(shù)進(jìn)行跳轉(zhuǎn)阻止默認(rèn)事件

    還得注意對(duì)應(yīng)的路由所要渲染的組件

    export default class MyVueRouter {  ...  // 實(shí)現(xiàn)組件  initComponents(Vue) {    // 實(shí)現(xiàn) router-link組件    Vueponent('router-link', {      props: {        // router-link上面的to屬性將訪問(wèn)的地址        to: String,      },      // 由于運(yùn)行版的Vue不能渲染template所以這里重新寫個(gè)render 這里h 也是個(gè)函數(shù)      // template: `<a :href="to"><slot></slot></a>`,      render(h) {        // 第一個(gè)參數(shù)是標(biāo)簽        return h(          'a',          // 第二個(gè)參數(shù)是對(duì)象是 tag 里面的屬性          {            // 設(shè)置屬性            attrs: {              href: this.to,            },            // 綁定事件            on: {              // 重新復(fù)寫點(diǎn)擊事件,不寫的話會(huì)點(diǎn)擊會(huì)向服務(wù)器發(fā)送請(qǐng)求刷新頁(yè)面              click: this.myClick,            },          },          // 這個(gè)是標(biāo)簽里面的內(nèi)容 這里渲染是 默認(rèn)插槽          [this.$slots.default]        )      },      methods: {        //router-link的點(diǎn)擊事件        myClick(e) {          // 因?yàn)槲疫@里是模擬是 history的路由所以用pushState ,hash路由可以這里用 push          // 使用history修改瀏覽器上面的地址          // pushState 第一個(gè)參數(shù)是傳遞的參數(shù),第二個(gè)是標(biāo)題,第三個(gè)是鏈接          history.pushState({}, '', this.to)          // 渲染相應(yīng)的組件          // 渲染的頁(yè)面也需要改變 data中的current是響應(yīng)式的 router-view是根據(jù)current來(lái)渲染的          this.$router.data.current = this.to          // 阻止默認(rèn)跳轉(zhuǎn)事件          e.preventDefault()        },      },    })復(fù)制代碼

    5.實(shí)現(xiàn) router-view 組件

    這里從之前解析的規(guī)則里面拿到當(dāng)前的對(duì)應(yīng)的組件進(jìn)行轉(zhuǎn)為虛擬DOM

    最后router-view占位渲染到頁(yè)面上

    export default class MyVueRouter {  ...  // 實(shí)現(xiàn)組件  initComponents(Vue) {    // 實(shí)現(xiàn) router-view組件    Vueponent('router-view', {      render(h) {        // 獲取的當(dāng)前路徑所對(duì)應(yīng)的組件        // 因?yàn)楫?dāng)前this是Vue,this.$router才是MyVueRouter        const component = this.$router.routerMap[this.$router.data.current]        // 轉(zhuǎn)化為虛擬Dom        return h(component)      },    })  }}復(fù)制代碼

    6.前進(jìn)和后退

    在完成之前的編寫還是不夠的,因?yàn)樵跒g覽器點(diǎn)后退和前進(jìn)雖然改變了瀏覽器的地址,但是組件卻沒(méi)有刷新,下面就來(lái)解決這個(gè)問(wèn)題

    export default class MyVueRouter {  ...  // 初始化事件  initEvent() {    // 監(jiān)聽瀏覽器地址的改變    window.addEventListener('popstate', () => {      // 改變VueRouter的當(dāng)前的地址 重新渲染組件      this.data.current = window.location.pathname    })  }}復(fù)制代碼

    7.在router掛載后進(jìn)行初始化

    最后寫個(gè)函數(shù)進(jìn)行初始化

    在router注冊(cè)到Vue之后進(jìn)行 初始化

    export default class MyVueRouter {  // 初始化  init() {    // 解析路由規(guī)則    this.createRouterMap()    // 初始化組件    this.initComponents(_Vue)    // 初始化事件    this.initEvent()  }      static install(Vue) {    if (MyVueRouter.install.installed) return    MyVueRouter.install.installed = true    _Vue = Vue    _Vue.mixin({      beforeCreate() {        if (this.$options.router) {          _Vue.prototype.$router = this.$options.router          // 注冊(cè)完router后進(jìn)行初始化          this.$options.router.init()        }      },    })    }  ...}復(fù)制代碼

    8.放上完整的 index.js

    // 保存一個(gè)全局變量 Vuelet _Vue = nullexport default class MyVueRouter {  // 實(shí)現(xiàn)install 注冊(cè) MyVueRouter vue提供install可供我們開發(fā)新的插件及全局注冊(cè)組件等  // 把Vue傳進(jìn)去  static install(Vue) {    // 定義一個(gè)標(biāo)識(shí)判斷是否注冊(cè)了 MyVueRouter ,注冊(cè)了就不用下一步了    if (MyVueRouter.install.installed) return    // 沒(méi)有就進(jìn)行下面的,把標(biāo)識(shí)改變true    MyVueRouter.install.installed = true    // 把全局變量 _Vue 保存    _Vue = Vue    // 為了獲取Vue中的this執(zhí)行這里使用 混入    _Vue.mixin({      // 在Vue實(shí)例創(chuàng)建好的時(shí)候進(jìn)行操做      beforeCreate() {        // 判斷是否是實(shí)例創(chuàng)建還是組件創(chuàng)建 ,可以判斷是否掛載 了router        if (this.$options.router) {          // 把router注冊(cè)到 _Vue上          _Vue.prototype.$router = this.$options.router          // 注冊(cè)完router后進(jìn)行初始化          this.$options.router.init()        }      },    })    // 判斷是否掛載  }  // 實(shí)現(xiàn)構(gòu)造方法  constructor(optoins) {    // 這個(gè)保存的是  routes    this.optoins = optoins    // routerMap 保存路由和 組件之間的關(guān)系    this.routerMap = {}    // 用來(lái)記錄數(shù)據(jù) 這里面的數(shù)據(jù)都是 響應(yīng)式    this.data = _Vue.observable({      // 當(dāng)前路由的地址      current: '/',    })  }  // 解析路由規(guī)則  createRouterMap() {    // 把之前構(gòu)造函數(shù)的中的傳入的 routes 規(guī)則進(jìn)行遍歷    this.optoins.routes.forEach((item) => {      // routes中的每一項(xiàng)都是一個(gè)對(duì)象 { path: '/XXX', component: XXX}      // 把路由 和 組件的對(duì)應(yīng)關(guān)系添加到 routerMap中      this.routerMap[item.path] = itemponent    })  }  // 實(shí)現(xiàn)組件  initComponents(Vue) {    // 實(shí)現(xiàn) router-link組件    Vueponent('router-link', {      props: {        // router-link上面的to屬性將訪問(wèn)的地址        to: String,      },      // 由于運(yùn)行版的Vue不能渲染template所以這里重新寫個(gè)render 這里h 也是個(gè)函數(shù)      // template: `<a :href="to"><slot></slot></a>`,      render(h) {        // 第一個(gè)參數(shù)是標(biāo)簽        return h(          'a',          // 第二個(gè)參數(shù)是對(duì)象是 tag 里面的屬性          {            // 設(shè)置屬性            attrs: {              href: this.to,            },            // 綁定事件            on: {              // 重新復(fù)寫點(diǎn)擊事件,不寫的話會(huì)點(diǎn)擊會(huì)向服務(wù)器發(fā)送請(qǐng)求刷新頁(yè)面              click: this.myClick,            },          },          // 這個(gè)是標(biāo)簽里面的內(nèi)容 這里渲染是 默認(rèn)插槽          // 比如<router-link to="/">首頁(yè)</router-link>          // 插槽就是給首頁(yè)兩個(gè)字留位置,當(dāng)前這只是個(gè)例子          [this.$slots.default]        )      },      methods: {        //router-link的點(diǎn)擊事件        myClick(e) {          // 因?yàn)槲疫@里是模擬是 history的路由所以用pushState ,hash路由可以這里用 push          // 使用history修改瀏覽器上面的地址          // pushState 第一個(gè)參數(shù)是傳遞的參數(shù),第二個(gè)是標(biāo)題,第三個(gè)是鏈接          history.pushState({}, '', this.to)          // 渲染相應(yīng)的組件          // 渲染的頁(yè)面也需要改變 data中的current是響應(yīng)式的 router-view是根據(jù)current來(lái)渲染的          this.$router.data.current = this.to          // 阻止默認(rèn)跳轉(zhuǎn)事件          e.preventDefault()        },      },    })    // 實(shí)現(xiàn) router-view組件    Vueponent('router-view', {      render(h) {        // 獲取的當(dāng)前路徑所對(duì)應(yīng)的組件        // 因?yàn)楫?dāng)前this是Vue,this.$router才是MyVueRouter        const component = this.$router.routerMap[this.$router.data.current]        // 轉(zhuǎn)化為虛擬Dom        return h(component)      },    })  }  // 初始化事件  initEvent() {    // 監(jiān)聽瀏覽器地址的改變    window.addEventListener('popstate', () => {      // 改變VueRouter的當(dāng)前的地址 重新渲染組件      this.data.current = window.location.pathname    })  }  // 初始化  init() {    // 解析路由規(guī)則    this.createRouterMap()    // 初始化組件    this.initComponents(_Vue)    // 初始化事件    this.initEvent()  }}復(fù)制代碼

    到了這里基礎(chǔ)的實(shí)現(xiàn)功能差不多了,上面的例子是為了下面打基礎(chǔ),所有的功能實(shí)現(xiàn)基本都是在一個(gè)文件下很不嚴(yán)謹(jǐn),下面就嚴(yán)格按照Vue Router 源碼來(lái)實(shí)現(xiàn)自己 Vue Router

    Vue Router實(shí)現(xiàn)

    經(jīng)過(guò)上面簡(jiǎn)單的實(shí)現(xiàn),現(xiàn)在我們按照Vue Router源碼的方式進(jìn)行編寫

    1.首先是Vue Router 構(gòu)造

    // 導(dǎo)出自己寫的 VueRouterexport default class VueRouter {  // 實(shí)現(xiàn)構(gòu)造函數(shù)功能  constructor(options) {    // 獲取options中的routes路由規(guī)則 沒(méi)有就為空數(shù)組    this._options = options.routes || []  }  // 初始化  init(Vue) {}}復(fù)制代碼

    2.注冊(cè)組件 install

    在 install.js 對(duì)自己寫的Vue-Router進(jìn)行全局的注冊(cè)

    之后還會(huì)在這里創(chuàng)建 router????router** **router????route

    還有注冊(cè) router-link router-view

    // 定義一個(gè)全局 的Vueexport let _Vue = null// 導(dǎo)出 install方法export default function install(Vue) {  // 保存到全局的Vue  _Vue = Vue  // 混入  _Vue.mixin({    // Vue實(shí)例創(chuàng)建完畢之后操做    beforeCreate() {      // 這里是new Vue      if (this.$options.router) {        // 保存 Vue        this._routerRoot = this        // 保存 Vue Router 的實(shí)例,以后可以通過(guò)Vue Router構(gòu)造的一些方法        this._router = this.$options.router        // 調(diào)用Vue Router的init(Vue) 初始化操做        this._router.init(this)      } else {        // 這里是創(chuàng)建 Vue的組件等等        // 判斷是否有父組件 ,有的話就把父組件的 _roterRoot(也就是Vue)給 子組件        // 沒(méi)有父組件就把 this 這是也是(Vue) 給子組件        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this      }    },  })}復(fù)制代碼

    然后在 index.js中導(dǎo)入install 進(jìn)行為構(gòu)造添加 install

    // 導(dǎo)入 installimport install from './install'// 導(dǎo)出自己寫的 VueRouterexport default class VueRouter {...}    // 為VueRouter 添加 install方法VueRouter.install = install復(fù)制代碼

    3.編寫 create-route-map.js

    這個(gè)主要的作用就是用來(lái)解析傳遞過(guò)來(lái)的路由 需要導(dǎo)出然后在 create-matcher.js進(jìn)行使用

    具體的細(xì)節(jié)都寫了注釋

    // 導(dǎo)出具體的路由解析export default function createRouteMap(routes, oldPathList, oldPathMap) {  // 傳入了就是添加動(dòng)態(tài)路由 沒(méi)有傳入就默認(rèn)為空  const pathList = oldPathList || []  const pathMap = oldPathMap || []  // 遍歷規(guī)則操作  routes.forEach((route) => {    // 記錄路由 也是核心的解析路由 為了分工明確寫的外面    addRouteRecord(route, pathList, pathMap)  })  // 返回新的路由列表 和 路由對(duì)應(yīng)關(guān)系  return {    pathList,    pathMap,  }}function addRouteRecord(route, pathList, pathMap, parentRecord) {  // 路由地址 判斷是否存在父級(jí)的路由 有的話拼接父級(jí)路由和當(dāng)前路由的path 沒(méi)有就是當(dāng)前route.path  const path = parentRecord ? `${parentRecord.path}/${route.path}` : route.path  // record作為一個(gè)路由記錄 記錄了路由地址,組件,父級(jí)路由   用于路由對(duì)應(yīng)關(guān)系去對(duì)應(yīng)相對(duì)應(yīng)的path  const record = {    path,    component: routeponent,    parent: parentRecord,  }  // 判斷是否在路由列表中 存在當(dāng)前路由,不存在進(jìn)行添加當(dāng)前路由,更新路由列表  if (!pathList[path]) {    // 向路由列表中添加路由    pathList.push(path)    // 向路由對(duì)應(yīng)關(guān)系中 添加path 相對(duì)應(yīng)的記錄    pathMap[path] = record  }  // 判斷當(dāng)前的 路由是否有子路由,有的話進(jìn)行遞歸  if (route.children) {    route.children.forEach((childRoute) => {      // 就簡(jiǎn)單說(shuō)下最后一個(gè)參數(shù) 就是父級(jí)路由記錄      addRouteRecord(childRoute, pathList, pathMap, record)    })  }}復(fù)制代碼

    4.編寫 create-matcher.js

    這個(gè)模塊的意義也是解析路由不過(guò)這個(gè)是個(gè)指揮家,上面實(shí)現(xiàn)的是具體解析操作

    在這個(gè)模塊里進(jìn)行調(diào)用上面的具體解析路由的方法就行了

    有了上面面具體的路由解析,這個(gè)create-matcher.js就容易實(shí)現(xiàn)了,只需要簡(jiǎn)單的調(diào)用它即可

    這個(gè)模塊返回了兩個(gè)方法

    match : 根據(jù)路由路徑創(chuàng)建路由規(guī)則對(duì)象,之后就可以通過(guò) 規(guī)則對(duì)象獲取到所有的路由信息然后拿到所有的組件進(jìn)行創(chuàng)建

    addRoutes : 添加動(dòng)態(tài)路由

    // 導(dǎo)入具體的路由解析規(guī)則import createRouteMap from './create-route-map'// 導(dǎo)出解析路由規(guī)則 傳入的是規(guī)則export default function createMatcher(router) {  // pathList 路由的列表  pathMap 路由與組件的對(duì)應(yīng)關(guān)系 nameMap這里沒(méi)有考慮,先完成個(gè)簡(jiǎn)單的  // 具體的解析規(guī)則是使用  createRouteMap  const { pathList, pathMap } = createRouteMap(router)  // match是 從pathMap 根據(jù)path獲取 相應(yīng)的路由記錄  function match(path) {      //待實(shí)現(xiàn)  }  // 添加動(dòng)態(tài)路由  function addRoutes(router) {    // 添加動(dòng)態(tài)路由肯定也要解析路由規(guī)則    createRouteMap(router, pathList, pathMap)  }  // 返回match 和 addRoutes  return {    match,    addRoutes,  }}復(fù)制代碼

    然后在index.js也就是Vue Router的構(gòu)造中使用 createMatcher. 使用this.matcher接收

    // 導(dǎo)入 installimport install from './install'// 導(dǎo)入解析路由import createMatcher from './create-matcher'// 導(dǎo)出自己寫的 VueRouterexport default class VueRouter {  // 實(shí)現(xiàn)構(gòu)造函數(shù)功能  constructor(options) {    // 獲取options中的routes路由規(guī)則 沒(méi)有就為空數(shù)組    this._routes = options.routes || []    // 解析路由 傳入規(guī)則 這里還返回了兩個(gè)方法 match,addRoutes 用matcher接收一下之后有用    this.matcher = createMatcher(this._routes)  }  // 初始化  init(Vue) {}}// 為VueRouter 添加 install方法VueRouter.install = install復(fù)制代碼

    5.編寫 createMatcher

    看見上面在 createMatcher中定義了 一個(gè)match了嗎,

    match是 從pathMap 根據(jù)path獲取 相應(yīng)的路由記錄

    上面還沒(méi)有去實(shí)現(xiàn),現(xiàn)在來(lái)實(shí)現(xiàn)它

    需要實(shí)現(xiàn)它的話還需要編寫個(gè) createRoute 方法,我這里寫在 uitl/route.js模塊里

    // 導(dǎo)出 createRouteexport default function createRoute(record, path) {  // 保存路由的記錄 里面可能有多個(gè)路由 是這種模式保存 [parentRecord,childRecord]  const matched = []  // 判斷是否是子路由  // 下面 record = record.parent 在不斷向上找parent有繼續(xù)執(zhí)行  // 沒(méi)有就直接return 下面的對(duì)象  while (record) {    // 循環(huán)得到的 record不斷插入到 數(shù)組的最前面    matched.unshift(record)    // 把父記錄給當(dāng)前record 繼續(xù)循環(huán)    record = record.parent  }  // 返回path 和 matched 以便之后 router-view渲染  return {    path,    matched,  }}復(fù)制代碼

    上面編寫了 createRoute方法我們就可以在 create-mathcer.js 調(diào)用 來(lái)獲取到記錄了

    然后再 create-mathcer.js中繼續(xù) 完善 match方法

    // 導(dǎo)入具體的路由解析規(guī)則import createRouteMap from './create-route-map'// 導(dǎo)入 createRouteimport createRoute from './util/route'// 導(dǎo)出解析路由規(guī)則 傳入的是規(guī)則export default function createMatcher(router) {  // pathList 路由的列表  pathMap 路由與組件的對(duì)應(yīng)關(guān)系 nameMap這里沒(méi)有考慮,先完成個(gè)簡(jiǎn)單的  // 具體的解析規(guī)則是使用  createRouteMap  const { pathList, pathMap } = createRouteMap(router)  // match是 從pathMap 根據(jù)path獲取 相應(yīng)的路由記錄  function match(path) {    // 取出path對(duì)應(yīng)的記錄    const record = pathMap[path]    // 判斷記錄是否存在    if (record) {      return createRoute(record, path)    }    return createRoute(null, path)  }  // 添加動(dòng)態(tài)路由  function addRoutes(router) {    // 添加動(dòng)態(tài)路由肯定也要解析路由規(guī)則    createRouteMap(router, pathList, pathMap)  }  // 返回match 和 addRoutes  return {    match,    addRoutes,  }}復(fù)制代碼

    6.歷史記錄的處理 History

    在 history目錄下新建一個(gè) base模塊用來(lái)編寫 父類

    這個(gè)父類有 hash 模式 和 history(html5) 模式共同的方法

    這里就主要演示下 hash 模式的代碼

    // 導(dǎo)入 我們上面寫好的 createRouteimport createRoute from '../util/route'// 導(dǎo)出 Historyexport default class History {  // router 是路由對(duì)象 也就是 VUe-Router的一個(gè)實(shí)例  constructor(router) {    // 賦值給自己的 router    this.router = router    // 默認(rèn)的的當(dāng)前路徑為 /    this.current = createRoute(null, '/')  }  // 將要跳轉(zhuǎn)的鏈接  // path 是路由的地址, onComplete是一個(gè)回調(diào)  transitionTo(path, onComplete) {    // 獲取當(dāng)前的應(yīng)該跳轉(zhuǎn)的路由  調(diào)用的是 Vue-Router中 this.matcher中收到的match方法    // 在這里 this.router就是 Vue-Router的一個(gè)實(shí)例 所以寫成    // this.router.matcher.match(path)    this.current = this.router.matcher.match(path)    // 回調(diào)存在觸發(fā)回調(diào)    onComplete && onComplete()  }}復(fù)制代碼

    編寫 HashHistory 模式 繼承 History

    // 導(dǎo)入 base中的 Historyimport History from './base'// 繼承了 Historyexport default class HashHistory extends History {  constructor(router) {    super(router)    // 確保第一次訪問(wèn)的時(shí)候路由加上 #/    ensuerSlash()  }  // 監(jiān)聽URL的改變 設(shè)置當(dāng)前的current  setUpListener() {    // 監(jiān)聽 hash的變化    window.addEventListener('hashchange', () => {      // 改變 this.current      this.transitionTo(this.getCurrentLocation())    })  }  // 獲取當(dāng)前的URL的hash 當(dāng)然這里要去除 #  getCurrentLocation() {    // 這里不建議寫成這個(gè) return window.location.hash.slice(1) 有兼容問(wèn)題    let href = window.location.href    const index = href.indexOf('#')    // 當(dāng)沒(méi)有 #的時(shí)候 直接返回 空字符串    if (index < 0) return ''    // 獲取 #后面的地址    href = href.slice(index + 1)    return href  }}// 確保第一次加上 #/function ensuerSlash() {  // 如果存在 hash的話就不行加 /  if (window.location.hash) {    return  }  // 如果沒(méi)有hash值 只要給 hash 加上一個(gè) / 它會(huì)自動(dòng)加上 /#/  window.location.hash = '/'}復(fù)制代碼

    關(guān)于 html5模式 這里 就沒(méi)寫了

    然后回到 index.js 就是自己寫的 Vue Router中繼續(xù)編寫模式判斷

    最后就是 初始化 init方法

    // 導(dǎo)入 installimport install from './install'// 導(dǎo)入解析路由import createMatcher from './create-matcher'// 導(dǎo)入 HashHistoryimport HashHistory from './history/hash'// 導(dǎo)入 HTML5Historyimport HTML5History from './history/html5'// 導(dǎo)出自己寫的 VueRouterexport default class VueRouter {  // 實(shí)現(xiàn)構(gòu)造函數(shù)功能  constructor(options) {    // 獲取options中的routes路由規(guī)則 沒(méi)有就為空數(shù)組    this._routes = options.routes || []    // 解析路由 傳入規(guī)則 這里還返回了兩個(gè)方法 match,addRoutes 用matcher接收一下之后有用    this.matcher = createMatcher(this._routes)    // 獲取模式 沒(méi)有就默認(rèn)為 hash 模式    this.mode = options.mode || 'hash'    // 使用 if 或者 分支都行 根據(jù)不同的模式執(zhí)行不同的路由跳轉(zhuǎn)功能等等    switch (this.mode) {      case 'history':        this.history = new HTML5History(this)        break      case 'hash':        // 模式的實(shí)例使用 this.history接收等下用的上        // 傳入的this是 VueRouter        this.history = new HashHistory(this)        break      default:        throw new Error('該模式不存在')    }  }  // 初始化  init(Vue) {    // 拿到模式的實(shí)例    const history = this.history    // 進(jìn)行跳轉(zhuǎn)  第一個(gè)參數(shù)是path ,第二個(gè)是回調(diào)函數(shù)    history.transitionTo(history.getCurrentLocation, () =>      // 監(jiān)聽URL的改變 設(shè)置當(dāng)前的 this.current      history.setUpListener()    )  }}// 為VueRouter 添加 install方法VueRouter.install = install復(fù)制代碼

    7.定義一個(gè)響應(yīng)值 _route

    渲染不同路由頁(yè)面有個(gè)前提的就是需要一個(gè)表示 當(dāng)前路由 響應(yīng)式的屬性

    所以我們來(lái)到 install.js 添加一個(gè)響應(yīng)式的 屬性**_route**

    和這個(gè)無(wú)關(guān)的代碼 ...省略

    export let _Vue = nullexport default function install(Vue) {  _Vue = Vue  Vue.mixin({    beforeCreate() {      if (this.$options.router) {        ...        // 創(chuàng)建一個(gè)代表當(dāng)前路由 響應(yīng)式的值_route        // 其實(shí)不建議使用 defineReactive直接創(chuàng)建。。        // 第一個(gè)參數(shù)是綁定在誰(shuí)身上,第二是值名稱,第二個(gè)是值        Vue.util.defineReactive(this, '_route', this._router.history.current)      } else {        ...      }    },  })}復(fù)制代碼

    然后得回到 history下面的 base 添加一個(gè)修改響應(yīng)式 _route的值的回調(diào) this.cb

    import createRoute from '../util/route'export default class History {  constructor(router) {    ...    // cb 一個(gè)回調(diào)函數(shù),它的作用就是修改 響應(yīng)式路由的值_route ,對(duì)應(yīng)的視圖然后就刷新    this.cb = null  }  // 通過(guò) listen來(lái)修改 cb的值  listen(cb) {    this.cb = cb  }  transitionTo(path, onComplete) {...    // cb 存在就修改響應(yīng)式路由的值    this.cb && this.cb(this.current)...  }}復(fù)制代碼

    最后在 index.js 的 init 調(diào)用 listen 方法 傳入回調(diào)修改 響應(yīng)式值**_route**

    ...export default class VueRouter {  ...  init(Vue) {    ...    // 修改 響應(yīng)式的 route    history.listen((route) => {      Vue._route = route    })  }}...復(fù)制代碼

    8.添加 $router 和 $route

    我們知道在 Vue Router 提供了 $router (這個(gè)是路由對(duì)象是**Vue Router**的實(shí)例) 還有 $route(路由規(guī)則對(duì)象)

    我們自己可以來(lái)到 install.js 中進(jìn)行 添加這兩個(gè)屬性

    ...export default function install(Vue) {  ...  // 添加 $router 路由對(duì)象  Object.defineProperty 參數(shù)分別是 為誰(shuí)添加,屬性名,屬性值  Object.defineProperty(Vue.prototype, '$router', {    get() {      // this._routerRoot代表的是 Vue ,他的_router是 Vue Router實(shí)例      // 可以回過(guò)去看看第二點(diǎn)      return this._routerRoot._router    },  })  // 添加 $route  Object.defineProperty(Vue.prototype, '$route', {    get() {      // 他的_route是就是剛才添加 響應(yīng)式 的當(dāng)前 路由      return this._routerRoot._route    },  })}復(fù)制代碼

    9.router-link

    基本的介紹就不多說(shuō)了,之前也是有介紹的。然后現(xiàn)在重新來(lái)實(shí)現(xiàn)下

    在 components 文件下新建 link.js

    // 導(dǎo)出 linkexport default {  props: {    to: {      type: String,      required: true,    },  },  // 渲染  render(h) {    // 轉(zhuǎn)化為虛擬DOM    return h(      // 標(biāo)簽名      'a',      // 標(biāo)簽屬性      {        domProps: {          href: '#' + this.to,        },      },      // 標(biāo)簽里面的內(nèi)容 這里是 默認(rèn)插槽      [this.$slots.default]    )  },}復(fù)制代碼

    10.router-view

    在 components 文件下新建 view.js 具體步驟干了什么都寫在注釋里了

    // 導(dǎo)出 viewexport default {  render(h) {    // 獲取路由規(guī)則對(duì)象    const route = this.$route    // 定義一個(gè)變量,用來(lái)等下 取 matched 中的值    let depth = 0    // 該組件為 router-view    this.routerView = true    // 嘗試去獲取父組件    let parent = this.$parent    // 判斷是否有父組件    while (parent) {      // 判斷該組件是否為 routerView      if (parent.routerView) {        depth++      }      // 繼續(xù)向上判斷還有無(wú)父組件      parent = parent.$parent    }    // 這里的route是 this.$route 就是 _route 響應(yīng)式值,也就是 current    // 當(dāng)初 current 是 調(diào)用了 match方法 獲取到的 返回值是 matched 和 path    // matched 里面是多個(gè)路由對(duì)象 是這種模式保存 [parentRecord,childRecord]    // 通過(guò) 變量depth取出來(lái) 舉個(gè)栗子 ['/login','/login/tab']    // 因?yàn)槭褂玫膗nshif添加后面的父組件添加到前面    // depth 一直加 ,直接取出后面即可    const record = route.matched[depth]    // 沒(méi)有記錄直接渲染    if (!record) {      return h()    }    // 有的話就獲取記錄中的組件    const component = recordponent    // 最后把組件渲染    return h(component)  },}復(fù)制代碼

    好了到了這里 Vue Router的第二次編寫就完成了,雖然和官方的差距很大。。額,因?yàn)檫@里是簡(jiǎn)化寫的

    11.文件目錄

    忘了最后貼上文件的目錄

    這個(gè)模擬Vue Router的demo 放在了 github,有需要的可以這里 MyVueRouter

    到了這里也只是僅僅實(shí)現(xiàn)了 VueRouter的一小部分功能

    但是大體上的功能都差不多實(shí)現(xiàn)了,嵌套路由 添加動(dòng)態(tài)路由也實(shí)現(xiàn)了

    其實(shí)我覺(jué)得到這里了也可以了,不過(guò)還是得繼續(xù)加油學(xué)習(xí)


    作者:小浪努力學(xué)前端
    鏈接:juejin/post/6988316779818778631
    來(lái)源:掘金

  •  
    (文/高雨凡)
    打賞
    免責(zé)聲明
    本文為高雨凡推薦作品?作者: 高雨凡。歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明原文出處:http://biorelated.com/news/show-177936.html 。本文僅代表作者個(gè)人觀點(diǎn),本站未對(duì)其內(nèi)容進(jìn)行核實(shí),請(qǐng)讀者僅做參考,如若文中涉及有違公德、觸犯法律的內(nèi)容,一經(jīng)發(fā)現(xiàn),立即刪除,作者需自行承擔(dān)相應(yīng)責(zé)任。涉及到版權(quán)或其他問(wèn)題,請(qǐng)及時(shí)聯(lián)系我們郵件:weilaitui@qq.com。
     

    微信

    關(guān)注
    微信

    微信二維碼

    WAP二維碼

    客服

    聯(lián)系
    客服

    聯(lián)系客服:

    在線QQ: 303377504

    客服電話: 020-82301567

    E_mail郵箱: weilaitui@qq.com

    微信公眾號(hào): weishitui

    客服001 客服002 客服003

    工作時(shí)間:

    周一至周五: 09:00 - 18:00

    反饋

    用戶
    反饋