You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(function(modules){//新建一个对象,记录导入了哪些模块varinstalledModules={};// The require function 核心执行方法function__webpack_require__(moduleId){/*内容暂时省略*/}// expose the modules object (__webpack_modules__) 记录传入的modules作为私有属性__webpack_require__.m=modules;// expose the module cache 缓存对象,记录了导入哪些模块__webpack_require__.c=installedModules;// Load entry module and return exports 默认将传入的数组第一个元素作为参数传入,这个s应该是start的意思了return__webpack_require__(__webpack_require__.s=0);})([(function(module,exports,__webpack_require__){/* 0 */vara=1;/***/})/******/])
function__webpack_require__(moduleId){// Check if module is in cache // 检查缓存对象中是否有这个id,判断是否首次引入if(installedModules[moduleId]){returninstalledModules[moduleId].exports;}// Create a new module (and put it into the cache) 添加到.c缓存里面varmodule=installedModules[moduleId]={i: moduleId,l: false,exports: {}};// Execute the module function 执行通过moduleId获取到的函数modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);// Flag the module as loaded// 表示module对象里面的模块加载了module.l=true;// Return the exports of the modulereturnmodule.exports;}
我们可以看一下传入第一个参数的内容,在上一章中是我们引入的文件内容var a = 1,但是这里却不是了。而是按模块引入顺序执行函数__webpack_require__(1),__webpack_require__(2),__webpack_require__(3),通过__webpack_require__函数去执行了我们引入的代码。
/******/functionwebpackJsonpCallback(data){/******/varchunkIds=data[0];// 模块id/******/varmoreModules=data[1];// 提取出来的公共模块,也就是文件内容/******/varexecuteModules=data[2];// 需要执行的模块,但演示中没有/******/// add "moreModules" to the modules object,/******/// then flag all "chunkIds" as loaded and fire callback/******/varmoduleId,chunkId,i=0,resolves=[];/******//******/for(;i<chunkIds.length;i++){/******/chunkId=chunkIds[i];/******/if(installedChunks[chunkId]){/******/resolves.push(installedChunks[chunkId][0]);/******/}/******/installedChunks[chunkId]=0;/******/}/******/for(moduleIdinmoreModules){/******/if(Object.prototype.hasOwnProperty.call(moreModules,moduleId)){/******/modules[moduleId]=moreModules[moduleId];/******/}/******/}/******/if(parentJsonpFunction)parentJsonpFunction(data);/******//******/while(resolves.length){/******/resolves.shift()();/******/}/******//******/// add entry modules from loaded chunk to deferred list/******/deferredModules.push.apply(deferredModules,executeModules||[]);/******//******/// run deferred modules when all chunks ready/******/returncheckDeferredModules();/******/};
1、基本版——单入口引入一个js文件
所谓的基本版,就是我只引入了一个
test.js
,代码只有一行var a = 1
。打包之后,发现生成的文件main.js
并没有多少代码,只有90行不到。截取出真正执行的代码就更加少了,只有下面4行。我们接下去就从这几行代码中看下打包出来的文件的执行流程是怎么样的。
首先很明显,整个文件是个自执行函数。传入了一个数组参数
modules
。这个自执行函数内部一开始新建了一个对象
installedModules
,用来记录打包了哪些模块。然后新建了函数
__webpack_require__
,可以说整个自执行函数最核心的就是__webpack_require__
。__webpack_require__
有许多私有属性,其中就有刚刚新建的installedModules
。最后自执行函数
return
了__webpack_require__
,并传入了一个参数0
。因为__webpack_require__
的传参变量名称叫做moduleId
,那么传参传进来的也就是*模块id**。所以我大胆猜测这个0
可能是某个模块的id。这时候我瞄到下面有一行注释
/* 0 */
。可以发现webpack会在每一个模块导入的时候,会在打包模块的顶部写上一个id的注释。那么刚才那个0
就能解释了,就是我们引入的那个模块,由于是第一个模块,所以它的id是0。那么当传入了
moduleId
之后,__webpack_require__
内部发生了什么?__webpack_require__解析
首先通过
moduleId
判断这个模块是否引入过。如果已经引入过的话,则直接返回。否则installedModules
去记录下这次引入。这样子如果别的文件也要引入这个模块的话,避免去重复执行相同的代码。然后通过
modules[moduleId].call
去执行了引入的JS文件。看完这个函数之后,大家可以发现其实
webpack
打包之后的文件并没有什么很复杂的内容嘛。当然这很大一部分原因是因为我们的场景太简单了,那么接下来就增加一点复杂性。2、升级版——单入口引入多个文件
接下来我修改一下webpack入口,单个入口同时下引入三个个文件
三个文件的内容分别为
var a = 1
,var b = 2
,var c = 3
。接下来我们可以看看打包之后的代码打包之后的文件
main.js
核心内容并没有发生变化,和上面一模一样。但是这个自执行函数传入的参数却发生了变化。前面说过,自执行函数默认将传入的参数数组的第一个元素传入
__webpack_require__
执行代码。我们可以看一下传入第一个参数的内容,在上一章中是我们引入的文件内容
var a = 1
,但是这里却不是了。而是按模块引入顺序执行函数__webpack_require__(1)
,__webpack_require__(2)
,__webpack_require__(3)
,通过__webpack_require__
函数去执行了我们引入的代码。3.升级版——多入口,多文件引入方式
因为好奇如果多入口多文件是怎么样的,接下去我又将入口改了一下,变成了下面这样
打包生成了
index1.js
和index2.js
。发现index1.js
和第一章讲的一样,index2.js
和第二个文件一样。并没有什么让我很意外的东西。4、进阶版——引入公共模块
在前面的打包文件中,我们发现每个模块id似乎是和引入顺序有关的。而在我们日常开发环境中,必然会引入各种公共文件,那么webpack会怎么处理这些id呢
于是我们在配置文件中新增了
webpack.optimize.SplitChunksPlugin
插件。然后修改一下配置文件中的入口,我们开了两个入口,并且两个入口都引入了
test3.js
这个文件可以看到,打包后生成了3个文件。
首先
bundle.js
(文件名自己定义的)很明显是一个公共文件,里面应该有我们提取test3.js
出来的内容。打开文件后,发现里面的代码并不多,只有下面几行。单纯看文件内容,我们大概能推测出几点:
webpackJsonp
的数组2
,应该是这个模块的id{模块id:模块内容}
的对象。模块内容
就是我们test3.js
,被一个匿名函数包裹注意到一点,这个文件中的
2
并不像之前一样作为注释的形式存在了,而是作为属性名。但是它为什么直接就将这个模块id命名为2
呢,目前来看,应该是这个模块是第二个引入的。带着这个想法,我接下去看了打包出来的index1.js
文件截取出了真正执行并且有用的代码出来。
在引入
webpack.optimize.SplitChunksPlugin
之后,核心代码在原来基础上新增了两个函数webpackJsonpCallback
和checkDeferredModules
。然后在原来的installedModules
基础上,多了一个installedModules
,用来记录了模块的运行状态;一个deferredModules
,暂时不知道干嘛,看名字像是存储待执行的模块,等到后面用到时再看。此外,还有这个自执行函数最后一行代码调用形式不再像之前一样。之前是通过调用
__webpack_require__(0)
,现在则变成了checkDeferredModules
。那么我们便顺着它现在的调用顺序再去分析一下现在的代码。在分析了不同之后,接下来就按照运行顺序来查看代码,首先能看到一个熟悉的变量名字
webpackJsonp
。没错,就是刚才bundle.js
中暴露到全局的那个数组。由于在html中先引入了bundle.js
文件,所以我们可以直接从全局变量中获取到这个数组。前面已经简单分析过
window["webpackJsonp"]
了,就不细究了。接下来这个数组进行了一次for循环,将数组中的每一个元素传参给了方法webpackJsonpCallback
。而在这里的演示中,传入就是我们bundle.js
中一个包含模块信息的数组[[2],{2:fn}}]
。接下来就看
webpackJsonpCallback
如何处理传进来的参数了webpackJsonpCallback简析
这个函数中主要干了两件事情,分别是在那两个for循环中。
一是在
installedChunks
对象记录引入的公共模块id,并且将这个模块标为已经导入的状态0
。然后在另一个for循环中,设置传参数组
modules
的数据。我们公共模块的id是2
,那么便设置modules
数组中索引为2
的位置为引入的公共模块函数。我们知道modules是传进来的一个数组参数,在第二个章节中可以看到,我们会在最后执行函数
__webpack_require__(0)
,然后依顺序去执行所有引入模块。不过这次却和以前不一样了,可以看到
webpackJsonpCallback
最后返回的代码是checkDeferredModules
。前面也说了整个自执行函数最后返回的函数也是checkDeferredModules
,可以说它替代了__webpack_require__(0)
。接下去就去看看checkDeferredModules
发生了什么checkDeferredModules简析
这个函数关键点似乎是在
deferredModules
,但是我们刚才webpackJsonpCallback
唯一涉及到这个的只有这么一句,并且executeModules
其实是没有内容的,所以可以说是空数组。既然没有内容,那么
webpackJsonpCallback
就只能结束函数了。回到主线程,发现下面马上是两句代码,得,又绕回来了。不过现在就有
deferredModules
这个数组终于有内容了,一次for循环下来,最后去执行我们模块的代码仍然是这一句很熟悉,有木有,最后还是回到了
__webpack_require__
,然后就是熟悉的流程了但是当我看到这个内容竟然有这行代码时
__webpack_require__(2);
还是有点崩溃的。为什么?因为它代码明确直接执行了__webpack_require__(2)
。但是2
这个模块id是通过在全局属性webpackJsonp
获得的,代码不应该明确知道的啊。我原来以为的运行过程是,每个js文件通过全局变量
webpackJsonp
获得到公共模块id,然后push到自执行函数传参数组modules
。那么等到真正执行的时候,会按照for循环依次执行数组内的每个函数。它不会知道有1,2
这种明确的id的。可惜结果跟我想象的完全不一样,在
index1.js
直接指定执行哪些模块。这只能说明一个事情,其实webpack
内部已经将所有的代码顺序都确定好了,而不是在js文件中通过代码来确定的。事实上,当我去查看index2.js
文件时,更加确定了我的想法。仔细查看自执行函数的传参数组,发现它的第
0,1,2
位都是undefined。我们知道这几个数字其实就是每个模块本身的Id。而这几个id恰恰就是index1.js
和bundle.js
中的模块。理论上来说在浏览器下运行,index2.js
应该无法得知的,但是事实却完全相反。走到这一步,我对webpack打包后的代码也没有特别大的欲望了,webpack内部实现才是更重要的了。好了,不说了,我先去看网上webpack的源码解析了,等我搞明白了,再回来写续集。
The text was updated successfully, but these errors were encountered: