在現(xiàn)在的公司也做了一些零散的 H5 頁面,有一些相關(guān)實踐。反而因為基礎(chǔ)設(shè)施和體系不完善,接" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > 淺談移動端開發(fā)技術(shù)

淺談移動端開發(fā)技術(shù)

時間:2023-05-31 06:03:01 | 來源:網(wǎng)站運營

時間:2023-05-31 06:03:01 來源:網(wǎng)站運營

淺談移動端開發(fā)技術(shù):

前言

之前上家公司主要是做移動端 H5 開發(fā)的,但相關(guān)技術(shù)和配套體系已經(jīng)很成熟了,很難接觸到背后的這套體系。

在現(xiàn)在的公司也做了一些零散的 H5 頁面,有一些相關(guān)實踐。反而因為基礎(chǔ)設(shè)施和體系不完善,接觸到了更多東西。

剛好最近團隊和客戶端一起從零搭建 React Native 的體系,于是惡補了一些相關(guān)的知識,順便把 H5 開發(fā)中的一些東西也溫習記錄了一遍(做一個無情的縫合怪)。

說起移動端開發(fā),就繞不開 Hybrid 技術(shù)。這篇文章主要是引申出一些概念,方便后續(xù)介紹 js bridge、deeplink 等知識。

Native App

在說 Hybrid App 之前不得不先講到 Native App,這是最為傳統(tǒng)的一種移動端開發(fā)技術(shù)。

在 iOS 和安卓中官方的開發(fā)語言是 oc/swift、java/kotlin,使用這些開發(fā)出來的 App 一般稱之為原生應(yīng)用。

優(yōu)點

原生應(yīng)用一般體驗較好,性能比較高,可以提前把資源下載到本地,打開速度快。

除此之外,原生應(yīng)用可以直接調(diào)用系統(tǒng)攝像頭、通訊錄、相冊等功能,也可以訪問到本地資源,功能強大。

一般需要開發(fā) App,原生應(yīng)用應(yīng)該是首選。

缺點

原生應(yīng)用最大的缺點就是不支持動態(tài)化更新,這也是很多大廠不完全使用原生開發(fā)的原因。

考慮一下,如果線上出現(xiàn)嚴重問題,那該怎么辦呢?

首先客戶端開發(fā)修復了 bug 之后,就需要重新發(fā)版、提交應(yīng)用商店審核,這一流程走下來往往需要好幾天的時間。

如果發(fā)布了新版 App,用戶該怎么去更新呢?答案是沒法更新。他們只能重新去下載整個 App,但實際上可能只更新了一行文案,這樣就得不償失了。

除此之外,最麻煩的地方在于要兼容老版本的 App。比如我們有個列表頁原本是分頁加載的,接口返回分頁數(shù)據(jù)。產(chǎn)品說這樣體驗不好,我們需要換成全量加載,那接口就需要做成全量的。

但接口一旦換成了全量的,老版本的客戶端里面依然是分頁請求接口的,這樣就會出現(xiàn)問題。因此,接口不得不根據(jù)不同版本進行兼容。

Web App

Web App 就是借助于前端 HTML5 技術(shù)實現(xiàn)的在瀏覽器里面跑的 App,簡單來說就是一個 Web 網(wǎng)站。

因為是在瀏覽器里面運行,所以天然支持跨平臺,一套代碼甚至很容易支持移動端和 PC 端不需要安裝到手機里面,上線發(fā)版也比較容易。




缺點也很明顯,那就是只能使用瀏覽器提供的功能,無法使用手機上的一些功能。比如攝像頭、通訊錄、相冊等等,局限性很大。

也由于依賴于網(wǎng)絡(luò),加載頁面速度會受到限制,體驗較差。受限于瀏覽器 DOM 的性能,導致對一些場景很難做到原生的體驗,比如長列表。 同時,也因為不像客戶端一樣在手機上有固定入口,會導致用戶黏性比較低。

Hybrid App

Hybrid App 是介于 Native 和 Web 之間的一些開發(fā)模式,一般稱作混合開發(fā)。

簡單來說 Hybrid 就是套殼 App,整個 App 還是原生的,也需要下載安裝到手機,但是 App 里面打開的頁面既可以是 Web 的,又可以是原生的。

