bundler 基本原理

項目代碼:
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 樹轉(zhuǎn)化成代碼
    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=>{
// 注意文件依賴對象的結(jié)構(gòu)
        depsGraph[moduleInfo.file] = {
            deps:moduleInfo.deps,
            code:moduleInfo.code
        }
    })
    return depsGraph
}

// 入口函數(shù)
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)

經(jīng)過 babel 轉(zhuǎn)換之后的代碼

[圖片上傳失敗...(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 轉(zhuǎn)換之后的代碼不能直接在瀏覽器中運行,因為沒有定義 require、exports

require 方法的實現(xiàn)

`(function (graph) {
        function require(file) {
            function absRequire(relPath) {
                return require(graph[file].deps[relPath])
            }
// 因為bable 轉(zhuǎn)化之后的代碼中已使用了exports,所以只需要在外出聲明一個變量 exports 
            var exports = {}
            (function (require,exports,code) {
                eval(code)
            })(absRequire,exports,graph[file].code)
            return exports
        }
        require('${file}')
    })(${depsGraph})`

參考:Sunny-lucking#howToBuildMyWebpack

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容