为了账号安全,请及时绑定邮箱和手机立即绑定

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,正努力成为一名出色的工程师

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消