H5 頁面會跑在 Native 的一個叫做 WebView 的容器里面,我們可以簡單理解為在 App 里面打開了一個 Chrome 瀏覽器,在這個瀏覽器里面打開一個 Tab 去加載線上或者本地的 H5 頁面,這樣還可以實現(xiàn)打開多 WebView 來加載多個頁面。

優(yōu)勢

Hybrid App 同時擁有 Native 和 Web 的優(yōu)點,開發(fā)模式比較靈活。既可以做到動態(tài)化更新,有 bug 直接更新線上 H5 頁面就行了。 也可以使用橋接(JS Bridge)來調(diào)用系統(tǒng)的攝像頭、相冊等功能,功能就不僅僅局限于瀏覽器了。

由于 H5 的優(yōu)勢,Hybrid 也支持跨平臺,只要有 WebView,一套代碼可以很容易跨iOS、安卓、Web、小程序、快應(yīng)用多個平臺。

缺點

缺點主要還是 Web App 的那些缺點,加載速度比較慢。

同時,因為受制于 Web 的性能,在長列表等場景依然無法做到和原生一樣的體驗。

當然加載速度是可以優(yōu)化的,比如離線包。可以提前下載打包好的 zip 文件(包括 JS、CSS、圖片等資源文件)到 App 里面,App 自己解壓出來 JS 和 CSS 等文件。這樣每次訪問的是 App 本地的資源,加載速度可以得到質(zhì)的提升。

如果文件有更新,那么客戶端就去拉取遠程版本,和本地版本進行對比,如果版本有更新,那就去拉取差量部分的文件,用二進制 diff 算法 patch 到原來的文件中,這樣可以做到熱更新。

但是成本也比較高,不僅需要在服務(wù)端進行一次文件差分,還需要公司內(nèi)部提供一套熱更新發(fā)布平臺。

WebKit

WebView 是安卓中展示界面的一個控件,一般是用來展示 Web 界面。前面我們說過,可以把 WebView 理解為你正在使用的 Chrome 瀏覽器。

那么瀏覽器又是怎么去解析渲染 HTML 和 CSS,最終渲染到頁面上面的呢?

這也是一道經(jīng)典面試題里面的一環(huán):從URL輸入到頁面展現(xiàn)到底發(fā)生什么?

簡單來說就是瀏覽器拿到響應(yīng)的 HTML 文本后會解析 HTML 成一個 DOM 樹,解析 CSS 為 CSSOM 樹,兩者結(jié)合生成渲染樹。在 Chrome 中使用 Skia 圖形庫來渲染界面,Skia 也是 Flutter 的渲染引擎。

可以參考這張經(jīng)典圖:

PS:使用 Skia 去繪制界面,而非編譯成 Native 組件讓系統(tǒng)去渲染,也是 Flutter 區(qū)別于 React Native 的一個地方。

除了解析 HTML,瀏覽器還需要提供 JavaScript 的運行時,我們知道的 V8 引擎就是做這件事的。

WebKit 內(nèi)核

從上面我們可以得知,一個瀏覽器至少離不開一個渲染 HTML 的引擎和一個運行 JavaScript 的引擎。

當然,上面的這些操作都是瀏覽器由內(nèi)核來完成的?,F(xiàn)在主流的瀏覽器都使用了 WebKit 內(nèi)核。

WebKit 誕生于蘋果發(fā)布的 Safari 瀏覽器。后來谷歌基于 WebKit 創(chuàng)建了 Chromium 項目,在此基礎(chǔ)上發(fā)布了我們熟悉的 Chrome 瀏覽器。

WebKit 內(nèi)核的結(jié)構(gòu)如下圖所示。

我們依次從上往下看,WebKit 嵌入式接口就是提供給瀏覽器調(diào)用的,不同瀏覽器實現(xiàn)可能有所差異。

其中解析 HTML 和 CSS 這部分是 WebCore 做的,WebCore 是 WebKit 最核心的渲染引擎,也是各大瀏覽器保持一致的部分,一般包括 HTML 和 CSS 解釋器、DOM、渲染樹等功能。

WebKit 默認使用 JavaScriptCore 作為 JS 引擎,這部分是可以替換的。JavaScriptCore 也是 React Native 里面默認的引擎。

由于 JavaScriptCore 前期性能低下,于是谷歌在 Chrome 里面選用了 V8 作為 JS 引擎。

