整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

在 Webpack 中执行代码分割

在 Webpack 中执行代码分割

三种常用的代码分离方法:

  • 入口起点:使用 entry 配置手动地分离代码。
  • 防止重复:使用 CommonsChunkPlugin 去重和分离 chunk。
  • 动态导入:通过模块的内联函数调用来分离代码。

1、入口起点

这是迄今为止最简单、最直观的分离代码的方式。不过,这种方式手动配置较多

webpack.config.js


  1. const path=require('path');
  2. const HTMLWebpackPlugin=require('html-webpack-plugin');
  3. module.exports={
  4. entry: {
  5. index: './src/index.js',
  6. another: './src/another-module.js'
  7. },
  8. plugins: [
  9. new HTMLWebpackPlugin({
  10. title: 'Code Splitting'
  11. })
  12. ],
  13. output: {
  14. filename: '[name].bundle.js',
  15. path: path.resolve(__dirname, 'dist')
  16. }
  17. };

这将生成如下构建结果


  1. Hash: 309402710a14167f42a8
  2. Version: webpack 2.6.1
  3. Time: 570ms
  4. Asset Size Chunks Chunk Names
  5. index.bundle.js 544 kB 0 [emitted] [big] index
  6. another.bundle.js 544 kB 1 [emitted] [big] another
  7. [0] ./~/lodash/lodash.js 540 kB {0} {1} [built]
  8. [1] (webpack)/buildin/global.js 509 bytes {0} {1} [built]
  9. [2] (webpack)/buildin/module.js 517 bytes {0} {1} [built]
  10. [3] ./src/another-module.js 87 bytes {1} [built]
  11. [4] ./src/index.js 216 bytes {0} [built]

正如前面提到的,这种方法存在一些问题:

  • 如果入口 chunks 之间包含重复的模块,那些重复模块都会被引入到各个 bundle 中。
  • 这种方法不够灵活,并且不能将核心应用程序逻辑进行动态拆分代码。

2、防止重复

CommonsChunkPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。让我们使用这个插件,将之前的示例中重复的 lodash 模块去除:

webpack.config.js


  1. const path=require('path');
  2. + const webpack=require('webpack');
  3. const HTMLWebpackPlugin=require('html-webpack-plugin');
  4. module.exports={
  5. entry: {
  6. index: './src/index.js',
  7. another: './src/another-module.js'
  8. },
  9. plugins: [
  10. new HTMLWebpackPlugin({
  11. title: 'Code Splitting'
  12. - })
  13. + }),
  14. + new webpack.optimize.CommonsChunkPlugin({
  15. + name: 'common' // 指定公共 bundle 的名称。
  16. + })
  17. ],
  18. output: {
  19. filename: '[name].bundle.js',
  20. path: path.resolve(__dirname, 'dist')
  21. }
  22. };

这里我们使用 CommonsChunkPlugin 之后,现在应该可以看出,index.bundle.js 中已经移除了重复的依赖模块。需要注意的是,CommonsChunkPlugin 插件将 lodash 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了大小。执行 npm run build 查看效果:


  1. Hash: 70a59f8d46ff12575481
  2. Version: webpack 2.6.1
  3. Time: 510ms
  4. Asset Size Chunks Chunk Names
  5. index.bundle.js 665 bytes 0 [emitted] index
  6. another.bundle.js 537 bytes 1 [emitted] another
  7. common.bundle.js 547 kB 2 [emitted] [big] common
  8. [0] ./~/lodash/lodash.js 540 kB {2} [built]
  9. [1] (webpack)/buildin/global.js 509 bytes {2} [built]
  10. [2] (webpack)/buildin/module.js 517 bytes {2} [built]
  11. [3] ./src/another-module.js 87 bytes {1} [built]
  12. [4] ./src/index.js 216 bytes {0} [built]

