动态路由与导入

文章目录

路由

最近写了一次动态路由,由于路径原因导致了开发时的一些问题,在这里记录一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
一、require.ensure() 方法来实现代码打包分离

require.ensure() 是 webpack 特有的,已经被 import() 取代。

require.ensure(
dependencies: String[],
callback: function(require),
errorCallback: function(error),
chunkName: String
)
给定 dependencies 参数,将其对应的文件拆分到一个单独的 bundle 中,此 bundle 会被异步加载。
当使用 CommonJS 模块语法时,这是动态加载依赖的唯一方法。
意味着,可以在模块执行时才运行代码,只有在满足某些条件时才加载依赖项。
这个特性依赖于内置的 Promise。如果想在低版本浏览器使用 require.ensure
记得使用像 es6-promise 或者 promise-polyfill 这样 polyfill 库, 来预先填充(shim) Promise 环境。
var a = require('normal-dep');
if ( module.hot ) {
require.ensure(['b'], function(require) {
var c = require('c');
// Do something special...
});
}
按照上面指定的顺序,webpack 支持以下参数:

dependencies:字符串构成的数组,声明 callback 回调函数中所需的所有模块。
callback:只要加载好全部依赖,webpack 就会执行此函数。require 函数的实现,作为参数传入此函数。当程序运行需要依赖时,可以使用 require() 来加载依赖。函数体可以使用此参数,来进一步执行 require() 模块。
errorCallback:当 webpack 加载依赖失败时,会执行此函数。
chunkName:由 require.ensure() 创建出的 chunk 的名字。通过将同一个 chunkName 传递给不同的 require.ensure() 调用,我们可以将它们的代码合并到一个单独的 chunk 中,从而只产生一个浏览器必须加载的 bundle。
虽然我们将 require 的实现,作为参数传递给回调函数,然而如果使用随意的名字,
例如 require.ensure([], function(request) { request('someModule'); })
则无法被 webpack 静态解析器处理,所以还是请使用 require,例如 require.ensure([], function(require) { require('someModule'); })。

二、在vue中使用import()来代替require.ensure()实现代码打包分离

有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。
只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

Webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。

例:require.ensure()实现
const notFound = r => require.ensure([], () => r(require('@views/common/404')), 'index')
const login = r => require.ensure([], () => r(require('@views/common/login')), 'index')
const register = r => require.ensure([], () => r(require('@views/common/register')), 'index')
const main = r => require.ensure([], () => r(require('@views/main')), 'index')
const myWorks = r => require.ensure([], () => r(require('@views/my/index')), 'my')
const myAccountSetting = r => require.ensure([], () => r(require('@views/my/account-setting')), 'my')
const makeIndex = r => require.ensure([], () => r(require('@views/make/index')), 'make')
例:import()实现
const notFound = () => import(/* webpackChunkName: "index" */ '@views/common/404')
const login = () => import(/* webpackChunkName: "index" */ '@views/common/login')
const register = () => import(/* webpackChunkName: "index" */ '@views/common/register')
const main = () => import(/* webpackChunkName: "index" */ '@views/main')
const myWorks = () => import(/* webpackChunkName: "my" */ '@views/my/index')
const myAccountSetting = () => import(/* webpackChunkName: "my" */ '@views/my/account-setting')
const makeIndex = () => import(/* webpackChunkName: "make" */ '@views/make/index')

动态路由的组件加载写法
() => import(‘@/views/index’) 只能写字符串,不能写变量,因为这种写法是在打包时进行的
(resolve) => require([‘@/views/index’], resolve) ,可以写成变量形式,但是必须有头尾,如 (resolve) => require([‘@/‘ + path + ‘.vue’], resolve) ,webpack 通过 @/ 路径来确定要打包的目录, 通过 .vue 确定要打包的内容,缺一不可,不能写成 (resolve) => require([path], resolve),因为 webpack 也会同样不知道需要打包什么,打包的时候不知道要打包什么,就不会打包,编译时就会报错

