theme: smartblue
在开发过程中,大家多少都会遇到项目越来越大,打包构建速度越来越慢的问题。
下面是我整理总结的一些方法,附带详细步骤。
一、分析report
使用 webpack-bundle-analyzer 对项目模块进行分析生成report,完成后查看哪些模块体积过大,然后针对性优化。
安装:
npm install -–save-dev webpack-bundle-analyzer
配置:
//webpack.prod.config.js
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
运行:
npm run build --report
二、打包构建优化
1、配置webpack的externals
配置好后,项目运行时从外部获取扩展依赖,而不是将库打包到包中。
externals: {
'element-ui': 'Element',
'v-charts': 'VCharts'
}
然后在项目中移除相关库的import或者require。
在index.html中以cdn的形式添加进去。
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
2、处理文件的范围
(1)loader
查看配置,将打包范围缩小到src项目文件。
include:表示哪些目录中的 .js 文件需要进行 babel-loader
exclude:表示哪些目录中的 .js 文件不要进行 babel-loader
module: {
loaders: [{
test: /\.js$/,
loader: 'babel-loader',
include: [
path.resolve(__dirname, "app/src"),
path.resolve(__dirname, "app/test")
],
exclude: /node_modules/
}]
}
(2)eslint
{
test: /\.js$/,
loader: 'eslint-loader',
enforce: "pre",
include: [path.resolve(__dirname, 'src')], // 指定检查的目录
options: {
// 这里的配置项参数将会被传递到 eslint 的 CLIEngine
formatter: require('eslint-friendly-formatter') // 指定错误报告的格式规范
}
}
3、并行打包(webpack-parallel-uglify-plugin)
webpack默认使用 UglifyJS 插件,该插件采用单线程压缩。
可将此插件替换为 webpack-parallel-uglify-plugin 从而实现并行运行 UglifyJS 插件,减少大量构建时间。
npm i -D webpack-parallel-uglify-plugin
将 webpack.conf.js 里对 UglifyJS 的代码替换成下面。
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
plugins: [
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS:{
output: {
comments: false
},
compress: {
warnings: false,
drop_debugger: true,
drop_console: false
}
}
}),
]
UglifyJS 具体的配置参数如下:
uglifyJS: {
output: {
/*
是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,
可以设置为false
*/
beautify: false,
/*
是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false
*/
comments: false
},
compress: {
/*
是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出,可以设置为false关闭这些作用
不大的警告
*/
warnings: false,
/*
是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句
*/
drop_console: true,
/*
是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1; y = x, 转换成 y = 5, 默认为不
转换,为了达到更好的压缩效果,可以设置为false
*/
collapse_vars: true,
/*
是否提取出现了多次但是没有定义成变量去引用的静态值,比如将 x = 'xxx'; y = 'xxx' 转换成
var a = 'xxxx'; x = a; y = a; 默认为不转换,为了达到更好的压缩效果,可以设置为false
*/
reduce_vars: true
}
}
此外 ParallelUglifyPlugin 还可配置如下:
new ParallelUglifyPlugin({
uglifyJS: {
},
test: /.js$/g, // 正则去匹配哪些文件需要被 ParallelUglifyPlugin 压缩,默认是 /.js$/
include: [], // 正则去包含被 ParallelUglifyPlugin 压缩的文件,默认为 [].
exclude: [], // 正则去不包含被 ParallelUglifyPlugin 压缩的文件,默认为 [].
cacheDir: '', // 缓存压缩后的结果,下次遇到一样的输入时直接从缓存中获取压缩后的结果并返回,cacheDir 用于配置缓存存放的目录路径。默认不会缓存,想开启缓存请设置一个目录路径。
workerCount: '', // 开启几个子进程去并发的执行压缩。默认是当前运行电脑的 CPU 核数减去1。
sourceMap: false // 是否为压缩后的代码生成对应的Source Map, 默认不生成,开启后耗时会大大增加,一般不会将压缩后的代码的sourceMap发送给网站用户的浏览器。
});
4、启动node的多线程进行构建(happypack)
node运行下的 webpack 是采用单线程打包的,这是因为nodejs本身就是单线程。采用 happypack 是启动node的多线程进行构建,从而提高构建速度。
npm install --save-dev happypack
修改 webpack.base.conf.js
const HappyPack = require('happypack')
const os = require('os')
let happyThreadPool = HappyPack.ThreadPool({
size: os.cpus().length });
//加入此插件
plugins:[
new HappyPack({
id:'babel',
loaders:['babel-loader?cacheDirectory=true'],
threadPool:happyThreadPool
})
],
//将js loader作用代码替换
// loader: 'babel-loader' 替换成下方loader
loader: 'happypack/loader?id=babel',
5、使用 DllPlugin 插件单独编译一些不经常改变的代码
DllPlugin是webpack内置的插件,不需要额外安装,直接配置webpack.dll.config.js文件
const path = require("path")
const webpack = require("webpack")
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
entry: {
// 第三方库
react: ['react', 'react-dom', 'react-redux']
},
output: {
// 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
filename: '[name].dll.js',
path: resolve('dist/dll'),
// library必须和后面dllplugin中的name一致 后面会说明
library: '[name]_dll_[hash]'
},
plugins: [
// 接入 DllPlugin
new webpack.DllPlugin({
// 动态链接库的全局变量名称,需要和 output.library 中保持一致
// 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
name: '[name]_dll_[hash]',
// 描述动态链接库的 manifest.json 文件输出时的文件名称
path: path.join(__dirname, 'dist/dll', '[name].manifest.json')
}),
// 压缩打包的文件
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
}),
]
}
在webpack.prod.conf.js和webpack.dev.conf.js中加入当前插件
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,//与DllPlugin中的context保持一致
/*这个地址对应webpack.dll.conf.js中生成的那个json文件的路径,这样webpack打包的时候
会检测当前文件中的映射,不会把已经存在映射的包再次打包进bundle.js */
manifest: require('./vendor-manifest.json')
}),
]
在package.json中新加命令。
用于执行webpack.dll.conf.js文件。每次添加新依赖后,重新运行npm run dll这个命令一次。
"dll": "webpack --config ./build/webpack.dll.conf.js"
最后在index.html中引入static/js/vendor.dll.js文件
<script src="dist/dll/react.dll.js"></script>