您的位置:首页 > Web前端 > Webpack

webpack性能优化 —— CommonsChunkPlugin

2017-09-10 15:21 1081 查看

前言:

相信大家对
webpack
并不陌生,用
webpack
打包也是非常常见的事,但是
webpack
在打包的时候有很多配置可以对打包就行优化。

最近正好在研究这方面的东西,所以在此记录一下,以便日后查看。

一、webpack-bundle-analyzer

在进入正文之前先说一个“好玩”的东西,是一个对webpack打包之后的文件进行分析的工具——webpack-bundle-analyzer

1. 安装

虽然是在
webpack
的配置文件中使用,但并不是
webpack
官方提供的插件,需要使用
npm
进行安装:

npm install webpack-bundle-analyzer --save-dev


2. 使用

首先在
webpack
的配置文件中引入
webpack-bundle-analyzer
,比如我的文件名是
webpack.demo.config.js


// webpack.demo.config.js

var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin


接着使用引入的
BundleAnalyzerPlugin


// webpack.demo.config.js

new BundleAnalyzerPlugin({
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
analyzerPort: 8888,
reportFilename: 'report.html',
defaultSizes: 'parsed',
openAnalyzer: true,
generateStatsFile: false,
statsFilename: 'stats.json',
statsOptions: null,
logLevel: 'info'
})


可以看到里面的参数还是很多的:

analyzerMode
:表示已什么方式查看分析结果,可选参数为
server
,
static
or
disabled


analyzerHost
analyzerMode
server
的时候生效,表示启动http服务的IP地址。

analyzerPort
analyzerMode
server
的时候生效,表示启动http服务的端口号。

reportFilename
analyzerMode
static
的时候生效,表示输出静态文件的文件名,输出路径
output
路径相同。

defaultSizes
:表示默认显示的数据模式,可选参数为
stat
,
parsed
or
gzip


openAnalyzer
true|false
,表示是否在打包完成之后自动打开分析界面。

generateStatsFile
true|false
,表示是否生成
.json
文件。

statsFilename
:设置
generateStatsFile
true
的时候生成的
.json
文件名。

statsOptions
:通过修改生成的
.json
文件中的
source:false
来排除模块的源码。

logLevel
:日志的显示方式,可选参数为
info
,
warn
,
error
or
silent


3. 效果

无论是通过http服务还是通过打开静态文件的方式效果都是一样的:



从上面就可以看出上面的数据有三种,分别是
defaultSizes
的三个参数,由于我设置的是
parsed
所以默认显示
parsed
的大小。

简单解释一下
defaultSizes
三个参数的含义:

stat
:是指打包之前的文件大小

parsed
:是指打包之后的文件大小

gzip
:是指通过
gzip
压缩之后
的文件大小

如果默认设置了
parsed
,但是我想看
stat
时候的文件大小视图改怎么办呢?

当然可以在这个分析界面中去调整:



4. 小结

把最近看到的好东西拿出来分享一下,觉得使用这个东西一眼就能看出打包后的结构,每个模块分的很清晰,而且通过视图中每个模块占的面积大小很直观的能看出各个文件的体积比例,这对打包的分析很有帮助,并且使用简单,不需要进行什么复杂的配置。

二、CommonsChunkPlugin

接下来进入正题,就是这个webpack本身提供的插件 —— CommonsChunkPlugin

首先,介绍一下插件的使用格式;

然后,分析其中的每个参数的作用;

最后,举一个实际的“栗子”。

1. 介绍

看到
CommonsChunkPlugin
这个名字,大概明白这个插件是跟公共模块有关系的。没错,这个插件就是用来提取出代码中公共的部分,并且将他们打包到一个单独的文件里面,这样就避免了重复打包。同时,由于配置中有
minChunks
(在文章后面会讲这个参数的作用)这个参数的存在可以满足一些其他的需求,不仅仅是提取公共部分这么简单了,而且一个文件中可以使用多次
CommonsChunkPlugin
插件。

另外,还需要知道两个名词:
chunk
chunkName