WebKit Ports 則是非共享的部分,由于平臺差異、依賴的庫不同,這部分就變成了可移植部分。主要涉及到網(wǎng)絡(luò)、視頻解碼、圖片解碼、音頻解碼等功能。

WebView 自然也使用了 WebKit 內(nèi)核。只是在安卓里面以 V8 作為 JS 引擎,在 iOS 里面以 JavaScriptCore 作為 JS 引擎。

由于渲染 DOM 和操作 JS 的是兩個引擎,因此當我們用 JS 去操作 DOM 的時候,JS 引擎通過調(diào)用橋接方法來獲取訪問 DOM 的能力。這里就會涉及到兩個引擎通信帶來的性能損失,這也是為什么頻繁操作 DOM 會導致性能低下。

React 和 Vue 這些框架都是在這一層面進行了優(yōu)化,只去修改差異部分的 DOM,而非全量替換 DOM。

iOS 中的 JavaScriptCore

JavaScriptCore 是 WebKit 內(nèi)核默認使用的 JS 引擎。既然是講解 WebView,那么就來介紹一下 iOS 里面的 JavaScriptCore 框架吧。

iOS 中的 JavaScriptCore 框架是基于 OC 封裝的 JavaScriptCore 框架。

它提供了調(diào)用 JS 運行環(huán)境以及 OC 和 JS 互相調(diào)用的能力,主要包含了 JSVM、JSContext、JSValue、JSExport 四個部分(其實只是想講 JSVM)。

JSVM

JSVM 全稱是 JSVirtualMachine,簡單來說就是 JS 的虛擬機。那么什么是虛擬機呢?

我們以 JVM 為例,一般來說想要運行一個 Java 程序要經(jīng)過這么幾步:

  1. 把 Java 源文件(.java文件)編譯成字節(jié)碼文件(.class文件,是二進制字節(jié)碼文件),這種字節(jié)碼就是 JVM 的“機器語言”。javac.exe 就可以看做是 Java 編譯器。
  2. Java 解釋器用來解釋執(zhí)行 Java 編譯器編譯后的程序。java.exe可以簡單看成是 Java 解釋器。
所以 JVM 是一種能夠運行 Java 字節(jié)碼的虛擬機。除了運行 Java 字節(jié)碼,它還會做內(nèi)存管理、GC 等方面的事情。

而 JSVM 則提供了 JS 的運行環(huán)境,也提供了內(nèi)存管理。每個 JSVM 只有一個線程,如果想執(zhí)行多個線程,就要創(chuàng)建多個 JSVM,它們都自己獨立的 GC,所以多個 JSVM 之間的對象無法傳遞。

JS 源代碼經(jīng)過了詞法分析和語法分析這兩個步驟,轉(zhuǎn)成了字節(jié)碼,這一步就是編譯。

但是不同于我們編譯運行 Java 代碼,JS 編譯結(jié)束之后,并不會生成存放在內(nèi)存或者硬盤之中的目標代碼或可執(zhí)行文件。生成的指令字節(jié)碼,會被立即被 JSVM 進行逐行解釋執(zhí)行。

字節(jié)碼

? 字節(jié)碼是已經(jīng)經(jīng)過編譯,但與特定機器碼無關(guān),需要解釋器轉(zhuǎn)譯后才能成為機器碼的中間代碼。
?
在 v8 中前期沒有引入字節(jié)碼,而是簡單粗暴地直接把源程序編譯成機器碼去運行,因為他們覺得先生成字節(jié)碼再去執(zhí)行字節(jié)碼會降低執(zhí)行速度。

但后期 v8 又再一次將字節(jié)碼引入進來,這是為什么呢?

早期 v8 將 JS 編譯成為二進制機器碼,但是編譯會占用很大一部分時間。如果是同樣的頁面,每次打開都要重新編譯一次,這樣就會大大降低了效率。

于是在 chrome 中引入了二進制緩存,將二進制代碼保存到內(nèi)存或者硬盤里面,這樣方便下次打開瀏覽器的時候直接使用。

但二進制代碼的內(nèi)存占用特別高,大概是 JS 代碼的數(shù)千倍,這樣就導致了如果在移動設(shè)備(手機)上使用,本來容量就不大的內(nèi)存還會被進一步占用,造成性能下降。

