webpack的Hot Module Replacement運行機制
使用webpack打包,難免會使用Hot Module Replacement功能,該功能能夠實現修改、添加或刪除前端頁面中的模塊代碼,而且是在頁面不刷新的前提下。它究竟是怎麼運作的呢?本文主要從調試工具、配置文件、官方文檔三個方面進行解析。
調試工具首先從chrome的調試工具network中看看,代碼改變的時候,頁面與後端之間發生了什麼?
頁面初始載入我們看到除了載入頁面所依賴的文件外,多了一個連接,這是一個Server-sent Events,相關的介紹可以參考這篇文章,而且每隔一段時間都會向發送一次數據。數據內容主要是
action:sync操作;
hash:f397e485c539fd7a10fb,是bundle的hash,因為和產出文件collections.f397e485c539fd7a10fbjs的內容hash值相同;
modules:產出bundle中的module id和對應的文件地址。
修改代碼然後修改一處代碼,webpack自動編譯後,發現network中發生了幾處變化,首先是客戶端收到後端發出的事件
action:built操作,通知瀏覽器webpack重新發起了編譯;
hash:最新產出bundle的內容hash值為debc36315df6764f157c;
modules:bundle中的模塊id和對應模塊的文件地址。
另外前端對後端發起了兩個請求,請求了f397e485c539fd7a10fb.hot-update.json和0.f397e485c539fd7a10fb.hot-update.js兩個文件,文件的hash值正好是未發生修改之前後端發送前端的bundle hash值。
我們查看一下兩個文件的內容。
json文件的內容:
h:debc36315df6764f157c,bundle內容的最新hash值;
c:"0": true, 表示bundle id為0的文件被修改了;
js文件的內容:
內容是一個函數,類似jsonp的返回形式,也就是頁面收到請求後執行了webpackHotUpdate函數,對bundle id為0的文件中的moudle id為50的模塊進行修改。
跟進到這裡,我們可以推測出這個交互過程:
(0)webpack首次編譯時將如何更新更新模塊(update-method)和接收後端推動事件(event-source)的代碼打包到bundle之中;
(1)webpack進入watch 模式,在項目代碼發生變化的時候重新編譯;
(2)將編譯產出存放在dev-server,此處的編譯只針對變動的模塊,產出應該包含上文中提到的oldbundlehash.hot-update.json和oldbundlehash.hot-update.js文件;
(3)dev-server中使用hot-middleware中間件向前端發送built事件;
(4)前端收到通知後,向後端請求最新的變動文件,請求到的js文件通過script標籤載入後執行,其實就是執行已經預埋到bundle中的函數(update-method),從而修改bundle文件。
配置文件接下來我們從項目的配置文件來驗證一下,配置文件主要參考vue-cli中的webapck項目。
webpack.dev.conf.js涉及到Hot Module Replacement的地方主要有兩處:
entry的配置:在每個入口bundle開頭引入了event-source,即在頁面中接收後端發送的事件
/*********./build/webpack.dev.conf.js********/
// 將event-source相關代碼,添加到每個入口chunk中,作為HRM Runtime的一部分。
// 後端相應的配置見dev-server的hotMiddleware部分
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = ["./build/dev-client"].concat(baseWebpackConfig.entry[name])
})
/*********./build/dev-client.js********/ // Event-Source對象用於接收伺服器端推送事件 // eventsource-polyfill用於擴展Event-Source對象在IE瀏覽器下的兼容性 require("eventsource-polyfill") var hotClient = require("webpack-hot-middleware/client?noInfo=true&reload=true") // 主要用於接受後端hotMiddleware的通知,執行reload操作 hotClient.subscribe(function (event) { if (event.action === "reload") { window.location.reload } })
插件的配置:引入HotModuleReplacementPlugin插件,將update-method的代碼打入bundle
plugins: [
...
// HMR插件將HMR Runtime代碼嵌入到bundle中,能夠操作APP代碼,完成代碼替換
new webpack.HotModuleReplacementPlugin,
// 報錯提示插件:報錯不阻塞,但是編譯後給出提示
new webpack.NoEmitOnErrorsPlugin,
new FriendlyErrorsPlugin
]
dev-server.js
涉及到Hot Module Replacement的地方主要有兩處:
將compiler掛載在devMiddleware上:對編譯產出提供靜態文件服務
// 將compiler掛載在dev-server上,監聽本地代碼變化,變化則啟動編譯並將編譯後的文件暫存到內存中
var devMiddleware = require("webpack-dev-middleware")(compiler, {
publicPath: config.dev.assetsPublicPath === "./" ? "" : config.dev.assetsPublicPath,
quiet: true
})
將compiler掛載在hotMiddleware上:通知前端event-source對象發生了rebuilt
// 編譯後發送通知到HRM Runtime,HRM Runtime收到update通知後,下載更新的模塊,通知APP更新,APP收到通知,然後要求HRM Runtime執行模塊替換
var hotMiddleware = require("webpack-hot-middleware")(compiler, {
log: => {}
})
由配置文件可以基本驗證之前通過network debug得到的推論,接下來去看一下官方文檔驗證一下。
官方文檔官方文檔先是總體介紹了一下 Hot Module Replacement的基本原理,然後將原理中涉及到幾個知識點進行了介紹。
基本原理webapck在編譯的過程中,將HMR Runtime嵌入到bundle中;編譯結束後,webpack對項目代碼文件進行監視,發現文件變動重新編譯變動的模塊,同時通知HMR Runtime,然後HMR Runtime載入變動的模塊文件,嘗試執行熱更新操作。更新的邏輯是:先檢查模塊是否能支持accept方法,不支持的話,則冒泡查找模塊樹的父節點,直到入口模塊,accept方法也就是模塊hot-replace的handler。
知識點(1)compiler
這裡的compiler也就是指webapck,主要提供update的信息,也就是update menifest(json文件格式)和update chunks(js文件格式);
(2)app
app也就是指前端頁面,app中的代碼主要調用HMR Runtime下載最新的模塊代碼,然後調用HMR Runtime執行update操作;
(3)HMR Runtime
HMR Runtime是webapck內嵌到前端頁面的代碼,主要提供來能給個職能check和apply。check用來下載最新模塊代碼,runtime能夠接收後端發送的事件和發送請求;apply用於更新模塊,主要將要更新的模塊打上tag,然後調用模塊的(也有可能是父模塊)的更新handler執行更新。
(4)module
HRM是一個可插拔的工具,只能影響包含HMR code的模塊。通常情況下,沒有必要為每個模塊寫入HMR code,更新的時候會進行冒泡檢查HMR code的是否存在。
根據官方文檔的介紹,基本和我們的推論吻合,區別在於官方文檔引入了HMR Runtime的概念,這個可以看作是推論中的event-source和update-method的結合體。
現在大家應該清楚了 webpack的Hot Module Replacement的基本原理了,官方文檔中提到了如何根據最新的模塊替換舊模塊的方法,這個會放到下一篇文章中進行介紹。
※Java 泛型在實際開發中的應用
※把編譯安裝的httpd 實現服務腳本,通過service和chkconfig 進行管理
※EasyNetQ之多態發布和訂閱
TAG:科技優家 |
※Surface Phone蹤跡再現:運行Andromeda系統
※運行Android Pie的Nokia 3.1Plus現身Geekbench 或即將得到更新
※LinkedIn 開源 TonY:在 Hadoop 上運行 TensorFlow 的框架
※LinkedIn開源TonY:在Hadoop上運行TensorFlow的框架
※在 Emacs 的 dired 和 tramp 中非同步運行 rsync
※Motorola One/One Power發布:運行Android One
※Google 的 Fuchsia OS 將能運行 Android 應用
※微軟發布Windows Defender System Guard運行時認證技術
※微軟結束Xbox Backward 兼容計劃 但Scarlett將能夠運行Xbox One遊戲
※Pixel 3a/XL現身Google Play:運行Android 9 Pie
※怎樣在 Kubernetes 上運行 PostgreSQL
※Chromebook將能夠運行Linux應用 Pixelbook搶鮮
※用英偉達Jetson Nano運行PyTorch&Fast.ai
※在mac上運行fiddler
※[圖]Chrome OS新功能:像原生應用運行Progressive Web Apps
※如何在 Kubernetes 環境中運行 Spark 集群
※通過 Docker 實現在 Linux 容器中運行 Microsoft SQL Server 資料庫
※Android端運行Tensorflow的demo去分類自己的數據集
※在 Kubernetes 上運行一個 Python 應用程序
※Google 的新開源系統 Fuchsia OS 將支持 Android 應用運行