React跨端動(dòng)態(tài)化之從JS引擎到RN落地詳解
一 為什么跨端動(dòng)態(tài)化迫在眉睫
目前很多互聯(lián)網(wǎng)大廠(chǎng)的移動(dòng)端開(kāi)發(fā)都在朝著跨端動(dòng)態(tài)化方向發(fā)展。由于快速迭代開(kāi)發(fā)或者對(duì)原生包體積要求嚴(yán)格,及其對(duì)資源成本的把控,實(shí)現(xiàn)跨端動(dòng)態(tài)化迫在眉睫。我們先來(lái)看看 Native 原生開(kāi)發(fā)的一些不足之處:
- 1 原生開(kāi)發(fā)周期時(shí)間長(zhǎng),審核周期長(zhǎng),會(huì)影響到需求發(fā)布和迭代效率,有些場(chǎng)景下會(huì)更加棘手,比如修復(fù)線(xiàn)上緊急 bug ,或者是頻繁迭代一些開(kāi)發(fā)需求。
- 2 目前移動(dòng)端主要的平臺(tái)就是 Android 和 iOS,如果一款前端應(yīng)用想要同時(shí)運(yùn)行在兩個(gè)平臺(tái)的話(huà),采用 Native 就需要雙端各自開(kāi)發(fā)一遍,這樣無(wú)疑浪費(fèi)了資源和提高了維護(hù)成本。
- 3 Native 開(kāi)發(fā)代碼要打包在客戶(hù)端包中,這樣增加了包的體積,用戶(hù)下載的時(shí)候,會(huì)下載更多的資源,輕量級(jí)的包會(huì)提高運(yùn)營(yíng)效率,而且 Android 和 iOS 應(yīng)用平臺(tái)也對(duì)包體積嚴(yán)格把控。
說(shuō)到跨端化方案,首先想到的就是 React Native,為什么這么說(shuō)呢? 我們往下看。
二 首當(dāng)其沖的為什么是 React Native
React 在跨端領(lǐng)域也有一席之地,功勞來(lái)源跨端方案 React Native,簡(jiǎn)稱(chēng) RN ,RN 是目前主流的動(dòng)態(tài)化方案之一,是 Facebook 在 2015 年開(kāi)源的 JS 框架 React 在原生移動(dòng)應(yīng)用平臺(tái)的跨平臺(tái)技術(shù),支持安卓和 iOS 平臺(tái)。
RN 的受歡迎并不僅僅是支持安卓和 iOS 平臺(tái),還有一個(gè)重要的因素就是動(dòng)態(tài)化,那么這種動(dòng)態(tài)化相比于原生客戶(hù)端有什么優(yōu)點(diǎn)呢?
RN 對(duì)于原生開(kāi)發(fā)有著明顯的優(yōu)點(diǎn):
- 1 RN 是采用運(yùn)行 React 的 JS 作為開(kāi)發(fā)平臺(tái),這樣可以讓 web 開(kāi)發(fā)者也能夠參與到 Native 開(kāi)發(fā)中來(lái),還有就是 RN 讓一套代碼可以運(yùn)行在兩端,大大減少了開(kāi)發(fā)和維護(hù)成本。
- 2 RN 是采用原生渲染的,性能和體驗(yàn)僅次于 Native 開(kāi)發(fā)。
- 3 還有一點(diǎn)也是最重要的,就是 RN 是動(dòng)態(tài)化的方案,也就是 RN 打出來(lái)的應(yīng)用包,并不是和 Native 包綁定在一起發(fā)布的,而是在運(yùn)行 Native 的時(shí)候拉下 RN 的包,這樣一是減少了 Native 包體積,二是 RN 包可以隨時(shí)發(fā)布,提高了迭代效率,也讓一些線(xiàn)上問(wèn)題能夠快速解決。
近兩年,也有一些興起的跨端技術(shù)方案,比如 Flutter,阿里巴巴開(kāi)源的 Rax 等,相比這些動(dòng)態(tài)化方法,RN 也有一定優(yōu)勢(shì):
- 1 生態(tài)成熟,技術(shù)社區(qū)活躍,采用 React 語(yǔ)法,學(xué)習(xí)成本低。
- 2 目前業(yè)界已經(jīng)出現(xiàn)了很多成熟方案,比如京東的 JDReact 和美團(tuán)的 MRN 等。
RN 是基于 React 框架開(kāi)發(fā)的原生應(yīng)用,React 憑借著 JSX 語(yǔ)法讓使用者結(jié)合多種設(shè)計(jì)模式使開(kāi)發(fā)變得非常靈活,React 是 JavaScript (簡(jiǎn)稱(chēng) JS ) 框架,那么如果想要運(yùn)行 RN ,那么就需要運(yùn)行 JS ,在我們的影響中,JS 作為腳本語(yǔ)言運(yùn)行到瀏覽器端,或者運(yùn)行在 Node.js 中,那么如今卻能夠作為跨端方案運(yùn)行到 Native 應(yīng)用中,這是為什么呢?
原來(lái)能夠讓 JS 運(yùn)行到 Native 中的法寶就是 JS 引擎,最常見(jiàn)的 JS 引擎就是 v8 ,v8 使用在 Chrome 瀏覽器和 Node.js 中,構(gòu)建了 JS 運(yùn)行時(shí),能夠執(zhí)行 JS 腳本。那么接下來(lái)我們就來(lái)看一下 RN 中的 JS 引擎。
三 JS 引擎讓跨端動(dòng)態(tài)化成為可能
V8 引擎簡(jiǎn)介
計(jì)算機(jī)本身并不能讀懂編程語(yǔ)言,計(jì)算機(jī)只能讀懂二機(jī)制文件,但是為了能夠讓編程語(yǔ)法能夠讓計(jì)算機(jī)讀懂,就必須編譯成二機(jī)制文件,這就是編譯語(yǔ)言,比如 JAVA GO 等都是編譯型的語(yǔ)言,編譯型語(yǔ)法在編譯成二機(jī)制文件后,會(huì)保存二機(jī)制文件,在運(yùn)行時(shí)候,可以直接運(yùn)行二機(jī)制文件,不需要重復(fù)編譯。
還有一類(lèi)語(yǔ)言,不需要編譯成文件,而是需要通過(guò)解釋器對(duì)語(yǔ)言進(jìn)行動(dòng)態(tài)解釋和執(zhí)行,這類(lèi)語(yǔ)言就是解釋型語(yǔ)言,比如 Python,JS 等。如下圖就是兩種類(lèi)型的語(yǔ)言執(zhí)行過(guò)程:

