管理后台大都存在权限菜单用来限制用户的访问范围,权限控制通常有两种实现方式,一种是纯前端控制,由前端维护权限菜单,并通过权限菜单上的角色标识来限制用户的访问行为;另一种是后端根据用户的角色返回其对应的权限菜单配置,交由前端动态注册。前者的实现成本低,但相应的对权限的控制程度较低;后者实现成本高,但相应的权限控制更安全也更灵活,对于需要定期发布版本的项目来说也更加适合。
这里我们关注下其中的核心问题-路由动态注册部分,以下的内容都基于Vue2、Vue Router3以及Webpack4进行说明。
路由动态注册
Vue Router3本身提供了addRoute的API,对我们的需求进行了最重要的支持。对于Vue以及React这种单页应用框架,首屏加载通常会加载大量的基础文件导致白屏,所以我们通常会分割代码块再辅以懒加载来进行优化。
懒加载静态路由组件
有经验的同学肯定立马想到了import(),但在某些更老的项目中也会出现require(),如下:
() => import('@/views/demo')
(resolve) => require(['@/views/demo'], resolve);
这两种懒加载的方式有什么不同呢?前者是ECMAScript提案的动态导入方案,后者是CommonJs规范,两者Webpack都提供了编译支持,但前者Webpack4才开始支持,如果使用Webpack2或更老的版本我们就需要使用require。
懒加载动态路由组件
在懒加载静态路由组件时一切运行都很顺利,但在懒加载动态路由组件时,开始变得不顺利。
1. 使用import进行动态组件的懒加载
/**
* 根据组件路径加载组件对象
*
* @param {string} url 组件路径
*/
function loadComponent(url) {
// TODO 处理组件路径信息
return () => import(url);
}
运行时啪啪打脸,提示我们找不到指定的模块。
当我写死url时,运行就丝滑了,那么就是Webpack对动态加载有什么特殊点,马上看Webpack的文档,其中有这么一段话。
It is not possible to use a fully dynamic import statement, such as import(foo). Because foo could potentially be any path to any file in your system or project. The import() must contain at least some information about where the module is located. Bundling can be limited to a specific directory or set of files so that when you are using a dynamic expression - every module that could potentially be requested on an import() call is included.
中国话意思就是不能使用完全动态的import语句,例如import(foo)。是因为foo可能是系统或项目中任何文件的任何路径。import()必须至少包含一些关于模块的路径信息。打包可以限定于一个特定的目录或文件集,以便于在使用动态表达式时,包括可能在import()调用中请求的每个模块。
知道原因后开始修正代码,如下:
/**
* 根据组件路径加载组件对象
*
* @param {string} url 组件路径
*/
function loadComponent(url) {
// TODO 移除组件路径包含@等特殊含义的字符
// TODO 移除组件路径包含的根路径信息
return () => import(`@/views/${url}`);
}
遗憾的是运行依旧报错,怎么肥事,我明明已经按文档所述进行了修改,给了特定的路径信息啊。
然后开始在github的Webpack官方仓库的issues查找问题解决方案,其中有这么一个问题dynamic import fails when using template literal,alexander-akait回复了提问人:Yes, you are using webpack v4, it is fixed in webpack v5,指出V4版本的Webpack不支持导入一个动态表达式,在V5版本中得到支持。
我一看文档果然是V5的文档,切换到V4文档后就没有导入一个动态表达式相关的描述,而我当前项目使用的是Webpack4,知道问题后继续修改。
2. 使用export进行动态组件的懒加载
/**
* 根据组件路径加载组件对象
*
* @param {string} url 组件路径
*/
function loadComponent(url) {
// TODO 移除组件路径包含@等特殊含义的字符
// TODO 移除组件路径包含的根路径信息
return (resolve) => require([`@/views/${url}`], resolve);
}
再运行非常的丝滑顺畅。但有一点需要特别注意,因为是导入了一个动态表达式路径,在运行时才会确认最终组件路径,在编译阶段,Webpack并不知道运行时的组件有哪些,所以需要我们在动态表达式中包含一些关于模块的路径信息,Webpack会对路径信息下的所以文件进行打包处理来确保在运行时中包含我们需要的模块包。
总结
1、使用Webpack5时,可以直接使用import来实现动态导入;
2、使用Webpack4时,import只能导入静态路径,不能导入动态路径,require支持静态和动态路径;
3、使用Webpack2时,只能使用require来实现动态导入;
综上所述,在Webpack5及更新版本中使用import来实现动态导入,其他老版本使用require来实现动态导入。
Now,发现惊喜,创造可能
欢迎关注我的微信公众号【IT札记】
感谢大家的阅读,祝你在末来的生活和学习中一切顺利!