詳解webpack腳手架

分割webpack配置文件的多種方法

參照: 看完這篇就看懂了很多webpack腳手架

(一)

將你的配置信息寫到多個分散的文件中去坛悉,然后在執(zhí)行webpack的時候利用--config參數(shù)指定要加載的配置文件志膀,配置文件利用moduleimports導(dǎo)出裤翩。你可以在webpack/react-starter 看到是使用這種發(fā)方法的。

// webpack 配置文件

|-- webpack-dev-server.config.js
|-- webpack-hot-dev-server.config.js
|-- webpack-production.config.js
|-- webpack.config.js

// npm 命令

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev-server": "webpack-dev-server --config webpack-dev-server.config.js --progress --colors --port 2992 --inline",
    "hot-dev-server": "webpack-dev-server --config webpack-hot-dev-server.config.js --hot --progress --colors --port 2992 --inline",
    "build": "webpack --config webpack-production.config.js --progress --profile --colors"
  },

(二)

調(diào)用第三方的webpack工具住诸,使用其集成的api耿导,方便進行webpack配置晦攒。HenrikJoreteg/hjs-webpack 這個repo就是這么做的。

var getConfig = require('hjs-webpack')

module.exports = getConfig({
  // entry point for the app
  in: 'src/app.js',

  // Name or full path of output directory
  // commonly named `www` or `public`. This
  // is where your fully static site should
  // end up for simple deployment.
  out: 'public',

  // This will destroy and re-create your
  // `out` folder before building so you always
  // get a fresh folder. Usually you want this
  // but since it's destructive we make it
  // false by default
  clearBeforeBuild: true
})

(三) Scalable webpack configurations

ones that can be reused and combined with other partial configurations

在單個配置文件中維護配置端辱,但是區(qū)分好條件分支梁剔。調(diào)用不同的npm命令時候設(shè)置不同的環(huán)境變量,然后在分支中匹配舞蔽,返回我們需要的配置文件荣病。

這樣做的好處可以在一個文件中管理不同npm操作的邏輯,并且可以共用相同的配置渗柿。webpack-merge這個模塊可以起到合并配置的作用个盆。


const parts = require('./webpack-config/parts');

switch(process.env.npm_lifecycle_event) {
  case 'build': 
    config = merge(common, 
      parts.clean(PATHS.build),
      parts.setupSourceMapForBuild(),
      parts.setupCSS(PATHS.app),
      parts.extractBundle({
        name: 'vendor',
        entries: ['react', 'vue', 'vuex']
      }),
      parts.setFreeVariable('process.env.NODE_ENV', 'production'),
      parts.minify()
      );
    break;
  default: 
    config = merge(common, 
      parts.setupSourceMapForDev(),
      parts.devServer(), 
      parts.setupCSS(PATHS.app));
}
// minify example
exports.minify = function () {
  return {
    plugins: [
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false,
          drop_console: true
        },
        comments: false,
        beautify: false
      })
    ]
  }
}

開發(fā)環(huán)境下的自動刷新

webpack-dev-server

webpack-dev-server在webpack的watch基礎(chǔ)上開啟服務(wù)器。

webpack-dev-server是運行在內(nèi)存中的開發(fā)服務(wù)器朵栖,支持高級webpack特性hot module replacement颊亮。這對于react vue這種組件化開發(fā)是很方便的。

使用webpack-dev-server命令開啟服務(wù)器陨溅,配合HMR及可以實現(xiàn)代碼更改瀏覽器局部刷新的能力终惑。

hot module replacement

Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running without a page reload.
當(dāng)應(yīng)用在運行期間hmr機制能夠修改、添加门扇、或者移除相應(yīng)的模塊雹有,而不使整個頁面刷新。

hmr機制適用于單頁應(yīng)用臼寄。

要實現(xiàn)hmr機制件舵,需要配合webpack-dev-server服務(wù)器,這個服務(wù)器本身就實現(xiàn)了監(jiān)察watch文件改動的能力脯厨,再開啟HMR選項铅祸,就添加了watch模塊變化的能力。這是HMR機制能生效的基礎(chǔ)。