chunk
:通过
CommonsChunkPlugin
生成的一个文件就是一个chunk,由于可能多次使用插件,所以每次打完包可能会很多
chunk


注:如果是多入口,每一个入口文件都是一个
chunk


chunkName
:上面说过每使用一次插件就会生成一个
chunk
,那这些
chunk
通过什么区分呢?答案就是
chunkName


2. 使用

由于是
webpack
提供的插件,所以就不需要安装了,直接在配置文件中引入,同样在此还是以
webpack.demo.config.js
为配置文件:

// webpack.demo.config.js

var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin")

/*
* 省略其他代码
**/

new CommonsChunkPlugin({

name: string, // or

names: string[],

filename: string,

minChunks: number|Infinity|function(module, count) -> boolean,

chunks: string[],

children: boolean,

async: boolean|string,

minSize: number,
})


or

当然,你也可以选择在头部一次引入
webpack
,以后内置的插件都可以通过
webpack
来使用,像这样:

// webpack.demo.config.js

var webpack = require('webpack')

/*
* 省略其他代码
**/

new webpack.optimize.CommonsChunkPlugin({

name: string, // or

names: string[],

filename: string,

minChunks: number|Infinity|function(module, count) -> boolean,

chunks: string[],

children: boolean,

async: boolean|string,

minSize: number,
})


3. 参数

name|names
:这里的
name
就是上面所说的
chunkName
,如果包括过个
chunk
,那么就使用
names
,以数组的形式传入
chunkName
,如:

names: ['vendor','utils']


filename
:打包后的文件名,可以省略,如果省略默认文件名为
name
的值。

minChunks
:定义提取公共部分的规则。

个人觉得这个
minChunks
是整个插件最屌的没有之一(自行忽略)。

如果值为数字,例:
minChunks:2
,就说明提取文件中最少被引用2次的代码,

如果值为
Infinity
,将会直接创建一个公共的
chunk
,但是里面没有模块,

如果值为函数,将会提取出满足函数条件的代码,生成一个公共
chunk
,所以可以在这里实现以下定制化的需求。函数会提供两个参数:
module
count


chunks
:定义从哪些
chunk
中提取公共模块,如果省略,默认为入口
chunk


children
:默认是
false
,多个
chunk
里面的模块可能会有相同的依赖关系,如果设置为
true
,将其中相同的依赖提到父元素中(但是会影响初始加载的时间)。

async
:异步加载的附加公共
chunk
, 当下载附加组块时,它会并行自动下载。

minSize
:创建公共
chunk
之前,所有公共模块的的最小体积。

4. 实战

上面说了一堆理论,讲了一下基本结构,具体用法现在开始了!

下面是我一个Vue工程的项目结构:



现在我想要提取出
node_modules
中以
.js
结尾的文件和
src
下以
.vue
结尾的文件,将他们分别打到两个文件中。

先看一下入口和出口文件:

entry: {
main: './examples/main.js'
},
output: {
path: path.resolve(__dirname, '../demo'),
filename: '[name].[chunkhash].js',
}


接着,按照上面说的,我首先引入
CommonsChunkPlugin


var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin")


接着使用这个插件:

