1、HMR
- css样式只需要在devServer 里面 hot 配置为true即可
- js文件需要在js文件里面加配置才可以实现,要不然是整个页面刷新
注意: 只适用于非入口文件js
if (module.hot) {
// 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
module.hot.accept('./print.js', function() {
// 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
// 会执行后面的回调函数
doSomething();
});
}
- html文件不需要,若特殊场景需要的话,可以写到entry的配置里面去,如下:
entry: ['./src/js/index.js', './src/index.html']
2、source-map
一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)
开发环境:速度快,调试更友好。
开发环境:速度快,调试更友好
速度快(eval>inline>cheap>…)
eval-cheap-souce-map
eval-source-map
调试更友好
souce-map
cheap-module-souce-map
cheap-souce-map
开发模式推荐配置:
eval-source-map / eval-cheap-module-souce-map
生产环境:源代码要不要隐藏? 调试要不要更友好
内联会让代码体积变大,所以在生产环境不用内联
nosources-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
生产环境推荐配置:
source-map / cheap-module-souce-map
3、oneOf
rules里面的多个loader可以用oneOf包起来,这样可以使文件只被其中一个loader匹配处理,提升打包的速度和效率。个别需要多个loader处理的,可以放到oneOf外面,并指定 ** enforce: ‘pre’(优先执行) ** 来达到效果。
module: {
rules: [
{
// 在package.json中eslintConfig --> airbnb
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{
// 以下loader只会匹配一个
// 注意:不能有两个配置处理同一种类型文件
oneOf: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
/*
正常来讲,一个文件只能被一个loader处理。
当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
先执行eslint 在执行babel
*/
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {version: 3},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
}
4、缓存
常用的缓存方式有
- babel缓存:通过配置cacheDirectory参数,提升编译速度,缓存文件,再次修改后,只编译修改的文件。
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: { version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
- 文件资源使用hash值来缓存
值 | 说明 |
---|---|
hash | 每次wepack构建时会生成一个唯一的hash值,js和css同时使用一个hash值 |
chunkhash | 根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样。js和css同时使用一个hash值 |
contenthash | 根据文件的内容生成hash值。不同文件hash值一定不一样 |
推荐使用 contenthash 。
output: {
filename: 'js/built.[contenthash:10].js',
path: resolve(__dirname, 'build')
}
5、tree shaking
前提:
- 必须使用ES6模块化
- 开启production环境
process.env.NODE_ENV = 'production';
- 搭配压缩插件使用,比如 terser-webpack-plugin (可以同时压缩js和css)
详细使用方法参见webpack配置详解【六】 – optimization详解
作用: 减少代码体积
避免误删一些文件,可以在package.json配置sideEffects
"sideEffects": ["*.css", "*.less", "./src/js/xxx.js"]
这样css 和 lese致远就不会被删减。
也可以直接配置某个js不进行tree shaking。
6、代码分割
- 多入口
entry: {
// 多入口:有一个入口,最终输出就有一个bundle
index: './src/js/index.js',
test: './src/js/test.js'
}
- optimization 提取公共文件为单独的trunk
/*
1. 可以将node_modules中代码单独打包一个chunk最终输出
2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
*/
optimization: {
splitChunks: {
chunks: 'all'
}
}
- js代码的方式,让webpack打包成单独的trunk
/*
通过js代码,让某个文件被单独打包成一个chunk
import动态导入语法:能将某个文件单独打包
*/
import(/* webpackChunkName: 'test' */'./test')
.then(({ mul, count }) => {
// 文件加载成功~
// eslint-disable-next-line
console.log(mul(2, 5));
})
.catch(() => {
// eslint-disable-next-line
console.log('文件加载失败~');
});
7、懒加载和预加载
在异步方法里面使用import来实现懒加载,比如在onclick事件里面使用import
加载方式 | 说明 | 特点 |
---|---|---|
懒加载 | 当文件需要使用时才加载,比如点击事件触发的时候 | 只适合小文件,大文件会有等待时间,体验不好 |
预加载 | 会在使用之前,提前加载js文件。(等其他资源加载完毕,浏览器空闲了,再偷偷加载资源) | 最优方案,但是有兼容问题 |
- 懒加载:当文件需要使用时才加载,比如点击事件触发的时候(不适应大问题)
- 预加载 prefetch:会在使用之前,提前加载js文件。(等其他资源加载完毕,浏览器空闲了,再偷偷加载资源)
document.getElementById('btn').onclick = function() {
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
console.log(mul(4, 5));
});
};
webpackChunkName可以指定输出的文件名,配置webpackPrefetch可以实现预加载。
8、PWA技术
在网络离线的时候,可以使用已经缓存过的资源。
部分浏览器支持,适配的时候需要判断浏览器是否支持。
此项技术依赖插件 workbox-webpack-plugin,使用前需要先安装
npm i -D workbox-webpack-plugin
在webpack.config.js中配置
//先引入
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
//然后再plugins里面配置
plugins:[
new WorkboxWebpackPlugin.GenerateSW({
/*
1. 帮助serviceworker快速启动
2. 删除旧的 serviceworker
生成一个 serviceworker 配置文件~
*/
clientsClaim: true,
skipWaiting: true
})
]
然后在入口js文件中注册serviceWorker
// 注册serviceWorker
// 处理兼容性问题
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/js/service-worker.js')
.then(() => {
console.log('sw注册成功了~');
})
.catch(() => {
console.log('sw注册失败了~');
});
});
}
这样在第一次访问后,会缓存到serviceWorker里面,下次访问的时候优先会从缓存里面取,就算没网也没关系。
9、多进程打包
利用thread-loader来实现,一般用来打包js。
进程启动、进程通信都有开销, 只使用打包时间需要很长的,才适用 ,要不然得不偿失,反而耗时更久。
npm i -D thread-loader
{
loader: 'thread-loader',
options: {
workers: 2 // 可以指定进程的个数
}
}
10、externals
忽略不想打的包,比如jquery我们只想cdn引入
webpack.config.js添加
externals: {
// 忽略库名 --npm包名
jquery: 'jQuery'
}
当我们忽略库的时候,一定要记得在html里面手动引入对应的库。
11、DLL
可以定义一个单独的webpack配置文件,里面专门打包这些不会经常变的库文件。
这样在需要的时候打包一次之后,其余业务代码变动的时候,不再对这些资源进行构建,这样来提升开发时候的构建效率。
- 步骤1:
比如我们新起一个配置文件叫webpack.dll.js
/*
使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
当库的内容变化的时候,直接运行 webpack.dll.js 文件打包一次即可
webpack --config webpack.dll.js
*/
const { resolve } = require('path');
const webpack = require("webpack");
module.exports = {
entry:{
jquery:['jquery']
},
output:{
filename:'[name].dll.js',
path:resolve(__dirname,'dist/dll'),
library:'[name]_[hash:10]'
},
plugins:[
new webpack.DllPlugin({
name:'[name]_[hash:10]',
path:resolve(__dirname,'dist/dll/[name].manifest.json')
})
],
mode: 'production'
}
第一次我们执行下打包即可
webpack --config webpack.dll.js
后续若有改动再次执行上述打包命令即可,无修改可不管。
- 步骤2:
先安装依赖插件add-asset-html-webpack-plugin
,
然后在webpack的默认配置文件webpack.config.js中增加内容
const webpack = require("webpack");
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
plugins:[
//告诉webpack哪些库不参与打包
new webpack.DllReferencePlugin({
manifest:resolve(__dirname,"dist/dll/jquery.manifest.json")
}),
//把manifest.json里面的文件打包出去,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath:resolve(__dirname,'dist/dll/jquery.dll.js')
})
]
这样不会经常变动的库,我们通过webpack --config webpack.dll.js
来打包,平常我们执行webpack就会更快一点。
如果打包多个库,参考代码:
- webpack.dll.js中配置多个入口文件
entry: {
jquery: ['jquery'],
react: ['react', 'react-dom']
},
- 相应的,webpack.config.js中也需配置多个new AddAssetHtmlWebpackPlugin和new DllReferencePlugin插件
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, 'dist/dll/jquery.dll.js')
}),
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, 'dist/dll/react.dll.js')
}),
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, 'dist/dll/jquery.manifest.json')
}),
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, 'dist/dll/react.manifest.json')
}),