配置优化

HMR 模块热替换

当一个模块改变时避免所有的模块都被重新编译一次,应该只更新修改的模块

通过 hot:true 开启hmr,此时样式文件(.css .scss) 可以进行热模块替换,style-loader内部的实现,但是js文件没有开启热替换,而且html的文件也不能更新了

因为热替换阻止了刷新,通过修改webpack.config.js 入口配置entry: ['./src/index.js','./src/index.html'],,可以重新开启index.html的刷新功能

另外 html文件不需要热替换的功能,因为每个入口只对应一个文件,一定要重新加载

.js文件的热替换不能是入口文件

1
2
3
4
5
6
if (module.hot) {
module.hot.accept('./print.js', function() {
console.log('Accepting the updated printMe module!');
printMe();
})
}
source-map

提供源代码到编译后代码映射的方案,可以追踪源代码的位置 通过devtool配置

[inline-|hidden-|eval][nosources-][cheap-[module-]]source-map

inline 构建速度快

source-map 能提示错误代码准确信息,和源代码中准确位置,会单独生成一个文件

inline-source-map source-map会内嵌到生成的js文件中,只生成一个内联的source-map ,能提示错误代码准确信息,和源代码中准确位置

eval-source-map source-map会内嵌到生成的js文件中,每个文件都会生成对应的source-map,可以提示错误原因,但不能追踪到源代码位置,只会定位到编译后的错误位置

hidden-source-map source-map文件会单独生成,可以提示错误原因,但不能追踪到源代码位置,只会定位到编译后的错误位置

cheap-source-map 在外部单独生成,只能提示到行,如果代码在一行中,不能准确的定位

cheap-module-source-map 在外部单独生成 与 cheap-source-map 类似,会将loader的source-map加入

nosources-source-map 可以提供作物信息,但是不能定位到错误位置,源代码和编译后代码都不可以

数度快慢: eval=> inline => cheap

开发环境:cheap-source-map
速度快
eval-cheap-source-map
eval-source-map 🆚
调试友好
source-map
cheap-module-source-map
cheap-source-map

生产环境
简单调试,
内联会让体积变大
nosources-source-map 全部隐藏代码,在线上环境使用🆚
hidden-source-map 只隐藏源代码

source-map 🆚,单独生成文件且便于调试

oneOf

用于提升构建的速度,只要有一个loader匹配到就不会继续匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
module: {
rules: [
{
//...
// 指定优先级,都会先被这个loader处理
enforce:'pre'
},
{
oneOf:[
// 其他的loader
]
}
]
}
}

缓存

在编译文件的时候,如果依赖文件没有改变,则直接使用编译好的缓存文件,无需所有文件都重新编译

1
2
3
4
5
6
7
8
9
{
test: /\.js$/i,
use: [{
loader: 'babel-loader',
options:{
cacheDirectory:true
}
}],
},

文件资源的缓存

  • hash 如果都使用hash的话,即每次修改任何一个文件,所有文件名的hash至都将改变。所以一旦修改了任何一个文件,整个项目的文件缓存都将失效.

  • chunkhash chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响。动态import也受chunkhash的影响.

因为我们是将样式作为模块import到JavaScript文件中的,所以它们的chunkhash是一致的,这样就会有个问题,只要对应css或则js改变,与其关联的文件hash值也会改变,但其内容并没有改变呢,所以没有达到缓存意义。固contenthash的用途随之而来。

  • contenthash是针对文件内容级别的,只有你自己模块的内容变了,那么hash值才改变

tree-shaking

使用es6模块化规范,开启production模式,webpack会自动启用tree-shaking

webpack.config.js 中添加sideEffects:false表示所有的代码都没有副作用,如果标记为false, 全局引入的文件(polyfile),或没有通过模块化使用的css,都会被删除

可以通过一个数组来标记不需要处理的资源 sideEffects:['*.css']

代码分割 code-split

生成chunk的几种方式

  • 多页面entry生成多个chunk
  • 异步组件生成chunk
  • code split
1
2
3
4
5
6
7
// 将node-modules 中代码单独打包成
// 分析多入口文件中有没有公共的依赖,会把依赖单独打包
optimization: {
splitChunks: {
chunks: 'all'
}
},

懒加载 预加载

dynamic-imports

babel-loader会自动处理 dynamic-imports语法, 如果eslint提示错误,在.eslintrc中添加"parser": "babel-eslint"

PWA

work-box -> workbox-webpack-plugin