据说以后都用 () => import(@/views/${path}),已经支持了,但潘家成这里不能用。

例子

动态路由流程

后台返回这样的数据时,前端需要做的事情有三步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
[
{
path: '/permission',
component: 'Layout',
redirect: '/permission/page',
alwaysShow: true, // will always show the root menu
name: 'Permission',
meta: {
title: 'Permission',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [
{
path: 'page',
component: 'permission/page',
name: 'PagePermission',
meta: {
title: 'Page Permission',
roles: ['admin'] // or you can only set roles in sub nav
}
},
{
path: 'directive',
component: 'permission/directive',
name: 'DirectivePermission',
meta: {
title: 'Directive Permission'
// if do not set roles, means: this page does not require permission
}
},
{
path: 'role',
component: 'permission/role',
name: 'RolePermission',
meta: {
title: 'Role Permission',
roles: ['admin']
}
}
]
},
]
  1. 在路由前置守卫 router.beforeEach 中,判断是否有 userInfo,没有的话就获取一下 userInfo,在这时候顺便把获取到的路由存放到 store(vuex) 里去,一般放到 user.state.routes,此时的路由由于 component 的原因,不能直接使用。
  2. 获取到 user 中的路由后,把 user 路由中的 component 转为真正的 vue 组件后保存在 permission 的 store 中,之后返回真正的 vue 路由,调用 router.addRoutes(newRoutes) 来新增路由
  3. 把 @/layout/components/Sidebar 里 的 v-for 遍历的 routes 改成 permission 里的 routes(全部路由,包含新增的)

部分代码如下,会写一些工具方法,getView 就是本文上面讨论的内容,也是写这篇记录的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/*
通过 component 的路径把 component 字段转为组件,这就是可行的写法。
组件格式: {
beforeCreate: {},
beforeDestroy: {},
components: {},
computed: {},
methods: {},
mixins: {},
name: {},
render: {},
staticRenderFns: {},
_Ctor: {},
__file: {},
_compiled: {},
_scopeId: {}
}
*/
function getView(path) {
/*
可以写成这样,是本文最上面的内容
return require.ensure([], (require) => {
resolve(require('@/views/' + path + '.vue'))
})
*/
return (resolve) => require(['@/views/' + path + '.vue'], resolve)
}

const keys = [
'path',
'children',
'hidden',
'redirect',
'meta',
'name',
'alwaysShow'
]

// 递归 children 字段把 component 字段转为组件
export function genRealRoutes(routes) {
return routes.map(route => {
const newRoute = {}
if (route.component) {
if (route.component === 'Layout') {
newRoute.component = Layout
} else {
newRoute.component = getView(route.component)
}
}
for (const key in route) {
if (keys.includes(key)) {
newRoute[key] = route[key]
}
}
if (newRoute.children && newRoute.children.length) {
newRoute.children = genRealRoutes(route.children)
}
return newRoute
})
}

// permission.js
// vuex 的 actions,调用 dispatch 返回一个路由
const actions = {
generateRoutes({ commit, state }, roles) {
return new Promise((resolve, reject) => {
let routes = store.user.routes
const accessedRoutes = genRealRoutes(routes)
commit(SET_ROUTES, accessedRoutes)
resolve(accessedRoutes)
})
}
}

/*
router.beforeEach
if (!userInfo) {
await store.dispatch('user/getInfo')
const routes = await store.dispatch('permission/generateRoutes')
router.addRoutes(routes)
next({ ...to, replace: true }) // hack 方法,由于此时直接进行下一个路由跳转,addRoute 路由可能未完成,重新进去一次当前路由可以保证路由添加成功
}
*/


// 剩下的改一下 @/layout/components/Sidebar 里 的 v-for 遍历的 routes 即可
分享到:

评论完整模式加载中...如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理