关于 Webpack + React 的性能优化
这篇文章很早就想写了,但是由于工作太忙,一直挤不出时间。正好趁月底挤干货的时间弄出来一篇。
由于本文内容是作者自互联网收集 + 实践与发散思维得出,故可能随时会更新修改,为了方便读者追本索源,转载请保留头部,谢谢
本文链接
另: 本文不是新手教程,关于 WebPack 如何入门配置、React Jsx 的Loader 等,请移步这里: 使用Jsx 或者 优化开发-Webpack & Jsx
先看一下最基础 Webpack 配置:
module.exports = {
entry: 'entry.jsx',
output: {
path: path.join(__dirname, '/dist'),
filename: 'bundle.js'
},
resolve: {
extensions: ['', '.js', '.jsx', '.scss']
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
},
{ test: /\.css$/, 'style!css' },
{ test: /\.scss$/, 'style!css!sass?sourceMap'},
]
}
}
文件结果:
- bundle.js : 852kb.
心想并没有什么复杂的代码,为什么有852kb!
目前情况: Webpack + react 文件打包出来过大,动则 几百k 上兆。
原因分析:
- 在使用 import css 的时候,Webpack Css 直接作为模块打包到js中。
- 所有Js 模块 + 依赖都会打包到一个文件,导致文件很大。
- React、React-dom 文件过大
解决方案:
分离 css 文件,将css 单独打包。
//使用 ExtractTextPlugin 插件,将css 打包进一个文件 plugins: [ new ExtractTextPlugin("bundle.css"), ], //同时需要在 loader 里加上方法,如下 { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') }, { test: /\.scss$/, loader: ExtractTextPlugin.extract('style-loader','css!sass?sourceMap')},
此时看下文件大小,变成:
- bundle.css - 56kb
- bundle.js -796kb
好像作用还不是很大… - -!
那么执行第二步 ,公用JavaScript 模块分离
如果文件 file1 使用了 a 模块, 文件 file2 也使用了 a 模块,那么这个模块就可以认为是公用模块,当然这个数量也可以自己定义,可以使用
CommonsChunkPlugin
分片插件来分离。//同样是插件 new webpack.optimizes.CommonsChunkPlugin('vendor', 'vendor.js')
此时看下效果,文件变成:
- bundle.css - 56kb
- bundle.js -760kb
- vendor.js -36kb
移出了 36kb,效果不是很明显。
最好是把 React、React-dom 这种移到 vendor.js 中让他去缓存。
//修改 entry 为: entry: { app: path.resolve(__dirname, 'entry.jsx'), vendor: ['react', 'react-dom'] },
变成:
- bundle.js - 16kb
- vendor.js - 780kb
压缩 JavaScript 代码
通过 UglifyJsPlugin 插件
new webpack.optimize.UglifyJsPlugin({
output: {
comments: false, // remove all comments
},
compress: {
warnings: false
}
})压缩后,文件有明显瘦身:
- bundle.js -16kb
- vendor.js -750kb
但是还是不太明显, 原因可能是 react、react-dom 本身就是min 状态,在压缩一遍也小不了多少。
那么就从 react 上入手,发现react 里面存在很多注释,警告等,是一个开发版本。那么如何切换成生产版本呢
通过 webpack.DefinePlugin 的process.env 来切换成生产版本
new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV), }, }),
当然,这里的
process.env.NODE_ENV
也可以直接换成production
,就不用在 build脚本里赋值了,如果按上面的写法,build 里需要赋值:"build": "set NODE_ENV=production&&webpack -p --progress --colors"
注意: 这里是 windows的写法,如果是 mac、linux 需要这样写 ——
NODE_ENV=production webpack -p --progress --colors
看一眼,这个影响最大,编译完成后:
- bundle.js -16kb
- vendor.js -144kb
上面四条已经差不多解决了文件过大的问题,但是还有童鞋发现,编译速度好慢.. 为什么?
编译速度慢是因为项目中引用了很多的模块,这些模块又互相依赖, webpack 去处理依赖关系的时候, 要去 npm中搜索, 硬盘搜索很占用资源。所以可以通过 alias 配置来解决
var node_modules = path.resolve(__dirname, 'node_modules'); var pathToReact = path.resolve(node_modules, 'react/dist/react.min'); resolve: { alias: { 'react': pathToReact } },
这样等于直接为 webpack 指定了目录,上述只是配置了 react , 如果需要,可以酌情配置其他库。
注: 上述所有插件,都需要 npm install 。
写在最后
优化了这么多,还有没有优化的空间?当然有。
- 服务端渲染
- 首屏优化
- 异步加载模块
- ….
- 还有很多
代码在这里: https://github.com/zhukejin/WebPack-Demo
公司即将倒闭,求一个工作机会。