Webpack 4給咱們帶來了一些改變。包括更快的打包速度,引入了SplitChunksPlugin插件來取代(以前版本里的)CommonsChunksPlugin插件。在這篇文章中,你將學習如何分割你的輸出代碼,從而提高咱們應用的性能。html
首先搞明白: webpack裏的代碼分割是個什麼鬼? 它容許你將一個文件分割成多個文件。若是使用的好,它能大幅提高你的應用的性能。其緣由是基於瀏覽器會緩存你的代碼這一事實。每當你對某一文件作點改變,訪問你站點的人們就要從新下載它。然而依賴卻不多變更。若是你將(這些依賴)分離成單獨的文件,訪問者就無需屢次重複下載它們了。node
使用webpack生成一個或多個包含你源代碼最終版本的「打包好的文件」(bundles),(概念上咱們看成)它們由(一個一個的)chunks組成。webpack
入口定義了咱們的應用代碼開始執行的那個文件,webpack從這個文件開始打包。你能定義一個入口點(常見於單頁應用 - Single-Page Application), 或者多個入口點(常見於多頁應用 - Multiple-Page Application)。web
定義一個入口點就生成一個chunk。若是你只是用字符串的方式定義了一個入口點,其就被命名爲main。若是你用對象的方式定義多個入口點,其就被命名爲入口對象中的鍵值。下面兩個例子是等價的:瀏覽器
entry: './src/index.js'
entry: { main: './src/index.js' }
輸出對象配置webpack如何輸出咱們的打包(bundles)和資源(assets),以及將它們放到哪裏。由於可能多於一個入口點,而只(能)指定一個輸出配置。事實上咱們就用chunks來給其一一命名。你能給打包輸出的文件定義一個肯定的名字,但既然咱們想要分割咱們的代碼,就不能這麼幹。你得使用[name]來建立輸出文件名的模板:緩存
output: { filename: '[name].[chunkhash].bundle.js', path: path.resolve(__dirname, 'dist') }
這裏要注意的重要事情是 [chunkhash]: 它基於你文件的內容給每一個chunk生成了一個特有的hash。它只有在你的文件內容自己變化的時候才變化。事實上,(若是內容沒有變化)瀏覽器會緩存它。若是文件名改變了(譯註:這裏是指hash變化了,而hash是文件名的一部分,即意味着文件的內容變化了),瀏覽器就知道要從新下載了。chunkhash看起來長得就象這樣子: 0c553ebfd158e16da428異步
如此這般,咱們的main chunk就會被打包成名爲 main.[chunkhash].bundle.js的文件。性能
正是有了SplitChunksPlugin插件,你能在你的應用中移出一部分到單獨的文件中。若是一個模塊被多個chunks使用,(分割出它以後)就能很容易的在這些chunks之間共享。這正是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的默認行爲會給共享庫建立分離的文件,但這涉及到異步chunks,即咱們異步導入文件。咱們在討論懶加載的時候再來更多的覆蓋這一主題。爲了針對全部類型的chunks,咱們須要稍微改改咱們的webpack配置:ui
// 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目錄中的文件。其次default這一項表示默認的緩存組,包括其它共享模塊。這裏有一個小小的問題:發生了重複。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 } } };
默認的cache group配置使得會生成一個名爲a~b.[chunkhash].bundle.js的文件。由於咱們的users.js文件大大小於30kb,若是沒有修改minSize屬性的話,它就不會分割成一個單獨的文件。在真實情形下,這是合理的,由於(如分割)並不能帶來性能確實的提高,反而使得瀏覽器多了一次對utilities.js的請求,而這個utilities.js又是如此之小(不划算)。
咱們能更進一小步,只針對utilities目錄中的文件:
// webpack.config.js 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。即便咱們如今設置一個全局的minSize: 0(在splitChunks對象中),默認的cache group也不會建立。這是由於被咱們建立的utilities group覆蓋了。utilities group默認的優先級值是0, 高於default cache group的。你可能已經注意到,默認cache group的優先級設置成了-20。
還有其它一些默認參數的設置,你能查閱SplitChunksPlugin的文檔。
即便你只有一個入口點(見於大多數的單頁應用),分離依賴到單獨的文件中也是一個好主意。使用SplitChunksPlugin來達到這一目標實際上很簡單,由於這是Webpack 4的默認行爲,頗有可能你只需設置chunks: 「all」就足夠了。若是你想要我說說其它有關的東西,告訴我吧。很快咱們將學習如何應用懶加載來更好的提高性能, 敬請期待!