以下是由社区提供的,一些对于代码分离很有帮助的插件和 loaders:

  • ExtractTextPlugin: 用于将 CSS 从主应用程序中分离。
  • bundle-loader: 用于分离代码和延迟加载生成的 bundle。
  • promise-loader: 类似于 bundle-loader ,但是使用的是 promises。

CommonsChunkPlugin 插件还可以通过使用显式的 vendor chunks 功能,从应用程序代码中分离 vendor 模块。

3、动态导入

当涉及到动态代码拆分时, 一般使用使用webpack提供的符合 ECMAScript 提案 的 import() 语法。

new Vue({


  1. el: '#app',
  2. components: {
  3. AsyncComponent: ()=> import('./AsyncComponent.vue')
  4. }
  5. });

实施代码分割并不难,难在搞清楚在什么时候、什么地方进行。

Vue.js 单页应用进行代码分割有三种思路:

  • 按页面分割
  • 使用折叠
  • 按条件分割

1. 按页面

按页面来进行代码分割,是最明显的一种方式。

如果能确保每个单文件组件代表一个页面,如 Home.vue, About.vue 以及 Contact.vue,那么我们就可以使用 Webpack 的 "动态导入" 函数 (import) 来将它们分割至单独的构建文件中。之后后,当用户访问一个新页面的时候,Webpack 将异步加载该请求的页面文件。

如果用到了 vue-router,由于页面已经分割成了单独的组件,实施起来会非常方便。


  1. const Home=()=> import(/* webpackChunkName: "home" */ './Home.vue');
  2. const About=()=> import(/* webpackChunkName: "about" */ './About.vue');
  3. const Contact=()=> import(/* webpackChunkName: "contact" */ './Contact.vue');
  4. const routes=[
  5. { path: '/', name: 'home', component: Home },
  6. { path: '/about', name: 'about', component: About },
  7. { path: '/contact', name: 'contact', component: Contact }
  8. ];

代码编译完成后,通过查看生成的统计数据得知:每个页面都有自己单独的文件,同时有多出来一个名为 build_main.js 的打包文件。里面包含一些公共的代码以及逻辑,用来异步加载其它文件,因此它需要在用户访问路由之前加载完成。

2. 折叠

“折叠” 是指页面初次加载时,视图的不可见部分。用户通常会花费 1~2 秒来浏览可视区域,特别是第一次访问网站的时候(可能更久),之后才开始向下滑动页面。

这个时候,可以异步加载剩余的内容。

3. 条件展示内容

代码分割另一种比较好的备选方式,是按条件展示。比如:模态框、标签页、下拉菜单之类。

如果我们需要一个模态框,给模态框设置 v-if 属性,绑定了 show 变量。一方面用来控制模态框是否显示,同时也决定了是否应该渲染模态框组件。当页面加载的时候,它的值为 false,模态框的代码只有当它显示的时候才会被加载。如果用户永远不打开这个模态框,这部分代码就永远不会被下载。缺点是,可能会增加很小的用户体验成本:用户点击按钮后,需要等待代码文件下载完成。

代码分离是webpack最关注的特性之一,此特性可以将代码分离到不同的bundle,以及控制加载资源优先级别,代码分离后可以压缩代码文件的大小,如果使用合理可以大幅提高资源文件的加载速度。

有三种常用的分离方法:

入口分离:使用entry手动配置分离代码。

防止重复:使用CommonsChunkPlugin去除重复和分离chunk.

动态导入:通过模块的内联函数调用来分离代码。

入口起点是最简单最直观的分离代码的方式。不过这种方式手动配置比较多,会有点坑。但是这些坑注定还是要解决的。

先建个demo项目,项目结构如下:

another-module.js:

index.js:

webpack.config.js:

可以看到index.js和another-module.js同时都有引入了lodash,这样做必然会产生重复引入loadsh。

构建项目结果:

这种方式虽然可以实现代码,但是造成代码重复引用。还好webpack提供了CommonsChunkPlugin插件去除重复,将重复的一部分抽取到单独一个公共文件里面。

