Webpack 4 給我們帶來了一些變化。其中包括更快地打包,引入了SplitChunksPlugin,並淘汰掉之前的CommomsChunksPlugin。在本文,你將學習如何拆分輸出代碼以提高應用的性能。 ...
轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。
原文出處:https://wanago.io/2018/06/04/code-splitting-with-splitchunksplugin-in-webpack-4/
Webpack 4 給我們帶來了一些變化。其中包括更快地打包,引入了SplitChunksPlugin,並淘汰掉之前的CommomsChunksPlugin。在本文,你將學習如何拆分輸出代碼以提高應用的性能。
代碼分離的思想
先說重要的:在Webpack中,到底什麼是代碼分離?代碼分離允許你把代碼拆分到多個文件中。如果使用得當,你的應用性能會提高很多。因為瀏覽器能緩存你的代碼。
每當你做出一次修改,包含修改的文件需要被所有訪問你網站的人重新下載。但你並不會經常修改應用的依賴庫(譯者註:你常修改的是你的業務邏輯)。如果你能把那些依賴庫拆分到完全分離的文件中,即使業務邏輯發生了更改,訪問者也不需要再次下載依賴庫,直接使用之前的緩存就可以了。
使用Webpack時,你會得到一個或多個生成的輸出文件,這些文件包含了我們源碼的最終輸出。而它們由代碼塊(chunks)組成。
入口(Entry)
入口定義了我們代碼中應用是從哪裡開始執行的,這也是Webpack開始打包的地方。你可以定義一個入口(常見於單頁應用),或者多個入口(常見於多頁應用)。
定義一個入口,就會得到一個chunk。如果你只使用字元串定義一個入口,那麼這個chunk名為main。如果你使用對象定義了多個入口,那麼它們會以entry對象的屬性來命名。下麵的例子會得到相同的chunk:
// 例1 entry: './src/index.js' // 例2 entry: { main: './src/index.js' }
輸出(Output)
在配置文件中,輸出配置是一個對象,它指明瞭Webpack應該在哪兒和如何對我們的打包結果和資源進行輸出。雖然可能有多個入口,但是只能有一個輸出配置對象。而chunk名稱的用處,就在於根據入口對應不同的輸出。你可以為我們的打包輸出定義一個固定的文件名,但若想代碼分離,就不應該這麼做。你可以使用[name]為我們的輸出文件創建文件名的模板:
output: { filename: '[name].[chunkhash].bundle.js', path: path.resolve(__dirname, 'dist') }
一件值得註意的重要東西是[chunkhash]:它是一個基於文件內容的屬於特定chunk的哈希值。它僅會隨著文件內容的改變而改變。因此,瀏覽器就可以利用這一點來緩存它。如果打包輸出的文件名變了,瀏覽器就知道自己需要重新下載它。一個chunkhash可能長這樣:0c553ebfd158e16da428。
我們的主chunk會被打包進一個叫main.[chunkhash].bundle.js的文件。
SplitChunksPlugin
由於有了SplitChunksPlugin,你可以把應用中的特定部分移至不同文件。如果一個模塊在不止一個chunk中被使用,那麼利用代碼分離,該模塊就可以在它們之間很好地被共用。這是Webpack的預設行為。
// utilities/users.js export default [ { firstName: "Adam", age: 28 }, { firstName: "Jane", age: 24 }, { firstName: "Ben", age: 31 }, { firstName: "Lucy", age: 40 } ] // a.js import _ from 'lodash'; import users from './users'; const adam = _.find(users, { firstName: 'Adam' }); // b.js import _ from 'lodash'; import users from './users'; const lucy = _.find(users, { firstName: 'Lucy' }); // webpack.config.js module.exports = { entry: { a: "./src/a.js", b: "./src/b.js" }, output: { filename: "[name].[chunkhash].bundle.js", path: __dirname + "/dist" } };
如果運行它,你會看到Webpack創建了兩個文件:a.[chunkhash].bundle.js和b.[chunkhash].bundle.js,而且每一個文件都包含對lodash庫的拷貝:這並不好!我之前說過,為共用的庫創建分離的文件是Webpack的一個預設行為,但這隻涉及非同步的chunk,即意味著只作用於我們非同步引入的那些文件。我們會在介紹懶載入的時候討論這個話題。為了使這個預設行為能支持所有類型的chunks,我們需要稍微改一下Webpack的配置:
// webpack.config.js module.exports = { entry: { a: "./src/a.js", b: "./src/b.js" }, output: { filename: "[name].[chunkhash].bundle.js", path: __dirname + "/dist" }, optimization: { splitChunks: { chunks: "all" } }, };
現在我們看到,創建了額外的vendors~a~b.[chunkhash].bundle.js文件,它包含了lodash庫的代碼。這是因為我們預設有一些cacheGroups配置:
splitChunks: { chunks: "all", cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true } } }
首先,是vendors,它包含的文件來自於你node_modules。再者,是所有其他共用模塊的預設緩存組。這裡有一個小點:有一些冗餘。a.[chunkhash].bundle.js和b.[chunkhash].bundle.js都包含了users.js的內容。這是因為,SplitChunksPlugin預設地只會分離大於30Kb的文件。我們可以輕鬆地修改它:
// webpack.config.js module.exports = { entry: { a: "./src/a.js", b: "./src/b.js" }, output: { filename: "[name].[chunkhash].bundle.js", path: __dirname + "/dist" }, optimization: { splitChunks: { chunks: 'all', minSize: 0 // 修改了這裡 } } };
結果是,它新創建了屬於預設緩存組的名為a~b.[chunkhash].bundle.js的文件。因為我們的users.js文件所占空間遠遠小於30Kb,在沒有修改minSize屬性的情況下,它不會被打包到單獨的分離文件。在真實環境下,這是一件好事,因為這樣不會帶來實質的性能提升,反而會強制瀏覽器為分離出的文件(它現在是很小的)再發一次額外的請求。
我們可以進一步,為僅在utilities目錄下的做特殊處理:
const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { a: "./src/a.js", b: "./src/b.js" }, output: { filename: "[name].[chunkhash].bundle.js", path: __dirname + "/dist" }, optimization: { splitChunks: { chunks: "all", cacheGroups: { utilities: { test: /[\\/]src[\\/]utilities[\\/]/, minSize: 0 } } } } };
這樣我們的輸出包含4個文件:a.[chunkhash].bundle.js,b.[chunkhash].bundle.js,vendors~a~b.[chunkhash].bundle.js和utilities~a~b.[chunkhash].bundle.js。(譯者註:這需要在打包之前,把users.js文件移至./src/utilities目錄下,並修改a.js和b.js中的相關引用路徑)。雖然我們現在可以讓minSize:0成為全局設置(即放在splitChunks對象中作為其屬性),但即使這樣,預設的緩存組也不會被創建。因為,所有可能被引入的文件都應該在我們剛創建的utilities組下。這個組具的優先順序是0,高於預設緩存組的優先順序。你可能已經註意到了,預設緩存組的優先順序被設置為了-20。
還有其他的配置供你使用,可查看SplitChunksPlugin文檔。
總結
即使你只有一個入口(常發生於大多數單頁應用中),把你的依賴放入一個獨立的文件依然是個好主意。這其實很容易做到,因為使用SplitChunksPlugin是Webpack 4的預設行為,可能你設置一下chunks: 'all'就足夠了。如果你想讓我討論此相關話題,歡迎留言。很快我們將會學習如何使用懶載入,讓你的應用性能更上一層樓。敬請期待!