尽管您可以使用代码拆分章节中介绍的 Webpack 代码拆分功能,但是 Webpack 还可以通过 require.context
提供更加动态的方式来处理代码。
使用 require.context 进行动态加载
require.context 提供了一种代码分割的一般形式。假设您正在 Webpack 上开发一个静态站点,您可以把站点内容放在一个 ./pages/
下的目录中,这个目录接受 Markdown 形式的文件。
每一个文件都有 YAML 格式的元数据信息,我们可以基于文件名生成一个页面 URL,并将其映射到站点中。我们可以通过 require.context
来实现这一想法,代码如下:
// 可以通过 `yaml-frontmatter-loader` 和 `json-loader` 来处理页面
// 前者将元数据信息抽取出来,后者将这些信息转换为 json 格式,以方便后面使用
// 当前我们并没有处理 Markdown 文件
const req = require.context(
"json-loader!yaml-frontmatter-loader!./pages",
true, // 以递归的形式加载文件,设为 false 可以禁止递归
/^\.\/.*\.md$/ // 匹配以 .md 结尾的文件
);
可以将 loader 的定义写入到 Webpack 配置中,这里只是为了方便演示。
require.context
返回了一个类似于 require
的函数。这个函数还有模块 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");
上述例子中的技术还可以用于检查 Webpack 中文件的添加和测试。在这种情况下,您可以使用 require.context
加载一份文件,然后将 Webpack entry
指向这份文件。
这些信息足以在 Antwar 中生成整个网站。
动态 import 和动态路径
同样的想法适用于动态 import
。您可以传递部分路径,而不是传递完整路径。Webpack 在内部设置上下文。下面是一个简短的例子:
// 设置一个目标文件名
const target = "fi";
// 然后在某个地方引入
import(`translations/${target}.json`).then(...).catch(...);
该方法也适用于 require
,因为 Webpack 可以执行静态分析。例如,require(assets/modals/${imageSrc}.js);
将根据 imageSrc
的值解析 require
。
使用动态导入时,请在路径中指定文件扩展名,以便保持较小的上下文从而提高性能。
结合多个 require.context
通过将多个单独的 require.context
包装在函数中,可以将它们组合成一个:
const { concat, uniq } = require("lodash");
const combineContexts = (...contexts) => {
function webpackContext(req) {
// 找到第一个匹配的然后执行它
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;
};
处理动态路径
鉴于此处讨论的方法依赖于静态分析,Webpack 必须能够找到相关的文件,它不适用于所有可能的情况。如果您需要的文件在另一台服务器上或必须通过特定的终端访问,那么Webpack 是不够的。
在这种情况下,请考虑在 webpack 之上使用浏览器端 loader,如 $script.js 或 little-loader。
总结
尽管 require.context
是一个比较小众的语法特性,但了解一下它也是件好事。如果您必须对文件系统中可用的多个文件执行查找,那么它就变得很有价值。如果您的查找比这更复杂,则必须使用其他允许您在运行时执行加载的替代方法。
回顾一下:
require.context
是一种经常隐藏在幕后的高级功能。如果必须对大量文件执行查找,请使用它。- 如果
import
以某种动态形式编写,Webpack 会生成一个require.context
调用。这种情况下的代码可读性可能稍微好一些。 - 这些技术仅适用于文件系统。如果你必须对网址进行操作,你应该研究客户端解决方案。
下一章将介绍如何在 Webpack 中使用 Web Worker。