代码分离

本指南继续沿用起步和管理输出中的代码示例。。请确保你至少已熟悉其中提供的示例。

已经准备好:请下载:

指南起步:百度链接:https://pan.baidu.com/s/1SWkTAuL_6TMU9zpWU0NNLQ    提取码:7iyq

指南起步+管理输出:百度链接:https://pan.baidu.com/s/1KUMDbTtdCA1SomdH_tajEQ  提取码:dxe4(本章节下载,或者下载上面的指南起步,从指南开始,加入过程中报错请查看提示)

代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。
有三种常用的代码分离方法:
入口起点:使用 entry 配置手动地分离代码。
防止重复:使用 CommonsChunkPlugin 去重和分离 chunk。
动态导入:通过模块的内联函数调用来分离代码。

入口起点(entry points)
这是迄今为止最简单、最直观的分离代码的方式。不过,这种方式手动配置较多,并有一些陷阱,我们将会解决这些问题。先来看看如何从 main bundle 中分离另一个模块:
根据官方文档:


这将生成如下构建结果:

Administrator@PC-20190222QKVD MINGW64 /d/webpack-demo
$ npm run build
> webpack-demo@1.0.0 build D:webpack-demo
> webpack
Hash: 4370e653fc4d9f72e755
Version: webpack 4.30.0
Time: 6592ms
Built at: 2019-04-29 20:50:41
            Asset       Size  Chunks             Chunk Names
another.bundle.js   70.3 KiB       0  [emitted]  another
    app.bundle.js   70.6 KiB    1, 2  [emitted]  app
       index.html  315 bytes          [emitted]
  print.bundle.js   1.02 KiB       2  [emitted]  print
Entrypoint app = app.bundle.js
Entrypoint print = print.bundle.js
Entrypoint another = another.bundle.js
[1] ./src/print.js 85 bytes {1} {2} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} {1} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} {1} [built]
[4] ./src/index.js 451 bytes {1} [built]
[5] ./src/another-module.js 92 bytes {0} [built]
    + 1 hidden module
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
Child html-webpack-plugin for "index.html":
     1 asset
    Entrypoint undefined = index.html
    [2] (webpack)/buildin/global.js 472 bytes {0} [built]
    [3] (webpack)/buildin/module.js 497 bytes {0} [built]
        + 2 hidden modules
Administrator@PC-20190222QKVD MINGW64 /d/webpack-demo
$

正如前面提到的,这种方法存在一些问题:
如果入口 chunks 之间包含重复的模块,那些重复模块都会被引入到各个 bundle 中。
这种方法不够灵活,并且不能将核心应用程序逻辑进行动态拆分代码。
以上两点中,第一点对我们的示例来说无疑是个问题,因为之前我们在 ./src/index.js 中也引入过 lodash,这样就在两个 bundle 中造成重复引用。接着,我们通过使用 CommonsChunkPlugin 来移除重复的模块。
防止重复(prevent duplication)

CommonsChunkPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。让我们使用这个插件,将之前的示例中重复的 lodash 模块去除:
webpack.config.js文件代码(+表示新增,- 表示删除):

  const path = require('path');
+ const webpack = require('webpack');
  const HTMLWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: {
      index: './src/index.js',
      another: './src/another-module.js'
    },
    plugins: [
      new HTMLWebpackPlugin({
        title: 'Code Splitting'
-     })
+     }),
+     new webpack.optimize.CommonsChunkPlugin({
+       name: 'common' // 指定公共 bundle 的名称。
+     })
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };
这里我们使用 CommonsChunkPlugin 之后,现在应该可以看出,index.bundle.js 中已经移除了重复的依赖模块。需要注意的是,CommonsChunkPlugin 插件将 lodash 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了大小。执行 npm run build 查看效果:

Hash: 70a59f8d46ff12575481
Version: webpack 2.6.1
Time: 510ms
            Asset       Size  Chunks                    Chunk Names
  index.bundle.js  665 bytes       0  [emitted]         index
another.bundle.js  537 bytes       1  [emitted]         another
 common.bundle.js     547 kB       2  [emitted]  [big]  common
   [0] ./~/lodash/lodash.js 540 kB {2} [built]
   [1] (webpack)/buildin/global.js 509 bytes {2} [built]
   [2] (webpack)/buildin/module.js 517 bytes {2} [built]
   [3] ./src/another-module.js 87 bytes {1} [built]
   [4] ./src/index.js 216 bytes {0} [built]
更新完代码后,到此蓝色区域再测试的时候报错(官方文档不一定最新,问题同样是存在,而且相应的安装版本不一致也会报错)(如下提示):

Administrator@PC-20190222QKVD MINGW64 /d/webpack-demo
$ npm run build

> webpack-demo@1.0.0 build D:webpack-demo
> webpack