然而字節(jié)碼占用空間就比機器碼實在少太多了。因此,v8 團隊不得不再次引入字節(jié)碼。

JIT

除此之外,還有一個大家都很熟悉的概念,那就是 JIT(即時編譯)。

? 即時編譯(Just-in-time compilation: JIT):又叫實時編譯、及時編譯。是指一種在運行時期把字節(jié)碼編譯成原生機器碼的技術(shù),一句一句翻譯源代碼,但是會將翻譯過的代碼緩存起來以降低性能耗損。這項技術(shù)是被用來改善虛擬機的性能的。
?
簡單來說就是某段代碼要被執(zhí)行之前才進行編譯。還是以 JVM 為例子。

  1. JVM 的解釋過程:
java 代碼 -> 編譯字節(jié)碼 -> 解釋器解釋執(zhí)行

  1. JIT 的編譯過程:
java 代碼 -> 編譯字節(jié)碼 -> 編譯機器碼 -> 執(zhí)行

所以 Java 是一種半編譯半解釋語言。之所以說 JIT 快,是指執(zhí)行機器碼效率比解釋字節(jié)碼效率更高,而非編譯比解釋更快。因此,JIT 編譯還是會比解釋慢一些。

同樣,編譯成機器碼還是會遇到上面空間占用大的問題。所以在 JIT 中只對頻繁執(zhí)行的代碼就行編譯,一般包括下面兩種:

  1. 被多次調(diào)用的方法。
  2. 被多次執(zhí)行的循環(huán)體。
在編譯熱點代碼的時候,這部分就會被緩存起來。等下次運行的時候就不需要再次進行編譯,效率會大大提升。這也是為什么很多 JVM 都是用解釋器+JIT的形式。

小丁哥如是說:




JSContext

JSContext 就是 JS 運行的上下文,我們想要在 WebView 上面運行 JS 代碼,就需要 JSContext 這個運行環(huán)境。以下面這段代碼為例:

JSContext *context = [[JSContext alloc] init];[context evaluateScript:@"var i = 4"];NSNumber *number = [context[@"i"] toNumber];上面的 JSContext 調(diào)用 evaluateScript 來執(zhí)行一段 JS 代碼,通過 context 可以拿到對應(yīng)的 JSValue 對象。

JSValue

JS 和 OC 交換數(shù)據(jù)的時候 JSCore 幫我們做了類型轉(zhuǎn)換,JSCore 提供了10種類型轉(zhuǎn)換:

Objective-C type | JavaScript type --------------------+--------------------- nil | undefined NSNull | null NSString | string NSNumber | number, boolean NSDictionary | Object object NSArray | Array object NSDate | Date object NSBlock | Function object id | Wrapper object Class | Constructor object

JSExport

JSExport 支持把 Native 對象暴露給 JS 環(huán)境。例如:

@protocol NativeObjectExport <JSExport>@property (nonatomic, assign) BOOL property1;- (void)method1:(JSValue *)arguments;@end@interface NativeObject : NSObject<NativeObjectExport>@property (nonatomic, assign) BOOL property1;- (void)method1:(JSValue *)arguments;@end上面的 NativeObject 只要實現(xiàn)了 JSExport,就可以被 JS 直接調(diào)用。我們需要在 Context 里面注入一個對象,就可以在 JS 環(huán)境調(diào)用 Native 了。

context[@"nativeMethods"] = [NativeObject new];JS 中調(diào)用:

nativeMethods.method1({ callback(data) { }])

React Native

Hybrid 中的 H5 始終是 WebView 中運行的,WebKit 負責繪制的。因為瀏覽器渲染的性能瓶頸,F(xiàn)acebook 基于 React 發(fā)布了 React Native 這個框架。

由于 React 中 Virtual DOM 和平臺無關(guān)的優(yōu)勢,理論上 Virtual DOM 可以映射到不同平臺。在瀏覽器上就是 DOM,在 Native 里面就是一些原生的組件。

受制于瀏覽器渲染的性能,React Native 吸取經(jīng)驗將渲染這部分交給 Native 來做,大大提高了體驗。個人認為 React Native 也算是 Hybrid 技術(shù)的一種。

RN 中直接使用 JavaScriptCore 來提供 JS 的運行環(huán)境,通過 Bridge 去通知 Native 繪制界面,最終還是 Native 渲染的。

