webpack2的那些事兒 ------ 生成的文件是怎麼運行的

謝謝大家看我扯技術,最近在對webpack2進行的配置進行梳理和學習,webpack是在去年使用vue開始接觸的,我的感受webpack 融入到編程過程當中,提供了模塊化,將各類類型的文件都當作模塊,經過不一樣的 loader 進行處理和代碼組織,是一個比較新穎的編程體驗,應該說webpack的編程適用場景比較普遍,可以比較方便的引入第三方的各類 npm 模塊進行使用, 方便快速開發工做。
打算寫幾篇文章(若是能堅持的話= =)來總結下 webpack,文章不是教你怎麼使用webpack,而是讓你更好的瞭解你在使用的webpack是怎麼去運行的 ,想來想去,第一篇就先介紹下webpack生成的文件,是怎麼去執行的。html

webpack 的生成信息

首先咱們要先經過 webpack 去生成文件(好一句廢話),文章全部的代碼都會在文章最後面給出連接,下面是本文章使用的代碼的目錄:
圖片描述vue

咱們如今只要關注js目錄,裏面有兩個入口 app.jsbar.js,而後會引用 es5,es6中的各類測試模塊,具體你們能夠看代碼。而後代碼一跑!只見命令行蹭蹭蹭跑出來了好多信息,像下面同樣:
圖片描述webpack

首先咱們來看下生成的信息:git

  • Asset : 這個一看就明白是生成的文件相對於配置中output.path的路徑,能夠看到圖中生成的文件都是在 output.path底下的;而後咱們仔細看下文件名,好比第一個0.fb6d7f4.js,是由[name/chunkname].[hash/chunkhash].js組成的,這個能夠在output.filename 中配置,關於hashchunkhash的區別,這個後面會專門經過一篇文章進行簡介。es6

  • Size : 這個就沒啥好說的,就是生成文件的大小github

  • Chunks : 咱們會看到有些 Chunks是兩個數字,有些是一個,其實還可能出現更多,通過個人一堆實驗= =,發現Chunks中的第一個數字,就是這個文件的 ChunkId,然後面的是當前這個文件依賴的文件的ChunkId,從圖中咱們能夠看到,第一個文件的ChunkId0,它依賴的是ChunkId3manifest.a890c12.jsweb

  • Chunk Names : 這個就是這個生成文件的chunkName,能夠用於文件命名,能夠看到若是沒有在entry中指定,那麼chunkName會等於chunkIdchrome

程序加載流程

瞭解了生成的信息,接下來咱們把項目跑起來(能夠用 anywhere 跑項目),經過chrome developer tool能夠看到請求狀況圖片描述npm

能夠看到請求了頁面html以後,按順序分別加載了 manifest,index,0,2文件,這裏咱們先來分析下文件的分割和加載流程。編程

分割

能夠看到頁面的 js 被分割成爲了4個文件,一般來講,一個項目定義了一個 entry point,
webpack會以這個entry point做爲入口,進行代碼回溯,若是存在System.import或者是require.ensure的異步模塊調用,webpack會對使用的模塊進行單獨打包,好比文件中的02這兩個 js,若是沒有異步模塊調用,那麼會將全部的代碼生成在一個文件中,webpack 爲了使得打包的代碼進行優化,可使用CommonsChunkPlugin插件對代碼進行處理,將庫文件單獨打包,經過規則生成對應的 chunk 文件,其中的manifest爲 默認的 chunk,其中包含了打包文件的runtime信息,還有webpackJsonp模塊加載的封裝庫,全部的生成模塊都是採用webpackJsonp進行封裝的。

manifest