編譯型語(yǔ)言啟動(dòng)需要編譯成二進(jìn)制文件,所以啟動(dòng)速度會(huì)比很慢,但是執(zhí)行的時(shí)候是直接使用編譯好的二進(jìn)制文件,所以執(zhí)行速度會(huì)快一些。
但是相比解釋型語(yǔ)言,啟動(dòng)會(huì)很快,但是執(zhí)行時(shí)候,需要通過(guò)解釋器解析語(yǔ)法樹(shù),變成中間代碼,執(zhí)行字節(jié)碼,這樣就浪費(fèi)了時(shí)間,使得執(zhí)行速度會(huì)變慢。
由于 JS 是解釋型語(yǔ)言,它的執(zhí)行需要宿主環(huán)境提供,轉(zhuǎn)成語(yǔ)法樹(shù) ,并且讀懂語(yǔ)法樹(shù),轉(zhuǎn)成字節(jié)碼并執(zhí)行的能力,v8 引擎的工作就需要有這些能力:
Parser:將 JS 源碼轉(zhuǎn)換成抽象語(yǔ)法樹(shù),什么是抽象語(yǔ)法?在計(jì)算機(jī)科學(xué)中,抽象語(yǔ)法樹(shù)(abstract syntax tree 或者縮寫(xiě)為 AST),或者語(yǔ)法樹(shù)(syntax tree),是源代碼的抽象語(yǔ)法結(jié)構(gòu)的樹(shù)狀表現(xiàn)形式,這里特指編程語(yǔ)言的源代碼。
Lgniton:interpreter 解釋器,負(fù)責(zé)將 AST 轉(zhuǎn)換成指令字節(jié)碼,解釋執(zhí)行指令字節(jié)碼(ByteCode),解釋器執(zhí)行的時(shí)候主要有四個(gè)模塊:內(nèi)存中的字節(jié)碼,寄存器,棧和堆。
TurboFan:compiler 編譯器,通過(guò) Lgniton 收集的信息,將指令字節(jié)碼轉(zhuǎn)換成優(yōu)化匯編代碼。
Orinoco:garbage collector 簡(jiǎn)稱(chēng) GC,垃圾回收模塊,負(fù)責(zé)將程序不需要的內(nèi)存空間回收,提升引擎性能。
如上還有一個(gè)問(wèn)題就是如果每一次都通過(guò) TurboFan 將指令字節(jié)碼轉(zhuǎn)換成匯編代碼,那么這樣十分浪費(fèi)性能。在 v8 出現(xiàn)之前,所有的 JS 虛擬機(jī)所采用的都是解釋執(zhí)行的方式,這是 JS 執(zhí)行速度過(guò)慢的主要原因之一。
而 v8 率先引入了即時(shí)編譯(JIT)的雙輪驅(qū)動(dòng)的設(shè)計(jì)(混合使用編譯器和解釋器的技術(shù)),這是一種權(quán)衡策略,給 JS 的執(zhí)行速度帶來(lái)了極大的提升。
那么 JIT 就是取編譯執(zhí)行語(yǔ)言和解釋執(zhí)行語(yǔ)言的長(zhǎng)處,利用解釋器對(duì)代碼進(jìn)行處理,對(duì)于頻率高的代碼進(jìn)行熱區(qū)收集,在之后指令字節(jié)碼編譯成機(jī)器碼的時(shí)候,儲(chǔ)存高頻率的二機(jī)制機(jī)器碼,之后就可以復(fù)用并執(zhí)行二機(jī)制代碼,以減少解釋器和編譯器的壓力。
v8 通過(guò)優(yōu)化后的工作流程如下:

