1. 程式人生 > 其它 >愛奇藝RN低程式碼引擎:千變萬化、快速搭建的萬花筒

愛奇藝RN低程式碼引擎:千變萬化、快速搭建的萬花筒

愛奇藝RN低程式碼引擎:千變萬化、快速搭建的萬花筒 https://mp.weixin.qq.com/s/XO0A58ZFHcBERnFghqRmqg

愛奇藝RN低程式碼引擎:千變萬化、快速搭建的萬花筒

甘泉 QCon全球軟體開發大會 2022-05-19 09:00 發表於北京 嘉賓 | 甘泉編輯 | 李慧文

React Native 是具有高動態化能力的跨平臺開發框架,低程式碼是近幾年非常熱門的趨勢,而愛奇藝早在 2018 年就嘗試將二者結合,開發出了萬花筒引擎和專題頁低程式碼平臺,用於快速搭建愛奇藝 App 中的各類內容展示型頁面,賦能不會寫程式碼的內容運營和編輯們,針對熱門的綜藝和影視劇內容,用很少的人力投入就能快速搭建出具有豐富排版和動畫效果的專題頁面,並直接投放上線。

在 2021 年 5 月北京 QCon 全球軟體開發大會上,愛奇藝基線研發部高階經理甘泉分享了愛奇藝設計和應用萬花筒引擎和低程式碼平臺的實踐經驗、遇到的挑戰以及解決思路。我們整理了他的演講,以期解決您在 React Native 低程式碼系統架構設計時可能遭遇的某些問題。(下文以甘泉老師第一人稱敘述)

愛奇藝專題頁面包含豐富排版和動畫效果。這類頁面均由以下低程式碼方案實現:用零程式碼搭建頁面、用低程式碼開發頁面內常規元件實現常規動畫、用專業程式碼開發頁面內定製化元件實現個性動畫。

1一、業務背景

移動端常規頁面開發流程包括:提出需求、需求稽核、視覺設計、前後端開發、測試驗證、修復 Bug、跟版上線七步,涉及到產品經理、設計師、工程師、QA 等角色。

專題頁開發流程與一般頁面在流程、人員、成本上、釋出週期、向前相容性上均存在差異。專題頁開發只有提出需求、視覺設計、搭建頁面、自測驗證、投放上線五步。其中,需求是由編輯提出的。

在專題頁開發中,開發流程需極度簡化才能方便編輯操作,頁面需花樣豐富才能吸引使用者,搭建要足夠快捷才可保證內容時效性,增加新佈局樣式和特性需不依賴 App 發版才可便於更新。

為實現以上需求,我們對比了 H5、RN 和當時愛奇藝已有的自研的原生 DSL 方案的優缺點,綜合成本、效能、動態性等多方面的考慮,決定自研一個結合 RN 和 DSL 的新方案:Kaleidoscope 引擎(萬花筒引擎)。

2二、Kaleidoscope DSL 設計思想

這個引擎之所以被命名為 Kaleidoscope(萬花筒),是因為其靈感來自真正的萬花筒。萬花筒是在圓筒一端放入彩色碎片,圓筒中間放置三稜鏡,從另一端的孔中即可觀測到對稱的美麗影象,結構簡單、體積小巧,卻千變萬化。Kaleidoscope 採取了相通的設計思路。

Kaleidoscope 層次結構分為四層,App、Page、Item、Element。最外層為 App 層,一個 App 中包含一個 Page,Page 中包含一組 Item 元件(Item 可理解為一個長列表中的 Item),每個 Item 由多個 Element 構成。而 Element 就相當於萬花筒中的彩色碎片。

Element 分為多種型別,容器型允許互相巢狀,可包含子元素,可實現用簡單佈局(如橫縱佈局)組合出複雜的佈局;元件型為最小元素,無子元素,不可再分;複合型可實現特殊佈局和互動。

Kaleidoscope 資料格式採用了 JSON 格式,體積小,易生產,很通用;樣式佈局描述系統採用了 Flexbox,RN 支援良好,佈局能力強,學習成本低,其資料結構如下圖所示:

圖中左側為一個 JSON 結構,其中 Page 物件最重要的屬性為 Items 陣列,單個 Item 由多個 contents 巢狀組成(圖中右側),再加上容器型 Element 中的 content,即可實現千變萬化的佈局。

DSL 貫穿了 Kaleidoscope 方案,和 RN 堪稱天生一對:

  • JSON 結構和 RN 元件一一對應;

  • JSON Property 和 RN Prop 也一一對應;

  • RN 提倡組合大於繼承,適用 DSL 場景。

通過 RN 元件的設計和封裝,即可用一個簡潔的架構實現分而治之。

3三、整體方案的架構設計

Kaleidoscope 整體架構圖分為前端、後端和低程式碼平臺三部分。前端面向內容消費者,後端和低程式碼平臺面向內容生產者以及部分開發人員。

