最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解多頁(yè)應(yīng)用 Webpack4 配置優(yōu)化與踩坑記錄

 更新時(shí)間:2018年10月16日 10:51:39   作者:相學(xué)長(zhǎng)  
這篇文章主要介紹了詳解多頁(yè)應(yīng)用 Webpack4 配置優(yōu)化與踩坑記錄,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

前言

最近新起了一個(gè)多頁(yè)項(xiàng)目,之前都未使用 webpack4,于是準(zhǔn)備上手實(shí)踐一下。這篇文章主要就是一些配置介紹,對(duì)于正準(zhǔn)備使用 webpack4 的同學(xué),可以做一些參考。

webpack4 相比之前的 2 與 3,改變很大。最主要的一點(diǎn)是很多配置已經(jīng)內(nèi)置,使得 webpack 能“開(kāi)箱即用”。當(dāng)然這個(gè)開(kāi)箱即用不可能滿足所有情況,但是很多以往的配置,其實(shí)可以不用了。比如在之前,壓縮混淆代碼,需要增加uglify插件,作用域提升(scope hosting)需要增加ModuleConcatenationPlugin。而在 webpack4 中,只需要設(shè)置 mode 為 production即可。當(dāng)然,如果再?gòu)?qiáng)行增加這些插件也不會(huì)報(bào)錯(cuò)。

所以我建議,如果大家想遷移到 webpack4,還是從 0 開(kāi)始做加法,參考?xì)v史,重新做一個(gè)配置。而不是從歷史的配置里刪刪減減,再升級(jí)為 webpack4。這樣 webpack4 的配置會(huì)顯得更精簡(jiǎn)。

打包優(yōu)化

打包優(yōu)化主要就是多頁(yè)應(yīng)用構(gòu)建時(shí),對(duì)所有頁(yè)面加載的依賴進(jìn)行合理打包。這個(gè)目前業(yè)界都已經(jīng)有了很多實(shí)踐,包括 webpack4,也有很多文章介紹。我再補(bǔ)充幾個(gè)不容易注意的小細(xì)節(jié)。有些點(diǎn)我不詳細(xì)介紹,不熟悉 webpack 配置的同學(xué)可能會(huì)不明白,可以搜索對(duì)應(yīng)關(guān)鍵詞,網(wǎng)上肯定有非常詳細(xì)的文章介紹。

首先,構(gòu)建多頁(yè)應(yīng)用,往往會(huì)抽離如下幾個(gè) chunk 包:

  • common:將被多個(gè)頁(yè)面同時(shí)引用的依賴包打到一個(gè) common chunk 中。網(wǎng)上大部分教程是被引入兩次即打入 common。我建議可以根據(jù)自己頁(yè)面數(shù)量來(lái)調(diào)整,在我的工程中,我設(shè)置引入次數(shù)超過(guò)頁(yè)面數(shù)量的 1/3 時(shí),才會(huì)打入 common 包。
  • dll: 將每個(gè)頁(yè)面都會(huì)引用的且基本不會(huì)改變的依賴包,如 react/react-dom 等再抽離出來(lái),不讓其他模塊的變化污染 dll 庫(kù)的 hash 緩存。
  • manifest: webpack 運(yùn)行時(shí)(runtime)代碼。每當(dāng)依賴包變化,webpack 的運(yùn)行時(shí)代碼也會(huì)發(fā)生變化,如若不將這部分抽離開(kāi)來(lái),增加了 common 包 hash 值變化的可能性。
  • 頁(yè)面入口文件對(duì)應(yīng)的page.js

然后我們會(huì)給打出的 chunk 包名,注入 contentHash,以實(shí)現(xiàn)最大緩存效果。在我們分 chunk 的過(guò)程中,最關(guān)鍵的一個(gè)思想就是,每次迭代發(fā)布,盡量減少 chunk hash 值的改變。這個(gè)在業(yè)界也有很多非常多的實(shí)踐,比如這篇文章:https://github.com/pigcan/blog/issues/9

不過(guò)在 webpack4 中,我們不用再增加這么多插件啦,一個(gè) optimization 配置完全就能搞定。

我先貼上我的 webpack 的 optimization 配置,然后我再對(duì)其做一些介紹,加深大家印象

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頁(yè)面的引入才打入common包
    ...commonOptions
   }
  }
 }
}

runtimeChunk

