基本打包机制
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
打包过程可以拆分为四步:
- 利用babel完成代码转换,并生成单个文件的依赖
- 从入口开始递归分析,并生成依赖图谱
- 将各个引用模块打包为一个立即执行函数
- 将最终的bundle.js写入bundle.js中
简单的说
- 1、找到入口文件,分析内容
- 2、找出模块依赖, 拿到依赖路径,转换代码
- 3、输出源代码,把bundle.js输出到
dist目录中
手写一个bundle.js
实现:
1、拿到入口文件名字,读取入口文件,分析代码
2、若入口文件有依赖,拿到对应的依赖模块
3、循环递归分析出所有依赖关系
4、对依赖模块进行语法分析,转换对应的代码
1、借助@babel/parser :https://babeljs.io/docs/en/babel-parser#docsNav
- 这是 babel7的工具,来帮助我们分析内部的语法,包括es6,返回一个AST抽象语法树
- AST:Abstract Syntax Tree :抽象语法树
2、借助@babel/traverse : https://www.babeljs.cn/docs/babel-traverse
- 根据AST的对象中body:Ast.program.body里面分析结果,遍历出所有的引入模块,但是比较麻烦
- 这里还是推荐babel推荐的一个模块@babel/traverse来帮我们处理
- @babel/traverse 可以用来遍历@babel/parser生成的AST
3、借助@babel/core 、@babel/preset-env
- @babel/core: 这个库负责解析,方便各个插件分析语法进行相应的处理
- @babel/preset-env: 包含比较新的ES语法 ,es6转es5的转换规则,转换当前浏览器可支持的语法特性
npm install @babel/parser --save //返回一个AST抽象语法树
npm install --save @babel/traverse //用于遍历AST
//找到入口文件,分析内容,有依赖的话,拿到依赖路径,转换代码(浏览器里可以运行的)
const path = require("path");
const fs = require("fs");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const { transformFromAst } = require("@babel/core");
const entry = entryFile => {
const content = fs.readFileSync(entryFile, "utf-8");
const ast = parser.parse(content, {
sourceType: "module"
});
const dependecies = {};
traverse(ast, {
//以函数的方式
ImportDeclaration({ node }) {
const dirname = path.dirname(entryFile);
const newPath = "./" + path.join(dirname, node.source.value);
dependecies[node.source.value] = newPath;
}
});
const { code } = transformFromAst(ast, null, {
presets: ["@babel/preset-env"]
});
return {
entryFile,
dependecies,
code
};
};
//分析出所有依赖关系
const Dependecies = entryFile => {
const info = entry(entryFile);
const modules = [];
modules.push(info);
for (let i = 0; i < modules.length; i++) {
const item = modules[i];
const { dependecies } = item;
if (dependecies) {
for (let j in dependecies) {
modules.push(entry(dependecies[j]));
}
}
}
const obj = {};
modules.forEach(item => {
obj[item.entryFile] = {
dependecies: item.dependecies,
code: item.code
};
});
return obj;
};
//生成代码
const genCode = entryFile => {
const obj = Dependecies("./src/index.js");
const graph = JSON.stringify(obj);
const bundle = `(function(graph){
function require(module){
function localRequire(relativePath){
return require(graph[module].dependecies[relativePath])
}
var exports = {};
(function(require,exports,code){
eval(code)
})(localRequire,exports,graph[module].code);
return exports;
}
require('${entryFile}')
})(${graph})`;
fs.writeFileSync(path.resolve(__dirname, "./dist/main.js"), bundle, "utf-8");
};
genCode("./src/index.js");