D:webpack-demonode_moduleswebpack-clibincli.js:74
                                throw err;
                                ^

Error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.
    at Object.get [as CommonsChunkPlugin] (D:webpack-demonode_moduleswebpacklibwebpack.js:185:10)
    at Object.(D:webpack-demowebpack.config.js:16:27)
    at Module._compile (D:webpack-demonode_modulesv8-compile-cachev8-compile-cache.js:178:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
    at Function.Module._load (internal/modules/cjs/loader.js:531:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (D:webpack-demonode_modulesv8-compile-cachev8-compile-cache.js:159:20)
    at WEBPACK_OPTIONS (D:webpack-demonode_moduleswebpack-clibinutilsconvert-argv.js:115:13)
    at requireConfig (D:webpack-demonode_moduleswebpack-clibinutilsconvert-argv.js:117:6)
    at D:webpack-demonode_moduleswebpack-clibinutilsconvert-argv.js:124:17
    at Array.forEach ()
    at module.exports (D:webpack-demonode_moduleswebpack-clibinutilsconvert-argv.js:122:15)
    at yargs.parse (D:webpack-demonode_moduleswebpack-clibincli.js:71:45)
    at Object.parse (D:webpack-demonode_modulesyargsyargs.js:567:18)
    at D:webpack-demonode_moduleswebpack-clibincli.js:49:8
    at Object.(D:webpack-demonode_moduleswebpack-clibincli.js:368:3)
    at Module._compile (internal/modules/cjs/loader.js:701:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
    at Function.Module._load (internal/modules/cjs/loader.js:531:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)
    at Object.(D:webpack-demonode_moduleswebpackbinwebpack.js:156:2)
    at Module._compile (internal/modules/cjs/loader.js:701:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! webpack-demo@1.0.0 build: `webpack`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the webpack-demo@1.0.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     D:Program Filesnodejsnode_cache_logs2019-04-29T13_08_05_811Z-debug.log

Administrator@PC-20190222QKVD MINGW64 /d/webpack-demo
$ npm run build

> webpack-demo@1.0.0 build D:webpack-demo
> webpack

Hash: a075983a01dca59160fc
Version: webpack 4.30.0
Time: 6592ms
Built at: 2019-04-29 21:18:12
            Asset       Size  Chunks             Chunk Names
another.bundle.js   1.54 KiB       1  [emitted]  another
    app.bundle.js   1.72 KiB       2  [emitted]  app
commons.bundle.js   69.5 KiB       0  [emitted]  commons
       index.html  379 bytes          [emitted]
  print.bundle.js   1.43 KiB       3  [emitted]  print
Entrypoint app = commons.bundle.js app.bundle.js
Entrypoint print = commons.bundle.js print.bundle.js
Entrypoint another = commons.bundle.js another.bundle.js
[1] ./src/print.js 85 bytes {0} [built]
[2] ./src/index.js 451 bytes {2} [built]
[3] (webpack)/buildin/global.js 472 bytes {0} [built]
[4] (webpack)/buildin/module.js 497 bytes {0} [built]
[5] ./src/another-module.js 92 bytes {1} [built]
    + 1 hidden module

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
Child html-webpack-plugin for "index.html":
     1 asset
    Entrypoint undefined = index.html
    [2] (webpack)/buildin/global.js 472 bytes {0} [built]
    [3] (webpack)/buildin/module.js 497 bytes {0} [built]
        + 2 hidden modules

Administrator@PC-20190222QKVD MINGW64 /d/webpack-demo
$





如果也同样遇到错误请参考此篇文章:(迁移到webpack4:从webpack.optimize.CommonsChunkPlugin到config.optimization.splitChunk,以及有个搜出来的中文解决办法是错的)请复制括号中的文字百度,第一条数据就是。

继续下文:

以下是由社区提供的,一些对于代码分离很有帮助的插件和 loaders:
ExtractTextPlugin: 用于将 CSS 从主应用程序中分离。
bundle-loader: 用于分离代码和延迟加载生成的 bundle。
promise-loader: 类似于 bundle-loader ,但是使用的是 promises。
CommonsChunkPlugin 插件还可以通过使用显式的 vendor chunks 功能,从应用程序代码中分离 vendor 模块。

动态导入(dynamic imports)

当涉及到动态代码拆分时,webpack 提供了两个类似的技术。对于动态导入,第一种,也是优先选择的方式是,使用符合 ECMAScript 提案 的 import() 语法。第二种,则是使用 webpack 特定的 require.ensure。让我们先尝试使用第一种……
import() 调用会在内部用到 promises。如果在旧有版本浏览器中使用 import(),记得使用 一个 polyfill 库(例如 es6-promise 或 promise-polyfill),来 shim Promise。
在我们开始本节之前,先从配置中移除掉多余的 entry 和 CommonsChunkPlugin,因为接下来的演示中并不需要它们:
webpack.config.js文件代码(- 表示删除,+表示新增):
const path = require('path');
- const webpack = require('webpack');
  const HTMLWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: {
+     index: './src/index.js'
-     index: './src/index.js',
-     another: './src/another-module.js'
    },
    plugins: [
      new HTMLWebpackPlugin({
        title: 'Code Splitting'
-     }),
+     })
-     new webpack.optimize.CommonsChunkPlugin({
-       name: 'common' // 指定公共 bundle 的名称。
-     })
    ],
    output: {
      filename: '[name].bundle.js',
+     chunkFilename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };
注意,这里使用了 chunkFilename,它决定非入口 chunk 的名称。想了解 chunkFilename 更多信息,请查看 output 相关文档。接着,更新我们的项目,移除掉那些现在不会用到的文件:

project(项目目录)

webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
- |- another-module.js
|- /node_modules
现在,我们不再使用静态导入 lodash,而是通过使用动态导入来分离一个 chunk:
src/index.js文件代码:
- import _ from 'lodash';
-
- function component() {
+ function getComponent() {
-   var element = document.createElement('div');
-
-   // Lodash, now imported by this script
-   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+   return import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => {
+     var element = document.createElement('div');
+
+     element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+
+     return element;
+
+   }).catch(error => 'An error occurred while loading the component');
  }

- document.body.appendChild(component());
+ getComponent().then(component => {
+   document.body.appendChild(component);
+ })
注意,在注释中使用了 webpackChunkName。这样做会导致我们的 bundle 被命名为 lodash.bundle.js ,而不是 [id].bundle.js 。想了解更多关于 webpackChunkName 和其他可用选项,请查看 import() 相关文档。让我们执行 webpack,查看 lodash 是否会分离到一个单独的 bundle:

Hash: a27e5bf1dd73c675d5c9
Version: webpack 2.6.1
Time: 544ms
           Asset     Size  Chunks                    Chunk Names
lodash.bundle.js   541 kB       0  [emitted]  [big]  lodash
 index.bundle.js  6.35 kB       1  [emitted]         index
   [0] ./~/lodash/lodash.js 540 kB {0} [built]
   [1] ./src/index.js 377 bytes {1} [built]
   [2] (webpack)/buildin/global.js 509 bytes {0} [built]
   [3] (webpack)/buildin/module.js 517 bytes {0} [built]
由于 import() 会返回一个 promise,因此它可以和 async 函数一起使用。但是,需要使用像 Babel 这样的预处理器和Syntax Dynamic Import Babel Plugin。下面是如何通过 async 函数简化代码:

src/index.js文件代码:

- function getComponent() {
+ async function getComponent() {
-   return import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => {
-     var element = document.createElement('div');
-
-     element.innerHTML = _.join(['Hello', 'webpack'], ' ');
-
-     return element;
-
-   }).catch(error => 'An error occurred while loading the component');
+   var element = document.createElement('div');
+   const _ = await import(/* webpackChunkName: "lodash" */ 'lodash');
+
+   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+
+   return element;
  }

  getComponent().then(component => {
    document.body.appendChild(component);
  });

到此从新构建没报任何错误:

Administrator@PC-20190222QKVD MINGW64 /d/webpack-demo
$ npm run build

> webpack-demo@1.0.0 build D:webpack-demo
> webpack

Hash: 9b61bf2089f184ad0eb1
Version: webpack 4.30.0
Time: 5954ms
Built at: 2019-04-29 21:37:27
                   Asset       Size  Chunks             Chunk Names
           app.bundle.js   2.19 KiB       0  [emitted]  app
              index.html  189 bytes          [emitted]
vendors~lodash.bundle.js   69.4 KiB       1  [emitted]  vendors~lodash
Entrypoint app = app.bundle.js
[0] ./src/index.js 811 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {1} [built]
[3] (webpack)/buildin/module.js 497 bytes {1} [built]
    + 1 hidden module

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
Child html-webpack-plugin for "index.html":
     1 asset
    Entrypoint undefined = index.html
    [2] (webpack)/buildin/global.js 472 bytes {0} [built]
    [3] (webpack)/buildin/module.js 497 bytes {0} [built]
        + 2 hidden modules

Administrator@PC-20190222QKVD MINGW64 /d/webpack-demo
$


bundle 分析(bundle analysis)
如果我们以分离代码作为开始,那么就以检查模块作为结束,分析输出结果是很有用处的。官方分析工具 是一个好的初始选择。下面是一些社区支持(community-supported)的可选工具:
webpack-chart: webpack 数据交互饼图。
webpack-visualizer: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
webpack-bundle-analyzer: 一款分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户。

点赞(1) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部