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]%> }), <% } %>