從webpack編譯器角度

每次修改一個模塊的時候临梗,webpack會生成兩部分涡扼,一個是manifest.json,另一部分是關(guān)于這次模塊更新編譯完成的chunks盟庞。manifest.json中存著的是chunk更改前后的hash值吃沪。

從編譯器webpack的角度來講提供了hmr的原材料。供后續(xù)使用什猖。

從模塊的角度

模塊發(fā)生變化時票彪,webpack會生成之前講過的兩部分基礎(chǔ)文件,但是何時將變化后的模塊應(yīng)用到app中去不狮?這里就需要在應(yīng)用代碼中編寫handler去接受到模塊變化信息降铸。但是不能在所有模塊中編寫handler吧?這里就用到了消息冒泡機制摇零。

2018-11-01_211109.png

如圖A.js推掸、C.js沒有相關(guān)hmr代碼,B.js有相關(guān)hmr代碼驻仅,如果c模塊發(fā)生了變化谅畅,c模塊沒有hmr,那么就會冒泡到a噪服、b模塊毡泻。b模塊捕捉到了消息,hmr運行時會相應(yīng)的執(zhí)行一些操作粘优,而a.js捕捉不到信息牙捉,會冒泡到entry.js,而一旦有消息冒泡的入口塊敬飒,這就代表本次hmr失敗了邪铲,hmr會降級進行整個頁面的reload。

從HMR運行時的角度

HMR運行時是一些相關(guān)的操作api无拗,運行時支持兩個方法: check带到、apply

check發(fā)起 HTTP 請求去獲取更新的 manifest英染,以及一些更新過后的chunk揽惹。

2.png

環(huán)境變量的設(shè)置

var env = {
    'process.env.NODE_ENV': '"production"'
}
new webpack.DefinePlugin(env)

注意這里單引號間多了個雙引號 why?

以及webpack.DefinePlugin插件的原理四康?

開發(fā)的時候會想寫很多只在開發(fā)環(huán)境出現(xiàn)的代碼搪搏,比如接口mock等,在build命令后這些代碼不會存在闪金。

這對框架或者插件疯溺、組件的開發(fā)是很有幫助的论颅。vue,react等都會這么做囱嫩∈逊瑁可以在這些框架的dev模式提供很多有用的提示信息。

打包文件分割

為何要進行打包文件分割墨闲?

對于一個單頁應(yīng)用項目來說今妄,有分為業(yè)務(wù)代碼和第三方代碼,業(yè)務(wù)代碼會頻繁改動鸳碧,而第三方代碼一般來講變動的次數(shù)較少盾鳞,如果每次修改業(yè)務(wù)代碼都需要用戶將整個js文件都重新下載一遍,對于加載性能來講是不可取的瞻离,所以一般而言我們會將代碼分為業(yè)務(wù)代碼和第三方代碼分別進行打包腾仅,雖然多了一個請求的文件,增加了一些網(wǎng)絡(luò)開銷琐脏,但是相比于瀏覽器能將文件進行緩存而言,這些開銷是微不足道的缸兔。

我們在entry中定義了app入口日裙,相應(yīng)的業(yè)務(wù)邏輯都封裝在這個入口文件里,如果我們想要第三方代碼獨立出來惰蜜,就要再增加一個入口昂拂,我們習(xí)慣使用vendor這個命名。

// app.js

require('vue');
require('vuex');
// webpack.config.js

entry: {
    app: 'app/app.js',
    vendor: ['vue', 'vuex'],
  },

vendor入口的傳參是以一個數(shù)組的形式傳遞的抛猖,這是一種非常方便的注入多個依賴的方式格侯,并且能把多個依賴一起打包到一個chunk中。而且不用手動的創(chuàng)建真實存在的入口文件财著。

這相當(dāng)于:

// vendor.js

require('vue');
require('vuex');

// app.js

require('vue');
require('vuex');
// webpack.config.js

entry: {
    app: 'app/app.js',
    vendor: 'app/vendor.js',
  },

