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,对应的回调方法就会执行,代表模块加载完成。

 

posted @ 2018-05-16 10:56  HelloHello233  阅读(288)  评论(0编辑  收藏  举报