前端架構中 Kaleidoscope 引擎處於下面架構圖的第二層,主要負責資料的下載、快取和解析,依賴於 QYRN 框架。QYRN 框架是愛奇藝以 RN 官方框架為基礎封裝的框架,它豐富了官方框架的元件庫,擴充套件了 UI 元件和原生模組,實現了熱更新機制、資料投遞和 Bridge 例項複用。

後端架構如下所示。頁面資料來自於編輯們搭建頁面的配置資訊和內容原資料的資料流(虛線下的流程)。兩類資料一靜一動,會在不同端的 Worker Service 上融合,返回給不同使用者端。

低程式碼平臺架構的核心為第三層 Portal,由兩塊組成:面向元件開發同學的元件開發平臺和編輯使用的頁面視覺化搭建平臺。頁面視覺化搭建平臺實現完全零程式碼,元件開發平臺只需要用少量程式碼就可以開發元件,通常給外包人員使用。

4四、萬花筒引擎的實現思路

RN 的動態性一直是其不容小覷的優勢。愛奇藝原生的 DSL 引擎也具有一定的動態性,可迅速實現介面更新、UI 改版等。基於 RN 和 DSL 強強結合,萬花筒引擎實現了引擎的高動態性。

同時,愛奇藝基於可巢狀容器實現了高佈局靈活性。可巢狀容器為前述容器型 Element,此處不贅述了。我會重點介紹愛奇藝是如何基於 JS Card 實現高業務適應性、基於層疊實現低資料冗餘、基於依賴注入實現高擴充套件性的。

基於 JS Card 實現了高業務適應性

參考 HTML 中通過夾帶標籤編寫 JS 實現動態效果,為提高業務適應性,我們在 JSON 中也添加了 JS 指令碼。針對無法用 JSON 描述的非通用需求場景,我們允許以 Card(列表項)為粒度直接用 RN 開發,打包成 JS 字串之後和其他 JSON 資料一起下發,動態載入執行。Card 為前文中的 Item 被渲染後的帶介面的佔有一定高度的實體。

JS Card 工作分為六步:

  1. 開發 JS Card。開發 JS Card 和開發 RN 元件唯一差別是需要將元件類存入 global 物件。

  2. 打包 JS 字串。打包時需要注意雙引號轉義和包體積大小;

  3. 釋出 JS Card。在低程式碼平臺的元件開發平臺建立一個 JS Card 元件,進行釋出;

  4. 介面返回頁面資料。釋出後,端上請求介面即可獲取 JS 字串,放入 DSL 配置物件的 base.script 屬性中,允許一個頁面中新增多個 JS Card;

  5. 解析執行 JS 註冊。引擎解析資料後,使用 eval 函式執行 JS 字串,將字串中定義的元件的 class 加到上下文中,從 global 中獲取物件並新增到 JS Card 登錄檔中進行管理;

  6. 渲染。如 Item 有 ScriptID 屬性,則從 JS Card 登錄檔中找到對應元件進行渲染。

基於層疊實現低資料冗餘

為了減少頁面中的冗餘資訊,愛奇藝基於 CSS 的層疊樣式表提出了層疊的概念,支援了資訊複用和組合,針對樣式資料提出了 Style 和 Theme,針對 pingback 資料提出了 Statistics 和 CommonStats。可在 Style 中引用 Theme,在 Statistics 引用 CommonStats,且支援按先後順序覆蓋合成。

舉例來說,Style 可通過 ref 屬性引用 Theme 中定義好的類(如下圖左),引用了多個 Theme 可通過樣式繼承和覆蓋機制合成具體的 Style(如下圖右)。

基於依賴注入實現高擴充套件性

不同業務方前來對接,但是需求不同時,愛奇藝採用了剝離核心功能,支援業務方按需裁剪、擴充套件和定製的思路。我們採用依賴注入的方式實現和使用所有的 Page、Item 和 Element 元件,打破了元件之間的強依賴。業務方可自由組合複用已有的元件,自定義元件,實現過程如下圖所示:

  1. 配置核心抽象類的具體依賴:這裡核心抽象類可分為 5 種:Pages、Services、Items、Elements、Components;

  2. 建立業務配置類:向框架注入 5 種核心抽象類例項配置;

  3. 引入業務配置類:在程式碼入口註冊 App 元件;

  4. 引用舉例:在 App 元件中解耦引用注入的 listPage 元件。

5五、低程式碼平臺設計與實現

下圖為愛奇藝編輯日常使用的頁面搭建系統:

左側為元件庫,中間為預覽區;右側為配置區。元件均為 Card 粒度,滑鼠拖動即可選中,在配置區即可配置 Card 的資料和樣式。

該平臺擴充套件需要使用下圖中的元件開發平臺新增元件、排版和樣式。

製作元件需寫一個描述該元件 UI 形式的 JSON 模板。該 JSON 只有內容佔位符,具體內容需使用 Web IDE 針對元件編寫一個 JS 函式來填充。該函式有四個輸入:元件 JSON 模板內容、編輯搭建頁面時繫結的資料來源對應的內容資料和填寫的標題等配置資訊、手機螢幕解析度等需要參考的擴充套件資訊,返回一個輸出:描述一個 Item 的 JSON 物件。這個 JS 函式跑在基於 Nashorn 的 JS 引擎上,外包稍加培訓便可編寫。