知道了 v8 JS 引擎的工作流程之后,那么 RN 應(yīng)用中用什么 JS 引擎呢?
RN 在 0.60 版本之前使用 JSCore 作為默認(rèn)的 JS 引擎, JSCore 全名 JavaScriptCore ,JSCore 是 WebKit 默認(rèn)內(nèi)嵌的 JS 引擎,JSCore作為一個(gè)系統(tǒng)級(jí) Framework 被蘋(píng)果提供給開(kāi)發(fā)者,作為蘋(píng)果的瀏覽器引擎 WebKit 中重要組成部分。
所以在 iOS 應(yīng)用中默認(rèn)為 JSCore 引擎,這使得 RN 也用 JSCore ,但是 JSCore 沒(méi)有對(duì) Android 機(jī)型做好適配,在性能,體積,和內(nèi)存上和 v8 有著明顯的差別。
基于這個(gè)背景,RN 團(tuán)隊(duì)提供了 JSI (JavaScript Interface)框架,JSI 并不是 RN 的一部分,JSI 可以視作一個(gè)兼容層,意在磨平不同 JS 引擎中的差異性。
JSI 實(shí)現(xiàn)了引擎切換,比如在 iOS 平臺(tái)運(yùn)行的 JSCore ,在 Andriod 中運(yùn)行的是 v8 引擎。
JSI 同樣提供了抽象的 API 接口,定義了與各個(gè) JS 引擎交互的接口。
在 JS 中調(diào)用 C++ 注入到 JS 引擎中的方法,數(shù)據(jù)載體格式是通過(guò) HostObject 接口規(guī)范化后的,摒棄了舊架構(gòu)中以 JSON 作為數(shù)據(jù)載體的異步機(jī)制,讓 JS 和 C++ 相互感知。

