webpack是一个打包工具,核心概念
- entry:入口,webpack构建第一步,从哪儿开始
- output:输出
- loader:模块转换器,用于将模块的原内容按照需求转换成新内容(默认只能处理json和js文件)
- plugin:扩展插件,在webpack构建过程的特定时机注入扩展逻辑,用来改变或优化构建结果
- mode:控制打包环境,development、production或者none之中的一个,默认production
- devserver:是一个小型的nodejs,Express服务器,使用webpack-dev-middleware中间件来为通过webpack打包生成的资源文件提供web服务---特点:处理静态服务(图片)、live reloading(实时重新加载)、反向代理接口
核心原理(源码):读取所有文件放入一个js文件,index.js是入口。期间会遇到require和exports。
// index.js
var add = require('add.js').default
console.log(add(1 , 2))
// add.js
exports.default = function(a,b) {return a + b}
- 1、处理(模拟)exports对象:块内定义exports={ }, 包含exports的语句,直接eval出去,相当于不做操作,使用闭包防止变量污染,帮助理解,require函数才是真正的核心
(闭包)创建exports对象,获取并保存导出的内容字符串,eval输出
var exports = {}
(function (exports, code) {
eval(code)
})(exports, 'exports.default = function(a,b){return a + b}')
- 2、处理(模拟)require函数:全部复制,添加对象exports={ },return exports闭包连接exports对象。
function require(file) {
var exports = {};
(function (exports, code) {
eval(code)
})(exports, 'exports.default = function(a,b){return a + b}')
return exports
}
var add = require('add.js').default
console.log(add(1 , 2))
- 3、最后,来了多个文件的时候,建立一个key-value(文件名:文件内容)表传入参数执行: 1)require('index.js'): return {},内部执行:require('add.js').default和console
2)require('add.js'):return {default:fn},内部执行:exports赋值
(function (list) {
function require(file) {
var exports = {};
(function (exports, code) {
eval(code);
})(exports, list[file]);
return exports;
}
require("index.js");
})({
"index.js": `
var add = require('add.js').default
console.log(add(1 , 2))
`,
"add.js": `exports.default = function(a,b){return a + b}`,
});
4、真正webpack生成的bundle.js文件中还需要增加模块间的依赖关系。叫做依赖图(Dependency Graph)
{
"./src/index.js": {
"deps": { "./add.js": "./src/add.js" },
"code": "....."
},
"./src/add.js": {
"deps": {},
"code": "......"
}
}
5、总结步骤:分析依赖、es6转es5、替换exports和require
功能实现:从file:'./index.js'开始读取,遇到require,就引入所有内容
- 单独解析一个模块:读文件fs=>转成抽象语法树AST("@babel/parser")=>依赖收集("@babel/traverse")+转成ES5代码("@babel/core")=> moduleInfo = { file, deps, code };
- 定义一个数组,遍历deps,生成新的moduleInfo添加到数组,递归遍历孩子的deps并添加
- 生成bundle文件:
function bundle(file) {
const depsGraph = JSON.stringify(parseModules(file));
return `(function (graph) {
function require(file) {
function absRequire(relPath) {
return require(graph[file].deps[relPath])
}
var exports = {};
(function (require,exports,code) {
eval(code)
})(absRequire,exports,graph[file].code)
return exports
}
require('${file}')
})(${depsGraph})`;
}
const content = bundle('./index.js')
!fs.existsSync("./dist") && fs.mkdirSync("./dist");
fs.writeFileSync("./dist/bundle.js", content);
-
概述:Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数\
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译\
- 确定入口:根据配置中的 entry 找出所有的入口文件\
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理\
- 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系\
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会\
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
简单说
初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中
- 简述webpack原理与构建过程
原理
Webpack的构建原理是通过 最外层的自执行函数+闭包的模式,去模拟实现 exports 和 require函数,从入口文件开始,分析每一个模块之间的相互依赖关系,形成依赖图,然后把入口文件和所有依赖打包到一个 chunk,最终输出到 output 的过程
构建流程
- 初始化流程:从配置文件和 shell 语句中读取和合并参数,并初始化需要使用的插件和配置插件等执行环境所需的参数
- 编译构建流程:从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容,再找到该 Module 依赖的 Module,递归进行编译处理
- 输出流程:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件夹