webpack的简单代码分割

webpack官网把代码分割分了三个部分 => 地址

  • Vendor code splitting
  • CSS splitting
  • On demand code-splitting

这篇写个人对Vendor code splitting部分的一些尝试。

目的

Vendor code splitting主要处理第三方插件框架打包的问题,vendor是供应商的意思,因为这部分的代码基本不会变,如果和应用代码带包到一起(应用代码是经常改动的),打包出来的chunks也会每次都不一样,这样我们就不能利用缓存去加快访问这些资源的速度,所以我们希望将这部分代码分离出来生成一个独立的文件。

使用

以jquery的打包为例,首先安装jquery

1
npm install --save jquery

配置webpack.config.js,设置了入口和打包文件输出设置

entry
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

let path = require("path");
let webpack = require("webpack");

modules.exports = {
entry: {
app: "./src/app.js",
main: "./src/main.js",
},
output: {
filename: assetDir+"js/[name].[chunkhash].js",
publicPath: "",
path: path.resolve(__dirname, "./dist"),
},
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
]
}

在main.js中编写代码:

1
2
3
4
5
6
7
8

import $ from "jquery"
import image from "./image/2.jpg"

let $image = $("<img></img>");
$image.attr("src", image);
console.log($image);
$(document.body).append($image);

然后打包会发现有main.js打出来的包特别大(当然没启用代码压缩)

1
2
3
                                 Asset       Size  Chunks                    Chunk Names
static/js/main.1bfd830214e54480638a.js 281 kB 0 [emitted] [big] main
index.html 304 bytes [emitted]

为了把jquery打包成独立文件,需要使用CommonsChunkPlugin插件。
在entry配置加一项:

1
2
3
4
5
6
7

entry: {
app: "./src/app.js",
main: "./src/main.js",
vendor: ["jquery"]
},

再启用CommonsChunkPlugin插件,把entry的vendor项进行分割打包

webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

let path = require("path");
let webpack = require("webpack");

modules.exports = {
entry: {
app: "./src/app.js",
main: "./src/main.js",
vendor: ["jquery"]
},
output: {
filename: assetDir+"js/[name].[chunkhash].js",
publicPath: "",
path: path.resolve(__dirname, "./dist"),
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ["vendor"],
}),
]
}

重新运行webpack

1
2
3
4
5
6
7
8
                                  Asset       Size  Chunks                    Chunk Names
static/js/main.2dd0efc1bc2a64606e3f.js 10.4 kB 0 [emitted] main
static/js/vendor.563278ce106e847a9d92.js 274 kB 1 [emitted] [big] vendor
index.html 391 bytes [emitted]
[0] ./~/jquery/dist/jquery.js 268 kB {1} [built]
[1] ./src/image/2.jpg 9.75 kB {0} [built]
[2] ./src/main.js 455 bytes {0} [built]
[3] multi jquery 28 bytes {1} [built]

可以看到jquery被打包到chunk 1,即vendor.[chunkhash].js中,main.js体积变小了很多。

不过改下main.js的内容,加个console.log(“hello,world”)什么的再webpack发现vendor的哈希值变了,然而我们并没有改动jquery的内容。

But, if we change application code and run webpack again, we see that the hash for the vendor file changes. Even though we achieved separate bundles for vendor and main bundles, we see that the vendor bundle changes when the application code changes. This means that we still don’t reap the benefits of browser caching because the hash for vendor file changes on every build and the browser will have to reload the file.

The issue here is that on every build, webpack generates some webpack runtime code, which helps webpack do its job. When there is a single bundle, the runtime code resides in it. But when multiple bundles are generated, the runtime code is extracted into the common module, here the vendor file.

To prevent this, we need to extract out the runtime into a separate manifest file. Even though we are creating another bundle, the overhead is offset by the long term caching benefits that we obtain on the vendor file.

webpack官网的解释

把runtime code提取到manifest文件就可以了

1
2
3
4
5
6
7

plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ["vendor","manifest"],
}),
]

再改main.js发现vendor的哈希不变,manifest的哈希变了

如果不确定哪些第三方库需要分割,可以使用minChunks属性

minChunks: number|Infinity|function(module, count) -> boolean,
// The minimum number of chunks which need to contain a module before it’s moved into the commons chunk.
// The number must be greater than or equal 2 and lower than or equal to the number of chunks.
// Passing Infinity just creates the commons chunk, but moves no modules into it.
// By providing a function you can add custom logic. (Defaults to the number of chunks)

属性介绍

比如minChunks设为2,如果有两个模块都用到了jquery,就会自动分割打包jquery
minChunks设为函数时,接受两个参数module,count

You also have the ability to pass the minChunks property a function. This function is called by the CommonsChunkPlugin and calls the function with module and count arguments.

The module argument represents each module in the chunks you have provided via the name/names property. module has the shape of a NormalModule, which has two particularly useful properties for this use case:

module.context: The directory that stores the file. For example: ‘/my_project/node_modules/example-dependency’
module.resource: The name of the file being processed. For example: ‘/my_project/node_modules/example-dependency/index.js’
The count argument represents how many chunks the module is used in.

minChunks参数

比如把node_modules下的都分割出来,这样vendor就不用写了

webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

let path = require("path");
let webpack = require("webpack");

modules.exports = {
entry: {
app: "./src/app.js",
main: "./src/main.js",
// vendor: ["jquery"]
},
output: {
filename: assetDir+"js/[name].[chunkhash].js",
publicPath: "",
path: path.resolve(__dirname, "./dist"),
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ["vendor"],
minChunks: function (module) {
return module.context && module.context.indexOf('node_modules') !== -1;
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
}),
]
}

**更新**

这玩意变化的有点快。。一段时间就过时了,还是以文档为准吧,不写这种的了