從上面的圖中能夠看到,瀏覽器按順序分別加載了 manifest,index,0,2文件,其中manifest至關於webpackruntime工具,用於作模塊加載,其餘文件是邏輯文件; manifest中封裝了webpackJsonpCallback方法和__webpack_require__方法,下面咱們來進行分析:

  • webpackJsonpCallback(chunkIds, moreModules, executeModule):webpackJsonpCallback是chunk封裝的包裝方法,webpack在生成每個chunk的時候都是經過這個方法進行包裝的,咱們在上面看到的 chunksId,會做爲第一個參數,被包含進這個chunkmodule會被以數組的形式傳入第二個參數moreModules中,若是這個chunk中包含能夠執行的modules,須要將 moduleId傳入第三個參數 executeModule中,下面是 這個方法的代碼片斷:
    圖片描述這個方法主要作了下面幾件事:

    • 加載chunk

      咱們能夠看到這個方法用第一個循環分別將chunkIds處理進入installedChunks對象中,installedChunks對象用於記錄chunk的加載狀況,分別用0表示當前的chunkId已經加載完成,用一個長度爲3的數組表示當前的chunk正在加載中,數據中其實存儲着加載過程當中的resolve方法、reject方法和pormise對象,這種只在經過require.ensure或者是System.import纔會出現。所以咱們能夠看到,第一個for循環中判斷若是chunkId在 installedChunks 中存在且不爲0,則判斷是異步加載的模塊已經加載成功,將chunkresolve方法傳入resolves數組,而後後面運行,而後將chunk對應的狀態設置爲0。若是判斷以後不存在,這認爲這是一個同步加載的chunk,直接設置爲0,表示chunk已經加載完畢。

    • 加載 module

      加載 module 的邏輯比較簡單,判斷純不存在這個module以後,將 其寫入modules參數之中

    • 運行須要執行的module

      若是executeModule存在,則對其中對應moduleId的模塊進行運行

  • __webpack_require__: 這個對象包含了多個方法,主要用於modulechunk的加載,處理和運行,下面咱們一個一個分析:

    • __webpack_require__(moduleId) :代碼以下
      圖片描述 這個方法接收一個moduleId,構建一個 module 對象存入installedModules中,而且初始化這個 module, 最後返回module.export

    • __webpack_require__.e(chunkId) : 這個方法用於經過異步的方式加載 chunk 文件,代碼以下:
      圖片描述這個方法整體來講就是加載一個 script 文件,生成一個 promise對象,當 script 加載完成後運行,又會執行前面的webpackJsonpCallback註冊chunk,而後promise.resolve。這裏面須要注意的是紅框裏面的東西,這個涉及到一個優化點,若是沒有在使用CommonsChunkPlugin單獨打包manifest,那麼通常來講他會和你指定的其餘庫經過CommonsChunkPlugin打包在一塊兒,那麼你會發現即便你只是修改了庫以外的邏輯,庫文件生成的文件的hash或者是chunkhash也是會變的,緣由就在於manifest中紅框部分是動態生成的,致使文件的 hash 產生變化,不利於緩存,所以建議單獨打包manifest

    • __webpack_require__.oe:定義一個統一的錯誤處理函數

    • __webpack_require__.p:這個是和webpackoutput.publicPath對應的值

    • __webpack_require__.o: Object.prototype.hasOwnProperty的封裝
      前面幾個方法在 ES5的情景下面已經足夠運行這個模塊系統,咱們都知道webpack2加入了對ES6 MODULE的支持,下面幾個__webpack_require__是爲ES6使用的:

    • __webpack_require__.d:代碼以下:
      圖片描述這個是用於ES6中命名的export好比 圖片描述 webpack 遇到這種export,會對其用__webpack_require__.d進行包裝,變成: 圖片描述

    • __webpack_require__.i:用於返回一個正確的上下文的函數回去,針對的是export直接爲一個可運行方法的時候

以上就是webpack manifest中的大部分重要的函數,其實主要就是經過webpackJsonpCallback來註冊載入對應的chunk文件,經過__webpack_require__來處理模塊的關係。

總結

整個webpack 的在運行時都是經過 manifest去作控制處理的, webpackJsonpCallback對應的是對加載的chunk文件的處理,__webpack_require__是對加載模塊的處理,瞭解這些可使咱們更好的去優化咱們的代碼,幫助咱們去調試代碼,幫助咱們在複雜狀況下去解決問題提供一些其餘的思路。

最後附上代碼:先介紹下,webpack-base是我在使用webpack的過程當中本身總結的一套腳手架,文檔尚未完善,若是須要文檔能夠在issue裏面提,本次的項目在分支上面開發,代碼點擊這裏

相關文章
相關標籤/搜索