webpack手写打包多文件

  • 与打包单文件相比的不同点:

  • 在传入的对象中多了依赖模块的路径和内容,如

    "./src/a.js": // key
    (function (module, exports, __webpack_require__) { // value
        module.exports = "lnj";
    }),
  • 主模块函数需要接收自定义require方法,如const name = __webpack_require__("./src/a.js");
  • 需要在依赖模块的路径的前面加上主模块路径,如 ./src/a.js./src/b/b.js

    ({
        "./src/index.js": // key
            (function (module, exports, __webpack_require__) { // value
    const name = __webpack_require__("./src/a.js");
    const age = __webpack_require__("./src/b/b.js");
    console.log("it666");
    console.log(name, age);
            }),
    
        "./src/a.js": // key
            (function (module, exports, __webpack_require__) { // value
    module.exports = "lnj";
            }),
    
        "./src/b/b.js": // key
            (function (module, exports, __webpack_require__) { // value
    module.exports = "33";
            }),
    });

  • 创建一个parseModule新方法,主要用来修改主模块代码保存所有依赖的模块

    • 因为其中用到了抽象语法树,所以需要安装模块并引入

      // 抽象语法树
      const parser = require("@babel/parser");
      // 遍历抽象语法树
      const traverse = require("@babel/traverse").default;
      // 抽象语法树转化为代码
      const generate = require('@babel/generator').default;
      // 创建抽象语法树
      const t = require('@babel/types');
// 修改模块代码
parseModule(code) {
    // 转换成抽象语法树
    let ast = parser.parse(code);
    // 定义变量保存主模块路径
    let rootPath = path.dirname(this.config.entry);
    // 定义数组保存依赖的模块
    let dependencies = [];
    // 遍历抽象语法树
    traverse(ast, {
        // 只有遍历到对应的CallExpression类型才会调用
        CallExpression(nodePath) {
            // 获取当前节点
            let node = nodePath.node;
            if (node.callee.name === "require") {
                // 修改require为__webpack_require__
                node.callee.name = "__webpack_require__";
                // 获取当前require导入的路径
                let modulePath = node.arguments[0].value;
                // 修改require导入的路径
                modulePath = ".\\" + path.join(rootPath, modulePath);
                modulePath = modulePath.replace(/\\/g, "/");
                dependencies.push(modulePath);
                // t.StringLiteral()创建抽象语法树结点,将结点对象放在arguments数组中
                node.arguments = [t.StringLiteral(modulePath)];
            }
        }
    });
    // 将抽象语法树转为代码
    let resCode = generate(ast).code;
    // 返回修改后的代码和依赖的模块
    return {
        resCode,
        dependencies
    };
}
  • 重写buildModule方法:使其能处理依赖模块

    // 构建模块
    buildModule(modulePath) {
    // 传入入口文件,获取文件中的内容
    let code = this.getSource(modulePath);
    // 修改模块代码(多文件打包)
    let {
      resCode,
      dependencies
    } = this.parseModule(code);
    // 路径作为key,内容作为value保存到modules中
    this.modules[modulePath] = resCode;
    // 将dependencies数组中依赖的模块也保存到modules中
    dependencies.forEach((depath) => { // 不用箭头函数的话,this会被修改指向
      // 构建依赖模块
      this.buildModule(depath);
    })
    }
  • 修改main.ejs模板:使其能处理所有modules中保存的模块

    <% for (let key in modules) {%>
    "<%-key%>": (function (module, exports,__webpack_require__) {
      <%-modules[key]%>
    }),
    <% } %>

最后修改:2021 年 04 月 11 日 02 : 09 AM
如果觉得我的文章对你有用,请随意赞赏!