webpack模块化

2022-11-16 14:33:57 浏览数 (1)

  • 参考,
  • webpack: 4.35.2 版本

整体结构

代码语言:javascript复制
(function webpackUniversalModuleDefinition(root, factory){
  if (typeof exports === 'object' && typeof module === 'object')
    module.exports = factory();
  else if (typeof define === 'function' && define.amd)
    define([], factory);
  else {
    var a = factory();
    for (var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
  }
})(window, function(){
  return (function(modules){ // webpackBootstrap
    
    // 模块结果的缓存
    var installedModules = {};

    // 模块化方法的定义
    ...

    // 执行入口文件
    return __webpack_require__(__webpack_require__.s = "./src/app.js"); 
  })(
  // 模块文件的映射(文件名: 模块的定义),通过该该map来查找模块定义
  {
    "./src/app.js": (function(module, __webpack_exports__, __webpack_require__){...}),
    "./src/counter.js":  (function(module, exports){...})
  });
});

模块方法实现

webpack_require

代码语言:javascript复制
// moduleId 可以理解为模块名称
function __webpack_require__(moduleId){ 

  // Check if module is in cache
  if (installedModules[moduleId]) {
    return installedModules[moduleId].exports;
  }
  
  // 创建并缓存模块
  var module = installedModules[moduleId] = {
    i: moduleId,
    l: false,
    exports: {}
  };

  // 执行模块的定义
  modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

  // 模块加载完成的标识
  module.l = true;

  // 返回模块导出的结果
  return module.exports;
}

webpack_require.d

esModule通过该方法定义模块,重点在getter函数,通过闭包实现了esModule的特性:引用

代码语言:javascript复制
// define getter function for harmony exports
__webpack_require__.d = function(exports, name, getter){
  if (!__webpack_require__.o(exports, name)) {
    Object.defineProperty(exports, name, {enumerable: true, get: getter});
  }
};

webpack_require.r

defineProperty __esModule,标识为es Module

代码语言:javascript复制
// define __esModule on exports
__webpack_require__.r = function(exports){
  if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    Object.defineProperty(exports, Symbol.toStringTag, {value: 'Module'});
  }
  Object.defineProperty(exports, '__esModule', {value: true});
};

webpack_require.n

代码语言:javascript复制
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function(module){
  var getter = module && module.__esModule ?
    function getDefault(){
      return module['default'];
    } :
    function getModuleExports(){
      return module;
    };
  __webpack_require__.d(getter, 'a', getter);
  return getter;
};

其它

  • hasOwnProperty、
  • modules、installedModules、
  • __webpack_public_path__
代码语言:javascript复制
__webpack_require__.m = modules;

__webpack_require__.c = installedModules;

__webpack_require__.o = function(object, property){
  return Object.prototype.hasOwnProperty.call(object, property);
};

// __webpack_public_path__
__webpack_require__.p = "";

webpack_require.t 作用??

代码语言:javascript复制
 // create a fake namespace object
 // mode & 1: value is a module id, require it
 // mode & 2: merge all properties of value into the ns
 // mode & 4: return value when already ns object
 // mode & 8|1: behave like require
 __webpack_require__.t = function(value, mode) {
 	if(mode & 1) value = __webpack_require__(value);
 	if(mode & 8) return value;
 	if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
 	var ns = Object.create(null);
 	__webpack_require__.r(ns);
 	Object.defineProperty(ns, 'default', { enumerable: true, value: value });
 	if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
 	return ns;
 };

示例模块编译结果

commonjs

app.js

代码语言:javascript复制
const { num, increase } = require("./b");

console.log(num); // 1
increase();
console.log(num); // 1
代码语言:javascript复制
function(module, exports, __webpack_require__) {
    const { num, increase } = __webpack_require__(/*! ./b */ "./src/b.js");
    console.log(num);
    increase();
    console.log(num);
}

counter.js

代码语言:javascript复制
let num = 1;

function increase() {
  return num  ;
}

module.exports = { num, increase };
代码语言:javascript复制
function(module, exports) {
    let num = 1;
    function increase() {
      return num  ;
    }
    module.exports = { num, increase };
}

小结

num 属于基本类型,假设其内存地址指向 n1,当它被 赋值 给 module.exports['num'] 时,module.exports['num'] 已经指向了一个新的内存地址 n2,只不过其值同样为 1,但和 num 已是形同陌路,毫不相干。

代码语言:javascript复制
let num = 1;
// mun 相当于 module.exports['num']
mun = num;
代码语言:javascript复制
num = 999;
console.log(mun); // 1

increase 是一个函数,属于引用类型,即 increase 只作为一个指针,当它被赋值给 module.exports['increase'] 时,只进行了指针的复制,是 浅拷贝,其内存地址依旧指向同一块数据。所以本质上 module.exports['increase'] 就是 increase,只不过换个名字。 而由于词法作用域的特性,counter.js 中 increase() 修改的 num 变量在函数声明时就已经绑定不变了,永远绑定内存地址指向 n1 的 num.

es Module

app.js

代码语言:javascript复制
import { num, increase } from "./counter";

console.log(num); // 1
increase();
console.log(num); // 2
代码语言:javascript复制
function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    __webpack_require__.r(__webpack_exports__);
    var _counter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./counter */ "./src/counter.js");
    
    
    console.log(_counter__WEBPACK_IMPORTED_MODULE_0__["num"]);
    Object(_counter__WEBPACK_IMPORTED_MODULE_0__["increase"])();
    console.log(_counter__WEBPACK_IMPORTED_MODULE_0__["num"]);
}

counter.js

代码语言:javascript复制
let num = 1;

function increase() {
  return num  ;
}

export { num, increase };
代码语言:javascript复制
function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    __webpack_require__.r(__webpack_exports__);
    __webpack_require__.d(__webpack_exports__, "num", function() { return num; });
    __webpack_require__.d(__webpack_exports__, "increase", function() { return increase; });
    let num = 1;
    
    function increase() {
      return num  ;
    }
}

小结

与 CommonJS 不同,ES Modules 并没有对 module.exports 直接赋值,而是将值作为函数的返回值,再把函数赋值给 module.exports,之前我们提过词法作用域的概念,即这里的 num() 和 increase() 无论在哪里执行,返回的 num 变量和 increase 函数都是 counter.js 中的。

总结

问: CommonJS 和 ES Modules 中模块引入的区别?

CommonJS 输出的是一个值的拷贝;ES Modules 生成一个引用,等到真的需要用到时,再到模块里面去取值,模块里面的变量,绑定其所在的模块。

0 人点赞