Vue-router插件——嵌套路由

實際生活中的應用界面,通常由多層嵌套的組件組合而成。同樣地,URL 中各段動態路徑也按某種結構對應嵌套的各層組件,例如:

const router1 = [
  {
    path: '/',
    name: 'home',
    component: HelloWorld
  },
  {
    path: '/about',
    name: 'about',
    component: About,
    children: [
      {
        path: '/about/info',
        component: {
          render (h) {
            return h('div', 'info page')
          }
        }
      }
    ]
  }
]

這裏的/about/info就是一個嵌套路由,要對這樣的路由進行處理,我們在router-view中獲取對應的組件對象時,就有一點不一樣了。之前在KVueRouter類中,我們創建了一個響應式屬性current來保存當前的hash,並創建了一個map來方便在KRouterView.js中獲取對應的組件對象。這對於嵌套路由來說,就不能滿足需求了。

我們重新創建一個響應式屬性match,它是一個數組,同時,我們遞歸遍歷已配置的路由對象,將每一級的路由信息都在這個數組中保存下來,此時,current就不需要是一個響應式數據了。

修改後的KVueRouter類

class KVueRouter {
  constructor(options) {
    // options就是配置的路由信息,將它作爲KVueRouter對象的一個屬性
    this.$options = options
    // 創建一個響應式數據,來存儲當前的路由信息,在KRouterView組件中可以直接用這個變量
    // defineReactive()方法是vue創建響應式數據的方法,這裏是在KVueRouter對象上面創建一個名爲current的響應式屬性,初始值是'/'
      
    // 從這裏開始不同  
      
    // Vue.util.defineReactive(this, 'current', '/') 如果是嵌套路由,當前的current就不能匹配到每個場景了,則不需要響應式
    this.current = window.location.hash.slice(1) || '/'
    // 嵌套路由的情況下,需要一個數組來保存當前路由的層級,並且需要時響應式的數據,一遍routerview中使用
    Vue.util.defineReactive(this, 'match', [])
    // 使用遞歸,遍歷當前的路由,並存到this.match中去
    this.matchRouter()

    // 使用hashchange事件來監聽當前路由的變化,它監聽的是當前連接的錨部分(就是 # 後面的)的變化
    // 使用bind方法防止this指向發生變化
    window.addEventListener('hashchange', this.onHashChange.bind(this))
    window.addEventListener('load', this.onHashChange.bind(this))

    // 生成一個map,方便view組件獲取當前路由對應的組件,處理嵌套路由就用不上了
    // this.routerMap = {}
    // this.$options.router1.forEach(route => {
    //   this.routerMap[route.path] = route.component
    // })
  }
  onHashChange () {
    // window.location.hash就是url中錨部分,但是它以# 開頭,需要把#去掉
    this.current = window.location.hash.slice(1)
    this.match = []
    this.matchRouter()
  }
  matchRouter (routes) {
    // 由於是遞歸,所以需要接收遞歸是傳入的參數,第一次直接取所有的路由表
    routes = routes || this.$options.router1
    for (const route of routes) {
      // 如果是首頁,直接將route push到match數組裏面去
      if (route.path === '/' && this.current === '/') {
        this.match.push(route)
        return
      }
      if (route.path !== '/' && this.current.indexOf(route.path) !== -1) {
        this.match.push(route)
        if (route.children) {
          this.matchRouter(route.children)
        }
        return
      }
    }
  }
}

修改後的KRouterView.js

// 這個組件就是獲取當前路由對應的組件,拿到這個組件對象,並渲染到頁面中
export default {
  // 處理嵌套路由
  render (h) {
    let component = null
    // this.$vnode是當前組件的虛擬dom,我們在它虛擬dom的data屬性中設置一個自定義的屬性,代表自己是一個routerview
    this.$vnode.data.routerView = true
    // 需要標記當前路由的深度,循環獲取父組件,如果父組件的routerview爲true,則代表自己的深度加1
    let deep = 0
    let parent = this.$parent
    while (parent) {
      const vnodeData = parent.$vnode && parent.$vnode.data
      if (vnodeData && vnodeData.routerView) {
        deep++
      }
      parent = parent.$parent
    }
    // 通過match數組獲取當前的route
    const route = this.$router.match[deep]
    if (route) {
      component = route.component
    }
    return h(component)
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章