webpack核心概念与构建流程:
webpack核心概念包含入口(entry)、出口(output)、处理器(loader)、插件(plugin) 、模块(module)、模式(mode)、输出文件(bundle)、组合块(chunk)。这部分内容具体可以参考webpack官网。webpack中最易混淆的5个知识点
webpack构建流程大致是进入入口文件,解析出导入语句,根据module(模块)后缀名调出对应的loader进行处理,再找该依赖所依赖的module,递归地进行编译转换,最后将编译好的module整理组合成一个或多个chunk,最终生成bundle文件,整个过程是串行进行的。
由于项目在越来越大,构建速度也相应的越来越慢,打包体积也越来越大,因此有必要对项目进行webpack构建与打包的优化了。
这次主要是构建速度优化,每次去运行项目的时候会看到构建时间,优化目的主要是减少构建时间。
文章内容有如何去输出webpack构建内容分析,以及构建plugin和loader速度分析,还有对于打包的bundle文件大小关系分析。
优化思路:
-
首次run的时候尽量让构建速度快
缩小构建目标、减少文件检索范围(这块内容在下面)
-
二次run的时候可以使用到缓存提升构建速度
二次缓存优化:
eslint-loader
配置cache:true
https://www.npmjs.com/package/eslint-loader
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src')],
options: {
formatter: require('eslint-friendly-formatter'),
cache: true
}
}
loader缓存会被默认缓存在node_modules/.cache/eslint-loader
vue-loader
配置cacheDirectory属性 (暂时不明确)
{
test: /\.vue$/,
loader:'vue-loader',
options: {
cacheDirectory: path.resolve(__dirname,'node_modules/.cache')
}
}
babel-loader
配置在babel-loader中配置cacheDirectory
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory', // 开启转换结果缓存
include: [resolve('src')]
}
或者
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src')],
options:{
cacheDirectory: true
}
}
loader缓存会被默认缓存在node_modules/.cache/babel-loader
通过二次缓存提升构建速度有一个强大的插件(墙裂推荐,在下面插件使用中,在优化构建速度的时候可以优先考虑一下)
插件使用
JARVIS(可用来统计打包速度)
使用jarvis插件对webpack构建新能进行分析
ERRORS AND WARNING
(错误警告)
TOTAL ASSETS SIZE
(打包资源体积)
ALL MODULES
(所有打包数量)
安装:
npm i -D webpack-jarvis
使用:
const Jarvis = require("webpack-jarvis"); // 查看打包整体信息
module.exports = {
plugins:[
new Jarvis({
watchOnly: false,
port: 3002 // optional: set a port
}),
]
}
webpack-bundle-analyzer(分析打包内容体积)
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins:[
new BundleAnalyzerPlugin()
]
}
SpeedMeasurePlugin(分析plugin以及loader的构建耗时)
speed-measure-webpack-plugin
是一个专门测试webpack构建速度的工具,可以在终端列出所有Loader和Plugin的耗时。
安装:
npm i -D speed-measure-webpack-plugin
使用:使用插件的实例对象对webpack配置进行一次包裹,如代码所示,然后运行构建即可看到耗时分析
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const webpackPlugin = {
entry:"",
// ...
plugins:[]
}
module.exports = smp.wrap(webpackPlugin); // 包裹
HappyPack(在项目比较大时效果会比较明显,还可能出现构建速度变慢)
https://www.npmjs.com/package/happypack
使用 happypack 提升 Webpack 项目构建速度
HappyPack
可以让 Webpack
同一时间处理多个任务,发挥多核 CPU
的能力,将任务分解给多个子进程去并发的执行,子进程处理完后,再把结果发送给主进程。通过多进程模型,来加速代码构建。
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({size: 5}); //构建共享进程池,包含5个进程
mudule.exports = {
entry:...,
output:...,
mudule:{
rules: [
{
test: /\.js$/,
loader: 'happypack/loader?id=babel', // 开启转换结果缓存
include: [resolve('src')]
},
]
},
plugins: [
// happypack并行处理
new HappyPack({
// 用唯一ID来代表当前HappyPack是用来处理一类特定文件的,与rules中的use对应
id: 'babel',
loaders: ['babel-loader?cacheDirectory'],//默认设置loader处理 cacheDirectory对babel-loader进行缓存
threadPool: happyThreadPool,//使用共享池处理
})
]
}
HardSourceWebpackPlugin(效果明显,简直就是神器啊)
HardSourceWebpackPlugin
为模块提供了中间缓存,缓存默认的存放路径是: node_modules/.cache/hard-source
。
配置 hard-source-webpack-plugin
后,首次构建时间并不会有太大的变化,但是从第二次开始,构建时间大约可以减少 80%
左右。
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
// ...
plugins:[
new HardSourceWebpackPlugin()
]
}
由于这个插件效果太过于明显,太过于强大,以至于使用过后我觉得其他优化都可以忽略不计了。
缩小构建目标/减少文件检索范围
缩小构建目标:主要是exclude
与 include
的使用:(有效果)
https://www.webpackjs.com/configuration/module/#rule-exclude
- exclude: 不需要被解析的模块
- include: 需要被解析的模块
include和exclude用一个即可,默认exclude的优先级比include高。
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'happypack/loader?id=babel', // 开启转换结果缓存
include: [resolve('src')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'happypack/loader?id=img',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
},
include: [resolve('src')]
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
},
include: [resolve('src')]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]'),
// todo-tao 将来删除,调整[email protected]字体打包后的引用路径
publicPath: '../../'
},
include: [resolve('src'), resolve('node_modules/element-ui')]
}
]
}
减少文件检索范围:这个主要是resolve
相关的配置,用来设置模块如何被解析。通过resolve
的配置,可以帮助Webpack
快速查找依赖,也可以替换对应的依赖。
https://www.webpackjs.com/configuration/resolve/#resolve-modules
resolve.modules
:告诉 webpack
解析模块时应该搜索的目录resolve.modules
用于配置webpack去哪些目录下寻找第三方模块,默认是['node_modules']
,但是,它会先去当前目录的./node_modules
查找,没有的话再去../node_modules
最后到根目录
所以当安装的第三方模块都放在项目根目录时,就没有必要安默认的一层一层的查找,直接指明存放的绝对位置
resolve.extensions
:文件扩展名 ['js','vue','jsx']尽量少,且将会匹配到的多的文件后缀放到最前面。
resolve.mainFiles
:解析目录时要使用的文件名,默认是index
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src')
},
modules: [path.resolve(__dirname, '../node_modules')]
}
优化的方式还有很多,比如使用DllPlugin优化,原理是它能把第三方库代码分离开,并且每次文件更改的时候,它只会打包该项目自身的代码。所以打包速度会更快。但是在某些资料中发现这个优化不是很理想,vue和react官方都没有很友好的去用这个优化方式,本次优化项目构建速度目前就用到这些内容,如有更好的方式可以一起交流呀。
补充:使用HardSourceWebpackPlugin
会出现的问题:
由于HardSourceWebpackPlugin插件做了缓存,所以当更改了webpack配置,或者新增npm包的时候就会出现再次构建新增npm包不生效问题,因为再次构建还是使用的之前的缓存内容新增的npm包内容并没有进行构建,因此在配置发生改变时需要重新构建一次项目,配置如下:
new HardSourceWebpackPlugin({ // 二次构建缓存优化插件
environmentHash: {
root: process.cwd(),
directories: [],
files: ['package-lock.json', 'yarn.lock'],
},
})
在实例化HardSourceWebpackPlugin插件的时候传入环境hash配置项,官网是这么说的:
当loaders、plugins、其他构建时的脚本或其他动态依赖项发生更改时,hard-source
需要替换缓存以确保输出正确。环境哈希用于确定这一点。如果哈希与以前的构建不同,则将使用新的缓存。
补充:HardSourceWebpackPlugin 缓存ESlint信息, ESlint错误信息被缓存,二次构建时还会存在
根据官网提示新增一个插件,用来排除eslint-loader缓存
new HardSourceWebpackPlugin.ExcludeModulePlugin([
{
test: /eslint-loader/
}
])
https://github.com/mzgoddard/hard-source-webpack-plugin
补充:happy pack使用注意点
happy pack是用来多进程打包加速,这个多进程指的是采用cpu的进程进行加速打包的。
因此在使用的时候需要先获取cpu的进程。
使用os模块 os.cpus().length
来获取cpu的进程数。
const os = require('os');
var HappyPack = require('happypack');
var happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
happy pack原理解析
补充:弃用happypack使用thread-loader
在小组分享会上说使用多进程打包加速的时候,有伙伴提出来说,happypack官方停止去维护了,并且webpack4更推荐多进程使用thread-loader.
安装:
npm install --save-dev thread-loader
使用:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve("src"),
use: [
"thread-loader",
// your expensive loader (e.g babel-loader)
]
}
]
}
}
https://github.com/webpack-contrib/thread-loader