# webpack原理
原理
webpack是一个javascript文件的静态模块打包器。
webpack就像一个生产线,要经过一系列流程处理后才能将源文件转换为要输出的结果。这条生产线上的每一个流程的功能都是单一的,多个流程之前有依赖关系,只有完成当前处理后才能交给下一个流程处理。插件就像是插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack内部通过Tapable来组织这条生产线。在运行的过程中会广播对应的事件,插件只需要监听自己所关心的事件,就能加入到这条生产线中,去改变生产线的运作。
webpack的事件流机制保证了插件的有序性,使得整个系统的扩展性很好。
# webpack 构建流程
构建流程
webpack的打包过程是一个串行的过程,从启动到结束,会一个接一个流程的走下去,最终打包出需要的bundle。下面就是要执行的流程
- 1、初始化参数:从配置文件和
Shell语句中读取并合并参数,得出最终的配置对象 - 2、开始编译:用上一步得到的得到的参数初始化
Compiler对象,加载所有配置的插件,执行对象的run方法,开始编译 - 3、确定入口:根据配置中的entry找到所有入口文件
- 4、构建模块:从入口文件开始,开始构建模块,调用所有配置的
loader对模块进行处理,使用acorn这个库生成ast语法树,再遍历ast语法树收集该模块依赖的模块,再处理依赖的模块,直到所有模块都经过构建。 - 5、输出资源:通过
compilation.seal方法依次对每个模块和chunk进行整理,生成编译后的源码,合并、拆分。每一个chunk对应一个入口文件,开始生成最后的js。再通过MainTemplate.render(处理入口文件模块)和ChunkTemplate.render(处理异步加载的模块)方法将模块处理成带有__webpack__require()的格式,然后将处理完的js输出到output的path中
对应的webpack事件
entry-options:初始化参数compile:开始编译make:分析入口文件,创建模块对象build-module:构建模块,调用loader处理模块after-compile:完成所有的模块构建,结束编译emit:Compiler开始输出生成的assets,插件有最后的机会修改assets,after-emit:输出完成
# webpack打包优化
# 优化体积
- 1、使用splitChunks抽离公共组件
- 2、配置babel-plugin-component,按需引入element-ui组件库
- 3、剔除moment.js中无用的语言包
# 优化热更新
开发环境修改devtool为:'eval-cheap-module-source-map'
- eval:不单独生成.map文件,用eval包裹源码字符串。
- cheap:只映射到行,而不到列
- module:保留原始源码
- source-map:开启源码映射
# 优化打包速度
# 用thread-loader开启多进程loader转换
TIP
把thread-loader这个loader放在其他loader的前面(左边),放置在这个loader之后的loader就会在一个单独的worker池中worker pool运行,当项目比较复杂,文件比较多时,添加这个loader会减少转换时间。如果项目比较简单,文件比较少,反而会增加时间。
rules: [
{
test: /\.js$/,
use: [
{
loader: 'thread-loader', // 开启多进程处理 JS 逻辑
options: { workers: 3 }
},
'babel-loader'
]
},
{
test: /\.scss$/,
use: [// loader从右往左执行
'vue-style-loader',
'css-loader',
'postcss-loader',
{
loader: 'thread-loader', // 给复杂的 Sass 编译分配多线程
options: { workers: 2 }
},
'sass-loader'
]
}
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 配置loader缓存
TIP
在babel-loader中可以通过设置cacheDirectory来开启缓存,babel-loader?cacheDirectory=true,就会将每次的编译结果写进硬盘文件,不支持cacheDirectory的可以使用cache-loader,再次构建会先比较,如果文件没有改变则会直接使用缓存。
rules: [
{
test: /\.js$/,
use: [
'cache-loader',
{
loader: 'thread-loader', // 开启多进程处理 JS 逻辑
options: { workers: 3 }
},
'babel-loader'
]
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'cache-loader',
'css-loader',
'postcss-loader',
{
loader: 'thread-loader', // 给复杂的 Sass 编译分配多线程
options: { workers: 2 }
},
'sass-loader'
]
}
]
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
# sass-resources-loader
一次性注入全局css变量,避免重复注入
rules: [
{
test: /\.js$/,
use: [
'cache-loader',
{
loader: 'thread-loader', // 开启多进程处理 JS 逻辑
options: { workers: 3 }
},
'babel-loader'
]
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'cache-loader',
'css-loader',
'postcss-loader',
{
loader: 'thread-loader', // 给复杂的 Sass 编译分配多线程
options: { workers: 2 }
},
'sass-loader'
{
loader: 'sass-resources-loader', // 注入全局变量
options: {
// 填入你需要全局注入的 scss 文件路径
resources: [
path.resolve(__dirname, './src/assets/styles/variables.scss'),
path.resolve(__dirname, './src/assets/styles/mixins.scss')
]
}
}
]
}
]
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
# TerserWebpackPlugin
减小包体积、代码混淆、多进程压缩
const TerserPlugin = require('terser-webpack-plugin');
optimization: {
minimize: true, // 开启压缩
minimizer: [
new TerserPlugin({
// 1. 开启多进程压缩(提升速度的核心!)
// 默认是核心数 - 1,建议显式设为 true
parallel: true,
// 2. 开启缓存(提升二次打包速度)
cache: true,
// 3. 提取注释:防止每个文件头部都有一堆版权注释占体积
extractComments: false,
terserOptions: {
compress: {
// 4. 生产环境自动剔除 console.log
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log'] // 确保彻底删掉
},
output: {
// 5. 去掉所有的注释(最精简)
comments: false,
beautify: false
},
// 6. 配合 SourceMap 定位线上 Bug(如果需要的话)
sourceMap: true
}
})
]
}
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
# DllPlugin、DllReferencePlugin预编译第三方库
使用DllPlugin预编译第三方库vue、vue-router、axios、echarts等,生成dll.js和一份manifest.json,使用DllReferencePlugin插件引用预编译的json
← babel配置 tree-shaking →