动手写一个简单的代码编译器,了解一下webpack
的核心原理。
核心打包原理
- 获取入口文件的内容
- 分析入口文件,读取需要执行的模块内容
- 生成 AST 语法树
- 根据 AST 语法树生成浏览器能够正常运行的代码
撸代码
首先初始化一个项目
webpackTheory ├── src │ ├── sum.js │ ├── subtract.js │ └── index.js ├── package.json ├── bundle.js └── index.html
|
export default (a, b) => { return a + b; }
|
export const minus = (a, b) => { return a - b; }
|
import add from './sum.js'; import { minus } from './subtract.js'; const sum = add(2, 1); const subtract = minus(2, 1); document.write(`<h1>sum:${sum}</h1><h1>subtract:${subtract}</h1>`)
|
如果此时直接在index.html
中引入index.js
,页面是无法正常运行的,浏览器无法正常解析,就会抛出语法错误。
Uncaught SyntaxError: Cannot use import statement outside a module
|
所以需要一个编译器,把index.js
编译成浏览器能够正常解析的语法。
创建一个bundle.js
:
const fs = require("fs");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const path = require("path");
const babel = require("@babel/core");
const getModuleInfo = (file) => { const body = fs.readFileSync(file, "utf-8");
const AST = parser.parse(body, { sourceType: "module", });
const deps = {}; traverse(AST, { ImportDeclaration({ node }) { const dirname = path.dirname(file); const abspath = "./" + path.join(dirname, node.source.value); deps[node.source.value] = abspath; }, });
const { code } = babel.transformFromAst(AST, "", { presets: ["@babel/preset-env"], });
return { file, deps, code } };
const parseModules = file => { const entry = getModuleInfo(file); const temp = [entry]; for (let i = 0; i < temp.length; i++) { const deps = temp[i].deps; if (deps) { for (let key in deps) { temp.push(getModuleInfo(deps[key])) } } }
const depsObject = {}; temp.forEach(t => { depsObject[t.file] = { deps: t.deps, code: t.code } })
return depsObject }
const bundle = file => { const DEPSJSON = JSON.stringify(parseModules(file)); return ` ;(function (deps) { function require(f) { const customRequire = function(path){ return require(deps[f]['deps'][path]) }; const customExports = {}; (function(require, exports, code){ eval(code); })(customRequire, customExports, deps[f]['code']) return customExports; }; require('${file}'); })(${DEPSJSON}) ` } const JSCONTENT = bundle("./src/index.js");
fs.unlinkSync("./dist/bundle.js"); fs.rmdirSync("./dist"); fs.mkdirSync("./dist"); fs.writeFileSync("./dist/bundle.js", JSCONTENT);
|
在根目录下运行:
在dist
目录下就生成了编译好的文件,在index.html
中引入,就可以正常执行了。