webpack的code spliting與chunks

爲什麼要使用代碼分割code spliting?

對於一個大型項目而言,將所有代碼打包進一個文件可能會導致打包後的文件非常大(雖然減少了請求次數,單個文件過大會導致阻塞),而且有些代碼只是在某些情況下是必需的,這就導致了效率低下。webpack有一個code spliting的特性,它可以將你的代碼庫分割成按需加載的塊(chunks)。

怎麼定義了一個分割點split point

AMD和commonJS指定不同的方法去做代碼的按需加載。它們都支持代碼分隔並充當分割點:

commonJS

require.ensure(dependencies, callback);
require.ensure確保當執行回到函數callback的時候,dependencies中的每個依賴都能被同步的引用。callback中的參數是require。
示例:

require.ensure(["module-a", "module-b"], function(require) {
    var a = require("module-a");
    // ...
}); 

AMD

require(dependencies, callback)
AMD規範的分割點的定義使用異步的require方法,當調用時,所有依賴會被加載,回調函數callback中的參數是dependencies的輸出。
示例:

require(["module-a", "module-b"], function(a, b) {
    // ...
});

Note:在webpack中模塊是從左至右加載的。

ES6 modules

webpack不支持ES6 modules,但可以依據你的轉換器所創建的模塊格式決定使用require.ensure 或require
示例:

//static imports
import _ from 'lodash'

// dynamic imports
require.ensure([], function(require) {
  let contacts = require('./contacts')
})

代碼塊中的內容chunk content

所有分割點中的依賴dependencies都會放進一個新的代碼塊(new chunk)中。依賴dependencies還可以遞歸添加。
示例:

var b = require("./b");
require.ensure(["./a","./c"], function(require) {
    require("./b").xyz();
    var d = require("./d");
    var c=require('./c');
    console.log('c:',c);
    console.log('d:',d);
});

如上述代碼,a,b,c,d分別代表四個模塊,webpack打包後,會生成一個主bundle,和一個代碼塊文件chunk,在chunk中包含了a,c,d三個模塊。

chunk的優化

如果兩個chunk含有相同的模塊,它們會合併成一個(這就不會導致chunks有多個父級塊);
如果一個chunk中的某個模塊,在所有父級塊中都是可用的,這個模塊會從這個chunk中刪除;
如果一個chunk包含了另一個chunk的所有模塊,這個chunk將會存儲起來。

chunk的加載

chunks是根據configuration的target屬性來決定其加載的,比如,當target爲‘web’時,chunks是通過jsonp加載的。

chunk類型

  • entry chunk
  • normal chunk
  • initial chunk(no-entry)

入口塊(entry chunk)

含有一系列模塊,這些模塊有運行,那麼這個chunk就是一個entry chunk。

普通塊(normal chunk)

含有一系列模塊,這些模塊只是引入進來了,但並沒有運行,那麼這個chunk就是一個normal chunk。

初始塊 Initial chunk (non-entry)

Initial chunk也屬於normal-chunks,唯一不同的是它計入初始加載時間。這種類型可以與CommonsChunkPlugin結合使用。

分割程序代碼與第三方庫代碼Split app and vendor code

將項目分割成兩個文件:比如app.js和vendor.js,你可以在vendor.js中引入第三方代碼庫(vendor files),然後傳遞這個chunk的name(vendor)給CommonsChunkPlugin,如下所示:

var webpack = require("webpack");

module.exports = {
  entry: {
    app: "./app.js",
    vendor: ["jquery", "underscore", ...],
  },
  output: {
    filename: "bundle.js"
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", /* filename= */"vendor.bundle.js")
  ]
};

以上這樣的配置,會從app chunk中移除那些在vendor chunk中引入的模塊。bundle.js中只有程序代碼,外部庫代碼在vendor.bundle.js中。在你的html頁面中,先加載vendor.bundle.js

<script src="vendor.bundle.js"></script>
<script src="bundle.js"></script>

多入口塊 multiple entry chunks

可以設置多個入口點,這會導致多個入口塊。入口塊只能有一個運行時間。

運行多入口點

使用CommonsChunkPlugin時,運行時間(runtime)被移動到公共塊。這時入口點在初始塊(initial chunk)中,然而只能加載一個initial chunk,可以加載多個入口塊entry chunks,這顯示了在一個單頁中運行多入口點的可能性。
示例:

var webpack = require("webpack");
module.exports = {
    entry: { a: "./a", b: "./b" },
    output: { filename: "[name].js" },
    plugins: [ new webpack.optimize.CommonsChunkPlugin("init.js") ]
}

公用塊commons chunk

CommonsChunkPlugin能夠將出現在mutilple entry chunks中出現的模塊移動至一個新的chunk(commons chunk)中,runtime也一併移動至commons chunk中,這意味着原來的entry chunks現在是initial chunks了。

優化

有優化插件,可以根據特定的標準合併塊。
* LimitChunkCountPlugin
* MinChunkSizePlugin
* AggressiveMergingPlugin

命名塊Named chunks

require.ensure接受第三個參數,這個參數必須是個字符串,如果兩個分給點傳遞了相同的字符串,它們會使用相同的chunk。

require.include

require.include(request)

require.include是一個webpack特定的函數,這個函數添加一個模塊至現在的chunk中,但不執行它。
示例:

require.ensure(["./file"], function(require) {
  require("./file2");
});

// is equal to

require.ensure([], function(require) {
  require.include("./file");
  require("./file2");
});

如果一個模塊出現在多個子chunks中,require.include引入的模塊,在子chunks中會消失。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章