项目代码:
index.js
import add from "./add"
import { minus } from "./minus";
const sum = add(1,2);
const division = minus(2,1);
console.log(sum);
console.log(division);
add.js
export default (a,b)=>{
return a+b;
}
minus.js
export const minus = (a,b)=>{
return a-b
}
bundler 原理
const fs = require('fs')
const path = require('path')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const babel = require('@babel/core')
const getModuleInfo = (file)=>{
const body = fs.readFileSync(file,'utf-8')
const ast = parser.parse(body,{
sourceType:'module' // 表示我们要解析的是ES模块
});
const deps = {}
// 遍历 AST 树,解析 import 语句,收集每个模块的依赖
traverse(ast,{
ImportDeclaration({node}){
const dirname = path.dirname(file)
const abspath = "./" + path.join(dirname,node.source.value)
deps[node.source.value] = abspath
}
})
// 由 AST 树转化成代码
const {code} = babel.transformFromAst(ast,null,{
presets:["@babel/preset-env"]
})
//
const moduleInfo = {file,deps,code}
return moduleInfo
}
const parseModules = (file) =>{
const entry = getModuleInfo(file)
const temp = [entry]
const depsGraph = {}
// 从入口文件递归,收集所有文件的依赖
for (let i = 0;i<temp.length;i++){
const deps = temp[i].deps
if (deps){
for (const key in deps){
if (deps.hasOwnProperty(key)){
temp.push(getModuleInfo(deps[key]))
}
}
}
}
temp.forEach(moduleInfo=>{
// 注意文件依赖对象的结构
depsGraph[moduleInfo.file] = {
deps:moduleInfo.deps,
code:moduleInfo.code
}
})
return depsGraph
}
// 入口函数
const 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('./src/index.js')
console.log(content);
//写入到我们的dist目录下
fs.mkdirSync('./dist');
fs.writeFileSync('./dist/bundle.js',content)
经过 babel 转换之后的代码
[图片上传失败...(image-1fa071-1616396327635)]
index.js
// index.js
"use strict"
var _add = _interopRequireDefault(require("./add.js"));
var _minus = require("./minus.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
var sum = (0, _add["default"])(1, 2);
var division = (0, _minus.minus)(2, 1);
console.log(sum); console.log(division);
add.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true});
exports["default"] = void 0;
var _default = function _default(a, b) { return a + b;};
exports["default"] = _default;
babel 转换之后的代码不能直接在浏览器中运行,因为没有定义 require、exports
require 方法的实现
`(function (graph) {
function require(file) {
function absRequire(relPath) {
return require(graph[file].deps[relPath])
}
// 因为bable 转化之后的代码中已使用了exports,所以只需要在外出声明一个变量 exports
var exports = {}
(function (require,exports,code) {
eval(code)
})(absRequire,exports,graph[file].code)
return exports
}
require('${file}')
})(${depsGraph})`