所以性能上比 Hybrid 更好,但受限于 JS 和 Native 通信的性能消耗,性能上依然不及 Native。

JS 和 Native 通信原理

在 JS 和 Native 通信的時候往往要經(jīng)過 Bridge,這一步是異步的。

在 App 啟動的時候,Native 會創(chuàng)建一個 Module 配置表,這個表里面包括了所有模塊和模塊方法的信息。

{ "remoteModuleConfig": { "XXXManager": { "methods": { "xxx": { "type": "remote", "methodID": 0 } }, "moduleID": 4 }, ... },}由于在 OC 里面每個提供給 JS 調(diào)用的模塊類都實現(xiàn)了 RCTBridgeModule 接口,所以通過 objc_getClassListobjc_copyClassList 獲取項目中所有的類,然后判斷每個類是否實現(xiàn)了 RCTBridgeModule,就可以確定是否需要添加到配置表中。

然后 Native 會將這個配置表信息注入到 JS 里面,這樣在 JS 里面就可以拿到 OC 里面的模塊信息。

其實如果你寫過 JS Bridge,會發(fā)現(xiàn)這個流程和 WebViewJavaScriptBridge 有些類似。主要是這么幾個步驟:

  1. JS 調(diào)用某個 OC 模塊的方法
  2. 把這個調(diào)用轉(zhuǎn)換為 ModuleName、MethodName、arguments,交給 MessageQueue 處理。
  3. 將 JS 的 Callback 函數(shù)放到 MessageQueue 里面,用 CallbackID 來匹配。再根據(jù)配置表將 ModuleName、MethodName映射為 ModuleID 和 MethodID。
  4. 然后把上面的 ID 都傳給 OC
  5. OC 根據(jù)這幾個 ID 去配置表中拿到對應(yīng)的模塊和方法
  6. RCTModuleMethod 對 JS 傳來的參數(shù)進行處理,主要是將 JS 數(shù)據(jù)類型轉(zhuǎn)換為 OC 的數(shù)據(jù)類型
  7. 調(diào)用 OC 模塊方法
  8. 通過 CallbackID 拿到對應(yīng)的 Callback 方法執(zhí)行并傳參

熱更新

相比 Native,RN 的一大優(yōu)勢就是熱更新。我們將 RN 項目最后打包成一個 Bundle 文件提供給客戶端加載。在 App 啟動的時候去加載這個 Bundle 文件,最后由 JavaScriptCore 來執(zhí)行。

如果有新版本該怎么更新?這個其實很簡單,重新打包一個 Bundle 文件,用 BS Diff 算法對不同版本的文件進行二進制差分。

客戶端會比較本地版本和遠程版本,如果本地版本落后了,那就去下載差量文件,同樣使用 BS Diff 算法 patch 進 Bundle 里面,這樣就實現(xiàn)了熱更新。

這種方式也適用于 H5 的離線包更新,可以很大程度上解決 H5 加載慢的問題。

RN 新架構(gòu)

在 RN 老架構(gòu)中,性能瓶頸主要體現(xiàn)在 JS 和 Native 通信上面,也就是 Bridge 這里。

我們寫的 RN 代碼會通過 JS Thread 進行序列化,然后通過 Bridge 傳給 shadow Thread 反序列化獲得原生布局信息。

之后又通過 Bridge 傳給 UI Thread,UI Thread 反序列化之后會根據(jù)布局信息進行繪制。這里就有三個線程通過 Bridge 來通信。

由于多次序列化/反序列化以及 Bridge 通信,這樣就造成了一些性能損耗。

尤其是在快速滑動列表的時候容易造成白屏,然而瀏覽器里面快速滑動卻沒有白屏,這又是為什么呢?

主要還是瀏覽器中,JS 可以持有 C++ 對象的引用,所以這里其實是同步調(diào)用。

由于受到 Flutter 的沖擊,RN 團體提出了新的架構(gòu)來解決這些問題。為了解決 Bridge 通信的問題,RN 團隊在 JavaScriptCore 之上抽象了一層 JSI(JavaScript Interface),允許底層更換成不同的 JavaScript 引擎。

除此之外,JS 還可以拿到 C++ 的引用,這樣就可以直接和 Native 通信,不需要反復序列化對象,也節(jié)省了 Bridge 通信的開支。

