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)
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章