现在修改一下webpack.config.js文件

new webpack.optimize.CommonsChunkPlugin({

name:'common'

})

添加抽取公共代码实例对象,common公共文件名称的前缀。

执行构建命令,在dist文件夹下多了common.bundle.js。这个就是公共文件

打开index.html,在控制台还是可以看到有index.js和another-module.js打印的信息。

在webpack中还有一种方式是动态导入的方式,这个可以去搜一下,这里就不演示了。

码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。

常用的代码分离方法有三种:

  • 入口起点:使用 entry 配置手动地分离代码。
  • 防止重复:使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk。
  • 动态导入:通过模块的内联函数调用来分离代码。

1.8.1 入口起点

这是迄今为止最简单直观的分离代码的方式。不过,这种方式手动配置较多,并有一些隐患,我们将会解决这些问题。先来看看如何从 main bundle 中分离 another module(另一个模块):

src目录下创建 another-module.js文件:

09-code-splitting/src/another-module.js

import _ from 'lodash'

console.log(_.join(['Another', 'module', 'loaded!'], ' '))

这个模块依赖了 lodash,需要安装一下:

 [felix] webpack5 $ npm install lodash --save-dev

修改配置文件:

module.exports={
   entry: {
     index: './src/index.js',
     another: './src/another-module.js',
   },
   output: {
     filename: '[name].bundle.js'
   },
 }

09-code-splitting/webpack.config.js

//...

module.exports={
entry: {
   index: './src/index.js',
   another: './src/another-module.js',
},

output: {
   filename: '[name].bundle.js'
    //...
  },

 //...
}

执行编译:

[felix] 09-code-splitting $ npx webpack
assets by status 744 KiB [cached] 4 assets
assets by status 1.44 MiB [emitted]
  asset another.bundle.js 1.38 MiB [emitted] (name: another)
  asset index.bundle.js 65.1 KiB [emitted] (name: index)
  asset app.html 441 bytes [emitted]
Entrypoint index 68.9 KiB (740 KiB)=styles/4a9cff551c7a105e1554.css 3.81 KiB index.bundle.js 65.1 KiB 3 auxiliary assets
Entrypoint another 1.38 MiB=another.bundle.js
runtime modules 3.23 KiB 12 modules
cacheable modules 549 KiB (javascript) 738 KiB (asset) 2.65 KiB (css/mini-extract)
  javascript modules 546 KiB
    modules by path ../node_modules/ 540 KiB 9 modules
    modules by path ./src/ 5.48 KiB 8 modules
  asset modules 3.1 KiB (javascript) 738 KiB (asset)
    ./src/assets/img-1.png 42 bytes (javascript) 101 KiB (asset) [built] [code generated]
    ./src/assets/webpack-logo.svg 2.99 KiB [built] [code generated]
    ./src/assets/example.txt 25 bytes [built] [code generated]
    ./src/assets/qianfeng-sem.jpg 42 bytes (javascript) 637 KiB (asset) [built] [code generated]
  json modules 565 bytes
    ./src/assets/json/data.toml 188 bytes [built] [code generated]
    ./src/assets/json/data.yaml 188 bytes [built] [code generated]
    ./src/assets/json/data.json5 189 bytes [built] [code generated]
  css ../node_modules/css-loader/dist/cjs.js!./src/style.css 2.65 KiB [built] [code generated]
webpack 5.54.0 compiled successfully in 854 ms

asset another.bundle.js 1.38 MiB [emitted] (name: another) , 我们发现 lodash.js也被打包到 another.bundle.js 中。

查看 app.html

09-code-splitting/dist/app

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>千锋大前端教研院-Webpack5学习指南</title>
<link href="styles/4a9cff551c7a105e1554.css" rel="stylesheet">
</head>

<body>
<script defer src="index.bundle.js"></script>
<script defer src="another.bundle.js"></script>
</body>

</html>

两个入口的 bundle文件都被链接到了 app.html中。