但是這樣做只是聲明了一個vendor入口而已联四,對于app這個入口來說,打包完成的文件還是會有vue和vuex依賴撑教,而新增的入口vendor打包完成的文件也有了vue和vuex兩個依賴朝墩。模塊依賴關(guān)系如下圖所示。

3.png

這里的A可以代表vue依賴伟姐,最后生成的打包文件是兩個平行關(guān)系的文件收苏,且都包含vue的依賴。

此時需要引入CommonsChunkPlugin插件

This is a pretty complex plugin. It fundamentally allows us to extract all the common modules from different bundles and add them to the common bundle. If a common bundle does not exist, then it creates a new one.

這是個相當(dāng)復(fù)雜的插件愤兵,他的基礎(chǔ)功能是允許我們從不同的打包文件中抽離出相同的模塊鹿霸,然后將這些模塊加到公共打包文件中。如果公共打包文件不存在秆乳,則新增一個懦鼠。同時這個插件也會將運行時(runtime)轉(zhuǎn)移到公共chunk打包文件中。

4.png
plugins: [
  new webpack.optimize.CommonsChunkPlugin({
    names: ['vendor', 'manifest']
  })
]

這里的name可以選擇已經(jīng)存在的塊芥永,這里就選擇了vendor塊芥颈,因為我們本來就是將vendor塊當(dāng)做管理第三方代碼的入口的。

而names傳入一個數(shù)組止状,數(shù)組里包含兩個trunk name淑趾,表示CommonsChunkPlugin插件會執(zhí)行兩次這個方法阳仔,第一次將公共的第三方代碼抽離移到vendor的塊中,這個過程之前也講過會將運行時runtime也轉(zhuǎn)移到vendor塊中扣泊,第二次執(zhí)行則是將運行時runtime抽離出來轉(zhuǎn)移到manifest塊中近范。這步操作解決了緩存問題。

這樣處理延蟹,最后會生成3個打包文件chunk评矩,app.js是業(yè)務(wù)代碼,vendor則是公共的第三方代碼阱飘,manifest.js則是運行時斥杜。

chunk type 塊的類型大揭秘

webpack1.0官網(wǎng)介紹中的chunk類型讀起來及其拗口chunk type, 所以我這里解讀一下沥匈。

chunk是webpack中最基本的概念之一蔗喂,且chunk常常會和entry弄混淆。在「打包文件分割部分」我們定義了兩個入口entry point -- app和vendor高帖,而通過一些配置缰儿,webpack會生成最后的一些打包文件,在這個例子中最后生成的文件有app.js 散址、 vendor.js 乖阵、 manifest.js。這些文件便被稱為塊chunk预麸。

entry & chunk 可以簡單的理解為一個入口瞪浸、一個出口

在官方1.0文檔中webpack的chunk類型分為三種:

  1. entry chunk 入口塊
  2. normal chunk 普通塊
  3. initial chunk 初始塊

entry chunk 入口塊

entry chunk 入口塊不能由字面意思理解為由入口文件編譯得到的文件,由官網(wǎng)介紹

An entry chunk contains the runtime plus a bunch of modules

可以理解為包含runtime運行時的塊可以稱為entry chunk吏祸,一旦原本存在運行時(runtime)的entry chunk失去了運行時默终,這個塊就會轉(zhuǎn)而變成initial chunk

normal chunk 普通塊

A normal chunk contains no runtime. It only contains a bunch of modules.

普通塊不包含運行時runtime犁罩,只包含一系列模塊齐蔽。但是在應(yīng)用運行時,普通塊可以動態(tài)的進行加載床估。通常會以jsonp的包裝方式進行加載含滴。而code splitting主要使用的就是普通塊。

initial chunk 初始塊

An initial chunk is a normal chunk.

官方對initial chunk的定義非常簡單丐巫,初始塊就是普通塊谈况,跟普通塊相同的是同樣不包含運行時runtime勺美,不同的是初始塊是計算在初始加載過程時間內(nèi)的。在介紹入口塊entry chunk的時候也介紹過碑韵,一旦入口塊失去了運行時赡茸,就會變成初始塊。這個轉(zhuǎn)變經(jīng)常由CommonsChunkPlugin插件實現(xiàn)祝闻。

