webpack打包极限优化--基本介绍
1.为什么我们需要构建工具
(a)转换ES6语法
(b)转换JSX
(c)CSS前缀补全/预处理器
(d)压缩混淆
(e)图片压缩
2.初级分析-使用Webpack内置的stats
(a)stats:构建的统计信息
(b)package.json中使用status
1 "scripts": { 2 "build:stats": "webpack ---env production --json > stats.json" 3 ... 4 }
(c)Node API中使用
1 const webpack = require('webpack'); 2 const config = require('./webpack.config.js')("production"); 3 4 webpack(config, (err, stats) => { 5 if (err) { 6 return console.error(err); 7 } 8 if (stats.hasErrors()) { 9 return console.error(stats.toString("errors-only")) 10 } 11 console.log(stats); 12 })
3.速度分析-使用speed-measure-webpack-plugin
1 const speedMeasurePlugin = require("speed-measure-webpack-plugin"); 2 const smp = new SpeedMeasurePlugin(); 3 4 const webpackConfig = smp.wrap({ 5 plugins: { 6 new MyPlugin(), 7 new MyOtherPlugin() 8 } 9 }) 10 // 可以看到每个loader和插件执行耗时
4.体积分析-使用webpack-bundle-analyzer
1 const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 2 module.exports = { 3 plugins: { 4 new BundleAnalyzerPlugin() 5 } 6 } 7 // 构建完成后会在8888端口展示各个打包后文件的大小
webpack打包极限优化--构建速度优化
1.速度优化策略
(a)使用webpack4
(b)多进程/多实例构建
(c)分包
(d)缓存
(e)缩小构建目标
2.使用webpack4-优化原因
(a)V8带来的优化(for of替代forEach、Map和Set替代Object、includes替代indexOf)
(b)默认使用更快的md4 hash算法
(c)webpacks AST可以直接从loader传递给AST,减少解析时间
(d)使用字符串方法替代正则表达式
3.多进程/多实例-使用HappyPack解析资源
原理:每次webpack解析一个模块,HappyPack会将它及它的依赖分配给worker线程中
1 exports.plugins = { 2 new HappyPack({ 3 id: 'jsx', 4 threads: 4, // 固定线程数,但是不建议 5 loaders: ['babel-loader'] 6 }), 7 new HappyPack({ 8 id: 'styles', 9 threads: 2, // 固定线程数,但是不建议 10 loaders: ['style-loader', 'css-loader', 'less-loader'] 11 }) 12 }
4.多进程/多实例-并行压缩
方法一:使用parallel-uglify-plugin插件(webpack3使用)
1 const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin'); 2 module.exports = { 3 plugins: [ 4 new ParallelUglifyPlugin({ 5 uglifyJS: { 6 output:{ 7 beautity: false, 8 comments: false, 9 }, 10 compress: { 11 warnings: false, 12 drop_console: true, 13 collapse_vars: true, 14 reduce_vars: true 15 } 16 } 17 }) 18 ] 19 }
方法二:使用uglifyjs-webpack-plugin开启parallel参数(webpack4使用)
1 const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 2 module.exports = { 3 plugins: [ 4 new UglifyJsPlugin({ 5 uglifyOptions: { 6 warnings: false, 7 parse: {}, 8 compress: {}, 9 mangle: true, 10 output: null, 11 toplevel: false, 12 nameCache: null, 13 ie8: false, 14 keep_fnames: false 15 }, 16 parallel: true 17 }) 18 ] 19 }
5.分包-设置Externals
思路:将react、react-dom基础包通过cdn引入,不打入bundle中
方法:使用html-webpack-externals-plugin
1 const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin'); 2 plugins: [ 3 new HtmlWebpackExternalsPlugin({ 4 external: [ 5 { 6 module: 'react', 7 entry: '........./react-with-addons.min.js?_bid=313', 8 global: 'React' 9 }, 10 { 11 module: 'react-dom', 12 entry: '........./react-dom.min.js?_bid=313', 13 global: 'ReactDOM' 14 } 15 ] 16 }) 17 ]
6.进一步分包-预编译资源模块
思路:将react、react-dom、redux、react-redux基础包和业务基础包打包成一个文件。
方法:使用DLLPlugin进行分包,DllReferencePlugin对manifest.json引用
1 module.exports = { 2 plugins: [ 3 new webpack.DllPlugin({ 4 name: '[name]', 5 path: './build/library/[name].json' 6 }) 7 ] 8 }
7.缓存
目的:提升二次构建速度
方法:使用HardSourceWebpackPlugin或者cache-loader
比较推荐使用HardSourceWebpackPlugin,速度更快
8.缩小构建目标
目的:尽可能的少构建模块
比如babel-loader不解析node_modules
1 module.exports = { 2 rules: { 3 test: /.js$/, 4 loader: 'happypack/loader', 5 exclude: 'node_modules' 6 } 7 }
webpack打包极限优化--构建体积优化
1.体积优化策略
(a)Scope Hoisting
(b)Tree-shaking
(c)公共资源分离
(d)图片压缩
(e)动态Polyfill
2.Scope Hoisting
原理:将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突
对比:通过scope hoisting可以减少函数声明代码
1 // 代码示例: 2 module.exports = { 3 plugins: [ 4 new webpack.opimize.ModuleConcatenationPlugin() 5 ] 6 } 7 // 要求:必须是ES6的语法,CJS的方式不支持
3.Tree-shaking
概念:1个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到bundle里面去,Tree-shaking
就是只把用到的方法打入bundle,没用到的方法会在uglify阶段被删掉
使用:webpack默认支持,在.babelrc里设置modules:false即可
要求:必须是ES6的语法,CJS的方式不支持
4.公共资源分离
目的:提取多页面公共JS chunk代码
使用:webpack3使用CommonsChunkPlugin
webpack4使用SplitChunksPlugin
5.图片压缩
要求:基于Node库的imagemin或者tinypng API
使用:配置image-webpack-loader