在 webpack4 之前,抽離 manifest,需要使用 CommonsChunkPlugin,配置一個(gè)指定 name 屬性為'manifest'的 chunk。在 webpack4 中,無(wú)需手動(dòng)引入插件,配置 runtimeChunk 即可。

splitChunks

這個(gè)配置能讓我們以一定規(guī)則抽離想要的包,我們可能會(huì)抽好幾個(gè)包,如 verdor + common,所以 splitChunks 中提供 cacheGroups 字段,cacheGroups 每增加一個(gè) key,就相當(dāng)于多一個(gè)抽包規(guī)則。

在網(wǎng)上很多教程中,dll 往往是專門再加一個(gè) webpack 配置,使用 DllPlugin 來(lái)構(gòu)建 dll 庫(kù),再在自己項(xiàng)目工程的 webpack 中利用 DllReferencePlugin 來(lái)映射 dll 庫(kù)。雖然這樣構(gòu)建速度會(huì)快不少,但是,哎,是真 TM 煩.....

我是一個(gè)很怕煩的人,我情愿在 webpack4 中利用 splitChunks,配好規(guī)則,再抽離對(duì)應(yīng)的 dll 包。當(dāng)然這個(gè)大家可以自己根據(jù)實(shí)際情況選擇方案。

除了 dll 與 common 兩個(gè) chunk,我還加了一個(gè) polyfill。這是因?yàn)槲覀冇玫哪承┬碌膸?kù)或者使用某些 ES6+語(yǔ)法(如 async/await)需要 runtime 墊片。比如我工程中使用了 react16,需要增加Map/Set/requestAnimationFrame (https://reactjs.org/docs/javascript-environment-requirements.html)那我必須在 dll 庫(kù)加載之前增加 polyfill,因此我將所有 core-js 與 babel 引入的包專門打進(jìn) polyfill,保證后續(xù)加載的 chunk 能執(zhí)行。priority字段用來(lái)配置 chunk 的引入優(yōu)先級(jí),一般的項(xiàng)目應(yīng)該都是 polyfill > dll > common > page。

splitChunks 中配置項(xiàng)maxInitialRequests表示在一個(gè)入口(entry)中,最大初始請(qǐng)求 chunk 數(shù)(不包含按需加載的,即 dom 中 script 引入的 chunk),默認(rèn)值是 3。我現(xiàn)在 cacheGroups 中已經(jīng)有三個(gè),又因?yàn)榕渲昧?runtimeChunk,會(huì)打出 manifest,故而總共有 4 個(gè) chunk 包,超出了默認(rèn) 3 個(gè),因此需要重新配置值。

moduleIds

稍微了解過(guò) webpack 運(yùn)行機(jī)制的同學(xué)會(huì)知道,項(xiàng)目工程中加載的 module,webpack 會(huì)為其分配一個(gè) moduleId,映射對(duì)應(yīng)的模塊。這樣產(chǎn)生的問(wèn)題是一旦工程中模塊有增刪或者順序變化,moduleId 就會(huì)發(fā)生變化,進(jìn)而可能影響所有 chunk 的 content hash 值。只是因?yàn)?moduleId 變化就導(dǎo)致緩存失效,這肯定不是我們想要的結(jié)果。

在 webpack4 以前,通過(guò) HashedModuleIdsPlugin 插件,我們可以將模塊的路徑映射成 hash 值,來(lái)替代 moduleId,因?yàn)槟K路徑是基本不變的,故而 hash 值也基本不變。

但在 webpack4 中,只需要optimization的配置項(xiàng)中設(shè)置 moduleIds 為 hashed 即可。

namedChunks

除了 moduleId,我們知道分離出的 chunk 也有其 chunkId。同樣的,chunkId 也有因其 chunkId 發(fā)生變化而導(dǎo)致緩存失效的問(wèn)題。由于manifest與打出的 chunk 包中有chunkId相關(guān)數(shù)據(jù),所以一旦如“增刪頁(yè)面”這樣的操作導(dǎo)致 chunkId 發(fā)生變化,可能會(huì)影響很多的 chunk 緩存失效。

在 webpack4 以前,通過(guò)增加NamedChunksPlugin,使用 chunkName 來(lái)替換 chunkId,實(shí)現(xiàn)固化 chunkId,保持緩存的能力。在 webpack4 中,只需在optimization的配置項(xiàng)中設(shè)置 namedChunks 為 true 即可。

