webpack模块化原理
https://segmentfault.com/a/1190000010349749
commonJS
案例:
//index.js 'use strict'; var bar = require('./bar'); function foo() { return bar(); } //bar.js 'use strict'; exports.bar = function () { return 1; } //配置 var path = require("path"); module.exports = { entry: path.join(__dirname, 'index.js'), output: { path: path.join(__dirname, 'outs'), filename: 'index.js' }, };
打包后生成代码如下:
(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 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, { configurable: false, enumerable: true, get: getter }); } }; // 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 = 0); }) /************************************************************************/ ([ /* 0 */ (function(module, exports, __webpack_require__) { "use strict"; var bar = __webpack_require__(1); bar(); }), /* 1 */ (function(module, exports, __webpack_require__) { "use strict"; exports.bar = function () { return 1; } }) ]);
对以上代码进行简化:
(function (modules) {/* 省略函数内容 */}) ([ function (module, exports, __webpack_require__) { /* 模块index.js的代码 */ }, function (module, exports, __webpack_require__) { /* 模块bar.js的代码 */ } ]);
可以发现,以上就是一个IIFE,两个模块分别以函数的形式,存放到一个modules数组中,然后传递给匿名函数,执行。将这个匿名函数的代码抽取出来:
// 1、模块缓存对象 var installedModules = {}; // 2、webpack实现的require function __webpack_require__(moduleId) { // 3、判断是否已缓存模块 if(installedModules[moduleId]) { return installedModules[moduleId].exports; } // 4、缓存模块 var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} }; // 5、调用模块函数 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // 6、标记模块为已加载 module.l = true; // 7、返回module.exports return module.exports; } // 8、require第一个模块 return __webpack_require__(__webpack_require__.s = 0);
installedModules对象存放了对应模块的信息,如模块id,导出的值和是否已经执行过
webpack_require用于根据id加载一个模块,如果这个模块已经执行过,则直接返回导出的值,否则执行这个模块,然后标记这个模块已经执行,最后导出值
Code Spliting
https://segmentfault.com/a/1190000011435407
使用方式如下:
// index.js 'use strict'; import(/* webpackChunkName: "foo" */ './foo').then(foo => { console.log(foo()); }) import(/* webpackChunkName: "bar" */ './bar').then(bar => { console.log(bar()); }) // foo.js 'use strict'; exports.foo = function () { return 2; } // bar.js 'use strict'; exports.bar = function () { return 1; } // 配置文件 var path = require("path"); module.exports = { entry: path.join(__dirname, 'index.js'), output: { path: path.join(__dirname, 'outs'), filename: 'index.js', chunkFilename: '[name].bundle.js' }, };
以上打包出来,模块会在独立的文件中,这些独立出来的模块文件内容大致如下:
webpackJsonp([0],[ /* 0 */, /* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.foo = function () { return 2; } /***/ }) ]);
当需要获取这些模块文件时,通过 webpack_require.e 动态插入一个script标签来加载以上文件,然后返回一个promise来通知外界这个模块的加载情况,用于执行回调方法。这个promise有可能已经处于resolve状态,也可能处理等待状态。
以上script内容下载完成后,webpackJsonp就会执行,参数就是模块的代码(这种获取模块的方式类似于Jsonp),webpackJsonp就会获取对应模块的Promise,然后改变promise的状态。这样一来其他地方从webpack_require.e获取到的promise,对应的回调方法就会执行,代表模块加载完成。