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产生
- Webpack4通过optimization.runtimeChunk = true | {name: value}将webpack运行时代码单独打包出来
- 拆出运行时会形成webpackJsonp的形式,和一些deferred的延迟加载
- 不拆分的运行时是直接在相应的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'
})