webpack中esmodule及commonjs打包原理

24 阅读2分钟

commonjs

基本使用

name.js

module.exports = "一句话";

main.js

let word = require('./name.js')
console.log('word',word)

核心理解如下

var modules = {
  "./name.js": () => {
    var module = {};
    module.exports = "一句话";
    return module.exports;
  },
};
const require = (modulePath) => {
  return modules[modulePath]();
};

let word = require("./name.js");
console.log(word, "word");

源码实现过程

  • 初始化:定义 modules 对象
  • 定义缓存对象cache
  • 定义加载模块函数require
  • 执行入口函数
//接受模块的路径为参数,返回具体的模块的内容
function require(modulePath) {
  var cachedModule = cache[modulePath]; //获取模块缓存
  if (cachedModule !== undefined) {
    //如果有缓存则不允许模块内容,直接retuen导出的值
    return cachedModule.exports;
  }
  //如果没有缓存,则定义module对象,定义exports属性
  //这里注意!!!module = cache[modulePath] 代表引用的是同一个内存地址
  var module = (cache[modulePath] = {
    exports: {},
  });
  //运行模块内的代码,在模块代码中会给module.exports对象赋值
  modules[modulePath](module, module.exports, require);

  //导入module.exports对象
  return module.exports;
}

esmodule

基本使用

name.js


const author = "a man";

export const age = "18";
export default author;

main.js

import author, { age } from "./name";

console.log(author, "author");
console.log(age, "age");

编译原理

前置步骤与commonjs相同,module.exports对象挂载属性做如下操作

  • 初始化:定义 modules 对象
  • 定义缓存对象cache
  • 定义加载模块函数require
  • 执行入口函数
  • 通过setModuleTag将当前模式标记为es module +
  • 将age和default代理给exports对象将其导出 +
//模块定义
var modules = {
  "./src/name.js": (module, exports, require) => {
    //给该模块设置tag:标识这是一个ES Module
    require.setModuleTag(exports);
    //通过代理给exports设置属性值
    require.defineProperty(exports, {
      age: () => age,
      default: () => DEFAULT_EXPORT,
    });
    const author = "a man";
    const age = "18";
    const DEFAULT_EXPORT = author;
  },
};

var cache = {};
function require(modulePath) {
  var cachedModule = cache[modulePath];
  if (cachedModule !== undefined) {
    return cachedModule.exports;
  }
  var module = (cache[modulePath] = {
    exports: {},
  });
  modules[modulePath](module, module.exports, require);
  return module.exports;
}

//对exports对象做代理
require.defineProperty = (exports, definition) => {
  for (var key in definition) {
    Object.defineProperty(exports, key, {
      enumerable: true,
      get: definition[key],
    });
  }
};

//标识模块的类型为ES Module
require.setModuleTag = (exports) => {
  Object.defineProperty(exports, Symbol.toStringTag, {
    value: "Module",
  });

  Object.defineProperty(exports, "__esModule", {
    value: true,
  });
};

//以下是main.js编译后的代码
//拿到模块导出对象exports
var _name__WEBPACK_IMPORTED_MODULE_0__ = require("./src/name.js");

console.log(_name__WEBPACK_IMPORTED_MODULE_0__["default"], "author");
console.log(_name__WEBPACK_IMPORTED_MODULE_0__.age, "age");

从commonjs加载esModule

name.js

export const age = 18;
export default "author"

main.js

let obj = require("./name");
console.log(obj, "obj");

用esmodule的导出 commonjs的导入

var modules = {
  "./src/name.js": (module, exports, require) => {
    require.setModuleTag(exports);
    require.defineProperty(exports, {
      age: () => age,
      default: () => DEFAULT_EXPORT,
    });
    const age = 18;
    const DEFAULT_EXPORT = "author";
  },
};
var cache = {};
function require(moduleId) {
  var cachedModule = cache[moduleId];
  if (cachedModule !== undefined) {
    return cachedModule.exports;
  }
  var module = (cache[moduleId] = {
    exports: {},
  });
  modules[moduleId](module, module.exports, require);
  return module.exports;
}

require.defineProperty = (exports, definition) => {
  for (var key in definition) {
    Object.defineProperty(exports, key, {
      enumerable: true,
      get: definition[key],
    });
  }
};

require.setModuleTag = (exports) => {
  Object.defineProperty(exports, Symbol.toStringTag, {
    value: "Module",
  });

  Object.defineProperty(exports, "__esModule", {
    value: true,
  });
};

(() => {
  let obj = require("./src/name.js");
  console.log(obj, "obj");
})();

从esModule加载commonjs

name.js

module.exports = "author";

main.js

import author from "./name";

console.log(author, "author");

会多一个 require.n,用于将default挂载,核心原理不变

用commonjs原理导出 esmodule引入

var modules = {
  "./src/name.js": (module) => {
    module.exports = "不要秃头啊";
  },
};
var cache = {};
function require(modulePath) {
  var cachedModule = cache[modulePath];
  if (cachedModule !== undefined) {
    return cachedModule.exports;
  }
  var module = (cache[modulePath] = {
    exports: {},
  });
  modules[modulePath](module, module.exports, require);
  return module.exports;
}

require.n = (module) => {
  var getter =
    module && module.__esModule ? () => module["default"] : () => module;
  require.defineProperty(getter, {
    a: getter,
  });
  return getter;
};

require.defineProperty = (exports, definition) => {
  for (var key in definition) {
    Object.defineProperty(exports, key, {
      enumerable: true,
      get: definition[key],
    });
  }
};

require.setModuleTag = (exports) => {
  Object.defineProperty(exports, Symbol.toStringTag, {
    value: "Module",
  });

  Object.defineProperty(exports, "__esModule", {
    value: true,
  });
};

var __webpack_exports__ = {};
(() => {
  "use strict";
  require.setModuleTag(__webpack_exports__);
  var _name__WEBPACK_IMPORTED_MODULE_0__ = require("./src/name.js");
  var _name__WEBPACK_IMPORTED_MODULE_0___default = require.n(
    _name__WEBPACK_IMPORTED_MODULE_0__
  );
  console.log(_name__WEBPACK_IMPORTED_MODULE_0___default(), "author");
})();