webpack打包-模塊分布解析 發(fā)布于 1 個(gè)月前 作者 yongningfu 385 次瀏覽 來(lái)自 分享 webpack打包原理解析
一開(kāi)始,對(duì)webpack打包原理很不熟悉,看了不少資料,但是講的都不是很清楚,現(xiàn)在來(lái)梳理一遍。
所有資源統(tǒng)一入口
這個(gè)是什么意思呢?就是webpack是通過(guò)js來(lái)獲取和操作其他文件資源的,比如webpack想處理less, 但是它并不會(huì)直接從本地的文件夾中直接通過(guò)路徑去讀取CSS文件,而且通過(guò)執(zhí)行入口js文件,如果入口 文件中,或者入口文件相關(guān)聯(lián)的js文件中含有 require(xx.less) 這個(gè)less文件,那么它就會(huì)通過(guò) 對(duì)應(yīng)的loader去處理這個(gè)less文件
打包中的文件管理
重點(diǎn)來(lái)了: webpack是如何進(jìn)行資源的打包的呢?
總結(jié):
每個(gè)文件都是一個(gè)資源,可以用require導(dǎo)入js 每個(gè)入口文件會(huì)把自己所依賴(即require)的資源全部打包在一起,一個(gè)資源多次引用的話,只會(huì)打包一份 對(duì)于多個(gè)入口的情況,其實(shí)就是分別獨(dú)立的執(zhí)行單個(gè)入口情況,每個(gè)入口文件不相干(可用CommonsChunkPlugin優(yōu)化) js單文件入口的情況
img1.gif
比如整個(gè)應(yīng)用的入口為 entry.js
entry.js引用 util1.js util2.js, 同時(shí)util1.js又引用了util2.js
關(guān)鍵問(wèn)題是: 它打包的會(huì)不會(huì)將 util2.js打包兩份?
其實(shí)不會(huì)的,webpack打包的原理為,在入口文件中,對(duì)每個(gè)require資源文件進(jìn)行配置一個(gè)id, 也 就是說(shuō),對(duì)于同一個(gè)資源,就算是require多次的話,它的id也是一樣的,所以無(wú)論在多少個(gè)文件中 require,它都只會(huì)打包一分
對(duì)于和上面一致拓?fù)鋱D,打包后的js代碼為 img2.gif
注: /* 1 */ 代碼打包模塊為 id 為 1
通過(guò)上面的圖片我們看到,
entry.js 入口文件對(duì)應(yīng)的id為 1 util1.js的id為 2 util2.js的id為 3 entry.js引用了 2 和 3, util1.js引用了 3,說(shuō)明了entry和util1引用的是同一份,util2.js不會(huì)重復(fù)打包
css單文件入口的情況
上面分析了js的情況,其實(shí)css也是一個(gè)道理。它同樣也會(huì)為每個(gè)css資源分配一個(gè)id, 每個(gè)資源同樣也只會(huì)導(dǎo)入一次。
img3.gif
可以看到, entry.js 和 util1.js共同引用了 style2.less,那么它們作用的結(jié)果是怎么樣的呢?
img4.gif
可以看到 entry.js 和 util1.js使用的是同一個(gè) css資源
注意: /* 1 */ 注釋表示id為1的模塊
聽(tīng)說(shuō) ExtractTextPlugin 導(dǎo)出css的插件比較火, 讓我們來(lái)看看它到底幫我們干了啥?配置好相關(guān)引用后
newExtractTextPlugin(‘[name].[chunkhash].css’) 運(yùn)行,在static下面生成了 一個(gè).css 一個(gè).js
生成的css代碼為
.style2 { color: green; } .style3 { color: blue; } .style1 { color: red; } 實(shí)際就是把三個(gè)css導(dǎo)出到一個(gè)css文件,而且同一個(gè)入口中,多次引用同一個(gè)css,實(shí)際只會(huì)打包一份
生成的.js為
img5.gif
可以看到 /* 2 */ 實(shí)際就是 util1.js模塊,還是引用 id為3的模塊 即css2, 但是我們看 3 模塊 的定義,為空函數(shù), 它給出的解釋是 removed by extract-text-webpack-plugin 實(shí)際 就是newExtractTextPlugin把相關(guān)的css全部移出去了
多文件入口的情況
講完了單入口,還需講講多入口,很多時(shí)候我們項(xiàng)目是需要多入口
img6.gif
修改webpack config
img7.gif
可以看到 輸出為兩個(gè)js文件
先對(duì) entry.js 對(duì)應(yīng)的輸出文件進(jìn)行分析
img8.gif
其實(shí)可以看到, util1.js util2.js的內(nèi)容都打包進(jìn) 對(duì)應(yīng)的js里面去了
在對(duì) entry2.js 輸出的文件進(jìn)行分析
img9.gif
可以看到,把entry2.js依賴的 util2.js也打包進(jìn)去了
所以多 入口 實(shí)際就是 分別執(zhí)行多個(gè)單入口,彼此之間不影響
問(wèn)題來(lái)了,多入口對(duì)應(yīng)的css呢? 還是上面的原則,css也是分別打包的,對(duì)于每個(gè)入口所依賴的css全部打包, 輸出就是這個(gè)入口對(duì)應(yīng)的css
最后討論 CommonsChunkPlugin
之前提到過(guò),每個(gè)入口文件,都會(huì)獨(dú)立打包自己依賴的模塊,那就會(huì)造成很多重復(fù)打包的模塊,有沒(méi)有一種方法 能把多個(gè)入口文件中,共同依賴的部分給獨(dú)立出來(lái)呢? 肯定是有的 CommonsChunkPlugin
這個(gè)插件使用非常簡(jiǎn)單,它原理就是把多個(gè)入口共同的依賴都給定義成 一個(gè)新入口
為何我這里說(shuō)是定義成新入口呢,因?yàn)檫@個(gè)名字不僅僅對(duì)應(yīng)著js 而且對(duì)于著和它相關(guān)的css等,比如 HtmlWebpackPlugin 中 就能體現(xiàn)出來(lái),可以它的 chunks中 加入 common 新入口,它會(huì)自動(dòng)把common 的css也導(dǎo)入html
img10.gif
可以看到, 不僅僅js公共模塊獨(dú)立出來(lái),連css同樣也是,感受到了 webpack強(qiáng)大了吧
我們可以大概對(duì) common.js index.xxxxx.js(entry.js對(duì)應(yīng)的代碼) index2.xxxx.js(entry2.js對(duì)應(yīng)的代碼) 打包出來(lái)的代碼分析一下
提前知識(shí):
webpack的id 有兩種 一種為 chunkid 一種為moduleId
每個(gè)chunkid 對(duì)應(yīng)的是一個(gè)js文件 每個(gè)moduleid對(duì)應(yīng)的是一個(gè)個(gè)js文件的內(nèi)容的模塊(一個(gè)js文件里面可以require多個(gè)資源,每 個(gè)資源分配一個(gè)moduleid) 為何要出來(lái)一個(gè)chunkid呢? 這個(gè)chunkid的作用就是,標(biāo)記這個(gè)js文件是否已經(jīng)加載過(guò)了
/**/ // object to store loaded and loading chunks /**/ // “0” means “already loaded” /**/ // Array means “l(fā)oading”, array contains callbacks /**/ var installedChunks = { /**/ 2:0 /**/ }; installedChunks 是記錄一個(gè)chunkid是否已經(jīng)加載過(guò)了
我們先從 common.js分析
common.js是公共分離出來(lái)的模塊,所以按理來(lái)說(shuō),它應(yīng)該是一個(gè)頂層的模塊,實(shí)際上,確實(shí)如此
看 common.js代碼
/**/ (function(modules) { // webpackBootstrap /**/ // install a JSONP callback for chunk loading
/**/ var parentJsonpFunction = window[“webpackJsonp”]; /**/ window[“webpackJsonp”] = function webpackJsonpCallback(chunkIds, moreModules) { /**/ // add “moreModules” to the modules object, /**/ // then flag all “chunkIds” as loaded and fire callback 定義了一個(gè) webpackJsonp 函數(shù)
看 index.xxxx.js代碼
webpackJsonp([0],[ /* 0 */ /*/ function(module, exports, webpack_require) {
module.exports = __webpack_require__(1);/*/ }, 使用 common.js中定義的函數(shù) webpackJsonp, 所以在瀏覽器中加載的話,必須先加載 common.js
這個(gè) webpackJsonp([0],[]) 第一個(gè)參數(shù) [0] 是什么意思呢? 它就是 chunkid ,我們知道一個(gè)chunkid id對(duì)應(yīng)一個(gè) js 文件,它對(duì)應(yīng)的js文件就是它的入口文件 entry.js, 那為何這里還要是一個(gè)數(shù)組
因?yàn)橐粋€(gè)入口文件的話,可以依賴多個(gè)js文件,其他的 id 就是它所依賴的, 其實(shí)就是配置webpack的時(shí)候
那個(gè)入口js對(duì)應(yīng)的 數(shù)組
index: [‘./src/js/entry.js’], [0] 和 現(xiàn)在的id數(shù)組一一對(duì)應(yīng)
在 index2.xxx.js中,
webpackJsonp([1],{
/*/ 0: /*/ function(module, exports, webpack_require) {
module.exports = __webpack_require__(8);[1] 就是 webpack.config配置的數(shù)組 js 對(duì)應(yīng)的 thunkid
index2: [‘./src/js/entry2.js’]
即 entry2.js 就是 thunkid 1
上面說(shuō)完了thunkid, 下面就說(shuō) moduleid了, common.js為頂層js文件,通過(guò)調(diào)用webpackJsonp 對(duì)其他文件進(jìn)行處理,
common.js中
/**/ var parentJsonpFunction = window[“webpackJsonp”]; /**/ window[“webpackJsonp”] = function webpackJsonpCallback(chunkIds, moreModules) { /**/ // add “moreModules” to the modules object, /**/ // then flag all “chunkIds” as loaded and fire callback
/**/ for(moduleId in moreModules) {
/**/ modules[moduleId] = moreModules[moduleId]; /**/ } webpackJsonpCallback(chunkIds, moreModules)定義為 chunkIds, moreModules,chunkIds 說(shuō)了, 就是入口文件對(duì)應(yīng)的 js 文件的 thunkid, 那么這個(gè)moreModules是什么呢? 就是一個(gè)js文件中,一個(gè)個(gè)依賴的 和 導(dǎo)出本身的 module
可以看index2.xxxx.js
webpackJsonp([1],{
/*/ 0: /*/ function(module, exports, webpack_require) {
module.exports = __webpack_require__(8);/*/ },
/*/ 8: /*/ function(module, exports, webpack_require) {
var util2 = __webpack_require__(3)var css1 = __webpack_require__(4)/*/ }
}); 這里 moreModules 為一個(gè)對(duì)象,key 為 moduleid, value 就是一個(gè)個(gè) module定義
比如 function(module, exports, webpack_require) { module.exports = webpack_require(8);}
最重要的來(lái)了, 這里的話, utils2 是公共的module,它被定義在commong.js中,
common.js
/* 0 */, /* 1 */, /* 2 */, /* 3 */ /*/ function(module, exports) {
module.exports = {"name": "util2.js"}/*/ }, /* 4 */ 可以看到, index.xxx.js 的 調(diào)用 和 index2.xxx.js的調(diào)用都是一樣的,都是下面這句
var util2 = webpack_require(3)
它會(huì)尋找 moduleid 為3的模塊,然后返回
如何尋找呢 看看 webpack_require 定義
/**/ 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 /**/ }; if(installedModules[moduleId]) 如果緩存中存在的話,就直接返回。
由于 index.xxx.js 和 index2.xxx.js都是返回的同一個(gè) id 的模塊,所以實(shí)際上 它們使用的是同一個(gè)對(duì)象
這和nodejs里面的require是一樣的,所以整個(gè)項(xiàng)目中,require公共模塊中的資源的話,實(shí)際返回的都是同一個(gè)對(duì)象
歡迎討論和star
新聞熱點(diǎn)
疑難解答
圖片精選