我们再来修改一下 index.js文件:

import _ from 'lodash'

console.log(_.join(['index', 'module', 'loaded!'], ' '))

09-code-splitting/src/index.js

// 导入模块
//...
import _ from 'lodash'

//...

console.log(_.join(['index', 'module', 'loaded!'], ' '))

执行编译:

[felix] 09-code-splitting $ npx webpack
assets by status 744 KiB [cached] 4 assets
assets by path . 2.82 MiB
  asset index.bundle.js 1.44 MiB [emitted] (name: index)
  asset another.bundle.js 1.38 MiB [compared for emit] (name: another)
  asset app.html 441 bytes [compared for emit]
Entrypoint index 1.44 MiB (740 KiB)=styles/4a9cff551c7a105e1554.css 3.81 KiB index.bundle.js 1.44 MiB 3 auxiliary assets
Entrypoint another 1.38 MiB=another.bundle.js
runtime modules 3.35 KiB 13 modules
cacheable modules 549 KiB (javascript) 738 KiB (asset) 2.65 KiB (css/mini-extract)
  javascript modules 546 KiB
    modules by path ../node_modules/ 540 KiB 9 modules
    modules by path ./src/ 5.57 KiB 8 modules
  asset modules 3.1 KiB (javascript) 738 KiB (asset)
    ./src/assets/img-1.png 42 bytes (javascript) 101 KiB (asset) [built] [code generated]
    ./src/assets/webpack-logo.svg 2.99 KiB [built] [code generated]
    ./src/assets/example.txt 25 bytes [built] [code generated]
    ./src/assets/qianfeng-sem.jpg 42 bytes (javascript) 637 KiB (asset) [built] [code generated]
  json modules 565 bytes
    ./src/assets/json/data.toml 188 bytes [built] [code generated]
    ./src/assets/json/data.yaml 188 bytes [built] [code generated]
    ./src/assets/json/data.json5 189 bytes [built] [code generated]
  css ../node_modules/css-loader/dist/cjs.js!./src/style.css 2.65 KiB [built] [code generated]
webpack 5.54.0 compiled successfully in 898 ms

观察一下:

assets by path . 2.82 MiB
  asset index.bundle.js 1.44 MiB [emitted] (name: index)
  asset another.bundle.js 1.38 MiB [compared for emit] (name: another)
  asset app.html 441 bytes [compared for emit]

我们发现:index.bundle.js 文件大小也骤然增大了,可以 lodash.js也被打包到了 index.bundle.js中了。

正如前面提到的,这种方式的确存在一些隐患:

  • 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中。
  • 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来。

以上两点中,第一点对我们的示例来说无疑是个问题,因为之前我们在 ./src/index.js 中也引入过 lodash,这样就在两个 bundle 中造成重复引用。

1.8.2 防止重复

  • 入口依赖 配置 "https://webpack.docschina.org/configuration/entry-context/#dependencies">dependOn option 选项,这样可以在多个 chunk 之间共享模块:
module.exports={
  entry: {
    index: {
      import: './src/index.js',
      dependOn: 'shared',
    },
    another: {
      import: './src/another-module.js',
      dependOn: 'shared',
    },
    shared: 'lodash',
  }
}

09-code-splitting/webpack.config.js

//...

module.exports={
entry: {
index: {
 import: './src/index.js',
 dependOn: 'shared',
},
another: {
 import: './src/another-module.js',
   dependOn: 'shared',
   },
    shared: 'lodash',
  },

 //...
}

执行编译

