webpack源碼分析之二:code-splitting

前言

code-splitting是webpack最引人矚目的特性之一,此特性將代碼分離到不一樣的bundle文件中。詳細介紹官網code-split,此次實現則在筆者上次文件打包之上作開發。webpack

功能分析

官網上有三種方式實現git

  1. 入口起點:使用 entry 選項手動分離代碼。
  2. 防止重複:使用 CommonsChunkPlugin 去重和分離 chunk。
  3. 動態導入:經過模塊的內聯函數調用來分離代碼。

1本質則是多個入口的chunk,2則在以common.js爲入口文件,將多入口的chunk切分爲按切割文件,經過jsonp加載。在這裏筆者則介紹最爲複雜的3的實現,github

對於webpack 的切割文件的引入本質就是jsonp,動態引入一個約定好格式的js,並運行。web

__webpack_require__.e = function requireEnsure(chunkId) {
....
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.src = __webpack_require__.p + "" + chunkId + ".bundle.js";
    head.appendChild(script);
....
}

切割文件去除註釋以下:json

webpackJsonp([1],[function(){},function(){}])

而在入口文件的webpackJsonpCallback函數內,則是將切割的文件包含的modules依次放入存儲在modules內數組

function webpackJsonpCallback(chunkIds, moreModules){
....
for(moduleId in moreModules) {
             if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
                 modules[moduleId] = moreModules[moduleId];
             }
         }
}     
....

因此實現以上功能需求以下:數據結構

  1. parse模塊:定位切割點並組裝異步加載文件所須要的依賴。
  2. chunks模塊:各個chunk包含module模塊的集合,經過文件樹裏面模塊的依賴關係生成。
  3. writeChunks模塊:根據chunks,經過文件流寫入文件。

例子app

require('d');

function a() {
    require.ensure(['./a'], function () {
        require('c');
    });
}

require.ensure(['./b'], function () {
    require('./m');
});

require('./e');

實現

parse模塊

實現思路:異步

  1. 經過遞歸,以及文件樹的特徵定位到require.ensure
  2. 將arguments第一個參數的數組,第二個參數的函數內遞歸搜索require,存入數組asyncs內,並遞歸下去

數據結構以下:async

{
    filename: '/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/main.js',
    id: 0,
    requires: [{
            name: 'd',
            range: [8, 11],
            id: 1
        }],
    rangeRequires: [[0, 7]],
    asyncs: [{
            requires: [{
                    name: './a',
                    id: 2
                }, {
                    name: 'c',
                    range: [88, 91],
                    id: 3
                }],
            asyncs: [],
            rangeRequires: [80, 87],
            ensureRequires: [34, 58]
        },
        {
            requires: [{
                    name: './b',
                    id: 4
                }, {
                    name: './m',
                    range: [156, 161],
                    id: 5
                }],
            asyncs: [],
            rangeRequires: [148, 155],
            ensureRequires: [106, 130]
        }],
}

chunks模塊

因爲各個依賴文件的源碼都包含在modlues內,因此chunks包含的是具體各個切割文件所包需module的moduleId。

實現思路:

  1. 經過入口mainPath 找到modules的入口mainModule
  2. 將mainModule 的requires遍歷,將值納入本chunk的modules內,將asyncs遍歷,依次新建chunk,並關聯父chunk,以上兩個依次遞歸遍歷。
  3. 最終生成完了以後,將各個非根節點的chunk遍歷,將依賴的modules遍歷對比父節點的chunk,若有重複標記'in-parent'

數據結構以下

{ '0': 
   { id: 0,
     modules: { '0': 'include', '1': 'include', '2': 'include' } },
  '1': 
   { id: 1,
     modules: 
      { '1': 'in-parent',
        '3': 'include',
        '4': 'include',
        '5': 'include',
        '6': 'include' },
     parentId: 0 },
  '2': 
   { id: 2,
     modules: { '5': 'include', '6': 'include' },
     parentId: 0 
     }
}

writeChunks模塊

實現思路:

  1. 判斷是否有多個chunk,來區分引入的模版。若是chunks的個數超過1,入口chunk則加載包含webpackJsonp,__webpack_require__.e等支持jsonp函數的模版,未超過則加載簡單的僅包含__webpack_require__的模版
  2. 區分入口chunk,入口/非入口chunk加載不一樣的頭部。
  3. webpackJsonp的入參有兩種,一種數組,一種以moduleId爲key的對象。爲數組時候則須要將以[,,modlue]等方式保證順序

如:

clipboard.png

代碼實現

本人的簡易版webpack實現simple-webpack

(完)

相關文章
相關標籤/搜索