webpack的动态import
符合预期的CoyPan(id: Coy_Pan)
作者:CoyPan,BAT某厂符合预期的FE,正努力成为一名出色的工程师
写在前面
最近在搞一个通过配置生成页面的项目,遇到了这样一个问题:
for (let i = 0, len = pageData.selectSection.length; i < len; i++) {
const sectionName = pageData.selectSection[i].section_name;
import(`./template/${pageData.template_name}/${sectionName}/index`).then(item => {
const module = item.default;
...
});
}
我需要读取后台下发的配置文件,然后去动态import本地代码中对应的组件。
最终使用webpack打包后,每一个组件都会产出一个chunk。那么问题来了,我的组件引用路径是在运行时才能确定的,webpack是怎么在本地静态打包的时候,就找到我的组件的呢?
webpack的动态引用
查看webpack的文档,有以下的描述:
完全动态的语句(如
import(foo)
),因为 webpack 至少需要一些文件的路径信息,而foo
可能是系统或项目中任何文件的任何路径,因此foo
将会解析失败。import()
必须至少包含模块位于何处的路径信息,所以打包应当限制在一个指定目录或一组文件中。
调用
import()
时,包含在其中的动态表达式 request,会潜在的请求的每个模块。例如,import(
./locale/${language}.json)
会导致./locale
目录下的每个.json
文件,都被打包到新的 chunk 中。在运行时,当计算出变量language
时,任何文件(如english.json
或german.json
)都可能会被用到。
从文档可以看出,动态import,是会根据代码里面的给出的路径信息去查找文件的,并且会将所有满足这个路径信息的文件都打成一个chunk,以备后续使用。
对于运行时才能确定的路径,webpack到底是怎么处理的呢?
以上是在webpack打包过程中的一个对象。
可以看到,webpack其实是把
./template/${pageData.template_name}/${sectionName}/index
这个url中的变量直接全部忽略了,直接使用了一个正则/^\.\/.*\/index$/
和request
字段来表示最终的资源路径。
这个正则是怎么生成的呢?
在webpack源码的ContextDependencyHelpers.js
里面,有下面的代码:
// ContextDependencyHelpers.js
...
let prefixRaw =
param.prefix && param.prefix.isString() ? param.prefix.string : "";
let postfixRaw =
param.postfix && param.postfix.isString() ? param.postfix.string : "";
const valueRange = param.range;
const { context, prefix } = splitContextFromPrefix(prefixRaw);
const { postfix, query } = splitQueryFromPostfix(postfixRaw);
// 重点关注这行代码。拿到ast分析后的prefix和postfix,中间加上一个字符串,生成一个正则
const regExp = new RegExp(
`^${quotemeta(prefix)}${options.wrappedContextRegExp.source}${quotemeta(
postfix
)}$`
);
...
其中,wrappedContextRegExp
这个字段的值,默认是被webpack写死的,就是一个正则表达式:
// WebpackOptionDefaulter.js
this.set("module.wrappedContextRegExp", /.*/);
webpack通过loader(babel)拿到js代码的AST后,会对AST进行分析,获取到动态import的路径参数后,进行上述处理,并且把本地满足上述正则的路径里面的文件全都拆出来,生成chunk。所以,为了避免打出无用的chunk,在使用动态import的时候,尽可能使用准确的静态路径,减少变量的影响范围。一般可以像下面这样使用:
import('./components/'+ ComponentName).then(module => { ... })
这里需要注意,在动态import的时候,路径不能只使用一个变量表示,这样的话,webpack没有办法找到对应的文件。
// 下面代码是无效的
const a = './temp';
import(a).then(module => {
console.log(module.default);
});
// Critical dependency: the request of a dependency is an expression
写在后面
本文对webpack的动态import进行了一定的研究,对于webpack的动态import的策略有了进一步的认识,也为以后的使用扫除了一些坑,符合预期。
符合预期的CoyPan(id: Coy_Pan)
作者:CoyPan,BAT某厂符合预期的FE,正努力成为一名出色的工程师
共同学习,写下你的评论
评论加载中...
作者其他优质文章