[felix] 09-code-splitting $ npx webpack
assets by status 744 KiB [cached] 4 assets
assets by status 1.45 MiB [emitted]
asset shared.bundle.js 1.39 MiB [emitted] (name: shared)
asset index.bundle.js 57.1 KiB [emitted] (name: index)
asset another.bundle.js 1.53 KiB [emitted] (name: another)
asset app.html 487 bytes [emitted]
Entrypoint index 60.9 KiB (740 KiB)=styles/4a9cff551c7a105e1554.css 3.81 KiB index.bundle.js 57.1 KiB 3 auxiliary assets
Entrypoint another 1.53 KiB=another.bundle.js
Entrypoint shared 1.39 MiB=shared.bundle.js
runtime modules 4.47 KiB 9 modules
cacheable modules 549 KiB (javascript) 738 KiB (asset) 2.65 KiB (css/mini-extract)
javascript modules 546 KiB
modules by path ../node_modules/ 540 KiB 9 modules
modules by path ./src/ 5.57 KiB 8 modules
asset modules 3.1 KiB (javascript) 738 KiB (asset)
./src/assets/img-1.png 42 bytes (javascript) 101 KiB (asset) [built] [code generated]
./src/assets/webpack-logo.svg 2.99 KiB [built] [code generated]
./src/assets/example.txt 25 bytes [built] [code generated]
./src/assets/qianfeng-sem.jpg 42 bytes (javascript) 637 KiB (asset) [built] [code generated]
json modules 565 bytes
./src/assets/json/data.toml 188 bytes [built] [code generated]
./src/assets/json/data.yaml 188 bytes [built] [code generated]
./src/assets/json/data.json5 189 bytes [built] [code generated]
css ../node_modules/css-loader/dist/cjs.js!./src/style.css 2.65 KiB [built] [code generated]
webpack 5.54.0 compiled successfully in 1237 ms

观察一下:

 assets by status 1.45 MiB [emitted]
  asset shared.bundle.js 1.39 MiB [emitted] (name: shared)
  asset index.bundle.js 57.1 KiB [emitted] (name: index)
  asset another.bundle.js 1.53 KiB [emitted] (name: another)
  asset app.html 487 bytes [emitted]

index.bundle.jsanother.bundle.js共享的模块lodash.js被打包到一个单独的文件shared.bundle.js中。

SplitChunksPlugin

SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。让我们使用这个插件,将之前的示例中重复的 lodash 模块去除:

entry: {
	index: './src/index.js',
  another: './src/another-module.js'
},
    
optimization: {
  splitChunks: {
  	chunks: 'all',
  },
},

09-code-splitting/webpack.config.js

//...

module.exports={
// entry: {
//   index: {
//     import: './src/index.js',
//     dependOn: 'shared',
//   },
//   another: {
//     import: './src/another-module.js',
//     dependOn: 'shared',
//   },
//   shared: 'lodash',
// },
entry: {
 index: './src/index.js',
 another: './src/another-module.js'
},

//...

optimization: {
   //...

 splitChunks: {
   chunks: 'all',
 }
 },
}

执行编译

执行编译:
[felix] 09-code-splitting $ npx webpack
assets by status 744 KiB [cached] 4 assets
assets by status 1.46 MiB [emitted]
  asset vendors-node_modules_lodash_lodash_js.bundle.js 1.37 MiB [emitted] (id hint: vendors)
  asset index.bundle.js 75.3 KiB [emitted] (name: index)
  asset another.bundle.js 17.2 KiB [emitted] (name: another)
  asset app.html 518 bytes [emitted]
Entrypoint index 1.45 MiB (740 KiB)=vendors-node_modules_lodash_lodash_js.bundle.js 1.37 MiB styles/4a9cff551c7a105e1554.css 3.81 KiB index.bundle.js 75.3 KiB 3 auxiliary assets
Entrypoint another 1.39 MiB=vendors-node_modules_lodash_lodash_js.bundle.js 1.37 MiB another.bundle.js 17.2 KiB
runtime modules 8.1 KiB 17 modules
cacheable modules 549 KiB (javascript) 738 KiB (asset) 2.65 KiB (css/mini-extract)
  javascript modules 546 KiB
    modules by path ../node_modules/ 540 KiB 9 modules
    modules by path ./src/ 5.57 KiB 8 modules
  asset modules 3.1 KiB (javascript) 738 KiB (asset)
    ./src/assets/img-1.png 42 bytes (javascript) 101 KiB (asset) [built] [code generated]
    ./src/assets/webpack-logo.svg 2.99 KiB [built] [code generated]
    ./src/assets/example.txt 25 bytes [built] [code generated]
    ./src/assets/qianfeng-sem.jpg 42 bytes (javascript) 637 KiB (asset) [built] [code generated]
  json modules 565 bytes
    ./src/assets/json/data.toml 188 bytes [built] [code generated]
    ./src/assets/json/data.yaml 188 bytes [built] [code generated]
    ./src/assets/json/data.json5 189 bytes [built] [code generated]
  css ../node_modules/css-loader/dist/cjs.js!./src/style.css 2.65 KiB [built] [code generated]