該平臺面向多業務開放。業務申請開通後,開發後端的資料來源即可利用愛奇藝現有元件搭建頁面,也可用低程式碼平臺開發元件。

6六、效能優化萬花筒引擎的效能優化實踐

我們在引擎上實現了懶載入(Inline Requires),當需使用模組時再載入其資訊,提升了首屏載入效能。在 Android 端使用了 Hermes 引擎,效能優於 JSC。

針對引擎瘦身,我們實現了分包和精簡依賴,減少了 Bundle 體積和首屏載入耗時。愛奇藝眾多業務共享一個引擎,使用自研打包工具可將包含 RN 基礎功能的程式碼剝離出去(大約 684KB),打出只有業務程式碼的 Bundle。精簡 NPM 依賴庫,可拆出不常用的元件,解除不必要的依賴。

JS Card 方面,我們在業務分包後再次分包,用自研的 JS Card 專用打包工具進行打包,將 JS Card 中萬花筒引擎自身已經包含的依賴庫在打包的時候剔除。經過層層分包愛奇藝已上線的 JS Card 如下圖所示,體積明顯下降。

7七、開發、測試、除錯、上線相關工具生態建設

在日常開發中,我們開發了一些工具,解決 DevOps 中的各種問題。

前端開發測試工具

我們開發了註冊制二維碼生成器,生成 RN 專題頁跳轉二維碼。掃碼後即可預覽,啟動 RN 的 JS 遠端除錯工具後,即可除錯引擎或 JS Card。註冊制是愛奇藝統一的引數化路由機制,如下圖所示,在頁面中設定專題頁引數,用愛奇藝 App 掃描生成的二維碼,即可開啟對應的專題頁。

JS Card 質量監控

業務方開發的 JS Card 程式碼問題不好定位。我們實現了 JS Card 執行時異常的捕獲和投遞,建設了統一的 JS Card 打包服務。利用該打包服務,可獲取所有 source map 檔案,可在愛奇藝的 APM 系統中還原崩潰棧中被混淆的 JS 程式碼符號,效果如下所示:

端上捕獲投遞的原始 JS 崩潰棧(圖中左側),沒有詳細資訊。還原後(圖中右側)路徑檔名詳細,便於問題診斷。

元件生態

內容上我們建設了模板中心和元件市場。編輯搭建頁面時,挑選模板中心中較合適的模板稍加修改即可。

元件市場上業務方可貢獻自己的元件,複用已有的元件,共同建設元件生態。

8八、上線效果和經驗總結

Kaleidoscope 方案先後在愛奇藝、隨刻、愛奇藝票務、愛奇藝小說共 4 款 App 上落地,在 GPhone、iPhone、iPad 三端總 UV 峰值過億,主要應用於首頁頂導航特色專題、一般專題、播單、會員權益等 4 個場景,這些場景各有特點:

  • 在首頁頂導航特色專題頁上,實現了兩個 RN 專題頁之間切換效果;導航欄背景色可隨著切換頁面而變化,為使用者帶來沉浸式體驗;

  • 在會員頂導航專題頁上,大量定製化 JS Card 元件實現了眾多個性化需求,例如:吸頂導航元件,可以滾動頁面到指定的 Item;

  • 在播單上,實現了播放器根據頁面滾動位置自動開播,一個視訊播放完畢後自動滾動到下一個視訊自動開播等高階互動特性;

  • 在一般專題上,無需引擎迭代,即可快速搭建、上線頁面。

從 2018 年 12 月正式全量上線,Kaleidoscope 穩定執行至今 2 年半左右,在 GPhone、iPhone、iPad 三端總共上線過 1297 個專題頁,平均 1 天 1.4 個,最近 30 天愛奇藝 Phone 端上線了 63 個頁面,平均搭建時長 19.25 分鐘。

在這個過程中 RN+DSL+JS Card 方案滿足了各類需求,且幾乎不需要跟版;得益於 RN 跨平臺特點和簡潔靈活的架構設計,萬花筒引擎開發和維護成本極低;引擎每次變更都經過程式碼評審,保證了線上持久穩定執行。

同時,我們發現 JS Card 是一把雙刃劍,由於開發權外放,要注意效能,並進行治理;DSL 和引擎不斷迭代會帶來一些前後端相容性問題,需要引入較為複雜的版本管理機制;另外,新老框架過渡替換也會帶來的一系列問題,我們通過多輪灰度進行規避。

 嘉賓介紹

甘泉
2016 年底加入愛奇藝,先後從事過 RN 框架開發,RN 框架、Flutter 框架、小程式框架、WebView 容器優化和開發團隊管理,目前擔任 App 中臺團隊負責人。在跨平臺開發框架優化和應用方面具有豐富經驗,在 App 中臺建設方面也有一些心得。