cisco路由器設置步驟圖,router路由器設置怎麼清除

Vue Router 基礎

讓我們先來了解下Vue Router的簡單使用吧,先了解怎麼使用,之後再去想辦法怎麼去實現

1.簡介

路由:本質上是一種對應關係

分類分為前端路由後端路由

後端路由

比如node.js 的路由是 URL的請求地址和伺服器上面的資源對應,根據不同的請求地址返回不同的資源

前端路由

SPA(單頁應用)中根據用戶所觸發的事件改變了URL 在無需刷新的前提下 顯示不同的頁面內容,比如等下就要講的Vue Router

2.Vue-Router最基礎的使用步驟

2.1.引入Vue-Router文件

<!-- 使用vue router前提 vue 必不可少 -->
<script src="./js/vue.js"></script>
<!-- 引入vue-router文件 -->
<script src="./js/vue-router_3.0.2.js"></script>

2.2.在頁面上添加 router-link 和 router-view

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

2.3.創建路由組件

//創建路由組件
const home = {
 template: `
<div>歡迎來到{{name}}</div>
`,
  data() {
   return {
    name: '首頁',
   }
  },
}

const login = {
 template: `
  <div>歡迎來到登錄頁</div>
	`,
}

2.4.配置路由規則

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

2.5.掛載路由

let vm = new Vue({
 el: '#app',
 data: {},
 methods: {},
  // 掛載到vue 上面
 router,
 })
從使用到自己實現簡單Vue Router看這個就行了

3.嵌套路由

這裡的嵌套路由是基於上面的例子繼續寫的

3.1.在路由裡面添加 子路由鏈接和 佔位符

