webpack 将文件打包成什么
CommonJs 下的打包结果
- webpack 的打包结果是一个立即执行函数IIFE。这个IIFE接收一个modules对象作为参数,modules对象的key是依赖路径,value是经过处理后的依赖脚本。
(funcition(modules) { // ... }({ 'src/index.js': (function(){ // ... }), 'src/hello.js': (function(){ // ... }), // ... })) - 打包结果中定义了一个模块加载函数
_webpack_require_。 - 首先使用
_webpack_require_去加载入口文件 - 加载函数
_webpack_require_中会使用一个闭包变量installedModules, 用来保存已经加载过的依赖,避免重复加载
ES下的打包结果
- ES 下的打包结果大致和CommonJs相同,只是其中多了一句
_webpack_require_.r(_webpack_exports_)这个方法的作用是给模块的exports对象打上ES模块化规范的标记。 - 这个标记用来区分Commonjs 和 ES规范混用的情况
按需加载下的打包结果
首先,回顾一下webpack触发按需加载的方式。通过import()方法指定要单独打包的依赖文件,import方法会返回一个promise对象用来处理依赖加载后的回调
import('./hello').then(sayHello => {
console.log(sayHello('lala~'))
})
打包之后文件内容相较于之前有比较大的变化,其中最核心的是多了一个_webpack_require_.e 和webpackJsonp
_webpack_require_.e_初始化了一个promise数组,用来插入script脚本webpackJsonp会挂载在全局对象上,进行模块安装
webpack的基本工作原理
- 读取
webpack.conf.js或从shell语句中获得配置参数; - 将所需的插件实例化,在webpack的事件流中注册钩子回调,这样在指定的阶段插件就能发挥作用,更改产出结果;
- 同时根据配置制定的入口文件,从入口文件开始遍历收集依赖。这个过程如下:
- 先通过loader对文件进行编译
- 编译好的内容再通过
acon解析生成AST - 然后分析文件依赖关系,并将不同模块化语法替换成
_webpack_require_,即使用webpack自己的模块化加载器
- 产出结果,打包到配置指定的文件目录。
抽象语法树AST
抽象语法数,是对源代码的一种抽象表示。通过树状的形式表示编程代码的语法结构,树上的每一个节点都是源码中的一种结构和表达。
AST为代码分析提供基础。webpack将代码转换成AST就是为了开发者方便提取模块文件中的信息。
js处理AST的工具
- 通过
esprima把源码转化成AST
let esprima = require('esprima');
let code = 'function ast(){}';
let ast = esprima.parse(code);
console.log(ast);
- 通过
estraverse遍历AST的节点,并做修改
estraverse.traverse(ast, {
enter(node) {
console.log('enter', node.type)
if (node.type == 'Indentifier') {
node.name += 'enter';
}
},
leave(node) {
console.log('leave', node.type)
if (node.type == 'Indentifier') {
node.name += 'leave';
}
}
})
- 通过
escodegen将AST重新生成源码
let result = escodegen.generate(ast)
借助babel工具也可以对AST进行操作
具体参考 babel官方文档