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 日
如果觉得我的文章对你有用,请随意赞赏!