1
yarn add -D workbox-webpack-plugin

webpack.config.js 中添加插件和配置项

1
2
3
4
5
6
7
8
9
10
11
{
plugins:[
//...
new GenerateSW({
// 帮助serviceWork快速启动,
// 删除旧的serverwork
clientsClaim:true,
skipWaiting:true
})
]
}

注册servicework

在入口文件index.js

1
2
3
4
5
6
7
8
9
10
11
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('./service-worker.js')
.then(() => {
console.log('注册成功');
})
.catch(() => {
console.log('注册失败');
});
});
}

如果eslint不支持全局变量,在.eslinrc添加{env:browser: true,}

多进程打包

thread-loader

进程的启用会占用时间(大约600ms),只有复杂的任务处理的时候才会有明显的效果

externals 忽略某些资源

webpack.config.js中添加externals

1
2
3
4
5
6
module.exports = {
//...
externals: {
jquery: 'jQuery'
}
};

和dll的区别是,externals并没有打包文件,需要通过cdn的方式引入进来,dll只是把指定的包单独打包,并通过插件把单独打包的文件重新引入

dll

对第三方的库,进行单独打包 webpack5不适用

通过两份配置,可以避免每次对第三方的库重新打包

webpack.dll.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const path = require('path');
const webpack = require('webpack');

module.exports = {
entry:{
lodash:['lodash'],
jquery:['jquery'],
moment:["moment"]
},
output:{
// 生成文件的名称
filename:'[name]_[contenthash:8].js',
path:path.resolve(__dirname,'dll'),
// 单独打包的库对外暴露的名称
library: "[name]_[fullhash]"
},
plugins:[
new webpack.DllPlugin({
context: __dirname,
// 映射单独打包的库的名称
name: '[name]_[fullhash]',
// 生成的manifest文件
path:path.resolve(__dirname,'dll/[name]_manifest.json')
})
],
mode:'production'
}

webpack.config.js

使用 add-asset-html-webpack-plugin把单独打包的资源引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.export = {
plugins:[
new webpack.DllReferencePlugin({
// 存在于manifest文件中的包,不会被打包
manifest: require(path.resolve(__dirname,'dll/moment_manifest.json'))
}),
new webpack.DllReferencePlugin({
manifest: require(path.resolve(__dirname,'dll/lodash_manifest.json'))
}),
new webpack.DllReferencePlugin({
manifest: require(path.resolve(__dirname,'dll/jquery_manifest.json'))
}),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, './dll/*.js'),
})
]
}

optimization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
optimization: {
minimize: true,
// https://webpack.docschina.org/plugins/terser-webpack-plugin/
minimizer: [new TerserPlugin({
cache:true,// 开启缓存
parallel:true,//多进程打包
sourceMap:true //启用source-map
})],
splitChunks: {
// async表示只从异步加载得模块(动态加载import())里面进行拆分
// initial表示只从入口模块进行拆分
// all表示以上两者都包括
chunks: 'async', //all
minSize: 1024 * 30 , //分割chunk的最小大小,大于30kb才会被提取
minRemainingSize: 0,
maxSize: 0,// 没有最大限制
minChunks: 1, // 至少被引用一次
// 按需加载的时候并行加载文件的最大个数
// 可以理解为针对一个模块拆分后的个数,包括模块本身
// import()文件本身算一个请求
// 并不算js以外的公共资源请求比如css
// 如果同时有两个模块满足cacheGroup的规则要进行拆分,但是maxInitialRequests的值只能允许再拆分一个模块,那尺寸更大的模块会被拆分出来
// 一个按需加载的包最终被拆分成 n 个包,maxAsyncRequests 就是用来限制 n 的最大值。
maxAsyncRequests: 30,
maxInitialRequests: 30, //入口js文件最大请求数量
enforceSizeThreshold: 50000,
name: true, // 可以使用命名规则
cacheGroups: {
// node_module中的包会被引入到defaultVendors包中
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
// 如果当前打包的模块和之前的是同一个就会复用,而不是重复打包
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
// 将当前模块中记录的其他模块的hash单独打包成一个文件
// 因为如果main.js 引用 a.js,main.js中会保存a.js打包时生成的cotenthash
// 如果a.js发生改变则contenthash发生改变,那么main.js的contenthash也会改变
// 代码分割的时候一定要加
runtimeChunk:()=> {
name:entrypoint=>`runtime-${entrypoint.name}`
}
},

},
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2025 SunZhiqi

此时无声胜有声!

支付宝
微信