webpack 5.54.0 compiled successfully in 914 ms

观察一下

assets by status 1.46 MiB [emitted]
  asset vendors-node_modules_lodash_lodash_js.bundle.js 1.37 MiB [emitted] (id hint: vendors)
  asset index.bundle.js 75.3 KiB [emitted] (name: index)
  asset another.bundle.js 17.2 KiB [emitted] (name: another)
  asset app.html 518 bytes [emitted]

使用 optimization.splitChunks 配置选项之后,现在应该可以看出,index.bundle.jsanother.bundle.js 中已经移除了重复的依赖模块。需要注意的是,插件将 lodash 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了大小。

1.8.3 动态导入

当涉及到动态代码拆分时,webpack 提供了两个类似的技术。第一种,也是推荐选择的方式是,使用符合 ECMAScript 提案 的 "https://webpack.docschina.org/api/module-methods/#import-1">import() 语法 来实现动态导入。第二种,则是 webpack 的遗留功能,使用 webpack 特定的 require.ensure。让我们先尝试使用第一种……

创建 async-module.js文件:

内容如下:

09-code-splitting/src/async-module.js

function getComponent() {
 return import('lodash')
    .then(({
      default: _
    })=> {
      const element=document.createElement('div')

      element.innerHTML=_.join(['Hello', 'webpack'], ' ')
      return element
    })
    .catch((error)=> 'An error occurred while loading the component')
}

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

在入口文件中导入:

import './async-module'

09-code-splitting/src/index.js

// 导入模块
//...
import './async-module'

//...

执行编译:

       [felix] 09-code-splitting $ npx webpack
assets by status 744 KiB [cached] 4 assets
assets by status 1.53 MiB [compared for emit]
  assets by chunk 1.46 MiB (id hint: vendors)
    asset vendors-node_modules_lodash_lodash_js.bundle.js 1.37 MiB [compared for emit] (id hint: vendors)
    asset vendors-node_modules_babel_runtime_regenerator_index_js-node_modules_css-loader_dist_runtime_-86adfe.bundle.js 93.8 KiB [compared for emit] (id hint: vendors)
  asset index.bundle.js 54.3 KiB [compared for emit] (name: index)
  asset another.bundle.js 17.2 KiB [compared for emit] (name: another)
  asset app.html 658 bytes [compared for emit]
Entrypoint index 1.52 MiB (740 KiB)=vendors-node_modules_lodash_lodash_js.bundle.js 1.37 MiB vendors-node_modules_babel_runtime_regenerator_index_js-node_modules_css-loader_dist_runtime_-86adfe.bundle.js 93.8 KiB styles/4a9cff551c7a105e1554.css 3.81 KiB index.bundle.js 54.3 KiB 3 auxiliary assets
Entrypoint another 1.39 MiB=vendors-node_modules_lodash_lodash_js.bundle.js 1.37 MiB another.bundle.js 17.2 KiB
runtime modules 9.21 KiB 18 modules
....

从打印的结果看,除了公共的 lodash 代码被单独打包到一个文件外,还生成了一个 vendors-node_modules_babel_runtime_regenerator_index_js-node_modules_css-loader_dist_runtime_-86adfe.bundle.js 文件。