//創建路由組件
const home = {
    template: `
    <div>
    歡迎來到首頁
    <br>
    <!-- 子路由鏈接 -->
    <router-link to="/tab1">Tab1</router-link>
    <router-link to="/tab2">Tab2</router-link>

    <!-- 子路由展示 -->
    <router-view></router-view>
    </div>
}
複製代碼

3.2.添加路由組件

// 創建兩個子路由組件
const tab1 = {
    template: `
    <div>
    子路由1
    </div>
    `,
}
const tab2 = {
    template: `
    <div>
    子路由2
    </div>
    `,
}
複製代碼

3.3.配置路由規則

// 配置路由規則
const router = new VueRouter({
    routes: [
        {
            path: '/home',
            component: home,
            //children 表示子路由規則
            children: [
                { path: '/tab1', component: tab1 },
                { path: '/tab2', component: tab2 },
            ],
        },
    ],
})
複製代碼
從使用到自己實現簡單Vue Router看這個就行了

4.動態路由

path屬性加上/:id 使用route對象的params.id獲取動態參數

比如現在有這麼多個路由,如果自己也配置多個路由,豈不是有點。。。多餘

<div id="app">
    <!-- 添加路由 -->
    <!-- 會被渲染為 <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>
    <!-- 展示路由的內容 -->
    <router-view></router-view>
</div>

然後這裡就可以使用 動態路由來解決

<script>
    //創建路由組件
    const goods = {
        // this.$route.parms.id 可以省略 this
        template: `
        <div>歡迎來到商品 {{$route.params.id}}頁</div>
        `,
        }
    // 配置路由規則
    const router = new VueRouter({
        routes: [
            {
                // 加上`/:id`
                path: '/goods/:id',
                component: goods,
            },
        ],
    })
    let vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        // 掛載到vue 上面
        router,
    })
</script>
從使用到自己實現簡單Vue Router看這個就行了

最後提一下還可以用query進行傳參.

// 比如
<router-link to="/goods?id=1">goods</router-link>
複製代碼

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

添加動態路由

使用 this.$router.addRoutes([]) 可以添加動態路由,裡面傳遞是一個數組 和 routes裡面一樣

5.路由傳參

我們可以使用 props 進行傳值

為啥要用 props 進行傳值,route不香了嗎,確實route 不夠靈活

props 值有三種情況

5.1.布爾值類型

//創建路由組件
const goods = {
    // 使用props接收
    props: ['id'],
    template: `
    <div>歡迎來到商品 {{id}}頁</div>
    `,
}
// 配置路由規則
const router = new VueRouter({
    routes: [
        {
            path: '/goods/:id',
            component: goods,
            //props為true, route.params將會被設置為組件屬性
            props: true,
        },
    ],
})
複製代碼

5.2.對象類型

但是這裡就獲取不到 id 了,會報錯

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

const goods = {
    // 使用props接收
    props: ['name', 'info', 'id'],
    // 這裡的 id 是獲取不到的
    template: `
    <div>{{info}}來到{{name}} {{id}}頁</div>
    `,
}
// 配置路由規則
const router = new VueRouter({
    routes: [
        {
            path: '/goods/:id',
            component: goods,
            //props為對象 就會把這個對象傳遞的路由組件
            //路由組件使用props接收
            props: {
                name: '商品',
                info: '歡迎',
            },
        },
    ],
})
複製代碼

5.3.函數

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

6.route 和 router

在上面提到了route 那麼和 router有什麼區別呢

  • route當前router跳轉對象裡面可以獲取pathparamshashqueryfullPathmatchedname
  • routerVueRouter實例new VueRouter創建的實例,想要導航到不同URL,則使用router.push方法
  • routesrouter路由實例用來配置路由對象(順帶提一下)

7.命名路由

路由組件

//創建路由組件
const goods = {
    // 使用props接收
    props: ['id'],
    template: `
    <div>商品{{id}}頁</div>
    `,
}
複製代碼

路由配置

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

綁定 :to 通過name找到定義的路由 還可以使用 params 傳遞參數

<router-link :to="{name: 'goods', params: { id: 1 } }">goods1</router-link>
<!-- 展示路由的內容 -->
<router-view></router-view>
複製代碼

8.編程式導航

8.1.聲明式導航

既然提到了編程式導航,那麼先簡單說下聲明式導航

上面所展示的都是聲明是導航 比如router-link

<router-link to=”/goods/1″>goods1</router-link>

還有a標籤

<a href=”#/goods/1″>goods1</a>

8.2.編程式導航

使用javaScript來控制路由跳轉

在普通的網頁中使用 loaction.href window.open 等等進行跳轉

現在我要介紹的是Vue Router中的編程式導航

我們平時都是用router.push() **router.go(n)**方法進行跳轉

//字元串
this.$router.push('/home')

//對象
this.$ruter.push({path:'/home'})

//比如這個 /goods?id=1
this.$router.push({path:'/goods',query:{id:'1'}})

//命名路由 /goods/1
this.$router.push({name:'goods',params:{id:1}})

//後退
this.$router.go(-1)
複製代碼

9.路由守衛

9.1.全局守衛

router.beforeEach 全局守衛 對所有的路由都起作用

router.beforeEach((to, from, next) => { 
      next();//使用時,千萬不能漏寫next!!!
    }).catch(()=>{
      //跳轉失敗頁面
      next({ path: '/error', replace: true, query: { back: false }}
    )
})
複製代碼

全局的守衛的三個參數

to: 即將要進入的目標 路由對象

from: 當前導航正要離開 路由對象

next: 參數不同做的事也不同

next() 直接進入下一個鉤子

next(false) 停止當前導航

next(‘/路徑’) 跳轉到path路由地址 當然這裡面也可以寫成對象形式 next({path : ‘/路徑’}) next(error): 如果傳入參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError()

9.2.路由獨享的守衛

beforeEnter 路由對象獨享的守衛寫在routes裡面

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

9.3.組件內的守衛(了解)

組件內的守衛 寫在組件內部 下面是官方介紹

  • beforeRouteEnter 進入路由前,組件還沒有被實例化所以這裡無法獲取到this
  • beforeRouteUpdate (2.2) 這個階段可以獲取this,在路由復用同一個組件時觸發
  • beforeRouteLeave 這個階段可以獲取this,當離開組件對應的路由時,此時可以用來保存數據,或數據初始化,或關閉定時器等等
const goods = {
  template: `<div>goods</div>`,
  beforeRouteEnter (to, from, next) {
    // 具體邏輯
  },
  beforeRouteUpdate (to, from, next) {
    // 具體邏輯
  },
  beforeRouteLeave (to, from, next) {
    // 具體邏輯
  }
}
複製代碼

10.組件緩存keep-alive

頁面重新載入會重新渲染頁面比如回退的時候等等,我們有的組件它不是一個活動的(數據不變)不希望它被重新渲染,所以這裡就可以使用 <keep-alive> </keep-alive> 包裹起來,這樣就不會觸發created鉤子

應用場景:獲取一個商品的詳情然後回退在前進的時候就使用緩存,提高性能

10.1.不使用 keep-alive例子

這裡home 組件在created進行列印當前的時間

<div id="app">
    <router-link to="/home">home</router-link>
	<router-link to="/login">login</router-link>

	<router-view></router-view>
</div>
複製代碼
<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>
複製代碼
從使用到自己實現簡單Vue Router看這個就行了

如上,每切換home 的路由 組件就會重新渲染,列印當前的時間

如果使用 keep-alive 會有什麼效果呢

10.2.使用keep-alive

這裡只需簡單的包裹起來就行了

<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>
複製代碼
從使用到自己實現簡單Vue Router看這個就行了

可以看到的是只列印一次,說明切換了路由它並沒有重新渲染組件

當然可以在 組件內取個name名字 keep-alive 標籤裡面添加 include 屬性就可以對相應的組件進行緩存

const login = {
    name: login,
    template: `
    <div>Login</div>
    `,
}
const home = {
    name: home,
    template: `
    <div>Home</div>
    `,
    created() {
    	console.log(new Date())
    },
}
複製代碼
<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>
複製代碼

10.3.activated 和 deactivated

keep-alive 生命周期執行順序

第一次訪問路由時:

  • created–>mounted –>activated
  • deactivated在退出後觸發

以後進入只會觸發 activated

11.hash 和 history 模式

11.1.hash模式

在vue-router中默認使用的是 hash 模式

hash是url中的錨點就是**#,通過錨點作為路由地址,我們通常改變的是改變#**後面部分,實現瀏覽器渲染指定的組件.,錨點發生改變會觸發 onhashchange 事件

11.2.history模式

history 模式就是平時正常的地址,使用方面需要伺服器支持

如果訪問的路徑資源沒有 直接就是 404

HTML5後新增了兩個API

pushState(): IE10後支持

replaceState()

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

const router = new VueRouter({
  mode: 'history'
})
複製代碼

實現一個基礎 Vue Router

複習上面的路由的基礎那麼我們不如來寫個Vue Router吧

實現的這個 Vue Router是基於 history模式

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

這個簡單的沒有按照Vue Router源碼來寫主要是一些基礎功能的實現

為後面的按照源碼寫打基礎

1.註冊全局Vue Router

首先就是先註冊自己的 Vue Router

判斷是否註冊了組件

在Vue實例創建完成進行註冊

// 保存一個全局變數 Vue
let _Vue = null

// 默認導出自己寫的 VueRouter
export default class MyVueRouter {
  // 實現install 註冊 MyVueRouter vue提供install可供我們開發新的插件及全局註冊組件等
  // 把Vue傳進去
  static install(Vue) {
    // 定義一個標識判斷是否註冊了 MyVueRouter ,註冊了就不用下一步了
    if (MyVueRouter.install.installed) return
    // 沒有就進行下面的,把標識改變true
    MyVueRouter.install.installed = true
    // 把全局變數 _Vue 保存
    _Vue = Vue
    // 為了獲取Vue中的this執行這裡使用 混入
    _Vue.mixin({
      // 在Vue實例創建好的時候進行操做
      beforeCreate() {
        // 判斷是否是實例創建還是組件創建 ,可以判斷是否掛載 了router
        if (this.$options.router) {
          // 把router註冊到 _Vue上
          _Vue.prototype.$router = this.$options.router
        }
      },
    })
  }
}
複製代碼

2.實現 構造方法

optoins 保存傳入的規則

routerMap 確定地址和組件的關係

current 表示當前的地址是響應式的之後渲染組件和它相關

export default class MyVueRouter {
  ...
  //實現構造
  constructor(optoins) {
    // 這個保存的是  routes
    this.optoins = optoins
    // routerMap 保存路由和 組件之間的關係
    this.routerMap = {}
    // 用來記錄數據 這裡面的數據都是 響應式
    this.data = _Vue.observable({
      // 當前路由的地址
      current: '/',
    })
  }
}
複製代碼

3.解析路由規則

傳入的路由規則拿到一個對象里 地址 和 組件一一匹配

export default class MyVueRouter {
  ...
  // 解析路由規則
  createRouterMap() {
    // 把之前構造函數的中的傳入的 routes 規則進行遍歷
    this.optoins.routes.forEach((item) => {
      // 把路由 和 組件的對應關係添加到 routerMap中
      this.routerMap[item.path] = item.component
    })
  }
}
複製代碼

4.實現 router-link 組件

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

因為一般使用的基本都是運行版的Vue 所以自己把組件轉為 虛擬DOM

還有就是鏈接會刷新的問題

自己寫個函數進行跳轉阻止默認事件

還得注意對應的路由所要渲染的組件

export default class MyVueRouter {
  ...
  // 實現組件
  initComponents(Vue) {
    // 實現 router-link組件
    Vue.component('router-link', {
      props: {
        // router-link上面的to屬性將訪問的地址
        to: String,
      },
      // 由於運行版的Vue不能渲染template所以這裡重新寫個render 這裡h 也是個函數
      // template: `<a :href="to"><slot></slot></a>`,
      render(h) {
        // 第一個參數是標籤
        return h(
          'a',
          // 第二個參數是對象是 tag 裡面的屬性
          {
            // 設置屬性
            attrs: {
              href: this.to,
            },
            // 綁定事件
            on: {
              // 重新複寫點擊事件,不寫的話會點擊會向伺服器發送請求刷新頁面
              click: this.myClick,
            },
          },
          // 這個是標籤裡面的內容 這裡渲染是 默認插槽
          [this.$slots.default]
        )
      },
      methods: {
        //router-link的點擊事件
        myClick(e) {
          // 因為我這裡是模擬是 history的路由所以用pushState ,hash路由可以這裡用 push
          // 使用history修改瀏覽器上面的地址
          // pushState 第一個參數是傳遞的參數,第二個是標題,第三個是鏈接
          history.pushState({}, '', this.to)
          // 渲染相應的組件
          // 渲染的頁面也需要改變 data中的current是響應式的 router-view是根據current來渲染的
          this.$router.data.current = this.to
          // 阻止默認跳轉事件
          e.preventDefault()
        },
      },
    })
複製代碼

5.實現 router-view 組件

這裡從之前解析的規則裡面拿到當前的對應的組件進行轉為虛擬DOM

最後router-view佔位渲染到頁面上

export default class MyVueRouter {
  ...
  // 實現組件
  initComponents(Vue) {
    // 實現 router-view組件
    Vue.component('router-view', {
      render(h) {
        // 獲取的當前路徑所對應的組件
        // 因為當前this是Vue,this.$router才是MyVueRouter
        const component = this.$router.routerMap[this.$router.data.current]
        // 轉化為虛擬Dom
        return h(component)
      },
    })
  }
}
複製代碼

6.前進和後退

在完成之前的編寫還是不夠的,因為在瀏覽器點後退和前進雖然改變了瀏覽器的地址,但是組件卻沒有刷新,下面就來解決這個問題

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

7.在router掛載後進行初始化

最後寫個函數進行初始化

在router註冊到Vue之後進行 初始化

export default class MyVueRouter {
  // 初始化
  init() {
    // 解析路由規則
    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
          // 註冊完router後進行初始化
          this.$options.router.init()
        }
      },
    })  
  }
  ...
}

複製代碼

8.放上完整的 index.js

// 保存一個全局變數 Vue
let _Vue = null

export default class MyVueRouter {
  // 實現install 註冊 MyVueRouter vue提供install可供我們開發新的插件及全局註冊組件等
  // 把Vue傳進去
  static install(Vue) {
    // 定義一個標識判斷是否註冊了 MyVueRouter ,註冊了就不用下一步了
    if (MyVueRouter.install.installed) return
    // 沒有就進行下面的,把標識改變true
    MyVueRouter.install.installed = true
    // 把全局變數 _Vue 保存
    _Vue = Vue
    // 為了獲取Vue中的this執行這裡使用 混入
    _Vue.mixin({
      // 在Vue實例創建好的時候進行操做
      beforeCreate() {
        // 判斷是否是實例創建還是組件創建 ,可以判斷是否掛載 了router
        if (this.$options.router) {
          // 把router註冊到 _Vue上
          _Vue.prototype.$router = this.$options.router
          // 註冊完router後進行初始化
          this.$options.router.init()
        }
      },
    })
    // 判斷是否掛載
  }
  // 實現構造方法
  constructor(optoins) {
    // 這個保存的是  routes
    this.optoins = optoins
    // routerMap 保存路由和 組件之間的關係
    this.routerMap = {}
    // 用來記錄數據 這裡面的數據都是 響應式
    this.data = _Vue.observable({
      // 當前路由的地址
      current: '/',
    })
  }
  // 解析路由規則
  createRouterMap() {
    // 把之前構造函數的中的傳入的 routes 規則進行遍歷
    this.optoins.routes.forEach((item) => {
      // routes中的每一項都是一個對象 { path: '/XXX', component: XXX}
      // 把路由 和 組件的對應關係添加到 routerMap中
      this.routerMap[item.path] = item.component
    })
  }
  // 實現組件
  initComponents(Vue) {
    // 實現 router-link組件
    Vue.component('router-link', {
      props: {
        // router-link上面的to屬性將訪問的地址
        to: String,
      },
      // 由於運行版的Vue不能渲染template所以這裡重新寫個render 這裡h 也是個函數
      // template: `<a :href="to"><slot></slot></a>`,
      render(h) {
        // 第一個參數是標籤
        return h(
          'a',
          // 第二個參數是對象是 tag 裡面的屬性
          {
            // 設置屬性
            attrs: {
              href: this.to,
            },
            // 綁定事件
            on: {
              // 重新複寫點擊事件,不寫的話會點擊會向伺服器發送請求刷新頁面
              click: this.myClick,
            },
          },
          // 這個是標籤裡面的內容 這裡渲染是 默認插槽
          // 比如<router-link to="/">首頁</router-link>
          // 插槽就是給首頁兩個字留位置,當前這只是個例子
          [this.$slots.default]
        )
      },
      methods: {
        //router-link的點擊事件
        myClick(e) {
          // 因為我這裡是模擬是 history的路由所以用pushState ,hash路由可以這裡用 push
          // 使用history修改瀏覽器上面的地址
          // pushState 第一個參數是傳遞的參數,第二個是標題,第三個是鏈接
          history.pushState({}, '', this.to)
          // 渲染相應的組件
          // 渲染的頁面也需要改變 data中的current是響應式的 router-view是根據current來渲染的
          this.$router.data.current = this.to
          // 阻止默認跳轉事件
          e.preventDefault()
        },
      },
    })
    // 實現 router-view組件
    Vue.component('router-view', {
      render(h) {
        // 獲取的當前路徑所對應的組件
        // 因為當前this是Vue,this.$router才是MyVueRouter
        const component = this.$router.routerMap[this.$router.data.current]
        // 轉化為虛擬Dom
        return h(component)
      },
    })
  }
  // 初始化事件
  initEvent() {
    // 監聽瀏覽器地址的改變
    window.addEventListener('popstate', () => {
      // 改變VueRouter的當前的地址 重新渲染組件
      this.data.current = window.location.pathname
    })
  }

  // 初始化
  init() {
    // 解析路由規則
    this.createRouterMap()
    // 初始化組件
    this.initComponents(_Vue)
    // 初始化事件
    this.initEvent()
  }
}
複製代碼

到了這裡基礎的實現功能差不多了,上面的例子是為了下面打基礎,所有的功能實現基本都是在一個文件下很不嚴謹,下面就嚴格按照Vue Router 源碼來實現自己 Vue Router

Vue Router實現

經過上面簡單的實現,現在我們按照Vue Router源碼的方式進行編寫

1.首先是Vue Router 構造

/* index.js */

// 導出自己寫的 VueRouter
export default class VueRouter {
  // 實現構造函數功能
  constructor(options) {
    // 獲取options中的routes路由規則 沒有就為空數組
    this._options = options.routes || []
  }
  // 初始化
  init(Vue) {}
}
複製代碼

2.註冊組件 install

install.js 對自己寫的Vue-Router進行全局的註冊

之後還會在這裡創建 router∗∗∗∗router** **router∗∗∗∗route

還有註冊 router-link router-view

/* install.js */

// 定義一個全局 的Vue
export let _Vue = null

// 導出 install方法
export default function install(Vue) {
  // 保存到全局的Vue
  _Vue = Vue
  // 混入
  _Vue.mixin({
    // Vue實例創建完畢之後操做
    beforeCreate() {
      // 這裡是new Vue
      if (this.$options.router) {
        // 保存 Vue
        this._routerRoot = this
        // 保存 Vue Router 的實例,以後可以通過Vue Router構造的一些方法
        this._router = this.$options.router
        // 調用Vue Router的init(Vue) 初始化操做
        this._router.init(this)
      } else {
        // 這裡是創建 Vue的組件等等
        // 判斷是否有父組件 ,有的話就把父組件的 _roterRoot(也就是Vue)給 子組件
        // 沒有父組件就把 this 這是也是(Vue) 給子組件
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
    },
  })
}
複製代碼

然後在 index.js中導入install 進行為構造添加 install

// 導入 install
import install from './install'

// 導出自己寫的 VueRouter
export default class VueRouter {
	...
}
    
// 為VueRouter 添加 install方法
VueRouter.install = install
複製代碼

3.編寫 create-route-map.js

這個主要的作用就是用來解析傳遞過來的路由 需要導出然後在 create-matcher.js進行使用

具體的細節都寫了注釋

/* create-route-map.js */

// 導出具體的路由解析
/**
 *
 * @param {*} routes 路由規則
 * @param {*} oldPathList 路由列表
 * @param {*} oldPathMap 路由和組件的對應關係
 */
export default function createRouteMap(routes, oldPathList, oldPathMap) {
  // 傳入了就是添加動態路由 沒有傳入就默認為空
  const pathList = oldPathList || []
  const pathMap = oldPathMap || []

  // 遍歷規則操作
  routes.forEach((route) => {
    // 記錄路由 也是核心的解析路由 為了分工明確寫的外面
    addRouteRecord(route, pathList, pathMap)
  })

  // 返回新的路由列表 和 路由對應關係
  return {
    pathList,
    pathMap,
  }
}

/**
 *
 * @param {*} route 路由規則
 * @param {*} pathList 路由列表
 * @param {*} pathMap 路由和組件之間的對應關係
 * @param {*} parentRecord  父路由
 */
function addRouteRecord(route, pathList, pathMap, parentRecord) {
  // 路由地址 判斷是否存在父級的路由 有的話拼接父級路由和當前路由的path 沒有就是當前route.path
  const path = parentRecord ? `${parentRecord.path}/${route.path}` : route.path
  // record作為一個路由記錄 記錄了路由地址,組件,父級路由   用於路由對應關係去對應相對應的path
  const record = {
    path,
    component: route.component,
    parent: parentRecord,
  }
  // 判斷是否在路由列表中 存在當前路由,不存在進行添加當前路由,更新路由列表
  if (!pathList[path]) {
    // 向路由列表中添加路由
    pathList.push(path)
    // 向路由對應關係中 添加path 相對應的記錄
    pathMap[path] = record
  }
  // 判斷當前的 路由是否有子路由,有的話進行遞歸
  if (route.children) {
    route.children.forEach((childRoute) => {
      // 就簡單說下最後一個參數 就是父級路由記錄
      addRouteRecord(childRoute, pathList, pathMap, record)
    })
  }
}
複製代碼

4.編寫 create-matcher.js

這個模塊的意義也是解析路由不過這個是個指揮家,上面實現的是具體解析操作

在這個模塊里進行調用上面的具體解析路由的方法就行了

有了上面面具體的路由解析,這個create-matcher.js就容易實現了,只需要簡單的調用它即可

這個模塊返回了兩個方法

match : 根據路由路徑創建路由規則對象,之後就可以通過 規則對象獲取到所有的路由信息然後拿到所有的組件進行創建

addRoutes : 添加動態路由

/* create-matcher.js */

// 導入具體的路由解析規則
import createRouteMap from './create-route-map'

// 導出解析路由規則 傳入的是規則
export default function createMatcher(router) {
  // pathList 路由的列表  pathMap 路由與組件的對應關係 nameMap這裡沒有考慮,先完成個簡單的
  // 具體的解析規則是使用  createRouteMap
  const { pathList, pathMap } = createRouteMap(router)
  // match是 從pathMap 根據path獲取 相應的路由記錄
  function match(path) {
      //待實現
  }
  // 添加動態路由
  function addRoutes(router) {
    // 添加動態路由肯定也要解析路由規則
    createRouteMap(router, pathList, pathMap)
  }
  // 返回match 和 addRoutes
  return {
    match,
    addRoutes,
  }
}
複製代碼

然後在index.js也就是Vue Router的構造中使用 createMatcher. 使用this.matcher接收

// 導入 install
import install from './install'
// 導入解析路由
import createMatcher from './create-matcher'

// 導出自己寫的 VueRouter
export default class VueRouter {
  // 實現構造函數功能
  constructor(options) {
    // 獲取options中的routes路由規則 沒有就為空數組
    this._routes = options.routes || []
    // 解析路由 傳入規則 這裡還返回了兩個方法 match,addRoutes 用matcher接收一下之後有用
    this.matcher = createMatcher(this._routes)
  }
  // 初始化
  init(Vue) {}
}
// 為VueRouter 添加 install方法
VueRouter.install = install

複製代碼

5.編寫 createMatcher

看見上面在 createMatcher中定義了 一個match了嗎,

match是 從pathMap 根據path獲取 相應的路由記錄

上面還沒有去實現,現在來實現它

需要實現它的話還需要編寫個 createRoute 方法,我這裡寫在 uitl/route.js模塊里

/* util/route.js */

// 導出 createRoute
/**
 *
 * @param {*} record 傳過來的記錄
 * @param {*} path 路由地址
 * @returns
 */
export default function createRoute(record, path) {
  // 保存路由的記錄 裡面可能有多個路由 是這種模式保存 [parentRecord,childRecord]
  const matched = []
  // 判斷是否是子路由
  // 下面 record = record.parent 在不斷向上找parent有繼續執行
  // 沒有就直接return 下面的對象
  while (record) {
    // 循環得到的 record不斷插入到 數組的最前面
    matched.unshift(record)
    // 把父記錄給當前record 繼續循環
    record = record.parent
  }
  // 返回path 和 matched 以便之後 router-view渲染
  return {
    path,
    matched,
  }
}
複製代碼

上面編寫了 createRoute方法我們就可以在 create-mathcer.js 調用 來獲取到記錄了

然後再 create-mathcer.js中繼續 完善 match方法

/* create-matcher.js */

// 導入具體的路由解析規則
import createRouteMap from './create-route-map'
// 導入 createRoute
import createRoute from './util/route'

// 導出解析路由規則 傳入的是規則
export default function createMatcher(router) {
  // pathList 路由的列表  pathMap 路由與組件的對應關係 nameMap這裡沒有考慮,先完成個簡單的
  // 具體的解析規則是使用  createRouteMap
  const { pathList, pathMap } = createRouteMap(router)
  // match是 從pathMap 根據path獲取 相應的路由記錄
  function match(path) {
    // 取出path對應的記錄
    const record = pathMap[path]
    // 判斷記錄是否存在
    if (record) {
      return createRoute(record, path)
    }
    return createRoute(null, path)
  }
  // 添加動態路由
  function addRoutes(router) {
    // 添加動態路由肯定也要解析路由規則
    createRouteMap(router, pathList, pathMap)
  }
  // 返回match 和 addRoutes
  return {
    match,
    addRoutes,
  }
}
複製代碼

6.歷史記錄的處理 History

history目錄下新建一個 base模塊用來編寫 父類

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

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

/* history/base.js */

// 導入 我們上面寫好的 createRoute
import createRoute from '../util/route'

// 導出 History
export default class History {
  // router 是路由對象 也就是 VUe-Router的一個實例
  constructor(router) {
    // 賦值給自己的 router
    this.router = router
    // 默認的的當前路徑為 /
    this.current = createRoute(null, '/')
  }
  // 將要跳轉的鏈接
  // path 是路由的地址, onComplete是一個回調
  transitionTo(path, onComplete) {
    // 獲取當前的應該跳轉的路由  調用的是 Vue-Router中 this.matcher中收到的match方法
    // 在這裡 this.router就是 Vue-Router的一個實例 所以寫成
    // this.router.matcher.match(path)
    this.current = this.router.matcher.match(path)
    // 回調存在觸發回調
    onComplete && onComplete()
  }
}
複製代碼

編寫 HashHistory 模式 繼承 History

/* /history/hash */

// 導入 base中的 History
import History from './base'

// 繼承了 History
export default class HashHistory extends History {
  constructor(router) {
    super(router)
    // 確保第一次訪問的時候路由加上 #/
    ensuerSlash()
  }
  // 監聽URL的改變 設置當前的current
  setUpListener() {
    // 監聽 hash的變化
    window.addEventListener('hashchange', () => {
      // 改變 this.current
      this.transitionTo(this.getCurrentLocation())
    })
  }
  // 獲取當前的URL的hash 當然這裡要去除 #
  getCurrentLocation() {
    // 這裡不建議寫成這個 return window.location.hash.slice(1) 有兼容問題
    let href = window.location.href
    const index = href.indexOf('#')
    // 當沒有 #的時候 直接返回 空字元串
    if (index < 0) return ''
    // 獲取 #後面的地址
    href = href.slice(index + 1)
    return href
  }
}

// 確保第一次加上 #/
function ensuerSlash() {
  // 如果存在 hash的話就不行加 /
  if (window.location.hash) {
    return
  }
  // 如果沒有hash值 只要給 hash 加上一個 / 它會自動加上 /#/
  window.location.hash = '/'
}
複製代碼

關於 html5模式 這裡 就沒寫了

然後回到 index.js 就是自己寫的 Vue Router中繼續編寫模式判斷

最後就是 初始化 init方法

/* index.js */

// 導入 install
import install from './install'
// 導入解析路由
import createMatcher from './create-matcher'
// 導入 HashHistory
import HashHistory from './history/hash'
// 導入 HTML5History
import HTML5History from './history/html5'

// 導出自己寫的 VueRouter
export default class VueRouter {
  // 實現構造函數功能
  constructor(options) {
    // 獲取options中的routes路由規則 沒有就為空數組
    this._routes = options.routes || []
    // 解析路由 傳入規則 這裡還返回了兩個方法 match,addRoutes 用matcher接收一下之後有用
    this.matcher = createMatcher(this._routes)
    // 獲取模式 沒有就默認為 hash 模式
    this.mode = options.mode || 'hash'
    // 使用 if 或者 分支都行 根據不同的模式執行不同的路由跳轉功能等等
    switch (this.mode) {
      case 'history':
        this.history = new HTML5History(this)
        break
      case 'hash':
        // 模式的實例使用 this.history接收等下用的上
        // 傳入的this是 VueRouter
        this.history = new HashHistory(this)
        break
      default:
        throw new Error('該模式不存在')
    }
  }
  // 初始化
  init(Vue) {
    // 拿到模式的實例
    const history = this.history
    // 進行跳轉  第一個參數是path ,第二個是回調函數
    history.transitionTo(history.getCurrentLocation, () =>
      // 監聽URL的改變 設置當前的 this.current
      history.setUpListener()
    )
  }
}
// 為VueRouter 添加 install方法
VueRouter.install = install
複製代碼

7.定義一個響應值 _route

渲染不同路由頁面有個前提的就是需要一個表示 當前路由 響應式的屬性

所以我們來到 install.js 添加一個響應式的 屬性**_route**

和這個無關的代碼 …省略

/* install.js */

export let _Vue = null

export default function install(Vue) {
  _Vue = Vue
  Vue.mixin({
    beforeCreate() {
      if (this.$options.router) {
        ...
        // 創建一個代表當前路由 響應式的值_route
        // 其實不建議使用 defineReactive直接創建。。
        // 第一個參數是綁定在誰身上,第二是值名稱,第二個是值
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        ...
      }
    },
  })
}
複製代碼

然後得回到 history下面的 base 添加一個修改響應式 _route的值的回調 this.cb

/* history/base.js */

import createRoute from '../util/route'

export default class History {
  constructor(router) {
    ...
    // cb 一個回調函數,它的作用就是修改 響應式路由的值_route ,對應的視圖然後就刷新
    this.cb = null
  }
  // 通過 listen來修改 cb的值
  listen(cb) {
    this.cb = cb
  }

  transitionTo(path, onComplete) {
	...
    // cb 存在就修改響應式路由的值
    this.cb && this.cb(this.current)
	...
  }
}
複製代碼

最後在 index.jsinit 調用 listen 方法 傳入回調修改 響應式值**_route**

/* index.js */

...

export default class VueRouter {
  ...
  init(Vue) {
    ...
    // 修改 響應式的 route
    history.listen((route) => {
      Vue._route = route
    })
  }
}
...

複製代碼

8.添加 $router 和 $route

我們知道在 Vue Router 提供了 $router (這個是路由對象是**Vue Router**的實例) 還有 $route(路由規則對象)

我們自己可以來到 install.js 中進行 添加這兩個屬性

/* install.js */

...
export default function install(Vue) {
  ...
  // 添加 $router 路由對象  Object.defineProperty 參數分別是 為誰添加,屬性名,屬性值
  Object.defineProperty(Vue.prototype, '$router', {
    get() {
      // this._routerRoot代表的是 Vue ,他的_router是 Vue Router實例
      // 可以回過去看看第二點
      return this._routerRoot._router
    },
  })
  // 添加 $route
  Object.defineProperty(Vue.prototype, '$route', {
    get() {
      // 他的_route是就是剛才添加 響應式 的當前 路由
      return this._routerRoot._route
    },
  })
}
複製代碼

9.router-link

基本的介紹就不多說了,之前也是有介紹的。然後現在重新來實現下

components 文件下新建 link.js

/* ./components/link.js */

// 導出 link
export default {
  props: {
    to: {
      type: String,
      required: true,
    },
  },
  // 渲染
  render(h) {
    // 轉化為虛擬DOM
    return h(
      // 標籤名
      'a',
      // 標籤屬性
      {
        domProps: {
          href: '#' + this.to,
        },
      },
      // 標籤裡面的內容 這裡是 默認插槽
      [this.$slots.default]
    )
  },
}
複製代碼

10.router-view

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

/* ./components/link.js */

// 導出 view
export default {
  render(h) {
    // 獲取路由規則對象
    const route = this.$route
    // 定義一個變數,用來等下 取 matched 中的值
    let depth = 0
    // 該組件為 router-view
    this.routerView = true
    // 嘗試去獲取父組件
    let parent = this.$parent
    // 判斷是否有父組件
    while (parent) {
      // 判斷該組件是否為 routerView
      if (parent.routerView) {
        depth++
      }
      // 繼續向上判斷還有無父組件
      parent = parent.$parent
    }
    // 這裡的route是 this.$route 就是 _route 響應式值,也就是 current
    // 當初 current 是 調用了 match方法 獲取到的 返回值是 matched 和 path
    // matched 裡面是多個路由對象 是這種模式保存 [parentRecord,childRecord]
    // 通過 變數depth取出來 舉個栗子 ['/login','/login/tab']
    // 因為使用的unshif添加後面的父組件添加到前面
    // depth 一直加 ,直接取出後面即可
    const record = route.matched[depth]
    // 沒有記錄直接渲染
    if (!record) {
      return h()
    }
    // 有的話就獲取記錄中的組件
    const component = record.component
    // 最後把組件渲染
    return h(component)
  },
}

複製代碼

好了到了這裡 Vue Router的第二次編寫就完成了,雖然和官方的差距很大。。額,因為這裡是簡化寫的

11.文件目錄

忘了最後貼上文件的目錄

從使用到自己實現簡單Vue Router看這個就行了

這個模擬Vue Routerdemo 放在了 github,有需要的可以這裡 MyVueRouter

到了這裡也只是僅僅實現了 VueRouter的一小部分功能

但是大體上的功能都差不多實現了,嵌套路由 添加動態路由也實現了

其實我覺得到這裡了也可以了,不過還是得繼續加油學習

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/233366.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-11 13:31
下一篇 2024-12-11 13:31

相關推薦

發表回復

登錄後才能評論