我们知道在nodejs中使用common.js进行模块化开发,但是在浏览器中并不支持,那么webpack在打包代码时,遇见common.js代码时,会模拟exports、require,比如:

  1. // index.js
  2. var add = require('add.js').default
  3. console.log(add(1 , 2))
  4. // add.js
  5. exports.default = function(a,b) {return a + b}

直接在浏览器中运行以上代码肯定会出问题,因为浏览器中并没有exports和require对象,那么webpack是怎么模拟他们的呢?

exports

既然我们想将一段代码共享给另一个模块使用,那么肯定得把他们打包起来,这里我们的common.js代码可以通过fs.readFileSync来读取,这会将代码转换为字符串,我们直接把这个字符串传给另一个模块不就行了?然后再通过eval来运行这段字符串代码!1.gif
转换后的代码:

  1. exports = {}
  2. eval('exports.default = function(a,b) {return a + b}') // node文件读取后的代码字符串
  3. exports.default(1,3)
  4. // 为了保证变量不会污染,使用IIFE来包装
  5. var exports = {}
  6. (function (exports, code) {
  7. eval(code)
  8. })(exports, 'exports.default = function(a,b){return a + b}')

require

require函数的功能时将模块导入,就是根据file名称加载对应模块,下面以导入add模块为例

  1. function require(file) {
  2. var exports = {};
  3. (function (exports, code) {
  4. eval(code)
  5. })(exports, 'exports.default = function(a,b){return a + b}')
  6. return exports
  7. }
  8. var add = require('add.js').default
  9. console.log(add(1 , 2))

如果我们有N个模块需要导入呢,这个时候就需要生成一个map,根据key-value来存储模块了,key为路径,value为对应的模块。

  1. (function (list) {
  2. function require(file) {
  3. var exports = {};
  4. (function (exports, code) {
  5. eval(code);
  6. })(exports, list[file]);
  7. return exports;
  8. }
  9. require("index.js");
  10. })({
  11. "index.js": `
  12. var add = require('add.js').default
  13. console.log(add(1 , 2))
  14. `,
  15. "add.js": `exports.default = function(a,b){return a + b}`,
  16. });

webpack在打包的时候还会生成依赖图集,这里是利用babel将字符串转换为AST,然后从入口递归解析出代码里的import、export语句,进行依赖收集生成依赖map,然后利用babel将es6转换为es5。

  1. const fs = require("fs");
  2. const path = require("path");
  3. const parser = require("@babel/parser");
  4. const traverse = require("@babel/traverse").default;
  5. const babel = require("@babel/core");
  6. function getModuleInfo(file) {
  7. // 读取文件
  8. const body = fs.readFileSync(file, "utf-8");
  9. // 转化AST语法树
  10. const ast = parser.parse(body, {
  11. sourceType: "module", //表示我们要解析的是ES模块
  12. });
  13. // 依赖收集
  14. const deps = [];
  15. traverse(ast, {
  16. ImportDeclaration({ node }) {
  17. deps.push(node.source.value)
  18. },
  19. });
  20. // ES6转成ES5
  21. const { code } = babel.transformFromAst(ast, null, {
  22. presets: ["@babel/preset-env"],
  23. });
  24. const moduleInfo = { file, deps, code };
  25. return moduleInfo;
  26. }
  27. const info = getModuleInfo("./src/index.js");
  28. console.log("info:", info);