乐趣区

关于webpack4:CommonJS规范下webpack单文件打包

依赖版本

"webpack": "^4.44.2",
"webpack-cli": "^3.3.12"

一个简略的文件通过 webpack 打包

// 导出
console.log('index.js')
module.exports = '导出内容'

// 导入
let log = require('./log.js')
console.log('index.js 内容')
console.log(log)

打包后文件

 (function (modules) { // webpackBootstrap
   // The module cache
   var installedModules = {};

   // The require function
   function __webpack_require__(moduleId) {

     // Check if module is in cache
     if (installedModules[moduleId]) {return installedModules[moduleId].exports;
     }
     // Create a new module (and put it into the cache)
     var module = installedModules[moduleId] = {
       i: moduleId,
       l: false,
       exports: {}};

     // Execute the module function
     //  把 index.js 导出内容挂载到 exports 上
     modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

     // Flag the module as loaded
     module.l = true;

     // Return the exports of the module
     return module.exports;
   }


   // expose the modules object (__webpack_modules__)
   __webpack_require__.m = modules;

   // expose the module cache
   __webpack_require__.c = installedModules;

   // 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
       });
     }
   };

   // 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});
   };

   // 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;
   };

   // 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;
   };

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

   // __webpack_public_path__
   __webpack_require__.p = "";

   // Load entry module and return exports
   return __webpack_require__(__webpack_require__.s = "./src/index.js");
 })

 ({

   "./src/index.js":
     /*! no static exports found */
     (function (module, exports) {console.log('index.js 内容')
       module.exports = '入口文件导出内容'
     })

 });

打包文件剖析特点剖析

  1. 打包后的文件就是一个函数自调用,以后函数调用时传入一个对象。这个对象是一个键值对
  2. 这个键名就是以后被加载模块的文件名与某个目录的拼接()
  3. 这个键值就是一个函数,和 node.js 里的模块加载有一些相似,会将被加载模块中的内容包裹于一个函数中
  4. 这个函数在未来某个工夫点上会被调用,同时会接管到肯定的参数,利用这些参数就能够实现模块的加载操作
  5. 针对于上述的代码就相当于是将 {}(模块定义)传递给了 modules


__webpack_require__办法是 webpack 当中自定义的,它的核心作用就是返回模块的 exports。
单文件模块打包产出文件,会失去一个自调用函数,模块定义会传给 modules, 在文件中会调用 __webpack_require__ 办法,传入主入口文件 id,导出内容会挂载到 module.exports 上,最初被返回给

  return __webpack_require__(__webpack_require__.s = "./src/index.js");

通过一个 CommonJS 单步调试,联合打包后代码能够晓得打包后文件一些办法的大略作用

 // 定义对象用于缓存已加载过的模块
  var installedModules = {};

//__webpack_require__办法是 webpack 自定义的一个加载办法,外围性能就是返回被加载模块中导出的内容(具体外部是如何实现的,后续再剖析)function __webpack_require__(moduleId)

// 将模块定义保留一份,通过 m 属性挂载到自定义的办法身上
  __webpack_require__.m = modules;

 // o 属性判断被传入的对象 obj 身上是否具备指定的属性 *, 如果有则返回 true 
  __webpack_require__.o = function (object, property) {return Object.prototype.hasOwnProperty.call(object, property);
  };

  // define getter function for harmony exports
  __webpack_require__.d = function (exports, name, getter) {
    // 如果以后 exports 身上不具备 name 属性,则条件成立,增加成员属性 name
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        enumerable: true,
        get: getter
      });
    }

  // define __esModule on exports, 给对象加一个标记,判断是否是 esModule
  __webpack_require__.r = function (exports) {
    // 解决 esModule 
    if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {// Object.prototype.toString.call(exports), 增加键,值是 Module
      Object.defineProperty(exports, Symbol.toStringTag, {value: 'Module'});
    }
    // 如果条件不成立,咱们也间接在 exports 对象的身上增加一个 __esModule 属性,它的值就是 true 
    Object.defineProperty(exports, '__esModule', {value: true});
  };

  // 调用 t 办法之后,咱们会拿到被加载模块中的内容 value,对于 value 来说咱们可能会间接返回,也可能会解决之后再返回
  __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;
  };

退出移动版