webpack - 如何使用 require.ensure 拆分 Webpack 中的代码以进行生产?

标签 webpack commonjs

我正在使用 Webpack,我想将我的客户端代码分成几部分,并在用户需要它们时加载它们。

这是我的文件结构:

app.js       <-- Entry point as introduced to Webpack
Module.js    <-- To be loaded dynamically
app.js 之间没有直接联系和 Module.js ,而是第二个文件由第一个文件加载,如下所示:

require.ensure([], (require) => {
    let path = "Module";
    let module = require("./" + path).default;
});

我用了"./" + path只是为了确保 Webpack 不会对我变聪明并尝试静态解析路径。无论如何,这段代码在 webpack-dev-server 的开发模式下工作。我的意思是Module.js在我触发事件运行上述代码之前不会下载。之后,它被完美地加载和执行。

但是当我打包项目进行生产时,它停止工作。它给出了以下错误(当我触发下载事件时在浏览器中),甚至没有尝试发送请求:

Uncaught Error: Cannot find module './Module'.



此外,当我动态组合路径时(如上面的代码),构建过程会发出以下警告:

WARNING in ./src/app/app.js Critical dependencies: 74:34-47 the request of a dependency is an expression



为生产配置 Webpack 以支持动态下载代码拆分的正确方法是什么?

我已经测试了@wollnyst 给出的解决方案,结果如下。当我这样实现它时,它可以工作:

require.ensure(["./Module"], (require) => {
    let path = "Module";
    let module = require("./" + path).default;
});

但这不是我想要的,我想要这样:

let path = "Module";
require.ensure(["./" + path], (require) => {
    let module = require("./" + path).default;
});

现在它在浏览器中发出运行时错误:

Uncaught TypeError: webpack_require(...).ensure is not a function



还是没有运气!

最佳答案

将您想要动态要求的路径放入 require.ensure数组第一个参数是错误的,不应该这样做。此数组旨在用于您要动态加载的模块的依赖项,以及回调中的异步代码安全运行所需的依赖项。

理解 webpack 如何处理代码拆分的关键部分是你不能做完全动态的语句,因为 webpack 需要一些位置信息来解析你想要的模块,因此 dependency is an expression警告。即使您可以通过预先添加 ./ 来变得更聪明,最好实际重复完整的ensure使用静态模块路径字符串的语句,即使您有很多模块要动态加载,这有点令人讨厌,这样您就不会遇到任何问题。

另一种方法是创建一个专用于该目的的文件夹,splits例如,从这里解析所有模块。但这意味着您在此目录中需要的每个模块都将位于同一个拆分点,除了特定的用例之外,这并不是任何人想要的。

关于配置,您需要使用 namedModulesPluginsCommonsChunkPlugin一。我个人所做的是拥有 main.js包含所有常见来源,一个 vendor.js ,包含所有常见的node_modules , 和 runtime.js (webpack 需要)。然后,这些 block 被分离,如果一个依赖于特定的 vendor 依赖,它将被添加到这个特定的 block 而不是常见的 vendor.js。 .

plugins: [
  new webpack.NamedModulesPlugin(),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: module => module.context && module.context.indexOf('node_modules'),
  }),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'runtime',
  }),
],

您还可以确保拥有类似于以下 output.filename 的内容:

output: {
  filename: '[name]-[chunkhash].js',
}

否则你的 vendor.js 的哈希值每次您的来源更改时都会更改,即使您没有修改您的部门,如果您担心缓存,这很糟糕。

近期webpack visualizer是一个非常好的工具,可以检查你的包和 block 的外观,并验证事情看起来是否正常或检测可以更好地分 block 的依赖关系。如果使用 StatsWriterPlugin,您可能需要更改一些配置。收集统计数据以便它处理您的 block :

new StatsWriterPlugin({
  fields: null,
  transform: (data, opts) => {
    const stats = opts.compiler.getStats().toJson({ chunkModules: true })
    return JSON.stringify(stats, null, 2)
  },
}),

还需要注意的一件事是 require.ensure特定于 webpack 并被 import() 取代它现在也可以处理。由于这个问题来自 2016 年,它可能与我们现在使用 webpack 2 的元素不同,所以我将对其进行一些扩展。

现在,dynamic import提案正在进入 ES,你可以在 webpack 中使用它。您需要一个 Promise polyfill和通天塔之类的东西 syntax-dynamic-import插件(现在应该在 stage-3 preset 中)或 dynamic-import-webpack如果这对您还不起作用(基本上将 import() 转换为 ensures )。

如果您想为每个模块获取一个 block ,您仍然需要指定完整路径,例如确保,但这是一种更好的语法,并且在 future 更加面向。

您可以在新的 webpack 文档页面上查看有关 code splitting 的许多其他资源。 .

关于webpack - 如何使用 require.ensure 拆分 Webpack 中的代码以进行生产?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37222466/

相关文章:

javascript - 将 Typescript 绝对路径转换为 ​​NodeJS 相对路径?

javascript - CommonJS、AMD 和 RequireJS 之间的关系?

google-chrome-extension - Chrome 扩展中的 CommonJS

webpack - SCSS 部分(@import)与 Webpack 文件导入?

webpack - Electron+Webpack = 找不到模块 : Error: Can't resolve fsevents/fs/etc in chokidar/etc

javascript - Webpack代码分割: Does it do anything? 好像没有效果

javascript - 如何将同构的 commonJS 代码与 webpack 捆绑在一起

javascript - 将 CouchDB 与 Node.js 库一起使用

nginx - 如何正确压缩 webpack block ?

typescript - 如何在 webpack 中使用 underscore.js