摘要:原理踩坑起因最近在做框架的熱更新,記錄一下的原理和小坑。文件系統(tǒng)接收更改并通知。運(yùn)行時(shí)通過(guò)請(qǐng)求這些更新。類(lèi)似的問(wèn)題還有很多,事件綁定手動(dòng)插入并且沒(méi)有銷(xiāo)毀的定時(shí)器等,記得把這些副作用一起干掉。參考官方文檔原理分析與實(shí)現(xiàn)
webpack hot-module-replacement 原理&踩坑 起因
最近在做san框架的熱更新,記錄一下webpack HMR的原理和小坑。
什么是HMR?熱更新是webpack的一個(gè)特性,通過(guò)無(wú)刷新實(shí)現(xiàn)代碼更新。
在HMR之前,大多數(shù)開(kāi)發(fā)體驗(yàn)是live reload,保存后自動(dòng)刷新瀏覽器,已經(jīng)是比刀耕火種的年代強(qiáng)很多了,但是自從某天在油管上看到dan神的redux時(shí)間穿梭,瞬間被驚艷到(當(dāng)然,HMR已經(jīng)是這之前很久就出現(xiàn)了)。
HMR大幅提高了開(kāi)發(fā)體驗(yàn),只更新變更內(nèi)容,調(diào)整樣式迅速,避免了大部分的網(wǎng)絡(luò)請(qǐng)求、瀏覽器重新渲染、app解析編譯顯示,讓我們來(lái)看下他是如何做到的。
hmr基本法 概念compile: webpack的核心。js編譯、拆包。
hmr-server: 建立連接并完成模塊熱更新的推送。
bundle-server: 靜態(tài)服務(wù)器。
bundle.js: client端。
hmr-runtime: 注入到bundle.js中的代碼。
熱更新開(kāi)啟后,當(dāng)webpack打包時(shí),會(huì)向client端注入一段HMR runtime代碼,同時(shí)server端(webpack-dev-server或是webpack-hot-middware)啟動(dòng)了一個(gè)HMR服務(wù)器,它通過(guò)websocket和注入的runtime進(jìn)行通信。
當(dāng)webpack檢測(cè)到文件修改后,會(huì)重新構(gòu)建,并通過(guò)ws向client端發(fā)送更新消息,瀏覽器通過(guò)jsonp拉取更新過(guò)的模塊,回調(diào)觸發(fā)模塊熱更新邏輯。
1.修改了一個(gè)或多個(gè)文件。
2.文件系統(tǒng)接收更改并通知Webpack。
3.Webpack重新編譯構(gòu)建一個(gè)或多個(gè)模塊,并通知HMR服務(wù)器進(jìn)行了更新。
4.HMR Server使用websockets通知HMR Runtime需要更新。HMR運(yùn)行時(shí)通過(guò)HTTP請(qǐng)求這些更新(jsonp)。
5.HMR運(yùn)行時(shí)替換更新中的模塊,如果確定這些模塊無(wú)法更新,則觸發(fā)整個(gè)頁(yè)面刷新(這是個(gè)大坑。。)。
// webpack/hot/dev-server if(module.hot) { var lastHash; //__webpack_hash__是每次編譯的hash值是全局的 //Only available with the HotModuleReplacementPlugin or the ExtendedAPIPlugin var upToDate = function upToDate() { return lastHash.indexOf(__webpack_hash__) >= 0; }; var check = function check() { // check([autoApply], callback: (err: Error, outdatedModules: Module[]) => void // If autoApply is truthy the callback will be called with all modules that were disposed. apply() is automatically called with autoApply as options parameter.(傳入哪些代碼已經(jīng)被更新的模塊) //If autoApply is not set the callback will be called with all modules that will be disposed on apply(). (不是true那么傳入的是哪些需要被apply處理的模塊) module.hot.check(true).then(function(updatedModules) { //檢查所有要更新的模塊,如果沒(méi)有模塊要更新那么回調(diào)函數(shù)就是null if(!updatedModules) { console.warn("[HMR] Cannot find update. Need to do a full reload!"); console.warn("[HMR] (Probably because of restarting the webpack-dev-server)"); window.location.reload(); return; } //如果還有更新 if(!upToDate()) { check(); } require("./log-apply-result")(updatedModules, updatedModules); //已經(jīng)被更新的模塊都是updatedModules if(upToDate()) { console.log("[HMR] App is up to date."); } }).catch(function(err) { var status = module.hot.status(); //如果報(bào)錯(cuò)直接全局reload if(["abort", "fail"].indexOf(status) >= 0) { console.warn("[HMR] Cannot apply update. Need to do a full reload!"); console.warn("[HMR] " + err.stack || err.message); window.location.reload(); } else { console.warn("[HMR] Update failed: " + err.stack || err.message); } }); }; var hotEmitter = require("./emitter"); //獲取MyEmitter對(duì)象 hotEmitter.on("webpackHotUpdate", function(currentHash) { lastHash = currentHash; if(!upToDate() && module.hot.status() === "idle") { //調(diào)用module.hot.status方法獲取狀態(tài) console.log("[HMR] Checking for updates on the server..."); check(); } }); console.log("[HMR] Waiting for update signal from WDS..."); } else { throw new Error("[HMR] Hot Module Replacement is disabled."); }
正常情況下,hmr只會(huì)更新模塊,不會(huì)觸發(fā)頁(yè)面刷新。
但是當(dāng)bundle.js中的代碼拋出異常時(shí),如果開(kāi)發(fā)者沒(méi)有手動(dòng)接收并處理,這個(gè)錯(cuò)誤會(huì)冒泡到webpack-hmr-runtime中。
runtime接收error后會(huì)console.log一些信息并立即刷新,通常情況下是沒(méi)辦法看到那些log的,因?yàn)樘炝恕?/p>
// vue-hot-reload-api.js // 不得不說(shuō),這個(gè)一開(kāi)始確實(shí)沒(méi)搞懂是為啥要包一層 // 自己實(shí)現(xiàn)的時(shí)候才知道,當(dāng)有error彈出時(shí) // 如果不手動(dòng)這樣接住error,webpack會(huì)接到然后立即location.reload() // 根本來(lái)不及看reload之前給出的提示 // 所以要手動(dòng)處理下error function tryWrap (fn) { return function (id, arg) { try { fn(id, arg) } catch (e) { console.error(e) console.warn("Something went wrong during Vue component hot-reload. Full reload required.") } } }
所以開(kāi)發(fā)者需要自定義一個(gè)類(lèi)似的高階函數(shù)手動(dòng)處理下錯(cuò)誤,防止看不到錯(cuò)誤信息而刷新。
副作用模塊的熱更新是好事,但是老模塊仍然有可能在client端留下了痕跡。試想一個(gè)組件被熱更新后,如果不處理之前的組件,那么新老兩個(gè)組件都會(huì)在瀏覽器中出現(xiàn)。
所以別忘了在module.hot.accept中清除掉舊的組件。
類(lèi)似的問(wèn)題還有很多,事件綁定、手動(dòng)插入并且沒(méi)有銷(xiāo)毀的dom、定時(shí)器等,記得把這些副作用一起干掉。
如果做不到的話,老老實(shí)實(shí)刷新你的瀏覽器吧。
參考webpack官方文檔
?Understanding Webpack HMR
webpack-dev-server原理分析與HMR實(shí)現(xiàn)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84825.html
摘要:踩坑入門(mén)系列一二添加三目錄重構(gòu)再談路由陸續(xù)更新個(gè)人對(duì)于腳手架的有一種執(zhí)念,如果搭建出來(lái)就是一個(gè)首頁(yè)標(biāo)簽跳轉(zhuǎn),實(shí)在不是我這個(gè)處女座的風(fēng)格,因此第二步我就想引用框架,相信很多使用的開(kāi)發(fā)者用的也都是這個(gè)框架吧。 Next.js踩坑入門(mén)系列 (一) Hello Next.js (二) 添加Antd && CSS (三) 目錄重構(gòu)&&再談路由 陸續(xù)更新... 個(gè)人對(duì)于腳手架的UI有一種執(zhí)...
摘要:構(gòu)建構(gòu)建就是把源代碼轉(zhuǎn)換成發(fā)布到線上的可執(zhí)行代碼,包括如下內(nèi)容。自動(dòng)刷新監(jiān)聽(tīng)本地源代碼的變化,自動(dòng)重新構(gòu)建刷新瀏覽器。自動(dòng)發(fā)布更新完代碼后,自動(dòng)構(gòu)建出線上發(fā)布代碼并傳輸給發(fā)布系統(tǒng)。將文件放入到項(xiàng)目中,在中新建一個(gè)放字體圖標(biāo)的文件夾。 項(xiàng)目地址 github.com/wudiufo/Web… 知識(shí)點(diǎn)概覽: Loader,HMR ,Create React App, Caching, Plug...
摘要:結(jié)論得到了開(kāi)發(fā)者社區(qū)的廣泛認(rèn)可,盡管它的安裝過(guò)程非常艱難,之所以受到歡迎的原因很大程度取決于它提供的靈活性,以及良好的谷歌背景,而有一個(gè)小型的社區(qū),增長(zhǎng)略微緩慢。 數(shù)人云之前分享了《聊聊調(diào)度框架,K8S、Mesos、Swarm 一個(gè)都不能少》那么你是否仍在Docker和Kubernetes選擇上陷入了困擾?所以不要擔(dān)心,因?yàn)檫@也是很多人的苦惱,這兩者都是非常優(yōu)秀的容器服務(wù),至于那種更好...
摘要:源碼解析起因最近在搞框架的熱加載方案,自然是少不了向成熟的框架學(xué)習(xí)偷窺。這將銷(xiāo)毀并重建整個(gè)組件包括子組件。通過(guò)使用說(shuō)明可以看出,暴露的接口還是很清晰的,下面來(lái)看下具體源碼實(shí)現(xiàn)。 Vue-hot-reload-api 源碼解析 起因 最近在搞san框架的熱加載方案,自然是少不了向成熟的框架學(xué)習(xí)(偷窺ing)。熱加載方案基本也只是主流框架在做,且做的比較成熟,大部分應(yīng)用開(kāi)發(fā)者并不會(huì)接觸到這...
閱讀 2331·2021-11-17 09:33
閱讀 858·2021-10-13 09:40
閱讀 586·2019-08-30 15:54
閱讀 789·2019-08-29 15:38
閱讀 2424·2019-08-28 18:15
閱讀 2487·2019-08-26 13:38
閱讀 1853·2019-08-26 13:36
閱讀 2140·2019-08-26 11:36