建議在github閱讀
通過下面的打包結(jié)果,并結(jié)合下面說的多入口文件打包問題,應(yīng)該是不難理解的。
/************************************************************************//******/ ([/* 0 *//***/ function(module, exports, __webpack_require__) { module.exports = __webpack_require__(1);/***/ },/* 1 *//***/ function(module, exports, __webpack_require__) { var util1 = __webpack_require__(2) var util2 = __webpack_require__(3)/***/ },/* 2 *//***/ function(module, exports, __webpack_require__) { var util2 = __webpack_require__(3)/***/ },/* 3 *//***/ function(module, exports) { module.exports = {"name": "util2.js"}/***/ }/******/ ]);這里只是需要注意一點,就是雖然有entry.js和util1.js同時引用了util2模塊,但是我們最終也只會為util2生成一個id
!webpack打包的原理為,在入口文件中,對每個require資源文件配置一個id, 也 就是說,對于同一個資源,就算是require多次的話,它的id也是一樣的,所以無論在多少個文件中 require,它都只會打包一份。
首先webpack.config.js配置如下:
var path = require('path')var HtmlWebpackPlugin = require('html-webpack-plugin')var ExtractTextPlugin = require('extract-text-webpack-plugin')var webpack = require('webpack')module.exports = { entry: { index: ['./src/js1/entry.js'], //配置兩個入口 index2: ['./src/js1/entry2.js'] }, output: { path: path.resolve(__dirname, './dist/static'), publicPath: 'static/', filename: '[name].[chunkhash].js' }, resolve: { extensions: ['', '.js', '.less', '.swig', '.html'] }, module: { loaders: [ ] }, plugins: [ ]}此時會在./dist/static目錄中生成兩個文件,分別對應(yīng)于index.[chunkhash].js和index2.[chunkhash].js。
其中index.[chunkhash].js文件內(nèi)容如下:
/************************************************************************//******/ ([/* 0 *//***/ function(module, exports, __webpack_require__) { module.exports = __webpack_require__(1);/***/ },/* 1 *//***/ function(module, exports, __webpack_require__) { var util1 = __webpack_require__(2) var util2 = __webpack_require__(3)/***/ },/* 2 *//***/ function(module, exports, __webpack_require__) { var util2 = __webpack_require__(3)/***/ },/* 3 *//***/ function(module, exports) { module.exports = {"name": "util2.js"}/***/ }/******/ ]);很顯然,入口文件的id是1,而入口文件引用的資源的id依次增加。同時注意一點:
/* 0 *//***/ function(module, exports, __webpack_require__) { module.exports = __webpack_require__(1);/***/ },也就是id為0的打包模塊其實是對入口文件調(diào)用的模塊,其中通過module.exports對外導(dǎo)出!
其中index2.[chunkhash].js文件內(nèi)容如下:
/************************************************************************//******/ ([/* 0 *//***/ function(module, exports, __webpack_require__) { module.exports = __webpack_require__(4);/***/ },/* 1 */,/* 2 */,/* 3 *//***/ function(module, exports) { module.exports = {"name": "util2.js"}/***/ },/* 4 *//***/ function(module, exports, __webpack_require__) { var util2 = __webpack_require__(3) setTimeout(function() {console.log(util2.name), 2000})/***/ }/******/ ]);首先,和上面分析的一樣,我們對入口文件調(diào)用的模塊id是0,通過module.exports向外導(dǎo)出。
/* 4 *//***/ function(module, exports) { // removed by extract-text-webpack-plugin/***/ },/* 5 */,/* 6 */,/* 7 */,/* 8 *//***/ function(module, exports) { // removed by extract-text-webpack-plugin/***/ },/* 9 */,/* 10 *//***/ function(module, exports) { // removed by extract-text-webpack-plugin/***/ }很清楚的知道id為5,6,7,8的情況沒有被分配,因為沒有被打包進(jìn)來,這在單入口文件中也是存在的,而和extract-text-webpack-plugin沒有關(guān)系!而之所以是這樣,我猜測(需要證實)是webpack在打包的過程中自己生成了很多文件,而這些文件也分配了ID值,但是在我們的文件中并不需要這些模塊,所以就沒有引入為空
!這可能和webpack自身的功能有關(guān)系。
下面給出了控制臺打包信息:
其中asset部分就是生產(chǎn)的文件的文件名,是name+chunkhash的格式;chunks就是對生成的文件的數(shù)字標(biāo)注;chunkName就是我們在webpack配置中指定;
結(jié)合上面的分析很容易知道,對于入口文件調(diào)用的模塊Asset(打包資源)部分都是0,而其余資源的部分都是webpac打包后生成的id值!
之前提到過,每個入口文件,都會獨立打包自己依賴的模塊,那就會造成很多重復(fù)打包的模塊,有沒有一種方法 能把多個入口文件中,共同依賴的部分給獨立出來呢? 肯定是有的 CommonsChunkPlugin
這個插件使用非常簡單,它原理就是把多個入口共同的依賴都給定義成一個新入口
。為何我這里說是定義成新入口呢,因為這個名字不僅僅對應(yīng)著js 而且對于著和它相關(guān)的CSS等,比如 HtmlWebpackPlugin 中 就能體現(xiàn)出來,可以它的 chunks中 加入 common 新入口,它會自動把common 的css也導(dǎo)入html
可以參見控制臺的部分:
從圖中可以看到,我們最終輸出的common.js也是會有自己獨立的chunkid的(也就是chunks列),雖然我們在webpack的entry配置中并沒有指明(每個chunkid對應(yīng)的是一個js文件):
entry: { index: ['./src/js1/entry.js'], index2: ['./src/js1/entry2.js'] },這是CommonChunkPlugin插件完成的。為何要出來一個chunkid呢? 這個chunkid的作用就是,標(biāo)記這個js文件是否已經(jīng)加載過了。我們首先分析下這個plugin抽取出來的多個入口公共的代碼部分:
/******/ (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/******/ var moduleId, chunkId, i = 0, callbacks = [];/******/ for(;i < chunkIds.length; i++) {/******/ chunkId = chunkIds[i];/******/ if(installedChunks[chunkId])//非0表示沒有加載完成,否則是數(shù)組,數(shù)組元素是函數(shù)/******/ callbacks.push.apply(callbacks, installedChunks[chunkId]);/******/ installedChunks[chunkId] = 0;//加載完成/******/ }/******/ for(moduleId in moreModules) {/******/ modules[moduleId] = moreModules[moduleId];/******/ }//modules中得到的都是函數(shù),in遍歷數(shù)組的時候key是下標(biāo),此處modules存儲的是鍵值是每一個函數(shù)/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules); //首先調(diào)用父級jsonpFunction/******/ while(callbacks.length)/******/ callbacks.shift().call(null, __webpack_require__); //得到callback數(shù)組中的函數(shù)并調(diào)用,每一個回調(diào)函數(shù)傳入一個__webpack_require__就可以了/******/ if(moreModules[0]) {/******/ installedModules[0] = 0;/******/ return __webpack_require__(0);/******/ } //加載第一個模塊0,這個模塊是我們的入口文件的exports對象,相當(dāng)于執(zhí)行模塊了!其他的模塊只是打包進(jìn)去,這個0表示直接執(zhí)行了/******/ };/******/ // The module cache/******/ var installedModules = {};/******/ // object to store loaded and loading chunks/******/ // "0" means "already loaded"/******/ // Array means "loading", array contains callbacks //如果是正在加載那么這里就是一個數(shù)組,而且數(shù)組中包含的是回調(diào)函數(shù)/******/ var installedChunks = {/******/ 2:0 //common.js的chunkID是2表示一開始的時候就加載完成,因為他是通用模塊必須先加載,然后加載其他的/******/ };/******/ // 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 //入口執(zhí)行模塊是函數(shù)簽名是function(module, exports, __webpack_require__) /******/ 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;/******/ }/******/ // This file contains only the entry chunk./******/ // The chunk loading function for additional chunks/******/ __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 + "." + ({"0":"index","1":"index2"}[chunkId]||chunkId) + "." + {"0":"2a3a9ad51d7007de1a89","1":"e7a73c1617697d3e2912"}[chunkId] + ".js";/******/ head.appendChild(script);/******/ }/******/ };/******/ // expose the modules object (__webpack_modules__) //向外導(dǎo)出的modules包含了common.js這個文件,同時也包含我們的moreModules數(shù)組中的modules/******/ __webpack_require__.m = modules;/******/ // expose the module cache //webpack緩存模塊/******/ __webpack_require__.c = installedModules;/******/ // __webpack_public_path__ //webpack的__webpack_public_path__/******/ __webpack_require__.p = "static/";/******/ })/************************************************************************//******/ ({/***/ 3:/***/ function(module, exports) { module.exports = {"name": "util2.js"}/***/ }/******/ });(1)webpackJsonp第一個參數(shù)是chunkId,但是我們的CommonChunkPlugin抽取出來的部分是作為modules參數(shù)傳入的,而且是一個引用
,所以每次加載一個chunk的時候,他們引用的都是同一個
(2)這里要看清楚什么是chunk,什么是module,在webpackJsonp這個函數(shù)中第一個參數(shù)就是chunkId對應(yīng)于chunk,而而第二個參數(shù)就是chunk中被打包進(jìn)去的多個module,這也是為什么下面的webpackJsonpCallback的函數(shù)簽名是如下:
js webpackJsonpCallback(chunkIds, moreModules)
但是仔細(xì)想想,我們的common.js其實更像是一個module而不是chunk(因為他會被modules形參接受)。但是從控制臺中的輸出你可以知道其實webpack已經(jīng)把它當(dāng)做chunk了。我們common.js雖然沒有在entry中配置,但是我們實際上在common.js中被當(dāng)做自執(zhí)行函數(shù)的參數(shù)傳入最后放在modules中,以后再每一個chunk中require公有的這個文件的時候就直接require這個參數(shù)指定的內(nèi)容就可以了
(3)modules得到的是一個chunk中所有的moreModules以及共有的commmon.js這個module(執(zhí)行的時候只要運行moreModules[0]就可以了)。每次加載一個chunk,其modules最終都會包含我們的CommonChunkPlugin抽取出來的部分
(4)webpack_require方法最后返回的是modules[moduleId].call(module.exports, module, module.exports, webpack_require);也就是執(zhí)行了id為0的入口文件的exports后得到的對象,這就是這個模塊的最后返回值
(5)在html中必須先加載common.js文件
<script src="{{ root }}common.js"><//script> <script src="{{ root }}index.js"><//script>下面是每一個打包后的文件對common.js部分的依賴:
webpackJsonp([1],[/* 0 *//***/ function(module, exports, __webpack_require__) { module.exports = __webpack_require__(4);/***/ },/* 1 */,/* 2 */,/* 3 */,/* 4 *//***/ function(module, exports, __webpack_require__) { var util2 = __webpack_require__(3) setTimeout(function() {console.log(util2.name), 2000})/***/ }]);結(jié)合上面的chunkid應(yīng)該不難理解
webpack打包原理解析
新聞熱點
疑難解答