vue-动态路由+动态组件+动态页面

前提是你 提交于 2019-12-05 06:57:00

动态路由

  • 路由组件是vue-router
  • 动态路由即从后端请求路由信息,然后转化生成路由信息。所以这里的关键是不会提前知道什么菜单对应什么组件,因此路由声明的时候不再是写死的组件,而是可替换的动态路径。相关的功能就是路由懒加载,以及异步组件
  • 具体过程就是导航守卫的前置守卫中,根据是否登录来判断是否请求用户信息以及路由信息,再将请求的路由信息转化成路由,最后添加到路由表
router.beforeEach((to, from, next) => {
  if (store.getters.roles.length === 0) {
    // 判断当前用户是否已拉取完user_info信息,得到用户信息后立即请求路由信息
    store
      .dispatch('GetUserInfo')
      .then(res => {
        // 拉取user_info
        const roles = res.data.Data.Roles // note: roles must be a array! such as: ['editor','develop']

        store
          .dispatch('GenerateRoutes', {
            roles
          })
          .then(() => {
            // 执行GenerateRoutes动作后,store.getters.addRouters得到的就是内置的路由以及请求的路由的集合
            router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
            next({
              ...to,
              replace: true
            }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
          })
      })
      .catch(err => {
        store.dispatch('FedLogOut').then(() => {
          Message.error(err || 'Verification failed, please login again')
          next({
            path: '/'
          })
        })
      })
  } else {
    // 在这里省略了根据当前用户角色判断是否有权限访问即将跳转的路由

    next()
  }
})
  • 多级菜单
function formatRoutes(routes) {
  const fmRoutes = []
  routes.forEach(router => {
      const component = router.component
      router.component = resolve => {
        require(['@/' + component + '.vue'], resolve)
      }
    } else if (router.template) {
      router.component = resolve => {
        resolve({
          template: router.template
        })
      }

    let children = router.children
    if (children && children instanceof Array) {
      children = formatRoutes(children)
    }

    router.children = children

    fmRoutes.push(router)
  })

  return fmRoutes
}

动态加载组件

  • 分析一下,千万不要全部都写变量,比如require([component], resolve),这样 webpack 不知道你的组件会是什么也不知道位置,所以不会编译也不会匹配该组件。注意到没,一个是编译一个是匹配。像上面的写法,组件不会被编译,更别提匹配了。即使假设某组件被编译进去了,在这里单单用变量也是匹配不到的,因为路径的问题。即根据 component 的路径,匹配不到已编译的组件,因为匹配期间不会再计算代码所在文件路径相对 component 的路径。比如 component 的值分别为@/views/index.vue../views/index.vue./views/index.vue,运行期间这些直接拿去跟已注册组件匹配,并不会再次计算真实路径
  • 看不懂上面也没关系,没经历过确实不太容易理解。经过上面的解释再理解下面写法就 ok 了,把下面的写法看成常规的require(['@/views/index.vue'], resolve)没毛病吧,再换成下面写法,webpack 知道组件位于@/views/,组件后缀名.vue,该文件夹的 vue 文件统统编译,变的只有中间部分路径
router.component = resolve => {
  require(['@/views/' + component + '.vue'], resolve)
}

动态生成页面

  • 动态生成页面是指一个路由对应的组件如果存在则加载,不存在则用 template 赋值一个默认页面
  • 此功能可用于大量结构类似的页面,比如很多菜单对应的都是表格页,常见于中后台管理系统。因为会先尝试加载默认路径,不存在才使用默认页,所以个别页面只需要在默认路径放置组件即可覆盖默认页面
if (router.component) {
  const component = router.component
  router.component = resolve => {
    require(['@/' + component + '.vue'], resolve)
  }
} else if (router.template) {
  router.component = resolve => {
    resolve({
      template: router.template
    })
  }
} else {
  // 如果没有指定component路径并且没有设置template,尝试加载默认路径位置组件或用默认页面
  const component = `${router.name}/index`
  router.component = async resolve => {
    try {
      // 尝试加载模块
      await require(['@/views/' + component + '.vue'], resolve)
    } catch {
      // 加载失败,不存在此模块,使用默认模板
      console.log('@/views/' + component + '.vue不存在,加载默认模板')
      resolve({
        template: `<table-base table-name="${router.name}" />`
      })
    }
  }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!