明白了 RN 內(nèi)部運(yùn)轉(zhuǎn)的背景之后,我們開(kāi)始正式進(jìn)入 RN 的世界。
四 走進(jìn) React Native 的世界
React Native 將原生開(kāi)發(fā)的最佳部分與 React 相結(jié)合, 致力于成為構(gòu)建用戶(hù)界面的頂尖 JavaScript 框架。
React Native 開(kāi)發(fā)和傳統(tǒng)的 web 端 React 應(yīng)用開(kāi)發(fā)類(lèi)似,并且都是 js 語(yǔ)言,使得 web 開(kāi)發(fā)者上手 RN 開(kāi)發(fā)特別簡(jiǎn)單。
在 React web 應(yīng)用中,打包,部署到上線(xiàn)的產(chǎn)物,是一個(gè) html ,css,js 文件的集合體,最后把這些產(chǎn)物放在服務(wù)器上就可以了。
但是在 RN 中,最后打包產(chǎn)物是一個(gè) js 文件,叫做 jsbundle ,在 Native 端運(yùn)行 RN 項(xiàng)目,本質(zhì)上是遠(yuǎn)程拉取了 jsbundle ,并通過(guò)上述的 js 引擎運(yùn)行當(dāng)前 jsbundle,每次運(yùn)行一個(gè) bundle 就需要外層容器提供一個(gè) js 引擎。
在 React 構(gòu)建的應(yīng)用為單頁(yè)面應(yīng)用,如果存在多個(gè)頁(yè)面,可以通過(guò)路由的方式實(shí)現(xiàn)頁(yè)面的跳轉(zhuǎn),在 RN 中,也有一些解決方案,通常的手段是一個(gè) jsbundle 對(duì)應(yīng)一個(gè)頁(yè)面,或者是一個(gè) jsbundle 對(duì)應(yīng)多個(gè)頁(yè)面,如下圖所示:

