筆者最近在準備給fle-cli
升級到webpack4
版本,以爲有必要將探索過程的經驗分享給你們,遂決定寫這篇文章。(不知道fle-cli
?看這裏)css
webpack4是大趨勢,升級是必然的,那麼爲何如今才升級?html
緣由有如下幾個方面:node
然而如今,筆者認爲以上這些已經成熟,是時候來一波升級了。webpack
本文不會講解webpack配置的每一個細節點,由於這些官方文檔均可以看到。筆者會挑一些難以理解的新概念、可能會碰到的問題,以及筆者總結下來的優化方案來分享,但願能夠給你們帶來一些幫助。git
mode是webpack4新增的參數選項,它的值有3個:development、production、none,可以幫助咱們加載一些默認配置,none即不加載默認配置。下面將對應的默認配置列出來供你們參考,以避免重複配置。github
developmentweb
注重提高代碼構建速度和開發體驗npm
module.exports = { cache: true, devtools: "eval", plugins: [ new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }) ] }
prodution瀏覽器
提供代碼優化,如壓縮、做用域提高等緩存
var UglifyJsPlugin = require('uglifyjs-webpack-plugin') module.exports = { plugins: [ new UglifyJsPlugin(/* ... */), new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }), new webpack.optimize.ModuleConcatenationPlugin(), new webpack.NoEmitOnErrorsPlugin() ] }
這個選項也是webpack4新增的,主要是用來自定義一些優化打包策略。
minimizer
在production模式,該配置會默認爲咱們壓縮混淆代碼,但這顯然知足不了咱們對於優化代碼的訴求。下面筆者分享一套自身實踐總結下來的配置及解釋:
var UglifyJsPlugin = require('uglifyjs-webpack-plugin') var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') module.exports = { optimization: { minimizer: [ // 自定義js優化配置,將會覆蓋默認配置 new UglifyJsPlugin({ exclude: /\.min\.js$/, // 過濾掉以".min.js"結尾的文件,咱們認爲這個後綴自己就是已經壓縮好的代碼,不必進行二次壓縮 cache: true, parallel: true, // 開啓並行壓縮,充分利用cpu sourceMap: false, extractComments: false, // 移除註釋 uglifyOptions: { compress: { unused: true, warnings: false, drop_debugger: true }, output: { comments: false } } }), // 用於優化css文件 new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css$/g, cssProcessorOptions: { safe: true, autoprefixer: { disable: true }, // 這裏是個大坑,稍後會提到 mergeLonghand: false, discardComments: { removeAll: true // 移除註釋 } }, canPrint: true }) ] } }
UglifyJsPlugin
這款插件相信你們也是常常用到,這裏再也不多說,這裏的亮點是過濾掉自己已是壓縮的js文件,可以提高咱們的編譯效率以及避免二次混淆壓縮而形成的未知bug。
OptimizeCssAssetsPlugin
這款插件主要用來優化css文件的輸出,默認使用cssnano
,其優化策略主要包括:擯棄重複的樣式定義、砍掉樣式規則中多餘的參數、移除不須要的瀏覽器前綴等,更多優化規則看這裏。前文咱們提到這裏有個大坑,相信你已經察覺到了,沒錯,就是這貨把咱們經過autoprefixer
加好了前綴給移除了。筆者查閱了許多資料,依舊沒有找到滿意的答案,沒辦法,只有硬着頭皮去源碼中找答案了,因而便有了這段配置autoprefixer: { disable: true }
,禁用掉cssnano對於瀏覽器前綴的處理。
runtimeChunk
分離出webpack編譯運行時的代碼,也就是咱們先前稱爲manifest
的代碼塊,好處是方便咱們作文件的持久化緩存。它能夠設置多種類型的值,具體能夠看這裏,其中single
即將全部chunk的運行代碼打包到一個文件中,multiple
就是給每個chunk的運行代碼打包一個文件。
咱們能夠配合InlineManifestWebpackPlugin
插件將運行代碼直接插入html文件中,由於這段代碼很是少,這樣作能夠避免一次請求的開銷,可是新版插件的配置和以前有些不太同樣,接下來詳細講解一下如何配置。
var HtmlWebpackPlugin = require('html-webpack-plugin') var InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin') module.exports = { entry: { app: 'src/index.js' }, optimization: { runtimeChunk: 'single' // 等價於 // runtimeChunk: { // name: 'runtime' // } }, plugins: [ new HtmlWebpackPlugin({ title: 'fle-cli', filename: 'index.html', template: 'xxx', inject: true, chunks: ['runtime', 'app'], // 將runtime插入html中 chunksSortMode: 'dependency', minify: {/* */} }), new InlineManifestWebpackPlugin('runtime') ] }
這段配置會產生一個叫作runtime
的代碼塊,和老版本不一樣的是,咱們並不須要在html模版中添加<%= htmlWebpackPlugin.files.webpackManifest %>
,只需將runtime加入chunks便可。這裏有一個點要注意,InlineManifestWebpackPlugin插件的順序必定要在HtmlWebpackPlugin以後,不然會致使編譯失敗。
splitChunks
終於要講到重頭戲了,也是筆者我的認爲最難以理解的一個配置項。webpack4移除了CommonsChunkPlugin插件,取而代之的是splitChunks。
咱們先來看下默認配置:
splitChunks: { chunks: "async", minSize: 30000, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: '~', name: true, cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true } } }
默認配置只會做用於異步加載的代碼塊,它限制了分離文件的最小體積,即30KB(注意這個體積是壓縮以前的),這個是前提條件,而後它有兩個分組:屬於node_modules模塊,或者被至少2個入口文件引用,它纔會被打包成獨立的文件。
爲何要限制最小體積呢?由於webpack認爲小於30KB的代碼塊分離出來,還要額外消耗一次請求去加載它,成本過高,固然這個值也不是隨便意淫出來的,而是通過大量的實踐總結獲得的,筆者我的認爲這是一個很是好策略。
而maxAsyncRequests
(最大的異步請求數)和maxInitialRequests
(最大的初始請求數)這兩個參數則是爲了限制代碼塊劃分的過於細緻,致使大量的文件請求。
可是隻分離異步代碼塊顯然知足不了咱們的需求,所以接下來筆者分享一套相對來講比較優雅的分離打包配置:
splitChunks: { cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, name: 'vendors', minSize: 30000, minChunks: 1, chunks: 'initial', priority: 1 // 該配置項是設置處理的優先級,數值越大越優先處理 }, commons: { test: /[\\/]src[\\/]common[\\/]/, name: 'commons', minSize: 30000, minChunks: 3, chunks: 'initial', priority: -1, reuseExistingChunk: true // 這個配置容許咱們使用已經存在的代碼塊 } } }
首先是將node_modules的模塊分離出來,這點就再也不累述了。異步加載的模塊將會繼承默認配置,這裏咱們就不須要二次配置了。
第二點是分離出共享模塊,筆者認爲一個優雅的項目結構,其公共代碼(或者稱爲可複用的代碼)應該是放置於同一個根目錄下的,基於這點咱們能夠將src/common
中的公用代碼提取出來。
固然你還能夠有另一種選擇,將後綴爲.js
且使用次數超過3次的文件提取出來,可是筆者不建議這個作,由於這不利於持久化緩存,新增或刪除文件都有可能影響到使用次數,從而致使原先的公共文件失效。
原先還想着講一下css插件部分的配置,限於篇幅,本文就再也不進行講解說明了,感興趣的小哥哥小姐姐能夠在這裏翻看源碼:webpack4-test。
順便在這裏推薦一款好用的全局通用腳手架fle-cli:旨在幫助咱們從複雜繁瑣的編譯配置中解放出來,全身心地投入業務開發中,提升開發效率;同時它也是真正意義上的全局腳手架,區別於市面上其餘的全局腳手架,它不會在項目工程中生成各類編譯配置文件,也不會給你安裝一系列編譯的依賴包,這意味着你的項目工程能夠很是乾淨純粹。