vue-cli4 新建的 vue2.x 项目中, 静态路由与动态路由的区别

目前我做的是造(抄)一个vue的后台管理 demo, 登录、退出、左侧菜单导航、动态路由这些基本都搞定了. 问题如下:
我在router/index中定义的静态路由和我用router.addRoutes添加的动态路由到底有啥区别呢?

动态路由的思路大约如下:
1: 从后台拿到菜单, 这个菜单已经是后台构造好的树结构
2: 在vuex中递归这个菜单树, 顶级菜单就把component置为 Layout , 其他子路由就动态import
3: 在路由守卫中把路由放进去

package.json

{
  "name": "bootsite-ui",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "dev": "vue-cli-service serve --mode dev",
    "fat": "vue-cli-service serve --mode fat",
    "local": "vue-cli-service serve --mode local",
    "prod": "vue-cli-service serve --mode prod"
  },
  "dependencies": {
    "axios": "^0.27.2",
    "core-js": "^3.6.5",
    "element-ui": "^2.4.5",
    "js-cookie": "^3.0.1",
    "mockjs": "^1.1.0",
    "nprogress": "^0.2.0",
    "vue": "^2.6.11",
    "vue-fragment": "^1.5.2",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.13",
    "@vue/cli-plugin-eslint": "~4.5.13",
    "@vue/cli-plugin-router": "~4.5.13",
    "@vue/cli-plugin-vuex": "~4.5.13",
    "@vue/cli-service": "~4.5.13",
    "@vue/eslint-config-standard": "^5.1.2",
    "babel-eslint": "^10.1.0",
    "babel-plugin-component": "^1.1.1",
    "eslint": "^6.7.2",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.0",
    "eslint-plugin-vue": "^6.2.2",
    "stylus": "^0.54.7",
    "stylus-loader": "^3.0.2",
    "vue-cli-plugin-element": "~1.0.1",
    "vue-template-compiler": "^2.6.11"
  }
}

路由vuex代码如下

import ApiAuth from '@/api/ApiAuth'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { constantRoutes } from '@/router'
import Layout from '@/layout'
import SystemUserIndex from '@/views/system/user'

const asyncRouteMap = new Map()
asyncRouteMap.set('SystemUserIndex', SystemUserIndex)

/**
 * 构造路由名称
 * @param {string} component 组件地址
 * @returns 路由名称
 */
function buildRouteName (component) {
  if (typeof component !== 'string' || component.length === 0) {
    return []
  }
  const arr = component.split('/')
  if (component.startsWith('/')) {
    arr.shift()
  }
  if (arr.length === 0) {
    return arr
  }
  let routeName = ''
  arr.forEach(e => {
    routeName = routeName + e.slice(0, 1).toUpperCase() + e.slice(1)
  })
  return routeName
}

/**
 * 构造动态路由
 * @param {*} routes 动态路由数组
 * @param {*} menus 用户菜单
 */
function filterAsyncRoutes (routes, menus) {
  menus.forEach(menu => {
    const route = {
      name: buildRouteName(menu.component),
      path: menu.path,
      // component: (resolve) => require([`@/views${menu.component}`], resolve),
      component: () => import('@/views' + menu.component),
      meta: {
        icon: menu.icon,
        title: menu.name
      }
    }
    if (asyncRouteMap.has(route.name)) {
      route.component = asyncRouteMap.get(route.name)
    }
    if (menu.children) {
      route.children = []
      filterAsyncRoutes(route.children, menu.children)
    }
    routes.push(route)
  })
}

const auth = {
  state: {
    token: getToken(),
    name: '',
    avatar: '',
    roles: [],
    permissions: [],
    menus: [],
    addRoutes: [],
    routes: []
  },

  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_NAME: (state, name) => {
      state.name = name
    },
    SET_AVATAR: (state, avatar) => {
      state.avatar = avatar
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles
    },
    SET_PERMISSIONS: (state, permissions) => {
      state.permissions = permissions
    },
    SET_MENUS: (state, menus) => {
      state.menus = menus
    },
    SET_ROUTES: (state, routes) => {
      state.addRoutes = routes
      state.routes = constantRoutes.concat(routes)
    }
  },

  actions: {
    // 登录
    Login ({ commit }, data) {},

    // 获取用户信息
    GetInfo ({ commit, state }) {},

    // 退出
    LogOut ({ commit, state }) {},

    // 生成动态路由
    GenerateRoutes ({ commit, state }) {
      return new Promise((resolve, reject) => {
        ApiAuth.getMenuTree().then(res => {
          commit('SET_MENUS', res.body)
          const accessedRoutes = []
          state.menus.forEach(e => {
            e.parentId = 0
          })
          filterAsyncRoutes(accessedRoutes, state.menus)
          commit('SET_ROUTES', accessedRoutes)
          accessedRoutes.forEach(e => {
            e.component = Layout
          })
          accessedRoutes.push({
            path: '/*',
            redirect: '/404'
          })
          resolve(accessedRoutes)
        }).catch(error => {
          reject(error)
        })
      })
    }
  }
}

export default auth

路由守卫

import router from './router'
import { Message } from 'element-ui'
import store from './store'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'

NProgress.configure({ showSpinner: false })

// 前端路由白名单
const whiteList = ['/login']

router.beforeEach((to, from, next) => {
  NProgress.start()
  /* 已登录 */
  if (getToken()) {
    // TODO 设置标题
    // 已登录过不需要再访问登录页
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done()
    } else {
      // 如果用户信息为空, 则获取用户信息
      if (store.getters.roles.length === 0) {
        store.dispatch('GetInfo').then(() => {
          // 获取用户路由列表
          store.dispatch('GenerateRoutes').then(accessRoutes => {
            router.addRoutes(accessRoutes)
            router.options.routes.push(...accessRoutes)
            console.log(JSON.stringify(router.options.routes))
            next({ ...to, replace: true })
          })
        }).catch(error => {
          console.log(error)
          store.dispatch('LogOut').then(() => {
            Message.error(error)
            next({ path: '/' })
          })
        })
      } else {
        next()
      }
    }
  } else {
    /* 没有登录过 */
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  NProgress.done()
})

其中一级路由的component是 Layout,二级路由中 /system/user 使用的是import的组件, 其他的二级路由都是动态的import

第一个是路由数据的区别

我打印了路由之后发现有如下两个区别

image.png

第二个算是 vue 开发者工具的问题和第一个问题类似

为什么在 vue 开发者工具里面的vuex, 提前import的可以显示路径, 动态import的就不行

image.png

最后还有几个小问题

  1. 之前动态import一直在报错: vue Cannot read property 'range' of null, 然后降级babel-eslint好像也没什么用, 重新下载依赖也没用, 今天又重新下载之后又好了. 如果确实是某些依赖没有下载完整或者某些依赖版本不正确, 那有没有比较好的办法解决而不是看运气或者直接不用动态import
  2. vue 开发者工具中的路由的 options 是干啥的, 为啥一级路由有, 其他的子路由没有
    image.png
  3. 三级路由激活的时候, vue 开发者工具中的路由怎么没有 active 了

以上

阅读 1.3k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题