如果采用,原生 + RN + H5 等融合的技術(shù)方案開(kāi)發(fā)的話(huà),單 jsbundle 對(duì)應(yīng)單頁(yè)面的方式比較適合,但是如果是 Native 作為外層容器,里面都頁(yè)面都是 RN 的話(huà),單頁(yè)面多 bundle 也是一個(gè)不錯(cuò)的選擇。
基礎(chǔ)用法
知道了 RN 的本質(zhì)之后,我們看一下 RN bundle 的注冊(cè),在 RN 中每一個(gè)應(yīng)用都有一個(gè)入口文件,RN 中提供了注冊(cè)根本應(yīng)用的方法,那就是 AppRegistry,這一點(diǎn)和 React web 應(yīng)用會(huì)有一些區(qū)別,web 應(yīng)用中,主要依賴(lài)于 react-dom 中提供的 api ,但是在 RN 項(xiàng)目中,無(wú)需再下載 react-dom,取而代之的是 react-native 包。
我們先來(lái)試著注冊(cè)一個(gè) RN 應(yīng)用:
import {AppRegistry} from 'react-native'
/* 根組件 */
import App from './app'
AppRegistry.registerComponent('Root', () => <App />)
如上我們注冊(cè)了 Root ,指向了組件 App。接下來(lái)就可以在 App 就可以正常開(kāi)發(fā)了。
在瀏覽器端,可以用 DOM 標(biāo)簽或者組件,但是在 RN 中是沒(méi)有 DOM 的,所以如果想要引入原生的視圖組件,就必須從 react-native 中引入,下面我們就編寫(xiě)一下 App 組件:
import react from 'react'
import { View, Text } from 'react-native';
function App(){
return <View>
<Text> Hello,React Native! </Text>
</View>
}
除了基礎(chǔ)的視圖容器組件之外,RN 還提供了一些移動(dòng)端常用的組件,比如列表組件 ScrollView,SectionList 等。
事件
對(duì)于一些用戶(hù)交互事件,RN 中也提供了對(duì)應(yīng)事件組件載體,比如點(diǎn)擊事件用的是 TouchableOpacity。 如下所示
function App(){
/* 處理點(diǎn)擊事件 */
const handlePress = ()=>{}
return <TouchableOpacity onPress={handlePress} >
<Text> click </Text>
</TouchableOpacity>
}
樣式
在 RN 中,是沒(méi)有 css 樣式文件的,RN 中的樣式就和 CSS IN JS 類(lèi)似,都是通過(guò) JS 來(lái)完成的,RN 提供了 StyleSheet 可以創(chuàng)建 style 對(duì)象,如下所示:
import { StyleSheet, View } from "react-native"
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: "#eaeaea"
}
})
function App(){
return <View style={styles.container} >
樣式處理
</View>
}
對(duì)于 RN 的基礎(chǔ)使用,如果參考官方文檔,學(xué)習(xí)成本不高,上手也很快。
弄明白 RN 的運(yùn)行環(huán)境和基礎(chǔ)使用之后,那么都知道 RN 最終的打包產(chǎn)物只是一個(gè) js 文件,那么這個(gè) js 文件是怎么運(yùn)行到 native 應(yīng)用中的,又是怎么和 native 應(yīng)用進(jìn)行交互的呢?
接下來(lái)文章中我們會(huì)對(duì) RN 的原理進(jìn)行探秘,探索一下 RN 內(nèi)部運(yùn)轉(zhuǎn)的機(jī)制。
另一方面,現(xiàn)在的動(dòng)態(tài)化方案已經(jīng)不僅僅是 Android 和 iOS 雙端,而是 Android ,iOS ,web ,小程序四端,我們通過(guò) RN 進(jìn)入到跨端動(dòng)態(tài)化方案上來(lái),研究一下以 React 做 dsl 四端動(dòng)態(tài)化方案的現(xiàn)狀與未來(lái)。
五 總結(jié)
本文從跨端發(fā)展現(xiàn)狀,再到 RN 運(yùn)轉(zhuǎn)的本質(zhì) JS 引擎,再到 RN 的使用,講述了移動(dòng)端動(dòng)態(tài)化的一個(gè)落地方案。 如果沒(méi)有用過(guò) RN 開(kāi)發(fā)過(guò) app 應(yīng)用的同學(xué),可以嘗試跑一下基礎(chǔ) demo 。
以上就是React跨端動(dòng)態(tài)化之從JS引擎到RN落地詳解的詳細(xì)內(nèi)容,更多關(guān)于React跨端動(dòng)態(tài)化JS引擎RN的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解React-Native全球化多語(yǔ)言切換工具庫(kù)react-native-i18n
這篇文章主要介紹了詳解React-Native全球化語(yǔ)言切換工具庫(kù)react-native-i18n,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
React實(shí)現(xiàn)文件上傳和斷點(diǎn)續(xù)傳功能的示例代碼
這篇文章主要為大家詳細(xì)介紹了React實(shí)現(xiàn)文件上傳和斷點(diǎn)續(xù)傳功能的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02
React Native時(shí)間轉(zhuǎn)換格式工具類(lèi)分享
這篇文章主要為大家分享了React Native時(shí)間轉(zhuǎn)換格式工具類(lèi),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
React組件如何優(yōu)雅地處理異步數(shù)據(jù)詳解
這篇文章主要為大家介紹了React組件如何優(yōu)雅地處理異步數(shù)據(jù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
React+TypeScript項(xiàng)目中使用CodeMirror的步驟
CodeMirror被廣泛應(yīng)用于許多Web應(yīng)用程序和開(kāi)發(fā)工具,之前做需求用到過(guò)codeMirror這個(gè)工具,覺(jué)得還不錯(cuò),功能很強(qiáng)大,所以記錄一下改工具的基礎(chǔ)用法,對(duì)React+TypeScript項(xiàng)目中使用CodeMirror的步驟感興趣的朋友跟隨小編一起看看吧2023-07-07
使用React?Router?v6?添加身份驗(yàn)證的方法
這篇文章主要介紹了使用React?Router?v6?進(jìn)行身份驗(yàn)證完全指南,本文將演示如何使用React?Router?v6創(chuàng)建受保護(hù)的路由以及如何添加身份驗(yàn)證,需要的朋友可以參考下2022-05-05
react實(shí)現(xiàn)阻止父容器滾動(dòng)
這篇文章主要介紹了react實(shí)現(xiàn)阻止父容器滾動(dòng)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11

