這篇文章將為大家詳細(xì)講解有關(guān)多頁應(yīng)用Webpack4配置優(yōu)化的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
成都創(chuàng)新互聯(lián)公司-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比榮昌網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式榮昌網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋榮昌地區(qū)。費(fèi)用合理售后完善,十年實(shí)體公司更值得信賴。
前言
webpack4 相比之前的 2 與 3,改變很大。最主要的一點(diǎn)是很多配置已經(jīng)內(nèi)置,使得 webpack 能“開箱即用”。當(dāng)然這個(gè)開箱即用不可能滿足所有情況,但是很多以往的配置,其實(shí)可以不用了。比如在之前,壓縮混淆代碼,需要增加uglify插件,作用域提升(scope hosting)需要增加ModuleConcatenationPlugin。而在 webpack4 中,只需要設(shè)置 mode 為 production即可。當(dāng)然,如果再強(qiáng)行增加這些插件也不會報(bào)錯(cuò)。
所以我建議,如果大家想遷移到 webpack4,還是從 0 開始做加法,參考?xì)v史,重新做一個(gè)配置。而不是從歷史的配置里刪刪減減,再升級為 webpack4。這樣 webpack4 的配置會顯得更精簡。
打包優(yōu)化
打包優(yōu)化主要就是多頁應(yīng)用構(gòu)建時(shí),對所有頁面加載的依賴進(jìn)行合理打包。這個(gè)目前業(yè)界都已經(jīng)有了很多實(shí)踐,包括 webpack4,也有很多文章介紹。我再補(bǔ)充幾個(gè)不容易注意的小細(xì)節(jié)。有些點(diǎn)我不詳細(xì)介紹,不熟悉 webpack 配置的同學(xué)可能會不明白,可以搜索對應(yīng)關(guān)鍵詞,網(wǎng)上肯定有非常詳細(xì)的文章介紹。
首先,構(gòu)建多頁應(yīng)用,往往會抽離如下幾個(gè) chunk 包:
common:將被多個(gè)頁面同時(shí)引用的依賴包打到一個(gè) common chunk 中。網(wǎng)上大部分教程是被引入兩次即打入 common。我建議可以根據(jù)自己頁面數(shù)量來調(diào)整,在我的工程中,我設(shè)置引入次數(shù)超過頁面數(shù)量的 1/3 時(shí),才會打入 common 包。
dll: 將每個(gè)頁面都會引用的且基本不會改變的依賴包,如 react/react-dom 等再抽離出來,不讓其他模塊的變化污染 dll 庫的 hash 緩存。
manifest: webpack 運(yùn)行時(shí)(runtime)代碼。每當(dāng)依賴包變化,webpack 的運(yùn)行時(shí)代碼也會發(fā)生變化,如若不將這部分抽離開來,增加了 common 包 hash 值變化的可能性。
頁面入口文件對應(yīng)的page.js
然后我們會給打出的 chunk 包名,注入 contentHash,以實(shí)現(xiàn)最大緩存效果。在我們分 chunk 的過程中,最關(guān)鍵的一個(gè)思想就是,每次迭代發(fā)布,盡量減少 chunk hash 值的改變。這個(gè)在業(yè)界也有很多非常多的實(shí)踐,比如這篇文章:https://github.com/pigcan/blog/issues/9
不過在 webpack4 中,我們不用再增加這么多插件啦,一個(gè) optimization 配置完全就能搞定。
我先貼上我的 webpack 的 optimization 配置,然后我再對其做一些介紹,加深大家印象
const commonOptions = { chunks: 'all', reuseExistingChunk: true } export default { namedChunks: true, moduleIds: 'hashed', runtimeChunk: { name: 'manifest' }, splitChunks: { maxInitialRequests: 5, cacheGroups: { polyfill: { test: /[\\/]node_modules[\\/](core-js|raf|@babel|babel)[\\/]/, name: 'polyfill', priority: 2, ...commonOptions }, dll: { test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, name: 'dll', priority: 1, ...commonOptions }, commons: { name: 'commons', minChunks: Math.ceil(pages.length / 3), // 至少被1/3頁面的引入才打入common包 ...commonOptions } } } }
runtimeChunk
在 webpack4 之前,抽離 manifest,需要使用 CommonsChunkPlugin,配置一個(gè)指定 name 屬性為'manifest'的 chunk。在 webpack4 中,無需手動引入插件,配置 runtimeChunk 即可。
splitChunks
這個(gè)配置能讓我們以一定規(guī)則抽離想要的包,我們可能會抽好幾個(gè)包,如 verdor + common,所以 splitChunks 中提供 cacheGroups 字段,cacheGroups 每增加一個(gè) key,就相當(dāng)于多一個(gè)抽包規(guī)則。
在網(wǎng)上很多教程中,dll 往往是專門再加一個(gè) webpack 配置,使用 DllPlugin 來構(gòu)建 dll 庫,再在自己項(xiàng)目工程的 webpack 中利用 DllReferencePlugin 來映射 dll 庫。雖然這樣構(gòu)建速度會快不少,但是,哎,是真 TM 煩.....
我是一個(gè)很怕煩的人,我情愿在 webpack4 中利用 splitChunks,配好規(guī)則,再抽離對應(yīng)的 dll 包。當(dāng)然這個(gè)大家可以自己根據(jù)實(shí)際情況選擇方案。
除了 dll 與 common 兩個(gè) chunk,我還加了一個(gè) polyfill。這是因?yàn)槲覀冇玫哪承┬碌膸旎蛘呤褂媚承?ES6+語法(如 async/await)需要 runtime 墊片。比如我工程中使用了 react16,需要增加Map/Set/requestAnimationFrame (https://reactjs.org/docs/javascript-environment-requirements.html)那我必須在 dll 庫加載之前增加 polyfill,因此我將所有 core-js 與 babel 引入的包專門打進(jìn) polyfill,保證后續(xù)加載的 chunk 能執(zhí)行。priority字段用來配置 chunk 的引入優(yōu)先級,一般的項(xiàng)目應(yīng)該都是 polyfill > dll > common > page。
splitChunks 中配置項(xiàng)maxInitialRequests表示在一個(gè)入口(entry)中,最大初始請求 chunk 數(shù)(不包含按需加載的,即 dom 中 script 引入的 chunk),默認(rèn)值是 3。我現(xiàn)在 cacheGroups 中已經(jīng)有三個(gè),又因?yàn)榕渲昧?runtimeChunk,會打出 manifest,故而總共有 4 個(gè) chunk 包,超出了默認(rèn) 3 個(gè),因此需要重新配置值。
moduleIds
稍微了解過 webpack 運(yùn)行機(jī)制的同學(xué)會知道,項(xiàng)目工程中加載的 module,webpack 會為其分配一個(gè) moduleId,映射對應(yīng)的模塊。這樣產(chǎn)生的問題是一旦工程中模塊有增刪或者順序變化,moduleId 就會發(fā)生變化,進(jìn)而可能影響所有 chunk 的 content hash 值。只是因?yàn)?moduleId 變化就導(dǎo)致緩存失效,這肯定不是我們想要的結(jié)果。
在 webpack4 以前,通過 HashedModuleIdsPlugin 插件,我們可以將模塊的路徑映射成 hash 值,來替代 moduleId,因?yàn)槟K路徑是基本不變的,故而 hash 值也基本不變。
但在 webpack4 中,只需要optimization的配置項(xiàng)中設(shè)置 moduleIds 為 hashed 即可。
namedChunks
除了 moduleId,我們知道分離出的 chunk 也有其 chunkId。同樣的,chunkId 也有因其 chunkId 發(fā)生變化而導(dǎo)致緩存失效的問題。由于manifest與打出的 chunk 包中有chunkId相關(guān)數(shù)據(jù),所以一旦如“增刪頁面”這樣的操作導(dǎo)致 chunkId 發(fā)生變化,可能會影響很多的 chunk 緩存失效。
在 webpack4 以前,通過增加NamedChunksPlugin,使用 chunkName 來替換 chunkId,實(shí)現(xiàn)固化 chunkId,保持緩存的能力。在 webpack4 中,只需在optimization的配置項(xiàng)中設(shè)置 namedChunks 為 true 即可。
css 相關(guān)
在 webpack4 以前,使用 extract-text-webpack-plugin 插件將 css 從 js 包中分離出來單獨(dú)打包。在 webpack 中則需要換成 MiniCssExtractPlugin。并且在生產(chǎn)環(huán)境或者需要 HMR(模塊熱替換)時(shí),要用 MiniCssExtractPlugin.loader 替換 style-loader。
注意,這里有個(gè)坑。由于開發(fā)環(huán)境我們會配置熱更新,css 的熱更新目前MiniCssExtractPlugin.loader自身還待支持,故而還需要增加 css-hot-loader。 切記,css-hot-loader一定不能在生產(chǎn)環(huán)境下使用。否則每次構(gòu)建過程所有 js chunk 包的 contentHash 值都會不一致,進(jìn)而導(dǎo)致所有 js 緩存失效。 因?yàn)樯a(chǎn)環(huán)境增加這個(gè)配置不會有任何報(bào)錯(cuò),頁面也能正常構(gòu)建,故而容易忽視。
簡化多頁應(yīng)用的入口文件
使用react/vue等框架的同學(xué)知道,我們一般需要一個(gè)入口index.js,如這樣:
import React from 'react' import ReactDOM from 'react-dom' import App from './app' ReactDOM.render(<App />, document.getElementById('root'))
如果你還需要使用dva,或者給所有 react 頁面增加一個(gè) layout 功能的話,可能就會變成這樣:
import React from 'react' import dva from 'dva' import Model from './model' import Layout from '~@/layout' import App from './app' const app = dva() app.router(() => ( <Layout> <App /> </Layout> )) app.model(Model) app.start(document.getElementById('root'))
如果每個(gè)頁面都這樣,略略有點(diǎn)兒難受,因?yàn)槌绦騿T最怕寫重復(fù)的東西了。但是它又必須要有,沒辦法抽離成一個(gè)單獨(dú)文件。因?yàn)檫@個(gè)是入口文件,而多頁工程,每個(gè)頁面必須要有自己的入口文件,即使他們長得一模一樣。于是,我們的資源目錄就會是這樣:
- src - layout.js - pages - pageA - index.js - app.js - model.js - pageB - index.js - app.js - model.js
因?yàn)樗械?index 都一樣,我理想中的頁面的入口文件僅僅需要app.js就好,像這樣:
- src - layout.js - pages - pageA - app.js - model.js - pageB - app.js - model.js
作為一名前端開發(fā)工程師,Node 對于我們來說,應(yīng)該是熟練運(yùn)用的工具,而不是僅僅拿別人已經(jīng)封裝好的各類工具。
在這個(gè)問題中,我們大可以在 webpack 構(gòu)建前,通過Node的文件系統(tǒng)(File System),對應(yīng)我們的每個(gè)頁面,通過同一個(gè)入口文件模板,創(chuàng)建一些臨時(shí)入口文件:
- src - .entires - pageA.js - pageB.js - layout.js - pages
然后將這些臨時(shí)文件,作為 webpack 的 entry 配置。代碼如下:
const path = require('path') const fs = require('fs') const glob = require('glob') const rimraf = require('rimraf') const entriesDir = path.resolve(process.cwd(), './src/.entries') const srcDir = path.resolve(process.cwd(), './src') // 返回webpack entry配置 module.exports = function() { if (fs.existsSync(entriesDir)) { rimraf.sync(entriesDir) } fs.mkdirSync(entriesDir) return buildEntries(srcDir) } function buildEntries(srcDir) { return getPages(srcDir).reduce((acc, current) => { acc[current.pageName] = buildEntry(current) return acc }, {}) } // 獲取頁面數(shù)據(jù),只考慮一級目錄 function getPages(srcDir) { const pagesDir = `${srcDir}/pages` const pages = glob.sync(`${pagesDir}/**/app.js`) return pages.map(pagePath => { return { pageName: path.relative(pagesDir, p).replace('/app.js', ''), // 取出page文件夾名 pagePath: pagePath } }) } // 構(gòu)建臨時(shí)入口文件 function buildEntry({ pageName, pagePath }) { const fileContent = buildFileContent(pagePath) const entryPath = `${entriesDir}/${pageName}.js` fs.writeFileSync(entryPath, fileContent) return entryPath } // 替換模板中的 App 模塊地址,返回臨時(shí)入口文件內(nèi)容 function buildFileContent(pagePath) { return ` import React from 'react' import dva from 'dva' import Model from './model' import Layout from '~@/layout' import App from 'PAGE_APP_PATH' const app = dva() app.router(() => ( <Layout> <App /> </Layout> )) app.model(Model) app.start(document.getElementById('root')) `.replace(PAGE_APP_PATH, pagePath) }
這樣一來,我們就簡單的去掉了重復(fù)的入口文件,還增加了一個(gè) layout 的功能。這只是簡單的代碼,實(shí)際項(xiàng)目可能還有多級目錄,多個(gè) model 等等,需要自己再定制啦。
關(guān)于“多頁應(yīng)用Webpack4配置優(yōu)化的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯(cuò),請把它分享出去讓更多的人看到。
本文標(biāo)題:多頁應(yīng)用Webpack4配置優(yōu)化的示例分析
鏈接URL:http://aaarwkj.com/article8/godgop.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、外貿(mào)建站、網(wǎng)站導(dǎo)航、網(wǎng)站設(shè)計(jì)公司、云服務(wù)器、網(wǎng)站收錄
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)