我们看到,静态和动态载入的模块都正常工作了。

1.8.4 懒加载

懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

创建一个 math.js 文件,在主页面中通过点击按钮调用其中的函数:

09-code-splitting/src/math.js

export const add=()=> {
	return x + y
}

export const minus=()=> {
	return x - y
}

编辑 index.js文件:

const button=document.createElement('button')
button.textContent='点击执行加法运算'
button.addEventListener('click', ()=> {
  import(/* webpackChunkName: 'math' */ './math.js').then(({ add })=> {
    console.log(add(4, 5))
  })
})
document.body.appendChild(button)

这里有句注释,我们把它称为 webpack 魔法注释:webpackChunkName: 'math', 告诉webpack打包生成的文件名为 math

启动服务,在浏览器上查看:

?

第一次加载完页面,math.bundle.js不会加载,当点击按钮后,才加载 math.bundle.js文件。

1.8.5 预获取/预加载模块

Webpack v4.6.0+ 增加了对预获取和预加载的支持。

在声明 import 时,使用下面这些内置指令,可以让 webpack 输出 "resource hint(资源提示)",来告知浏览器:

  • prefetch(预获取):将来某些导航下可能需要的资源
  • preload(预加载):当前导航下可能需要资源

下面这个 prefetch 的简单示例中,编辑 index.js文件:

const button=document.createElement('button')
button.textContent='点击执行加法运算'
button.addEventListener('click', ()=> {
  import(/* webpackChunkName: 'math', webpackPrefetch: true */ './math.js').then(({ add })=> {
    console.log(add(4, 5))
  })
})
document.body.appendChild(button)

添加第二句魔法注释:webpackPrefetch: true

告诉 webpack 执行预获取。这会生成 <link rel="prefetch" href="math.js"> 并追加到页面头部,指示着浏览器在闲置时间预取 math.js 文件。

09-code-splitting/src/index.js

// 导入模块
//...
import './async-module'

//...

const button=document.createElement('button')
button.textContent='点击执行加法运算'
button.addEventListener('click', ()=> {
import( /* webpackChunkName: 'math', webpackPrefetch: true */ './math.js').then(({
   add
})=> {
   console.log(add(4, 5))
})
})
document.body.appendChild(button)

启动服务,在浏览器上查看:


我们发现,在还没有点击按钮时,math.bundle.js就已经下载下来了。同时,在 app.html里webpack自动添加了一句:

?

点击按钮,执行 4+5的加法运算。

与 prefetch 指令相比,preload 指令有许多不同之处:

  • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
  • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
  • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
  • 浏览器支持程度不同。

创建一个 print.js文件:

export const print=()=> {
  console.log('preload chunk.')
}

修改 index.js文件:

const button2=document.createElement('button')
button2.textContent='点击执行字符串打印'
button2.addEventListener('click', ()=> {
  import(/* webpackChunkName: 'print', webpackPreload: true */ './print.js').then(({ print })=> {
    print(4, 5)
  })
})
document.body.appendChild(button2)

09-code-splitting/src/index.js

// 导入模块
//...

import './async-module'

//...

const button2=document.createElement('button')
button2.textContent='点击执行字符串打印'
button2.addEventListener('click', ()=> {
import( /* webpackChunkName: 'print', webpackPreload: true */ './print.js').then(({
   print
})=> {
   print()
})
})
document.body.appendChild(button2)

启动服务,打开浏览器:

?仔细观察,发现 print.bundle.js未被下载,因为我们配置的是 webpackPreload, 是在父 chunk 加载时,以并行方式开始加载。点击按钮才加载的模块不会事先加载的。

我们修改一下引入方式:

09-code-splitting/src/index.js

//...
import( /* webpackChunkName: 'print', webpackPreload: true */ './print.js').then(({
print
})=> {
print()
})

再次刷新浏览器页面:

print.bundle.js被加载下来,是和当前index.bundle.js并行加载的。