动态加载

尽管您可以使用代码拆分章节中介绍的 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.jslittle-loader

总结

尽管 require.context 是一个比较小众的语法特性,但了解一下它也是件好事。如果您必须对文件系统中可用的多个文件执行查找,那么它就变得很有价值。如果您的查找比这更复杂,则必须使用其他允许您在运行时执行加载的替代方法。

回顾一下:

  • require.context 是一种经常隐藏在幕后的高级功能。如果必须对大量文件执行查找,请使用它。
  • 如果 import 以某种动态形式编写,Webpack 会生成一个 require.context 调用。这种情况下的代码可读性可能稍微好一些。
  • 这些技术仅适用于文件系统。如果你必须对网址进行操作,你应该研究客户端解决方案。

下一章将介绍如何在 Webpack 中使用 Web Worker。

用户头像
登录后发表评论