webpack性能优化 —— CommonsChunkPlugin

来源:互联网 发布:ipad电脑mac地址怎么查 编辑:程序博客网 时间:2024/06/05 17:41

前言:

相信大家对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.jsvar BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

接着使用引入的BundleAnalyzerPlugin

// webpack.demo.config.jsnew 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

  • analyzerHostanalyzerModeserver的时候生效,表示启动http服务的IP地址。

  • analyzerPortanalyzerModeserver的时候生效,表示启动http服务的端口号。

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

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

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

  • generateStatsFiletrue|false,表示是否生成.json文件。

  • statsFilename:设置generateStatsFiletrue的时候生成的.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插件。

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

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

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

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

2. 使用

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

// webpack.demo.config.jsvar 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.jsvar 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,所以可以在这里实现以下定制化的需求。函数会提供两个参数:modulecount

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 Namescomponents.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

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

demo结构

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 Namescomponents.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 Namescomponents.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会提供两个参数:modulecount。其中,module有两个属性module.contextmodule.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,如果想用可以自己再慢慢探索。

三、总结

    到这里文章就结束了,主要的目的还是为了记录和学习,顺便分享一下,以上都是亲自实践以及一些个人的理解,有兴趣的话也可以互相交流,交流是人类进步的阶梯。

原创粉丝点击