例子解釋

還是拿「打包文件分割」的代碼做例子占卧,

// app.js

require('vue');
require('vuex');
// webpack.config.js

entry: {
    app: 'app/app.js',
    vendor: ['vue', 'vuex'],
  },

沒有使用CommonsChunkPlugin插件之前,兩個entry分別被打包成兩個chunk联喘,而這兩個chunk每個都包含了運行時华蜒,此時被稱為entry chunk入口塊。

而一旦使用了CommonsChunkPlugin插件豁遭,運行時runtime最終被轉(zhuǎn)移到了manifest.js文件叭喜,此時最終打包生成的三個chunkapp.js 、 vendor.js 蓖谢、 manifest.js捂蕴,app.js、vendor.js失去了runtime就由入口塊變成初始塊闪幽。

code splitting

前文有講到將依賴分割開來有助于瀏覽器緩存啥辨,提高用戶加載速度,但是當(dāng)業(yè)務(wù)復(fù)雜度增加沟使,代碼量大始終是一個問題委可。這時候就需要normal chunk普通塊的動態(tài)加載能力了渊跋。

It allows you to split your code into various bundles which you can then load on demand — like when a user navigates to a matching route, or on an event from the user.
code splitting 允許我們將代碼分割到可以按需加載的不同的打包文件中腊嗡,當(dāng)用戶導(dǎo)航到對應(yīng)的路由上時,或者是用戶觸發(fā)一個事件時拾酝,異步加載相應(yīng)的代碼燕少。

我們需要在業(yè)務(wù)邏輯中手動添加一些分割點,標(biāo)明此處事件邏輯之后進行代碼塊的異步加載蒿囤。


// test
window.addEventListener('click', function () {
  require.ensure(['vue', 'vuex'], function (require) {

  })  
})

這段代碼表明當(dāng)用戶點擊時客们,異步請求一個js文件,這個文件中包含該有vue vuex的依賴材诽。

打包后會根據(jù)手動分割點的信息生成一個打包文件底挫,就是圖中第一行0開頭的文件。這個文件也就是異步加載的文件脸侥。

下面是之前的一個vue項目建邓,采用code splitting將幾個路由抽離出來異步加載之后,文件由212kb減少到了137kb睁枕,同樣樣式文件也由58kb減少到了7kb官边。對于首屏渲染來說沸手,性能是會增加不少的。

6.png

7.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末注簿,一起剝皮案震驚了整個濱河市契吉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诡渴,老刑警劉巖捐晶,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異玩徊,居然都是意外死亡租悄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門恩袱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泣棋,“玉大人,你說我怎么就攤上這事畔塔√侗玻” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵澈吨,是天一觀的道長把敢。 經(jīng)常有香客問我,道長谅辣,這世上最難降的妖魔是什么修赞? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮桑阶,結(jié)果婚禮上柏副,老公的妹妹穿的比我還像新娘。我一直安慰自己蚣录,他們只是感情好割择,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著萎河,像睡著了一般荔泳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上虐杯,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天玛歌,我揣著相機與錄音,去河邊找鬼擎椰。 笑死支子,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的确憨。 我是一名探鬼主播译荞,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瓤的,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吞歼?” 一聲冷哼從身側(cè)響起圈膏,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎篙骡,沒想到半個月后稽坤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡糯俗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年尿褪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片得湘。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡杖玲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淘正,到底是詐尸還是另有隱情摆马,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布鸿吆,位于F島的核電站囤采,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏惩淳。R本人自食惡果不足惜蕉毯,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望思犁。 院中可真熱鬧代虾,春花似錦、人聲如沸抒倚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽托呕。三九已至,卻和暖如春频敛,著一層夾襖步出監(jiān)牢的瞬間项郊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工斟赚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留着降,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓拗军,卻偏偏與公主長得像任洞,于是被迫代替她去往敵國和親蓄喇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容