webpakc性能优化
- 开发环境性能优化
1.优化打包构建速度
2.优化代码调试 - 生产环境性能优化
1.优化打包构建速度
2.优化代码运行的性能
一、HMR:hot modules replacement 热模块替换/模块热替换
作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有),提升构建速度
样式文件:可以使用HMR功能,因为style-loader内部实现了
js文件:默认不能使用HMR功能,
-
html文件:默认不能使用HMR功能
改变entry,[ './src/index.js','./src/index.html'],就可以开启HMR功能,html只有一个文件,每次重新打包会刷新
devServer:{
contentBase:resolve(__dirname,'build'),
compress:true,
port:3000,
open:true
//开启HMR功能
hot:true
}
//新建print.js
//修改index.js
if(module.hot){
//必须开启HMR功能,才会执行到这个函数
module.hot.accept('./print.js',function(){
//监听print.js文件是否发生变化
print();
})
}
二、source-map:一种提供源代码到构建后代码映射技术
devtool:'eval-source-map'
source-map:一种提供源代码到构建后代码映射技术(如果构建后代码出错,通过映射可以追踪到源代码错误)
值:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
source-map :外联
//错误代码准确信息 和 源代码的错误位置(精确到列)
inline-source-map :内联,只生成一个内联的source-map
//错误代码准确信息 和 源代码的错误位置(精确到列)
hidden-source-map :外联
//错误代码准确信息 和 构建代码的错误位置(只有构建后的代码)
eval-source-map :内联,每个文件都生成对应的source-map文件,都在eval
//错误代码准确信息 和 源代码的错误位置(精确到列)
nosources-source-map :外联
//错误代码准确信息 和 没有源代码信息
cheap-source-map :外联
//错误代码准确信息 和 源代码的错误位置(精确到行)
cheap-module-source-map :外联
//错误代码准确信息 和 没有源代码信息,module会将loader的source-map加入
内联和外联的区别:1.外部生成了文件,内联没有;2.内联构建速度更快
开发环境:速度快,调试更友好 ----->eval-source-map/ eval-cheap-module-source-map
速度快
eval-cheap-source-map
eval-source-map
调试友好
source-map
cheap-module-source-map
cheap-source-map
生产环境:源代码要不要隐藏,调试要不要更友好----->source-map/cheap-module-source-map
//内联会让代码体积变大,所以再生产环境下不用内联
源代码要不要隐藏
nosources-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
调试要不要更友好
source-map
三、oneof,以下loader只会匹配一个
//不能有2个配置处理同一类型的文件
//这里匹配2个js,将一个js提出与oneof平级,enforce优先处理
module: {
rules: [
{
test: /\.js$/,
exclude: /node-modules/,
enforce: 'pre', //优先语法检查
loader: 'eslint-loader',
options: {
fix: true
}
},
{
oneof: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
{
test: /\.js$/,
exclude: /node-modules/,
loader: 'babel-loader'
}
]
}
]
}
四、cache缓存
-
babel缓存
优点:第二次打包的速度更快
cacheDirectory:true
-
文件资源缓存
优点:代码上线运行缓存更好使用
-
hash
:webpack每次构建会生成一个唯一的hash值问题:js和css的hash值还是一样,如果重新打包会导致所有的缓存失效(可能改变一个文件)
-
chunkhash
:根据chunk生成的hash值。如果打包来源同一个chunk,那么hash值就一样问题:js和css的hash值还是一样
contenthash
:根据文件的内容生成hash值。不同的文件hash值一定不一样
-
//babel缓存
{
test:/\.js$/
exclude:/node-modules/
loader:'babel-loader'
options:{
parsets:[
[
'@babel/parset-env'
{
useBuiltIns:'usage'
corejs:{version:3},
targets:{
chrome:'60',
firefox:'50'
}
}
]
],
//开启babel缓存
//第二次构建会读取之前的缓存
cacheDirectory:true
}
}
五、tree shaking:去除无用代码
前提:1、使用ES6模块化。2、必须在production模式下。满足这2个条件自动开启
作用:减少代码体积,构建速度更快。
在package.json中配置
"sideEffects":false 所有的代码都没有副作用(都能进行tree shaking)
问题:可能会把 css / @babel/polyfill 文件干掉
"sideEffects":["*.css","*.less"]
六、code split:代码分割
//生产环境配置
const { resolve } = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//单入口
//entry: './src/js/index.js',
//多入口
entry:{
//有一个入口,最终输出就有一个bundle
index:'./src/js/index.js',
test:'./src/js/test.js'
},
output: {
//[name]:取当前文件名
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseInlineTagWhitespace: true,
removeComments:true
}
})
],
mode: 'production'
}
//生产环境配置
const { resolve } = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//多入口
entry:{
index:'./src/js/index.js',
test:'./src/js/test.js'
},
//如果2个js同时引入JQ,打包生成3个chunk
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseInlineTagWhitespace: true,
removeComments:true
}
})
],
//1.可以将node-modules中的代码单独打包成一个chunk最终输出
//2.自动分析多入口chunk中,有没有公共的文件,如果有会单独打包成一个chunk
optimization:{
splitChunks:{
chunks:'all'
}
},
mode: 'production'
}
//生产环境配置
const { resolve } = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:'./src/js/index.js'
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseInlineTagWhitespace: true,
removeComments:true
}
})
],
//可以将node-modules中的代码单独打包成一个chunk最终输出
optimization:{
splitChunks:{
chunks:'all'
}
},
mode: 'production'
}
//index.js
/*
通过js代码,让某个文件被单独打包成一个chunk
*/
/*webpackChunkName:'test'*/ 设置文件名
imoprt (/*webpackChunkName:'test'*/'./test').then(({mul,count})=>{
//文件加载成功
console.log(mul(2,3))
}).catch(()=>{
//文件加载失败
})
七、lazy loading:懒加载
//懒加载:当文件需要使用时才加载
//预加载 prefetch:会使用之前,提前加载js文件 webpackPrefetch:true
//正常加载:并行加载(同一时间加载多个文件)
imoprt(/* webpackChunkName:'test',webpackPrefetch:true*/ './test').then(({mul})=>{
console.log(mul(2,3))
})
八、PWA:渐进式网络应用开发程序(离线可访问)
//生产环境配置
const { resolve } = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const workboxWebpackPlugin = require('workbox-webpack-plugin')
//指定node环境变量
//process.env.NODE_ENV = 'development'
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
//还需要在package.json中配置browserslist
/*
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"no dead",
"no op_mini all"
]
},
*/
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
require('postcss-preset-env')()
]
}
}
]
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
{
//需要在package.json中配置eslintConfig--->airbnb
/*
"eslintConfig": {
"extends": "airbnb-base",
"env":{
"browser":true
}
}
*/
test: /\.js$/,
exclude: /node-modules/,
enforce: 'pre', //优先语法检查
loader: 'eslint-loader',
options: {
fix: true
}
},
{
test: /\.js$/,
exclude: /node-modules/,
loader: 'babel-loader', //将高版本的js转为ES5
options: {
presets: [
[
'@babel-loader',
{
useBuiltIns: 'usage',
corejs: {
version: 3
},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
esModule: false,
name: '[hash:10],[ext]',
outputPath: 'images'
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(css|less|html|js|jpg|png|gif)$/,
loader: 'file-loader',
options: {
name: '[hash:10],[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/bulit.css'
}),
new optimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseInlineTagWhitespace: true,
removeComments:true
}
}),
new workboxWebpackPlugin.GenerateSW({
/**
* 1.帮助serviceWorker快速启动
* 2.删除旧的serviceWorker
*
* 生成一个serviceworker文件
*/
clientClaim:true,
skipWaiting:true
})
],
mode: 'production',
devtool:'source-map'
}
//index.js
/**
* 1.eslint不认识window,navigtor等全局变量
* 解决:需要在package.json中修改eslintConfig配置
* "evn":{
* "browser":true
* }
* 2.必须在服务器上运行
*/
//在入口文件中注册serviceWorker
//处理兼容性问题
if('serviceWorker' in navigator){
window.addEventListente('load',()=>{
navigator.serviceWorker.register('/service-worker.js').then(()=>{
console.log('serviceWorker注册成功')
}).catch(()=>{
console.log('serviceWorker注册失败')
})
})
}
九、多进程打包
//thread-loader,开启多进程打包,启动大概600ms,只有工作消耗时间较长才会开启多进程打包
//npm install thread-loader -D
{
test:/\.js$/
exclude:/node-modules/,
use:[
{
//开启多进程打包
loader:'thread-loader',
options:{
workers:2,//开启两个进程
}
},
{
loader:'babel-loader',
options:{
presets: [
[
'@babel-loader',
{
useBuiltIns: 'usage',
corejs: {
version: 3
},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
}
]
}
十、externals防止将某些包打包输出到bundle里面,不需要打包,直接通过CDN引入
const { resolve } = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports={
entry:'./js/index.js',
output:{
filename:'./js/[name].[contenthash:10].js',
path:resolve(__dirname,'build')
},
plugins:[
new htmlWebpackPlugin({
template:'./src/index.html',
minfy:{
collapseInlineTagWhitespace: true,
removeComments:true
}
})
],
mode:'production',
externals:{
//拒绝jquery被打包进来
jquery:'jQuery'
}
}
十一、dll,将第三方库单独打包,直接引入
//webpack.dll.js
/* 使用dll技术,对第三方库(jQuery,vue,react...)单独打包 */
const {resolve} = require('path')
const webpack = require('webpack')
module.export = {
entry:{
//打包生成得name,要打包得库
jquery:['jquery']
},
output:{
filename:'[name].js',
path:resolve(__dirname,'dll'),
library:'[name]_[hash:10]'//打包得库,向外暴露得内容名字
},
plugins:[
//打包生成一个manifest.json文件 ---》 提供jQuery得映射
new webpack.DllPlugin({
name:'[name]_[hash:10]',//映射库得暴露名称
path:resolve(__dirname,'dll/manifest.json')//输出文件得目录
})
],
mode:'production'
}
//webpack.config.js
const { resolve } = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
module.exports={
entry:'./src/index.js',
output:{
filename:'js/[name].[contenthash:10].js',
path:resolve(__dirname,'build')
},
plugins:[
new htmlWebpackPlugin({
template:'./src/index.html',
minfy:{
collapseInlineTagWhitespace: true,
removeComments:true
}
}),
//告诉webpack那些库不打包,同时使用时得名称也在变
new webpack.DllReferencePlugin({
manifest:resolve(__dirname,'dll/manifest.json')
}),
//将第三方库单独打包,并在html自动引入
new AddAssetHtmlWebpackPlugin({
filepath:resolve(__dirname,'dll/jquery.js')
})
],
mode:'production'
}
性能优化总结
一、webpack性能优化
- 开发环境性能优化
- 生产环境性能优化
二、开发环境性能优化
- 优化打包构建速度
- HRM
- 优化代码调试
- source-map
- 开发环境:eval-source-map || eval-cheap-module-source-map
- 生产环境:source-map || cheap-module-source-map
- source-map
三、生产环境性能优化
- 优化打包构建速度
- oneOf
- babel缓存
- 多进程打包
- externals,
- dll
- 优化代码运行得性能
- 缓存(hash || chunkhash || contenthash)
- thee shaking(1、使用ES6模块化。2、必须在
production
模式下。满足这2个条件自动开启。) - code split
- lazy loading
- pwa