這里解釋一下,為啥拿到 C++ 引用就可以和 Native 通信。由于 OC 本身就是 C 語言的擴展,所以可以直接調(diào)用 C/C++ 的方法。Java 雖然不能 C 語言擴展,但它可以通過 JNI 來調(diào)用。

JNI 就是 Java Native Interface,它是 JVM 提供的一套能夠使運行在 JVM 上的 Java 代碼調(diào)用 C++ 程序、以及被 C++ 程序調(diào)用的編程框架。

相信新架構(gòu)的到來會解決 RN 原有的一些痛點,以及會帶來性能上的飛躍。

Flutter

傳統(tǒng)的跨端有兩種,一種是 Hybrid 那種實現(xiàn) JS 跑在 WebView 上面的,這種性能瓶頸取決于瀏覽器渲染。

另一種是將 JS 組件映射為 Native 組件的,例如 React Native、Weex,缺點就是依然需要 JS Bridge 來進行通信(老架構(gòu))。

Flutter 則是在吸取了 RN 的教訓之后,不再去做 Native 的映射,而是自己用 Skia 渲染引擎來繪制頁面,而 Skia 就是前面說過的 Chrome 底層的二維圖形庫,它是 C/C++ 實現(xiàn)的,調(diào)用 CPU 或者 GPU 來完成繪制。所以說 Flutter 像個游戲引擎。

Flutter 在語法上深受 React 的影響,使用 setState 來更新界面,使用類似 Redux 的思想來管理狀態(tài)。從早期的 WPF,到后面的 React,再到后來的 SwiftUI 都使用了聲明式 UI 的思想。

Flutter 架構(gòu)圖如下:

Framework 是用 Dart 實現(xiàn)的 SDK,封裝了一些基礎(chǔ)庫,比如動畫、手勢等。還實現(xiàn)了一套 UI 組件庫,有 Material 和 Cupertino 兩種風格。Material 適用于安卓,Cupertino 適用于 iOS。

Engine 是 C/C++ 實現(xiàn)的 SDK,主要包括了 Skia 引擎、Dart 運行時、文本渲染等。

Embedder 是一個嵌入層,支持把 Flutter 嵌入各個平臺。

Flutter 使用 Dart,支持 AOT 編譯成 ARM Code,這樣擁有更高的性能。在 Debug 模式下還支持 JIT。

在 Flutter 中,Widgets 是界面的基本構(gòu)成單位,和 React Component 有些類似。而 StatelessWidget 類似 React Functional Component。

class Echo extends StatelessWidget { const Echo({ Key key, @required this.text, this.backgroundColor:Colors.grey, }):super(key:key); final String text; final Color backgroundColor; @override Widget build(BuildContext context) { return Center( child: Container( color: backgroundColor, child: Text(text), ), ); }}在 Flutter 渲染過程中有三棵樹,分別是 Widgets 樹、Element 樹、RenderObject 樹。

如果你有寫過 React,會發(fā)現(xiàn)真的和 React 很類似。

當初始化的時候, Widgets 通過 build 方法來生成 Element,這類似于 React.createElement 生成虛擬 DOM(Virtual DOM)。

Element 重新創(chuàng)建的開銷會比較大,所以每次重新渲染它并不會重新構(gòu)建。從 Element Tree 到 RenderObject Tree 之間一般也會有一個 Diff 的環(huán)境,計算最小需要重繪的區(qū)域。

這里也和 React 渲染流程比較相似,虛擬 DOM 會和真實 DOM 進行一次 Diff 對比,最后將差異部分渲染到瀏覽器上。

瀏覽器渲染

在前面我們講過瀏覽器的渲染流程。一般是將 HTML 解析成 DOM 樹,將 CSS 解析為 CSSOM 樹,兩者合并成一顆 RenderObject 樹。

一個 RenderObject 對象保存了繪制 DOM 節(jié)點需要的各種信息,它知道怎么繪制自己。不同的 RenderObject 對象構(gòu)成一棵樹,就叫做 RenderObject 樹。它是基于 DOM 樹創(chuàng)建的一棵樹。

然后 WebKit 會為這些 RenderObject 對象創(chuàng)建新的 RenderLayer 對象,最后形成一棵 RenderLayer 樹。一般來說,RenderLayer 和 RenderObject 是一對多的關(guān)系。每個 RenderLayer 包括的是 RenderObect 樹的子樹。