css 相關(guān)

在 webpack4 以前,使用 extract-text-webpack-plugin 插件將 css 從 js 包中分離出來(lái)單獨(dú)打包。在 webpack 中則需要換成 MiniCssExtractPlugin。并且在生產(chǎn)環(huán)境或者需要 HMR(模塊熱替換)時(shí),要用 MiniCssExtractPlugin.loader 替換 style-loader。

注意,這里有個(gè)坑。由于開(kāi)發(fā)環(huán)境我們會(huì)配置熱更新,css 的熱更新目前MiniCssExtractPlugin.loader自身還待支持,故而還需要增加 css-hot-loader。 切記,css-hot-loader一定不能在生產(chǎn)環(huán)境下使用。否則每次構(gòu)建過(guò)程所有 js chunk 包的 contentHash 值都會(huì)不一致,進(jìn)而導(dǎo)致所有 js 緩存失效。 因?yàn)樯a(chǎn)環(huán)境增加這個(gè)配置不會(huì)有任何報(bào)錯(cuò),頁(yè)面也能正常構(gòu)建,故而容易忽視。

簡(jiǎn)化多頁(yè)應(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 頁(yè)面增加一個(gè) layout 功能的話,可能就會(huì)變成這樣:

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è)頁(yè)面都這樣,略略有點(diǎn)兒難受,因?yàn)槌绦騿T最怕寫(xiě)重復(fù)的東西了。但是它又必須要有,沒(méi)辦法抽離成一個(gè)單獨(dú)文件。因?yàn)檫@個(gè)是入口文件,而多頁(yè)工程,每個(gè)頁(yè)面必須要有自己的入口文件,即使他們長(zhǎng)得一模一樣。于是,我們的資源目錄就會(huì)是這樣:

- src
 - layout.js
 - pages
  - pageA
   - index.js
   - app.js
   - model.js
  - pageB
   - index.js
   - app.js
   - model.js

因?yàn)樗械?index 都一樣,我理想中的頁(yè)面的入口文件僅僅需要app.js就好,像這樣:

- src
 - layout.js
 - pages
  - pageA
   - app.js
   - model.js
  - pageB
   - app.js
   - model.js

作為一名前端開(kāi)發(fā)工程師,Node 對(duì)于我們來(lái)說(shuō),應(yīng)該是熟練運(yùn)用的工具,而不是僅僅拿別人已經(jīng)封裝好的各類工具。

在這個(gè)問(wèn)題中,我們大可以在 webpack 構(gòu)建前,通過(guò)Node的文件系統(tǒng)(File System),對(duì)應(yīng)我們的每個(gè)頁(yè)面,通過(guò)同一個(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
 }, {})
}
// 獲取頁(yè)面數(shù)據(jù),只考慮一級(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)
}

這樣一來(lái),我們就簡(jiǎn)單的去掉了重復(fù)的入口文件,還增加了一個(gè) layout 的功能。這只是簡(jiǎn)單的代碼,實(shí)際項(xiàng)目可能還有多級(jí)目錄,多個(gè) model 等等,需要自己再定制啦。

