webpack相关问题
# webpack相关问题
- webpack是基于
入口
的。webpack会自动地递归
解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能
# 问题1:Loader和Plugin的区别
- Loader:文件处理,转码,格式转换,
文件转换
,加载器,加载和解析
-eg:ccss转css,es6转es5 - Plugin:扩展插件,
扩展器
,压缩文件--eg:uglifyjs-webpack-plugin,通过UglifyES压缩ES6代码
# 问题2:常见的Loader及其作用
- babel-loader:把 ES6 转换成 ES5
- css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
- optimize-css-assets-webpack-plugin:压缩css
- style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
- eslint-loader:通过 ESLint 检查 JavaScript 代码
html-webpack-plugin
:生成的html页面- uglifyjs-webpack-plugin:压缩js,开启文件缓存和使用多进程
- compression-webpack-plugin:gzip压缩
# 问题3:常用Plugin及其作用
- commons-chunk-plugin:提取公共代码
- uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码
# 问题4:webpack的构建流程
初始化
参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数- 开始
编译
:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的run
方法开始执行编译 - 确定
入口
:根据配置中的entry
找出所有的入口文件 编译
模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归
本步骤直到所有入口依赖的文件都经过了本步骤的处理- 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
输出
资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk
,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容
写入
到文件系统
# 问题5:webpack打包过程,手写打包过程
打包过程:
- 获取模块内容
- 分析模块--
@babel/parse
,转AST--parser.parse() - 处理模块--
@babel/traverse
,遍历AST,收集依赖(res.node.source.value),用ImportDeclaration来解析import - --@babel/core
和@babel/preset-env
--es6转es5 - 递归模块--
广度优先
遍历 - 生成代码--JSON.stringify()后,require,localRequire,eval—自执行函数
手写代码
// index.js
import test from './test.js'
import test2 from './test2.js'
console.log(test, test2, 555)
1
2
3
4
5
2
3
4
5
// test.js
console.log('test777')
let test = 123
export default test
1
2
3
4
2
3
4
// test2.js
console.log('test888')
let tests2 = 456
export default tests2
1
2
3
4
2
3
4
// 核心
// webpack.js
const fs = require('fs')
// 转AST
const parser = require('@babel/parser')
// 遍历ast,收集依赖
// 注意要用.default,使用当前api解析
const traverse = require('@babel/traverse').default
// es6转es5
const { transformFromAst } = require('@babel/core')
function webpack(entry) {
// 读出utf-8的文件
const file = fs.readFileSync(entry, 'utf-8')
// 文件转ast树
const ast = parser.parse(file, { sourceType: 'module' })
// 遍历ast,收集依赖
const dependencies = []
traverse(ast, {
// 解析import
ImportDeclaration(res) {
// console.log(res, '解析的import')
// 依赖的值
const val = res.node.source.value
// './test'
dependencies.push(val)
// ['./test']
}
})
const { code } = transformFromAst(ast, null, {
presets: ['@babel/preset-env']
})
return {
code,
dependencies,
entry
}
}
// 广度优先遍历,查找所有模块输出,通过执行webpack方法输出
function breadthFirstTraversal(entry) {
const oneObjs = webpack(entry)
const graph = {} //收集所有 {code,dependencies,fileName}
const allArr = [oneObjs]
graph[entry] = oneObjs
while (allArr.length) {
const oneObj = allArr[0];
const dependencies = oneObj.dependencies;
for (let dep of dependencies) {
const item = webpack(dep)
allArr.push(item)
graph[dep] = item
}
allArr.shift()
}
return graph
}
// 拼写字符串,让浏览器可以递归运行对象里的每一段code
function getJsString(entry) {
// 获取到的模块对象
const getObj = breadthFirstTraversal(entry);
// console.log(getObj, 'getObj')
// 编写require进行递归
return `(function(getObj) {
function require(module) {
function localRequire(relative) {
return require(relative)
};
var exports = {};
(function(require, exports, code) {
eval(code)
})(localRequire, exports, getObj[module].code);
return exports;
};
require('${entry}')
})(${JSON.stringify(getObj)})`
}
const res= webpack('./index.js')
const deps2 = breadthFirstTraversal('./index.js')
const res2 = getJsString('./index.js')
// console.log(res, 999)
// console.log(deps2, 999)
console.log(res2, 999)
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
输出结果: