如何使用require.ensure在Webpack中拆分代码进行生产?

时间:2016-05-14 04:19:00

标签: webpack commonjs

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

这是我的文件结构:

app.js       <-- Entry point as introduced to Webpack
Module.js    <-- To be loaded dynamically

app.jsModule.js之间没有直接连接,而第二个文件由第一个文件加载,如下所示:

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

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

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

  

未捕获错误:无法找到模块&#39; ./ Module&#39;。

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

  警告在./src/app/app.js中   关键依赖关系:   74:34-47依赖的请求是表达式

为生产配置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;
});

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

  

未捕获TypeError: webpack_require (...)。确保不是函数

仍然没有运气!

2 个答案:

答案 0 :(得分:2)

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

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

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

关于配置,您需要使用namedModulesPluginsCommonsChunkPlugin。我个人所做的是拥有包含所有常见来源的main.jsvendor.js,包含所有常见node_modulesruntime.js(网络包需要)。然后,这些块被分开,如果一个块依赖于特定的供应商依赖关系,它将被添加到这个特定的块而不是普通的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的哈希值都会发生变化,即使您没有修改过您的deps,如果您对缓存感到担忧,那么这很糟糕。

最近的webpack visualizer是一个非常好的工具,用于检查您的包和块的外观,并验证看起来没问题或检测可能更好的分块。如果使用StatsWriterPlugin收集统计信息以便它处理您的块,您可能需要更改一下配置:

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和类似babel syntax-dynamic-import插件(现在应该在stage-3 preset中)或dynamic-import-webpack,如果不是为你工作(基本上将import()转换为ensures)。

您仍然需要指定完整路径,例如确保您是否希望获得每个模块的块数,但这是一种更好的语法,并且在未来更具针对性。

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

答案 1 :(得分:1)

您需要在require.ensure的第一个参数中传递您想要的模块:

require.ensure(['./Module'], function(require) {
    const module = require('./Module');
});