new CommonsChunkPlugin({
name: "list",
minChunks: function (module, count) {
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
new CommonsChunkPlugin({
name: 'components',
chunks: ['main'],
minChunks: function (module, count) {
return (
module.resource &&
/\.vue$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../src')
) === 0
)
}
}),
new CommonsChunkPlugin({
name: "manifest",
chunks: ['list', 'components']
})


打完包之后的结果是这样的,确实单独打出了两个文件:

Asset     Size  Chunks                    Chunk Names
components.8ec10c04b75e68ee707a.js   479 kB       1  [emitted]  [big]  components
main.a33511b70c4ad0a4aead.js   738 kB       2  [emitted]  [big]  main
list.b2de50f4150e217815ca.js   789 kB       3  [emitted]  [big]  list
manifest.9412c3435bf535f63115.js  6.07 kB       4  [emitted]         manifest


打包生成的文件目录结构是这样的:



webpack-bundle-analyzer
打开效果是这样的:



5. 进阶

看着文档很简单,但是实际操作起来就有许多问题,比如:

- 打包之后
Chunks
那一列数字是什么?

- 出口文件中的
chunkhash
是什么?

- 生成
manifest
又是什么?

-
minChunks
函数中的参数到底有什么作用?

chunkhash

首先,
chunkhash
既然叫
hash
,所以是一种
hash
值。当然,你也可以这么定义出口文件:

filename: '[name].[hash].js'


这里解释一下chunkhash与hash的区别:

- 对于打包生成的每个文件都有单独的hash值,这个hash值就叫做chunkhash;

- 而如果我们用
[hash]
定义出口文件,打包之后的文件hash值是一致的,因为这个hash是整个的hash值,我把出口文件改了之后打包生成的文件放在下面,可以跟上面打包后的文件对比一下;

- 所以,chunkhash与hash的区别就是私有hash与全局hash的关系。

Asset     Size  Chunks                    Chunk Names
components.26ae71410d030a4f8f66.js   479 kB       1  [emitted]  [big]  components
main.26ae71410d030a4f8f66.js   738 kB       2  [emitted]  [big]  main
list.26ae71410d030a4f8f66.js   789 kB       3  [emitted]  [big]  list
manifest.26ae71410d030a4f8f66.js  5.98 kB       4  [emitted]         manifest


manifest

那么
manifest
又是什么呢?其实就是
webpack
的一个清单文件,
manifest
的作用就是将你之前打包好的每个文件的
chunkhash
利用
webpack
缓存机制保存起来,如果下次没有修给这个
chunk
里面的文件,那你打包之后文件的
chunkhash
是不变的,修改过的会生成新的
chunkhash


接下来我修改上文中的其中一个
.vue
文件,在重新打包的结果是这样的,可以跟之前的对比一下,看是不是其他
chunkhash
没有变:

Asset     Size  Chunks                    Chunk Names
components.c2315d7bc104f905405c.js   479 kB       1  [emitted]  [big]  components
main.a33511b70c4ad0a4aead.js   738 kB       2  [emitted]  [big]  main
list.b2de50f4150e217815ca.js   789 kB       3  [emitted]  [big]  list
manifest.e3e56878b56c560148b7.js  6.07 kB       4  [emitted]         manifest


minChunks

minChunks
为函数的时候,
webpack
会提供两个参数:
module
count
。其中,
module
有两个属性
module.context
module.resource


-
module.resource
:表示正在处理的文件位置(其实就是文件的路径)

-
module.context
:表示文件所在的目录

-
count
:表示文件被引用的次数

有兴趣可以把module.context和module.resource打印出来看一下。

对于
count
的使用其实很简单,这里我还是想提一下,直接把官网的栗子拿过来说:

new CommonsChunkPlugin({
name: "list",
minChunks: function (module, count) {
// 这里就是说,将文件路径中存在somelib并且被引用了3次的文件
// 如果说没有前面的条件,完全可以直接设置minChunks:3
return module.resource && (/somelib/).test(module.resource) && count === 3
}
}),


到这里,上面写的
CommonsChunkPlugin
配置基本就很清晰了,但是对于打包之后的
Chunks
列数字问题,我在这里说一下,那些数字其实叫做
Chunks.id
,是自动生成的,从0开始。这时候细心的人发现我上面的打包结果中并没有0,是从1开始的,这是为什么呢?答案是:其实是有0的,只不过打出来的文件与本文并没有关系,所以让我手动把第一行给删了,不要纠结,就是从0开始的,没毛病。

6. 小节

上面就
CommonsChunkPlugin
的作用及配置进行了介绍,并且展示了一个小栗子,根据结果又分析出了很多东西。但是由于篇幅太长,配置里面的参数没有一一展示demo,如果想用可以自己再慢慢探索。

三、总结

到这里文章就结束了,主要的目的还是为了记录和学习,顺便分享一下,以上都是亲自实践以及一些个人的理解,有兴趣的话也可以互相交流,交流是人类进步的阶梯。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息