亲宝软件园·资讯

展开

实现一个简易版Webpack

linjiajun 人气:0
## 原理 * 1、解析一个文件及其依赖 * 2、构建一个依赖关系图 * 3、将所有东西打包成一个单文件 ## 代码实现 #### 文件结构 #### 1、解析文件及其依赖 通过babylon将文件解析成AST [在线解析器](https://astexplorer.net/): ![image](https://user-images.githubusercontent.com/3964466/78567304-0e8d9580-7853-11ea-98d2-c7ccdd7153ac.png) 代码实现: bundle.js ``` const fs = require("fs"); const babylon = require("babylon"); const traverse = require("babel-traverse").default; let ID = 0; function createAsset(filename) { const content = fs.readFileSync(filename, "utf-8"); // 解析文件成AST const ast = babylon.parse(content, { sourceType: "module", }); const dependencies = []; // 根据AST获取相关依赖 traverse(ast, { ImportDeclaration: ({ node }) => { dependencies.push(node.source.value); }, }); const id = ID++; return { id, filename, dependencies, }; } const mainAssets = createAsset("./example/entry.js"); console.log(mainAssets) ``` 输出结果: ![image](https://user-images.githubusercontent.com/3964466/78567360-22d19280-7853-11ea-8055-7d664c3343fa.png) #### 2、构建一个依赖关系图 ``` // 构建一个依赖关系图 function createGraph(entry) { const mainAssets = createAsset(entry); const queue = [mainAssets]; for (const asset of queue) { const dirname = path.dirname(asset.filename); asset.mapping = {}; asset.dependencies.forEach((relativePath) => { const absolutePath = path.join(dirname, relativePath); const child = createAsset(absolutePath); asset.mapping[relativePath] = child.id; queue.push(child); }); } return queue; } const graph = createGraph("./example/entry.js"); console.log(graph); ``` 输出结果: ![image](https://user-images.githubusercontent.com/3964466/78567385-2e24be00-7853-11ea-93c4-0bbe4199a67f.png) #### 3、将所有东西打包成一个单文件 在解析文件时,使用babel对代码进行转译 ``` // 解析一个文件及其依赖 function createAsset(filename) { const content = fs.readFileSync(filename, "utf-8"); const ast = babylon.parse(content, { sourceType: "module", }); const dependencies = []; traverse(ast, { ImportDeclaration: ({ node }) => { dependencies.push(node.source.value); }, }); const id = ID++; // 使用babel对代码进行转译 const { code } = babel.transformFromAst(ast, null, { presets: ["env"], }); return { id, filename, dependencies, code, }; } ``` ``` // 将所有东西打包成一个单文件 function bundle(graph) { let modules = ""; graph.forEach((mod) => { modules += `${mod.id}:[ function(require,module,exports){ ${mod.code} }, ${JSON.stringify(mod.mapping)} ],`; }); const result = ` (function(modules){ function require(id){ const [fn, mapping] = modules[id]; // 因为代码引入文件时根据相对路径,所以需要把相对路径跟id进行一个映射 function localRequire(relativePath){ return require(mapping[relativePath]) } const module = {exports:{}}; fn(localRequire,module,module.exports) return module.exports; } // 执行入口模块 require(0); })({${modules}}) `; return result; } const graph = createGraph("./example/entry.js"); const result = bundle(graph); console.log(result); ``` 输出结果: ``` (function(modules) { function require(id) { const [fn, mapping] = modules[id]; function localRequire(relativePath) { return require(mapping[relativePath]) } const module = { exports: {} }; fn(localRequire, module, module.exports) return module.exports; } require(0); })({ 0: [ function(require, module, exports) { "use strict"; var _message = require("./message.js"); var _message2 = _interopRequireDefault(_message); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } console.log(_message2.default); }, { "./message.js": 1 } ], 1: [ function(require, module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _name = require("./name.js"); exports.default = "hello " + _name.name + "!"; }, { "./name.js": 2 } ], 2: [ function(require, module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var name = exports.name = 'Aaron'; }, {} ], }) ``` 把代码复制到浏览器运行,执行成功! ![image](https://user-images.githubusercontent.com/3964466/78567412-3b41ad00-7853-11ea-88ec-fe6b1932f0cc.png) 一个简易版的Webapck完成了。 ## 相关链接 [例子源码](https://github.com/jiajunlin/Build-Your-Own-Webpack) [视频教程](https://www.youtube.com/watch?v=Gc9-7PBqOC8) [babylon](https://www.npmjs.com/package/babylon) [babel-traverse docs](https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md#babel-traverse)

加载全部内容

相关教程
猜你喜欢
用户评论