Webpack构建之法,Code Splitting

Code Splitting是Webpack的核心功能,其主要作用:

  • 管理加载顺序
  • 合并相同代码
  • 模块管理

Code Splitting维护了两个依赖树,一个是cmd标准的同步依赖树,另外一个是amd标准的异步依赖树。

当使用同步依赖的适合,使用require(path),path是模块的路径

当需要使用异步依赖的适合,使用require.ensure([deps], callback)deps是依赖的模块,callback是回调,当deps里面的模块加载完毕后,执行callback回调函数。

simple splitting

  • ab通过CommonJs同步require。
  • c 调用require.ensure保证下载并加载c后,执行回调函数。
  • bd是通过CommonJs同步加载,其中b的代码块,可以检测到在调用c之前已经被加载,代码块将会忽略,因为已经传输过了。

main.js

var a = require("a");
var b = require("b");
require.ensure(["c"], function(require) {
    require("b").xyz();
    var d = require("d");
});

对于单纯CommonJs的逻辑产生的ouput代码比较简单。

核心流程是:

  • 调度entry入口函数,return __webpack_require__(0)
  • 执行主流程函数。
/***/ function(module, exports, __webpack_require__) {

    var a = __webpack_require__(/*! a */ 1);
    var b = __webpack_require__(/*! b */ 2);
/***/ },
  • 按顺序通过__webpack_require__函数加载模块。

根据moduleId来寻找可执行的模块,如果发现缓存区内,则返回该模块结果,如果没有,则执行该模块,并将结果返回,并将模块结果保存到内存中。

/******/    // 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] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

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

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
  • 当遇到require.ensure的时候,如果遇到amd加载的时候,需要调用ensure.require
__webpack_require__.e/* nsure */(1, function(require) {
    __webpack_require__(/*! b */ 2).xyz();
    var d = __webpack_require__(/*! d */ 4);
});

__webpack_require__.e(chunkId, callback)逻辑分析

判断chunkId是否在已经加载过,如果已经加载过模块,直接返回。如果还没加载,加载script,如果加载了,但没有执行,则等待执行。

/******/    __webpack_require__.e = function requireEnsure(chunkId, callback) {
/******/        // "0" is the signal for "already loaded"
/******/        if(installedChunks[chunkId] === 0)
/******/            return callback.call(null, __webpack_require__);

/******/        // an array means "currently loading".
/******/        if(installedChunks[chunkId] !== undefined) {
/******/            installedChunks[chunkId].push(callback);
/******/        } else {
/******/            // start chunk loading
/******/            installedChunks[chunkId] = [callback];
/******/            var head = document.getElementsByTagName('head')[0];
/******/            var script = document.createElement('script');
/******/            script.type = 'text/javascript';
/******/            script.charset = 'utf-8';
/******/            script.async = true;

/******/            script.src = __webpack_require__.p + "" + chunkId + ".output.js";
/******/            head.appendChild(script);
/******/        }
/******/    };
  • 通过require.ensure异步加载目标script,执行script。
webpackJsonp([1],[,,,function(n,t){},function(n,t){}]);

判断chunkIds列表里面的模块是否加载完成,如果完成,执行模块,并将模块放进去modules里,方便后面的模块进行读取。

/******/    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
/******/        // add "moreModules" to the modules object,
/******/        // then flag all "chunkIds" as loaded and fire callback
/******/        var moduleId, chunkId, i = 0, callbacks = [];
/******/        for(;i < chunkIds.length; i++) {
/******/            chunkId = chunkIds[i];
/******/            if(installedChunks[chunkId])
/******/                callbacks.push.apply(callbacks, installedChunks[chunkId]);
/******/            installedChunks[chunkId] = 0;
/******/        }
/******/        for(moduleId in moreModules) {
/******/            modules[moduleId] = moreModules[moduleId];
/******/        }
/******/        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules);
/******/        while(callbacks.length)
/******/            callbacks.shift().call(null, __webpack_require__);

/******/    };

最后,完整代码
https://github.com/webpack/webpack/tree/master/examples/code-splitting

总结

对于webpack的代码编译的核心思想不难理解,webpack会维持一堆代码块modules,代码块0,是所有代码的开端,然后通过id 寻找需要加载的代码块,如果遇到amd加载的代码块,需要把代码块回写到modules里。

你可能感兴趣的:(Webpack构建之法,Code Splitting)