搭建React?Native熱更新平臺(tái)的詳細(xì)過程
一,什么是熱更新
所謂熱更新,也叫做動(dòng)態(tài)更新,一種類似 Web 的更新方式。相對(duì)于 App 的發(fā)版更新而言,熱更新能及時(shí)的修復(fù)線上存在的問題,大幅的提升業(yè)務(wù)迭代效率。我們都知道,互聯(lián)網(wǎng)業(yè)務(wù)講究兵貴神速,如果業(yè)務(wù)能夠通過熱更新來快速發(fā)版和迭代,這就相當(dāng)于在產(chǎn)品和用戶之間搭建了一座能夠隨時(shí)通行的橋梁,代替了原來好幾周才能有一次迭代的問題。
那么,熱更新和發(fā)版更新有什么不同呢?為什么熱更新比發(fā)版更新快這么多呢?下面是這兩種更新方式的原理對(duì)比圖。
發(fā)版更新,指的是你把 React Native App,當(dāng)作 Android App 和 iOS App,按照 Android、iOS 上架流程,通過各自的應(yīng)用商店進(jìn)行更新。通常每個(gè) Native App 都會(huì)有一個(gè)自己的上架節(jié)奏,可能是兩周,也可能是 4 周。此外,從提交應(yīng)用商店到審核通過,也需要等上幾天時(shí)間。甚至,即便新版本上架了,用戶更新到最新版本也需要一個(gè)過程,可能需要一個(gè)月的時(shí)間,新版本才能覆蓋到 90% 的用戶。
所以說,如果你的 React Native App 選擇發(fā)版更新,就會(huì)受到發(fā)版節(jié)奏、審核耗時(shí)和版本覆蓋耗時(shí)的影響,這些都會(huì)導(dǎo)致業(yè)務(wù)迭代速度變慢。
不過,React Native 的熱更新就可以繞過應(yīng)用商店直接進(jìn)行更新。只要你的是集成熱更新功能的 React Native App,在應(yīng)用商店上架過一次之后,后續(xù)你的業(yè)務(wù)都可以走你自己的熱更新流程,再也不用依賴應(yīng)用商店發(fā)版。這樣你的業(yè)務(wù)就能隨時(shí)上線,隨時(shí)更新了。
二,熱更新方案
雖然官方?jīng)]有提供標(biāo)準(zhǔn)的熱更新方案,不過能夠支持常見熱更新方案有pushy、CodePush、Expo 和自研。
不過,由于國內(nèi)網(wǎng)絡(luò)環(huán)境的原因,訪問國外的云服務(wù)速度比較慢,所以我不太推薦直接使用 Code Push 和 Expo。Code push 是微軟 App Center 的服務(wù)之一,它底層用的是微軟自家的 Azure 云服務(wù);Expo 使用的是亞馬遜的 AWS 和 Google Cloud 云服務(wù)。
不過,大家也可以基于 Code Push 或 Expo 自行搭建熱更新服務(wù)。如果你嫌自己搭建太麻煩,你也可以看看 React Native 中文網(wǎng)提供的 Pushy 熱更新方案。它使用的是國內(nèi)的阿里云服務(wù),且有比前兩者更省流量的差量更新方案,應(yīng)該是國內(nèi)目前市面上唯一可以直接使用的開源熱更新方案了。
三,熱更新原理
說到自研熱更新平臺(tái),如果你沒有接觸過,可能會(huì)認(rèn)為很難,但事實(shí)上并非如此。其實(shí),自研熱更新平臺(tái)的難度主要體現(xiàn)在如何大規(guī)模應(yīng)用上,還有到達(dá)率上。對(duì)于規(guī)模小的應(yīng)用來說,搭建一個(gè)自研熱更新平臺(tái)本身并不是很難??偟膩碚f,一個(gè)自研熱更新平臺(tái),主要包括這兩個(gè)部分:
- 打包服務(wù):Bundle Server;
- 靜態(tài)資源服務(wù):Static Server。
所謂的打包服務(wù),是將 React Native 項(xiàng)目中的所有 JavaScript 代碼打包成一個(gè) Bundle 文件的服務(wù)。而所謂的靜態(tài)資源服務(wù),是將 Bundle 文件分發(fā)給客戶端的服務(wù);當(dāng)客戶端拿到 Bundle 代碼后,執(zhí)行 加載Bundle 文件就能渲染 React Native頁面了。
其實(shí),這和我們本地的npm start的流程是一樣的。即我們?cè)诒镜赝ㄟ^ npm start 啟動(dòng)的 Metro 服務(wù)時(shí),Metro 服務(wù)就同時(shí)具備了打包和靜態(tài)資源下發(fā)兩種功能,再配合框架默認(rèn)的代碼加載功能就完成了熱更新。
也就是說,找臺(tái)服務(wù)器把 React Native 代碼放上去,然后運(yùn)行 npm start 命令,然后在客戶端配置對(duì)應(yīng)的 ip 和端口號(hào),并把相關(guān)的調(diào)試開關(guān)給關(guān)了,接著訪問服務(wù)器就能把 React Native 頁面渲染出來。
當(dāng)然,這個(gè)最簡單的熱更新流程是不能跑在線上的,畢竟 npm start 的本意是用于調(diào)試的,它的首次加載耗時(shí)太長,扛不住高并發(fā),而且服務(wù)可用性也是問題。
四,CDN 熱更新方案
不過,使用npm start 的熱更新方案不太靠譜,那有沒有靠譜點(diǎn)的熱更新方案呢?有,那接下來我們看另一種比較簡單的熱更新方案:CDN 熱更新方案。
在 npm start 方案中,Metor Server 提供了代碼打包和 Bundle 下發(fā)的功能。而在 CDN 方案中,代碼打包是通過 react-native bundle 命令提供的,Bundle 下發(fā)的能力是通過 CDN 提供的。
下面是使用CDN熱更新方案的流程:
首先,通過 react-native bundle 命令把 JavaScript 代碼打包成一個(gè) Bundle文件,命令如下:
npx react-native bundle --entry-file index.tsx --dev false --minify false --bundle-output ./build/index.bundle --assets-dest ./build
通過上述命令打包出來的是 index.bundle 文件,本質(zhì)是一個(gè)可執(zhí)行的 JavaScript 文件。如果你使用的是 Hermes,那么還需要把 JavaScript 文件轉(zhuǎn)成相應(yīng)的字節(jié)碼文件。Hermes 提供了把 JavaScript 文件轉(zhuǎn)成字節(jié)碼文件的方案,你可以先按照官方文檔 搭建 Hermes 環(huán)境,然后執(zhí)行如下命令進(jìn)行轉(zhuǎn)換:
hermes -emit-binary -out ./build/index.hbc ./build/index.bundle
接下來,就是將包上傳到 CDN 上,國內(nèi)比較出名的有阿里云、騰訊云,或者使用公司內(nèi)部的提供的 OSS 和 CDN 服務(wù)也是可以的。
以阿里云為例,在第一次使用時(shí),你可以先將包文件上傳到 OSS,然后開啟 CDN 加速。 完成文件上傳 CDN 這一步后,你將會(huì)得到一個(gè) CDN 地址,我們可以使用該地址來訪問你的文件,例如:
https://static001.geekbang.org/resource/rn/index.bundle
拿到包地址后,熱更新最后一步是,在客戶端請(qǐng)求和加載該地址的 .bundle 文件或 .hbc 文件,這樣就完成熱更新的整個(gè)流程了。
如果需要執(zhí)行熱更新,我們只需要用新包把老包給覆蓋掉即可,當(dāng)客戶端在加載的時(shí)候,會(huì)自動(dòng)加載最新的包,從而完成熱更新。
五,純 CDN 方案的弊端
但是對(duì)于大流量業(yè)務(wù),我并不推薦你用純 CDN 方案,為什么呢?因?yàn)榧?CDN 方案,會(huì)存在幾分鐘的更新延遲的問題。在小流量業(yè)務(wù)中,這種幾分鐘的更新延遲不是什么問題,但是對(duì)于大流量業(yè)務(wù)來說,如果線上出現(xiàn)了一個(gè)重大 BUG,需要等幾分鐘才能完全回滾,那么對(duì)用戶或者收入的影響會(huì)很大。
下圖演示了CDN 方案的時(shí)序圖:
上述時(shí)序圖中,涉及 React Native App、CDN 邊緣節(jié)點(diǎn)和 OSS 源站,以及 index.bundle 包文件的兩個(gè)版本。舊版本包是綠色的,新版包是藍(lán)色的。將舊版本更新為新版本的本質(zhì)是:刪除 CDN 中緩存的舊版資源,當(dāng) CDN 中沒有緩存了,這時(shí)來自用戶的請(qǐng)求才不會(huì)命中 CDN 中的緩存,而是到 OSS 上拉取最新的資源,返回給 React Native App。
然而,我們都知道 CDN 指的并不是某一臺(tái)具體的機(jī)器,它指的是上千個(gè)分布在全國各地的節(jié)點(diǎn)網(wǎng)絡(luò)。當(dāng)我們使用 CDN 的刷新能力時(shí),實(shí)際上是刪除上千個(gè)節(jié)點(diǎn)中的緩存。一位負(fù)責(zé) CDN 的同學(xué)告訴我,要把這上千個(gè)節(jié)點(diǎn)的緩存都刪除干凈的時(shí)間,最長可能需要個(gè) 5 分鐘吧,而且還不敢保證 5 分鐘的時(shí)效性。因此,在這 5 分鐘內(nèi),會(huì)存在三種情況:情況一,命中老版緩存;情況二,未命中緩存重新拉取新版資源;情況三,命中新版緩存;
六,線上方案
可以看到,不管是npm start 還是CDN方案,都存在一定的缺陷,那有什么比較完整的方案嗎。其實(shí)仔細(xì)思考一下,只需要在CDN 方案解決延遲問題即可達(dá)到線上運(yùn)行的需求,那如果改進(jìn)CDN 方案呢。
解決方案就是多發(fā)一次版本請(qǐng)求:既然上千個(gè)節(jié)點(diǎn) CDN 更新有延遲,那么就自己搭建一個(gè)版本服務(wù),資源依舊上傳 CDN,然后用我們自己的版本服務(wù)來控制更新。此時(shí),熱更新的時(shí)序圖變成如下:
增加一個(gè)版本服務(wù)后,可以看到整體流程發(fā)生了一些變化。純 CDN 方案的更新方式采用的是覆蓋更新,版本服務(wù)方案會(huì)提前告知更新,從而保持代碼的最新。那接下來,我們梳理下這種方案的完整流程。
1,上傳 Bundle 到源站,也就是 OSS
先在將本地打包好 Bundle 文件,并將文件命名為 “MD5”.bundle 上傳到 OSS 源站。此時(shí)理論上,只要 Bundle 內(nèi)容發(fā)生了變化,那么生成 MD5 值就是不一樣的,用 MD5 作為文件的命名能保證文件的唯一性。
2,正式發(fā)版上線
當(dāng)需要正式上線時(shí),我們只需要上傳前面生成的bundle包,然后將服務(wù)最新的線上 Bundle 的名字修改成最新的,這時(shí)版本服務(wù)會(huì)在內(nèi)部通過 mysql 或 redis 把線上最新文件名給記錄下來。
3,React Native App 發(fā)起版本請(qǐng)求
由于只有一個(gè)版本服務(wù),不會(huì)存在 CDN 上千個(gè)節(jié)點(diǎn)在某一時(shí)刻不同步的問題,版本服務(wù)會(huì)直接把最新的 Bundle 名字告訴 React Native 應(yīng)用。
4,React Native 發(fā)起 CDN 資源請(qǐng)求
資源請(qǐng)求會(huì)先詢問某個(gè) CDN 的邊緣節(jié)點(diǎn),如果該邊緣節(jié)點(diǎn)沒有緩存,則會(huì)去源站拉??;如果該邊緣節(jié)點(diǎn)有緩存,則直接返回。
七、整體方案梳理
事實(shí)上,無論是 CDN 熱更新方案,還是官方的版本方案,它們都不是完整的自研熱更新方案,只是提供了一種解決基礎(chǔ)資源更新的問題。因?yàn)樽匝袩岣缕脚_(tái)的核心難點(diǎn)在于,它需要你對(duì)前端、Node.js、React Native,甚至 Java 都有所了解,特別是在熱更新服務(wù)這一塊要特別的擅長。涉及的難點(diǎn)包括:
- 如何支持多人的并行打包;
- 如何支持多人的并行測(cè)試;
- 如何保障 CDN 資源更新成功;
- 如何保障版本服務(wù)的高并發(fā)、高可用。
依據(jù)多年的經(jīng)驗(yàn),我們?cè)O(shè)計(jì)了一張熱更新平臺(tái)的全貌圖。
可以看到,熱更新平臺(tái)整體上包括以下幾個(gè)部分:
- 熱更新平臺(tái)后臺(tái)服務(wù):一般兩臺(tái)機(jī)器就行,它提供打包、測(cè)試、上線、權(quán)限管理和相應(yīng)的前端頁面展示等能力。
- MySQL、Redis持久能力:MySQL 提供持久化存儲(chǔ)能力,Redis 用于緩存用來抗高并發(fā),這里推薦用成熟的相關(guān)服務(wù)就行,不要自行搭建。
- 打包集群:獨(dú)立集群,至少兩臺(tái)機(jī)器,具體看情況而定,用于支持多人的并行打包。把它獨(dú)立出來的原因是,打包是非常消耗 CPU、內(nèi)存資源的任務(wù),和其他服務(wù)混在一起容易導(dǎo)致其他服務(wù)卡頓。
- 版本服務(wù)集群:獨(dú)立集群,用于提供能支持高并發(fā)的版本服務(wù)。如果你有 node.js 抗高并發(fā)的經(jīng)驗(yàn),可以使用 node.js 來做,或者直接找 Java 同學(xué)開發(fā)。
接下來,就是搭建熱更新平臺(tái),我推薦的技術(shù)棧有:
- NestJS :它是一個(gè) Node.js 框架 ,它能提供高效、可靠和可擴(kuò)展的后端服務(wù)。
- Bull:它一個(gè)任務(wù)隊(duì)列庫,它能幫你解決熱更新平臺(tái)后臺(tái)集群如何向打包集群發(fā)布打包任務(wù)的難題,讓你的平臺(tái)可以支持多人并行打包。
八,總結(jié)
要實(shí)現(xiàn) React Native 熱更新功能,有四種思路 Code Push、Pushy、Expo 和自研。如果你選擇自研 React Native 熱更新功能,這就需要 React Native 熱更新平臺(tái)和 Native 熱更新模塊的緊密配合。
自研 React Native 熱更新功能,并不一定需要搭建一個(gè)熱更新平臺(tái),你也可以采用純 CDN 方案,比如你可以利用阿里云提供的靜態(tài)資源部署能力和對(duì)應(yīng)的 CDN 服務(wù)。如果決心要完全自研一個(gè)熱更新平臺(tái),那么你最好找一個(gè)對(duì)后端技術(shù)比較了解同學(xué)一起來設(shè)計(jì),我也為你提供了一個(gè)熱更新平臺(tái)的全貌圖,你可以用它來幫你進(jìn)行整體設(shè)計(jì)。
到此這篇關(guān)于搭建React Native熱更新平臺(tái)的文章就介紹到這了,更多相關(guān)React Native熱更新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺析history 和 react-router 的實(shí)現(xiàn)原理
react-router 版本更新非???但是它的底層實(shí)現(xiàn)原理確是萬變不離其中,在本文中會(huì)從前端路由出發(fā)到 react-router 原理總結(jié)與分享,本文對(duì)history 和 react-router實(shí)現(xiàn)原理講解的非常詳細(xì),需要的朋友跟隨小編一起看看吧2023-08-08React項(xiàng)目中應(yīng)用TypeScript的實(shí)現(xiàn)
TypeScript通常都會(huì)依賴于框架,例如和vue、react 這些框架結(jié)合,本文就主要介紹了React項(xiàng)目中應(yīng)用TypeScript的實(shí)現(xiàn),分享給大家,具體如下:2021-09-09詳解React-Native解決鍵盤遮擋問題(Keyboard遮擋問題)
本篇文章主要介紹了React-Native解決鍵盤遮擋問題(Keyboard遮擋問題),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07React?SSR架構(gòu)Streaming?Render與Selective?Hydration解析
這篇文章主要為大家介紹了React?SSR架構(gòu)Streaming?Render與Selective?Hydration解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03基于Webpack5 Module Federation的業(yè)務(wù)解耦實(shí)踐示例
這篇文章主要為大家介紹了基于Webpack5 Module Federation的業(yè)務(wù)解耦實(shí)踐示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12React中的權(quán)限組件設(shè)計(jì)問題小結(jié)
這篇文章主要介紹了React中的權(quán)限組件設(shè)計(jì),整個(gè)過程也是遇到了很多問題,本文主要來做一下此次改造工作的總結(jié),對(duì)React權(quán)限組件相關(guān)知識(shí)感興趣的朋友一起看看吧2022-07-07