Webpack 编译优化之速度篇

一、减小文件搜索范围

使 Webpack 更快寻找到目标,将对打包速度产生很是积极的影响

1、配置 resolve.modules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function resolve (dir) {
return path.join(__dirname, '..', dir)
}

module.exports = {
resolve: {
extensions: ['.js', '.vue', '.json'],
modules: [
resolve('src'),
resolve('node_modules')
],
alias: {
'vue$': 'vue/dist/vue.common.js',
'src': resolve('src'),
'assets': resolve('src/assets'),
'components': resolve('src/components'),
// ...
'store': resolve('src/store')
}
},
...
}

2、设置 test & include & exclude

合理的设置 include & exclude,将会极大地提升 Webpack 打包优化速度

  • test:必须满足的条件(正则表达式,不要加引号,匹配要处理的文件)
  • exclude:不能满足的条件(排除不处理的目录)
  • include:导入的文件将由加载程序转换的路径或文件数组(把要处理的目录包括进来)
  • loader:一串“!”分隔的装载机(2.0版本以上,”-loader”不可以省略)
  • loaders:作为字符串的装载器阵列
    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
    module: {
    preLoaders: [
    {
    test: /\.js$/,
    loader: 'eslint',
    include: [resolve('src')],
    exclude: /node_modules/
    },
    {
    test: /\.svg$/,
    loader: 'svgo?' + JSON.stringify(svgoConfig),
    include: [resolve('src/assets/icons')],
    exclude: /node_modules/
    }
    ],
    loaders: [
    {
    test: /\.vue$/,
    loader: 'vue-loader',
    include: [resolve('src')],
    exclude: /node_modules\/(?!(autotrack|dom-utils))|vendor\.dll\.js/
    },
    {
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url',
    exclude: /assets\/icons/,
    query: {
    limit: 10000,
    name: utils.assetsPath('img/[name].[hash:7].[ext]')
    }
    }
    ]
    }

二、增强代码压缩工具

1、合理使用uglifyJS

Webpack 默认提供的 UglifyJS 插件,由于采用单线程压缩,速度颇慢 ;推荐采用 webpack-parallel-uglify-plugin 插件,她可以并行运行 UglifyJS 插件,更加充分而合理的使用 CPU 资源,这可以大大减少的构建时间;当然,该插件应用于生产环境而非开发环境

开发环境

1
2
3
4
5
6
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
sourceMap: true
})

生产环境

1
2
3
4
5
6
7
8
9
10
11
12
var ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS:{
output: {
comments: false
},
compress: {
warnings: false
}
}
})

2、设置 babel 的 cacheDirectory 为true

babel-loader is slow! 所以不仅要使用exclude、include,尽可能准确的指定要转化内容的范畴,而且要充分利用缓存,进一步提升性能。babel-loader 提供了 cacheDirectory特定选项(默认 false):设置时,给定的目录将用于缓存加载器的结果。

1
2
3
4
5
6
7
8
9
rules: [
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory=true',
exclude: /node_modules/,
include: [resolve('src'), resolve('test')]
},
... ...
]

3、设置 noParse

如果你确定一个模块中,没有其它新的依赖,就可以配置这项, Webpack 将不再扫描这个文件中的依赖,这对于比较大型类库,将能促进性能表现

1
2
3
4
5
6
7
module: {
noParse: /node_modules\/(element-ui\.js)/,
rules: [
{
...
}
}

4、拷贝静态文件

引入 DllPlugin 和 DllReferencePlugin 来提前构建一些第三方库,来优化 Webpack 打包。而在生产环境时,就需要将提前构建好的包,同步到 dist 中;这里拷贝静态文件,你可以使用 copy-webpack-plugin 插件:把指定文件夹下的文件复制到指定的目录

1
2
3
4
5
6
7
8
9
10
11
12
13
var CopyWebpackPlugin = require('copy-webpack-plugin')

plugins: [
...
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]

三、正确使用 SourceMap

source-map 的基本原理是,在编译处理的过程中,在生成产物代码的同时生成产物代码中被转换的部分与源代码中相应部分的映射关系表。有了这样一张完整的映射表,我们就可以通过 Chrome 控制台中的”Enable Javascript source map”来实现调试时的显示与定位源代码功能。

“eval” “cheap” “module” “inline” “hidden” “nosource” “source-map”

在开发环境中

通常我们关注的是构建速度快,质量高,以便于提升开发效率,而不关注生成文件的大小和访问方式。

  • 如果对项目代码了如指掌,查看产物代码也可以无障碍地了解对应源代码的部分,那就可以关闭 devtool 或使用 eval 来获得最快构建速度。

  • 反之如果在调试时,需要通过 source map 来快速定位到源代码,则优先考虑使用 eval-cheap-modulesource-map,它的质量与初次/再次构建速度都属于次优级,以牺牲定位到列的功能为代价换取更快的构建速度通常也是值得的。

  • 在其他情况下,根据对质量要求更高或是对速度要求更高的不同情况,可以分别考虑使用 eval-source-map 或 eval-cheap-source-map。

在生产环境中

通常我们更关注是否需要提供线上 source map , 生成的文件大小和访问方式是否会对页面性能造成影响等,其次才是质量和构建速度。

四、Tree-Shaking

我们的JS文件,将其中用不到的代码”摇”掉

1、禁止Babel将ES6编译到CommonJS

Babel在很多应用中已经必不可少。不幸的是,它会让tree shaking变得困难。如果你使用babel-preset-env,它会将你的ES6编译到可兼容性更好的CommonJS。

问题在于对于CommonJS,tree shaking非常困难,而且webpack不知道哪些需要消除掉。不过呢,好在有一个很简单的解法:配置babel-preset-env,让其保持ES6不动,不要翻译。具体的配置放在你配置Babel的地方(.babelrc或则package.json):

1
2
3
4
5
6
7
8
9
# .babelrc
"presets": [
[
"es2015", {
"modules": false,
}
],
"stage-2"
],

2、谨记副作用(Side Effect)

应用中使用模块是否有副作用,若需要配置package.json,但谨慎使用。在操作全局函数的时候,会导致其他使用此函数的地方带来副作用。

通过配置"sideEffects":false表示模块是安全的,没有副作用的。

1
"sideEffects": false

3、按需导入

1
import { SwipeCell, Toast } from 'vant'

五、抽出第三方库

由于第三方库太多,容易导致vendor.js过大。可优化使用CDN或者外链 static 的JS文件,在index.html进行引入,不参与webpack构建。

vendor.js >5m

index.html