webpack HMR是如何工作的?

世有因果知因求果發表於2018-03-26

https://github.com/webpack/docs/wiki/hot-module-replacement-with-webpack

https://www.jianshu.com/p/941bfaf13be1

什麼是HMR?

Hot Module Replacement(HMR)在web應用正在執行時在無需整個頁面refresh的前提下,實現對特定模組替換,新增,或者刪除的操作。

HMR是如何工作的?

webpack會在構建過程中向bundle中新增一小段HMR Runtime程式碼,這段HMR Runtime將在你的app中執行。當構建結束時,webpack並不會退出,而是在那裡監聽原始碼的任何改變。如果webpack檢測到程式碼的變化,它只對變更過的module執行rebuild。依賴於webpack的配置,要麼webpack自己向HRM runtime程式傳送一個訊號,或者HMR Runtime主動向webpack徵詢程式碼變化。無論任何一種模式下,變更過的module將被髮往HMR runtime.而這個runtime程式碼將試圖應用這個hot update.首先runtime檢查被更新的module是否能夠accept這個hot update.如果不能,則runtime繼續檢查required這個update module的module是否能夠accept.如果還不能accept,則繼續bubble up往上冒泡,直到找到能夠accept的module,或者直到app entry point.而這種情況下,hot update將會fail掉。

從app來看

app程式碼請求HRM runtime執行檢查Update的工作。HMR Runtime下載這個update(async)並且告知app code有一個update已經ready. app程式碼請求HRM Runtime來apply 這個update. HMR runtime應用上這個update(sync). app程式碼可能會也可能不會需要使用者的互動(這個你自己決定)

從compiler(webpack)來看

除了普通的assets, compiler需要發射"update"事件以便執行系統更新這個update到新的版本。"update"包含兩部分內容:

1. the update manifest(json)

2. one or multiple update chunks(js)

manifest包含新的compilation hash和一個所有的updated chunks列表清單;

而update chunks則包含了所有的updated module的程式碼(或一個flag如果該模組被刪除掉的話)

compiler也會確保module和chunk id在這些build之間保持一致性。它使用一個records.json檔案來儲存這些資訊以便保持一致和同步。

從module的角度來看

HMR是一個可選開啟的功能,所以HMR僅僅會影響到那些包含HMR Code的模組。總的來說,module developer需要寫handlers程式碼,如果該模組的dependency更新了的話,這個Handlers程式碼將被呼叫以便接收這個update。他們也可以寫一個handler,當該module本身而不是其dependency更新時呼叫他。

在大多數情況下,並不需要在每個module中都寫HRM code。如果一個module沒有HMR handler那麼這個update將會bubble up.這意味著僅需一個handler就可以處理這個模組的依賴樹上的所有module update。如果該依賴樹中的任何單個模組做了更新,那麼整個module tree就將得到reloaded(only reloaded not transferred)

從HMR runtime的角度來看

HMR runtime是一段額外的程式碼注入用於跟蹤module parents和module children.

從管理的角度說,這個runtime支援兩個方法: check和apply

一個check執行http請求,請求manifest列表檔案。如果這個請求失敗了,將不會有任何update執行。否則,新的updated chunks將和當前已經載入過的chunks進行比較。針對每個已經loaded的chnks,如果有update,則將下載對應的update chunk.所有的模組更新在runtime看來都作為update來快取。runtime切換到ready狀態,意味著一個update已經下載並且可以用於applied.

對每一個new chunk(無loaded chunk對應),update chunk也需要下載。

apply方法標誌所有updated modules為invalid狀態。針對每一個invalid module,需要有一個update handler在本模組裡或者其父親模組裡存在。否則這個Invalid module狀態會bundle up並且設定其所有的父親元件為invalid.這個流程將一直進行下去直到沒有"bubbling up"可以發生為止。如果冒泡到了entry point,則這個process將fail掉。

現在所有invlaid modules將被disposed(處置掉,需要dispose handler)並且解除安裝掉。然後當前hash更新並且所有"accept" handlers將被呼叫。runtime最後會切換到idle狀態,繼續下一輪。

 

我們能做些什麼?

你可以作為LiveReload的替代品將HMR應用在dev過程中。實際上,webpack-dev-server實際上會在執行重新載入整個頁面前試圖使用HMR來完成熱更新。你只需要將webpack/hot/dev-server本身加到你的webpack build的entry point中去,並且在命令列加上--hot引數即可。

webpack/hot/dev-server將在HMR update失敗後重新載入整個頁面。如果你希望自己來執行reload page動作,你可以增加webpack/hot/only-dev-server到entry point中,而不是dev-server。

你也可以將它用在生產環境下作為一種更新的機制存在。這時,你需要寫自己的管理程式碼以便整合HRM到你的app中。

有一些loaders已經具有了產生hot-updateable module的能力,比如style-loader就可以swap stylesheet.在這種情況下,你無需做任何事情。

需要什麼方可使用HMR呢?

一個module只有你"accpet",才能實現HMR updated。所以,你需要"module.hot.accept"存在於你的模組或模組的父親鏈中。比如,一個router或者一個subview將是一個實現accept的好地方。

如果你僅僅希望和webpack-dev-server整合使用HMR而不是自己來寫程式碼,你可以增加webpack/hot/dev-server作為一個entry point, 否則,你需要寫一些HMR management code來呼叫check和apply.

你需要在compiler中enable records以便在不同的程式間跟蹤module id.(watch mode和webpack-dev-server儲存這些records在記憶體中,所以你開發時無需)

你需要在compiler中enable HMR,以便webpack向bundle中加入HMR runtime.

為什麼HMR這麼cool?

它就像這對每個module實現了liveReload一樣,而不是針對頁面來做liveReload

你可以在生產環境中使用它;

updates也會尊重code splitting,只會下載你的app變更部分

你可以僅在應用的部分中應用,而不會影響其他的模組。

如果HMR disabled,那麼所有的HMR code都將被webpack清除掉(wrap it in "

if(module.hot)

中,以便能被webpack輕鬆剔除

")

Tutorial

To use hot code replacement with webpack you need four things:

  • records (--records-pathrecordsPath: ...)
  • globally enable hot code replacement (HotModuleReplacementPlugin)
  • hot replacement code in your code module.hot.accept
  • hot replacement management code in your code module.hot.checkmodule.hot.apply

A small testcase:

/* style.css */
body {
	background: red;
}
/* entry.js */
require("./style.css");
document.write("<input type='text' />");

That's enough to use hot code replacement with the dev-server.

npm install webpack webpack-dev-server -g
npm install webpack css-loader style-loader
webpack-dev-server ./entry --hot --inline --module-bind "css=style\!css"

The dev server provides in memory records, which is good for development.

The --hot switch enables hot code replacement.

This adds the HotModuleReplacementPlugin. Make sure to use either the --hot flag, or the HotModuleReplacementPlugin in your webpack.config.js, but never both at the same time as in that case, the HMR plugin will actually be added twice, breaking the setup.

There is special management code for the dev-server at webpack/hot/dev-server, which is automatically added by --inline. (You don't have to add it to your webpack.config.js)

The style-loader already includes hot replacement code.

If you visit http://localhost:8080/bundle you should see the page with a red background and a input box. Type some text into the input box and edit style.css to have another background color.

Voilà... The background updates but without full page refresh. Text and selection in the input box should stay.

Read more about how to write you own hot replacement (management) code: hot module replacement

Check the example-app for a demo without coding. (Note: It's a bit old, so don't look at the source code, because the HMR API changed a bit in between)

相關文章