webpack模块执行分析

以下打包结果分析只考虑前端场景(target为node他会做另外的兼容) //TODO这里之后补充张图吧

Chunk文件

以分离出runtime的chunk进行分析 通过window["webpackJsonp"]的push([chunkId] ,{moreModules}, [executeModules])去执行,具体的这几个执行webpackJsonpCallback

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
["page4"], //[0], chunkIds 分支id,push到installedChunks 
{          //[1], moreModules 包含哪些模块,push到全局modules(__webpack__require__.m)
"./src/module/AMDModule.js":(function(module, exports,__wepback__require){....}),
"./src/module/module2": (function(...){}),
"./src/module/module3": (function(...){}),
"./src/page4.js": (function(...){}) //需要运行的主模块

},
[["./src/page4.js","runtime"]] //[2] executeModules 需要执行的模块& 依赖的模块
]);

运行时webpackBootstrap

runtime.bundle.js产生

  1. Webpack4通过optimization.runtimeChunk = true | {name: value}将webpack运行时代码单独打包出来
  2. 拆出运行时会形成webpackJsonp的形式,和一些deferred的延迟加载
  3. 不拆分的运行时是直接在相应的module里面的

runtime.bundle相关说明

  • 本身是IIFE函数,若runtime没有拆分则,会将该chunks的所有module放入[],这里之所以是个数组,是因为该modules包含了多个chunk的数组module —— [[chunk1's exeM], [chunk2's exeM]]
 (function(modules) { // webpackBootstrap
 })([])
  • 变量
/******/     // The module cache(缓存的模块)
/******/     var installedModules = {};
/******/
/******/     // object to store loaded and loading chunks(已经加载的chunks)
/******/     var installedChunks = {
/******/         "runtime": 0
/******/     };
/******/
/******/     var deferredModules = []; //(因为拆分了运行时所以modules都是deferred的)
  • 函数

webpackJsonpCallback

分片chunks的加载回调webpackJsonpCallback ,该方法被挂载在window["webpackJsonp"]对象作为push方法 (exports.output.jsonpFunction可修改)

data: [ ————Chunk文件中push的对象

  • chunkId: [],分支id
  • moreModules: {module1'sId:moduleFunction , module2'sId: sId:moduleFunction} 这个chunk所包含的执行模块
  • exetuceModules: [moduleId,depChunk1Id, depChunk2Id ] 需要执行的模块Id(index=0的位置),依赖的chunkId

]

/******/     // install a JSONP callback for chunk loading
/******/     function webpackJsonpCallback(data) {

//去出data中的数据,data的传入可查看weback打出来的包
/******/         var chunkIds = data[0]; //eg: ["page4"]
/******/         var moreModules = data[1]; //eg {"./src/page4.js": ()=>{} , "./componets/": ()=>{} }
/******/         var executeModules = data[2]; //eg: ["./src/page4.js" , "runtime"]
/******/         // add "moreModules" to the modules object,
/******/         // then flag all "chunkIds" as loaded and fire callback
/******/         var moduleId, chunkId, i = 0, resolves = [];

//chunkId存储在installedChunks
/******/         for(;i < chunkIds.length; i++) {
/******/       
/******/           chunkId = chunkIds[i];
/******/             if(installedChunks[chunkId]) {
/******/                 resolves.push(installedChunks[chunkId][0]);
/******/             }
/******/             installedChunks[chunkId] = 0;
/******/         }

//moreModules存储在IIFE的全局modules中
/******/         for(moduleId in moreModules) {
/******/             if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/                 modules[moduleId] = moreModules[moduleId];
/******/             }
/******/         }

//这里我理解为对旧的jsonp方法兼容,这就是window["webpackJsonp"]的普通数组的push方法
//下面的一段方法var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
//var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/******/         if(parentJsonpFunction) parentJsonpFunction(data);


//这里是多个chunkIds时候对其他chunkId进行处理,目前我不知道如何打出多个chunkId
/******/         while(resolves.length) {
/******/             resolves.shift()();
/******/         }
/******/

//exectueMoudles添加到defferedModules中,之后执行checkDeferredModules会运行[0]
/******/         // add entry modules from loaded chunk to deferred list
/******/         deferredModules.push.apply(deferredModules, executeModules || []);
/******/
/******/         // run deferred modules when all chunks ready
/******/         return checkDeferredModules();
/******/     };

checkDeferredModules 对要执行的模块检查所依赖的chunks是否加载,若加载则运行webpackrequire去加载executeModules[0]

/******/     function checkDeferredModules() {
/******/         var result;
/******/         for(var i = 0; i < deferredModules.length; i++) {
/******/             var deferredModule = deferredModules[i];
/******/             var fulfilled = true;

//检查exectueMoudles中依赖从index=1开始之后代表所依赖的chunk是否加载,例如还有common模块
/******/             for(var j = 1; j < deferredModule.length; j++) {
/******/                 var depId = deferredModule[j];
/******/                 if(installedChunks[depId] !== 0) fulfilled = false;
/******/             }
/******/             if(fulfilled) {
/******/                 deferredModules.splice(i--, 1);

//若依赖的chunk已经fulfilled,则加载defferdModule[0]
/******/                 result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
/******/             }
/******/         }
/******/         return result;
/******/     }

webpack__require 最重要的模块加载函数!!,会将本身传入要执行的模块,去进一步加载模块

// The require function
/******/     function __webpack_require__(moduleId) {
/******/
/******/         // Check if module is in cache
/******/         if(installedModules[moduleId]) {
/******/             return installedModules[moduleId].exports;
/******/         }

//创建一个兼容性的module,exports为module的实际输出,利用该module和module.exports传入到具体module的执行函数中达到兼容效果
/******/         // Create a new module (and put it into the cache)
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}
/******/         };
/******/

//这里就是函数的执行了,会传入module和module.exports以及__wepack__require进去
/******/         // 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;
/******/     }
/******/

这里有__webpack_require中绑定的一些兼容性,和方便模块后续调用的函数

/******/     // 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) {//...}
/******/
/******/     // define __esModule on exports
/******/     __webpack_require__.r = function(exports) {
/******/         Object.defineProperty(exports, '__esModule', { value: true });
/******/     };
/******/
/******/     // 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) {};

/******/     // 可用于异步加载的__webpack_public_path__
/******/     __webpack_require__.p = "/dist/";

执行

从上到下的最后一部分

//如果window下已经加载下来了其他chunk,会作为数组放入window["webpackJsonp"]
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
/******/     var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);

//window["webpackJsonp"]方法绑定
/******/     jsonpArray.push = webpackJsonpCallback;
/******/     jsonpArray = jsonpArray.slice();

//万一其他chunk被加载下来也没关系,这里会对已经加载下来的chunk都执行webpackJsonpCall
/******/     for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/******/     var parentJsonpFunction = oldJsonpFunction;
/******/
/******/

//直接运行checkModules的最后一部分
/******/     // run deferred modules from other chunks
/******/     checkDeferredModules();

包含运行时的chunk(module with webpackBootstrap)

(function(modules) { // webpackBootstrap
//...上述模块
//webpackJsonpCallback
//__webpack__require

//这里直接将引入的模块插入
//add entry module to deferred list
deferredModules.push(["./src/page1.js","common"]);

//若没有依赖common之类的deferred chunks则直接require,
// __webpack_require__(__webpack_require__.s = "./src/page4.js")

})({
'./src/module1': (function(...)),
'./src/module2': (function(...))
//'moduleId': 'moduleFunction'

})

results matching ""

    No results matching ""