webpack4出來(lái)已經(jīng)挺久了,文章寫(xiě)的有點(diǎn)兒滯后了,所以很多我覺(jué)得應(yīng)該大家都明白的地方就沒(méi)詳細(xì)寫(xiě)了。如果還有什么疑問(wèn)的話,歡迎評(píng)論~~

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • laydate如何根據(jù)開(kāi)始時(shí)間或者結(jié)束時(shí)間限制范圍

    laydate如何根據(jù)開(kāi)始時(shí)間或者結(jié)束時(shí)間限制范圍

    這篇文章主要為大家詳細(xì)介紹了laydate根據(jù)開(kāi)始時(shí)間或者結(jié)束時(shí)間限制范圍的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • JavaScript監(jiān)控埋點(diǎn)的實(shí)現(xiàn)與詳細(xì)用法

    JavaScript監(jiān)控埋點(diǎn)的實(shí)現(xiàn)與詳細(xì)用法

    埋點(diǎn)(Event Tracking)是用戶行為監(jiān)控的一種技術(shù)手段,通常用于收集用戶在網(wǎng)站或應(yīng)用中的操作數(shù)據(jù),例如點(diǎn)擊、頁(yè)面瀏覽、滾動(dòng)等,本文給大家介紹了JavaScript監(jiān)控埋點(diǎn)的實(shí)現(xiàn)與詳細(xì)用法,需要的朋友可以參考下
    2024-10-10
  • 簡(jiǎn)單談?wù)刯avascript代碼復(fù)用模式

    簡(jiǎn)單談?wù)刯avascript代碼復(fù)用模式

    這篇文章主要簡(jiǎn)單談?wù)刯avascript代碼復(fù)用模式,主要詳細(xì)介紹了類式繼承模式中的默認(rèn)模式,希望大家能夠喜歡。
    2015-01-01
  • TypeScript泛型使用詳細(xì)介紹

    TypeScript泛型使用詳細(xì)介紹

    泛型是靜態(tài)類型語(yǔ)言的基本特征,允許將類型作為參數(shù)傳遞給另一個(gè)類型、函數(shù)、或者其他結(jié)構(gòu)。TypeScript 支持泛型作為將類型安全引入組件的一種方式。這些組件接受參數(shù)和返回值,其類型將是不確定的,直到它在代碼中被使用
    2022-10-10
  • JS實(shí)現(xiàn)的新浪微博大廳文字內(nèi)容滾動(dòng)效果代碼

    JS實(shí)現(xiàn)的新浪微博大廳文字內(nèi)容滾動(dòng)效果代碼

    這篇文章主要介紹了JS實(shí)現(xiàn)的新浪微博大廳文字內(nèi)容滾動(dòng)效果代碼,可實(shí)現(xiàn)頁(yè)面圖文元素定時(shí)滾動(dòng)的效果,涉及JavaScript時(shí)間函數(shù)定時(shí)改變頁(yè)面元素的相關(guān)技巧,需要的朋友可以參考下
    2015-11-11
  • web網(wǎng)絡(luò)安全之跨站腳本攻擊(XSS)詳解

    web網(wǎng)絡(luò)安全之跨站腳本攻擊(XSS)詳解

    這篇文章主要介紹了web網(wǎng)絡(luò)安全之跨站腳本攻擊(XSS)的相關(guān)資料,跨站腳本攻擊XSS是一種常見(jiàn)的Web安全漏洞,攻擊者通過(guò)注入惡意腳本誘使用戶執(zhí)行,可能導(dǎo)致竊取敏感信息或執(zhí)行惡意操作,需要的朋友可以參考下
    2025-03-03
  • FusionCharts圖表顯示雙Y軸雙(多)曲線

    FusionCharts圖表顯示雙Y軸雙(多)曲線

    項(xiàng)目中需要FusionCharts圖表顯示雙Y軸,好像FCF_MSLine.swf不可以這樣,只能顯示一個(gè)Y軸,多條線,找了一下,用MSCombiDY2D.swf可以實(shí)現(xiàn),現(xiàn)在曬出來(lái)和大家分享一下
    2012-11-11
  • javascript之textarea打字機(jī)效果提示代碼推薦

    javascript之textarea打字機(jī)效果提示代碼推薦

    非常不錯(cuò)的提示輸入內(nèi)容,動(dòng)態(tài)的提示,給人親切感
    2008-09-09
  • JavaScript數(shù)學(xué)對(duì)象(Math)方法舉例詳解

    JavaScript數(shù)學(xué)對(duì)象(Math)方法舉例詳解

    這篇文章主要給大家介紹了關(guān)于JavaScript數(shù)學(xué)對(duì)象(Math)方法的相關(guān)資料,Math(數(shù)學(xué))對(duì)象的作用是執(zhí)行普通的算數(shù)任務(wù),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-03-03
  • javascript demo 基本技巧

    javascript demo 基本技巧

    js下的一些小技巧,需要的朋友可以下。
    2009-12-12

最新評(píng)論

彭州市| 化隆| 晋宁县| 海淀区| 临高县| 称多县| 嘉善县| 绿春县| 花莲县| 新晃| 渭源县| 新巴尔虎左旗| 台南市| 邯郸县| 肃南| 宣威市| 广饶县| 南汇区| 焦作市| 浮梁县| 金坛市| 乌拉特前旗| 德格县| 郸城县| 普兰店市| 吉林省| 习水县| 万安县| 邯郸县| 新余市| 东城区| 浠水县| 石家庄市| 黎平县| 鄂伦春自治旗| 射阳县| 郸城县| 井研县| 乐亭县| 新安县| 墨脱县|