什么情況下會創(chuàng)建 RenderLayer 對象呢?比如 Video 節(jié)點、Document 節(jié)點、透明的 RenderObject 節(jié)點、指定了位置的節(jié)點等等,這些都會創(chuàng)建一個 RenderLayer 對象。

一個 RenderLayer 可以看做 PS 里面的一個圖層,各個圖層組成了一個圖像。

Flutter 渲染

Flutter 渲染和瀏覽器渲染類似,Widget 通過 createElement 生成 Element,而 Element 通過 createRenderObject 創(chuàng)建了 RenderObject 對象,最終生成 Layer。

一般來說,RenderObject 上面存著布局信息,所以布局和繪制都是在 RenderObject 中完成。Flutter 通過深度遍歷渲染 RenderObject 樹,確定每個對象的位置和大小,繪制到不同的圖層中。繪制結(jié)束后,由 Skia 來完成合成和渲染。

所以 Flutter 的更新流程如下:

通信

Flutter 沒辦法完成 Native 所有的功能,比如調(diào)用攝像頭等,所以需要我們開發(fā)插件,而插件開發(fā)的基礎(chǔ)還是 Flutter 和 Native 之間進行通信。

Flutter 和 Native 之間的通信是通過 Channel 完成的,一般有下面幾種通信場景:

  1. Native 發(fā)送數(shù)據(jù)給 Dart
  2. Dart 發(fā)送數(shù)據(jù)給 Native
  3. Dart 發(fā)送數(shù)據(jù)給 Native,Native 回傳數(shù)據(jù)給 Dart
Flutter 實現(xiàn)通信有下面三種方式:

  1. EventChannel:一種 Native 向 Flutter 發(fā)送數(shù)據(jù)的單向通信方式,F(xiàn)lutter 無法返回任何數(shù)據(jù)給 Native。一般用于 Native 向 Flutter發(fā)送手機電量、網(wǎng)絡(luò)連接等。
  2. MethodChannel:Native 和 Flutter之間的雙向通信方式。通過 MethodChannel 調(diào)用 Native 和 Flutter 中相對應(yīng)的方法,該種方式有返回值。
  3. BasicMessageChannel:Native 和 Flutter之間的雙向通信方式。支持數(shù)據(jù)類型最多,使用范圍最廣。該方式有返回值。
BinaryMessenger 是 Flutter 和 Channel 通信的工具。它在安卓中是一個接口,使用二進制格式數(shù)據(jù)通信。

在 FlutterView 中實現(xiàn),它可以通過 JNI 來和系統(tǒng)底層通信。因此,基本上和原生調(diào)用差不多,不像 RN 中 Bridge 調(diào)用需要進行數(shù)據(jù)轉(zhuǎn)化。

所以,如果想開發(fā)插件,還是需要實現(xiàn)安卓和 iOS 的功能,以及封裝 plugin 的 api,總體上還是無法脫離 Native 來運作。

對比 React Native

  1. Flutter 官方暫時不支持熱更新,RN 有成熟的 Code Push 方案
  2. Flutter 放棄了 Web 生態(tài),RN 擁有 Web 成熟的生態(tài)體系,工具鏈更加強大。
  3. Flutter 將 Dart 代碼 AOT 編譯為本地代碼,通信接近原生。RN 不僅需要多次序列化,不同線程之間還需要通過 Bridge 來通信,效率低下。
更多細節(jié)對比可以參考本站這個問題:開發(fā)跨平臺 App 推薦 React Native 還是 Flutter?

PS:歡迎大家關(guān)注我的公眾號【前端小館】,大家一起來討論技術(shù)。

參考

  1. Understanding WebViews
  2. What is WebKit and how is it related to CSS?
  3. 在Chrome中的GPU加速合成(一)
  4. JavaScriptCore
  5. JIT 為什么能大幅度提升性能?
  6. 深入淺出 JIT 編譯器
  7. How Flutter renders Widgets
  8. Flutter原理及經(jīng)驗分享
  9. WebKit 技術(shù)內(nèi)幕
  10. React Native's New Architecture
  11. React Native 新架構(gòu)

關(guān)鍵詞:技術(shù),移動,淺談

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關(guān)閉