我们在 Code Spliting 一章中介绍了一些动态加载的技术。本章,我们介绍一种更加灵活的动态加载技术,require.context
。
require.context
来动态加载
通过 require.contextopen in new window 提供了一种通用的代码分割形式。
假设我们使用 Webpack 来构建一个静态网站。网站内容都在 ./pages/
目录下,以 Markdown 形式保存。每个 Markdown 文件都有一个 YAML frontmatter 来定义元数据。我们可以通过如下方式来加载 Markdown 文件。
// 通过 `yaml-frontmatter-loader` and `json-loader` 来处理文件。
// `yaml-frontmatter-loader` 将文件中的 frontmatter 和文件内容解析出来,
// `json-loader` 再将其转换成 JSON 结构
// 在这个过程中,Markdown 不会被处理。
const req = require.context(
'json-loader!yaml-frontmatter-loader!./pages',
true, // 递归加载文件
/^\.\/.*\.md$/ // 匹配 `.md` 结尾的文件
);
2
3
4
5
6
7
8
9
require.context
返回一个函数,我们使用这个函数来加载文件。require.context
会创建一个模块,有自己的模块 id,同时提供一个 .keys()
方法,返回模块的内容(文件列表)。
req.keys(); // ["./demo.md", "./another-demo.md"]
req.id; // 42
// {title: "Demo", body: "# Demo page\nDemo content\n\n"}
const demoPage = req('./demo.md');
2
3
4
5
如果与 TypeScript 一起使用,要确保安装了 @types/webpack-envopen in new window,否则
require.context
无法工作。
import
中的动态路径
当我们给 import
的路径信息是一个动态路径的时候,Webpack 会内部创建一个 context.
const target = "fi";
import(`translations/${target}.json`).then(...).catch(...);
2
3
require
在遇到动态路径的处理方式与 import
相同。比如 require(
assets/modals/${imageSrc}.js);
,将会创建一个 context,并解析到一个 imageSrc
的文件。
在使用动态加载的时候,建议明确指定文件后缀,这样可以减小 context 的大小,同时提高构建性能。
require.context
组合多个 我们可以将多个独立的 require.context
合并成一个。
const { concat, uniq } = require('lodash');
const combineContexts = (...contexts) => {
function webpackContext(req) {
// Find the first match and execute
const matches = contexts
.map((context) => context.keys().indexOf(req) >= 0 && context)
.filter((a) => a);
return matches[0] && matches[0](req);
}
webpackContext.keys = () =>
uniq(
concat.apply(
null,
contexts.map((context) => context.keys())
)
);
return webpackContext;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
处理运行时的动态路径
Webpack 的动态加载还是要基于静态分析依赖关系的。如果动态加载的模块在其他地方,比如是一个网络文件,那么 Webpack 将无法完成动态加载。我们需要借助于其他工具,比如 script.jsopen in new window 或者 little-loaderopen in new window。
总结
当我们需要大量文件的时候,require.context
会是一个非常有用的特性。
动态 import
会在内部调用 require.context
。
require.context
仅适用于文件系统,如果要加载网络文件等形式的文件,需要借助于其他工具。
关注微信公众号,获取最新推送~
加微信,深入交流~