亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

React?Native?的動(dòng)態(tài)列表方案探索詳解

 更新時(shí)間:2022年09月14日 11:17:16   作者:網(wǎng)易云音樂技術(shù)團(tuán)隊(duì)  
這篇文章主要為大家介紹了React?Native?的動(dòng)態(tài)列表方案探索示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

時(shí)至2022,精細(xì)化運(yùn)營已經(jīng)成為了各大App廠商的強(qiáng)需求,阿里的 DinamicX、Tangram 大家應(yīng)該都很熟悉了,很多App廠商也自研了一些類似框架,基于DSL的動(dòng)態(tài)化方案雖然有性能上的一些優(yōu)勢,但是畢竟不是圖靈完備,一些需要邏輯動(dòng)態(tài)下發(fā)的需求實(shí)現(xiàn)成本偏高,或由于DSL本身限制無法實(shí)現(xiàn),針對這個(gè)問題我們使用RN進(jìn)行了一下探索嘗試, 利用我們已經(jīng)相對完善的RN基建,結(jié)合客戶端列表能力低成本的實(shí)現(xiàn)了一套的動(dòng)態(tài)化能力,同時(shí)兼顧一定的性能體驗(yàn)。

基于 ReactNative 的動(dòng)態(tài)列表方案簡單來說就是將 ReactNative 容器內(nèi)嵌在 RecyclerView 的 ViewHolder 中,由于頁面主體框架還是由 Native 開發(fā)和渲染,所以首屏加載速度得到了保證,局部的RN實(shí)現(xiàn)也使頁面獲得動(dòng)態(tài)化的能力,從而在性能、”完備邏輯執(zhí)行“的動(dòng)態(tài)化能力之間取得了一個(gè)平衡點(diǎn),根據(jù)我們使用經(jīng)驗(yàn)對幾種動(dòng)態(tài)化方案排序如下:

  • 整體性能體驗(yàn)排序: 純 Native > 基于DSL動(dòng)態(tài)化方案 >= ReactNative 動(dòng)態(tài)列表方案 > 純 ReactNative 頁面 > H5
  • 動(dòng)態(tài)能力排序: H5 = 純 ReactNative 頁面 > ReactNative 動(dòng)態(tài)列表方案 > 基于DSL動(dòng)態(tài)化方案 > 純 Native
  • 實(shí)現(xiàn)能力排序: 純 Native >= RN 動(dòng)態(tài)列表方案 = 純 ReactNative 頁面 > H5 > 基于DSL的動(dòng)態(tài)化方案

從以上排序中可以看出 ReactNative 動(dòng)態(tài)列表方案整體處于中等或中等偏上的一個(gè)位置,在實(shí)現(xiàn)能力上遠(yuǎn)勝余基于 DSL 動(dòng)態(tài)方案,和 Native 能力基本對等,可以實(shí)現(xiàn)一些復(fù)雜的UI交互效果,并且相比于純 RN 實(shí)現(xiàn)的頁面首屏速度會(huì)有非常大的優(yōu)勢,另外不需要對頁面整體框架進(jìn)行更改就能比較方便的嵌入,在開發(fā)維護(hù)成本上 RN 動(dòng)態(tài)列表方案相比各種基于DSL的動(dòng)態(tài)化方案會(huì)有比較明顯的優(yōu)勢,不需要額外的開發(fā)組件管理平臺(tái),排查問題時(shí)也不用去讀難懂的 dsl,最重要的是 RN 具有圖靈完備的能力,所以綜合來看使用 RN 內(nèi)嵌到 Native RecyclerView 來實(shí)現(xiàn) Native 頁面部分動(dòng)態(tài)化的方式算是一種性價(jià)比相對較高的方式了,值得一試。

技術(shù)方案介紹

這里從 Android 視角分享下我們這套方案實(shí)現(xiàn)的一些技術(shù)細(xì)節(jié)、原理以及遇到的問題。首先我們常用的一些術(shù)語:

  • moduleName 是 RN 離線包的唯一 key,相當(dāng)于離線包的名字;
  • componentName 是 RN 中 registerComponent 的 component,對應(yīng)一個(gè) RN 實(shí)現(xiàn)的業(yè)務(wù)的執(zhí)行入口;
  • 卡片指云音樂首頁中每個(gè) viewholder 內(nèi)部的展示內(nèi)容,展示的 UI 樣式是卡片樣式;
  • RN 引擎指以 RN Bridge 為主的整個(gè) JS 離線包運(yùn)行時(shí)環(huán)境。

整體方案架構(gòu)如下:

從圖中可以看出整體方案采用數(shù)據(jù)驅(qū)動(dòng)的方式,服務(wù)端通過數(shù)據(jù)中攜帶的類型、component、moduleName等字段來唯一指定是否是使用 RN 來渲染,執(zhí)行 RN 離線包中的哪個(gè) component 邏輯

整體方案上有幾個(gè)細(xì)節(jié)點(diǎn):

  • 采用數(shù)據(jù)驅(qū)動(dòng)的方式,接入頁面無須關(guān)注具體展示數(shù)據(jù),只需要將數(shù)據(jù)透傳到 RN 的 JS 側(cè)即可
  • 由于 RN 需要將離線包加載后才能執(zhí)行 JS 生成客戶端視圖,在 RecyclerView 綁定數(shù)據(jù)時(shí)才開始加載 RN 的離線包勢必會(huì)拖慢整個(gè)模塊的展示,所以這里我們做了整個(gè)離線包的預(yù)加載
  • 首頁列表中每個(gè) ViewHolder 的展示元素我們叫做一個(gè)卡片,目前采取的策略是多個(gè)卡片放在一個(gè) RN 的離線包中,通過同一個(gè) RN 容器來分別展示,避免多個(gè)容器消耗過多的資源。

下面從數(shù)據(jù)流角度拆解整個(gè)方案,整體方案可以分為服務(wù)端數(shù)據(jù)定義和下發(fā),容器數(shù)據(jù)透傳,JS側(cè)數(shù)據(jù)解析三個(gè)主要步驟:

  • 服務(wù)端數(shù)據(jù)定義和下發(fā)

由于是服務(wù)端接口驅(qū)動(dòng) RecyclerView 中內(nèi)容展示,接口下發(fā)數(shù)據(jù)中需要有type字段標(biāo)識(shí)使用RN還是Native展示,可以服用Native展示樣式標(biāo)記字段,由于RN中具體展示的樣式和運(yùn)行哪些 JS 代碼直接相關(guān),所以服務(wù)端下發(fā)的數(shù)據(jù)中需要帶上對應(yīng)的 moduleName 和 componentName,整體數(shù)據(jù)結(jié)構(gòu)定義如下:

[    {  "type":"rn",        
                "rnInfo":{            
                "moduleName":"bizDiscovery",            
                "component":"hotSong",           
                "otherInfo":{            
                }        
        },        
         "data":{            
         "songInfo":{           
        }        
    }    
    },    
    {       
"type":"dragonball",        
"data":{            
"showInfo":{           
}        
}    
}]

獲取到數(shù)據(jù)之后只需要按照 RecyclerView 正常的使用方法將數(shù)據(jù)和不同的 ViewHolder 綁定即可

  • 容器數(shù)據(jù)透傳

RN 容器直接直接內(nèi)嵌在 ViewHolder 中,在 viewHolder 中只需要定義承載 RN JS 渲染視圖的 ViewGroup container,RN Bridge 創(chuàng)建好 ReactRootView 后將創(chuàng)建好的 ReactRootView 調(diào)用 add 方法添加到 container 中即可,數(shù)據(jù)傳遞是透傳的方式通過 RN 的 initialProperty 傳入到 JS 側(cè),在 JS 側(cè)解析和使用,數(shù)據(jù)傳遞代碼如下:

mReactRootView?.startReactApplication(reactInstanceManager, componentName, initialProperties)

這里面需要注意的點(diǎn)是,由于所有使用RN展示的卡片都是對應(yīng)的相同的 RecyclerView type 即相同的 ViewHolder,所以在 RecyclerView 復(fù)用時(shí)可能會(huì)出現(xiàn)兩種情況:

1. 只有一個(gè) RN 卡片,上下滑動(dòng) RecyclerView 時(shí)發(fā)生復(fù)用,這時(shí)基本不用處理,

2. 存在兩種不同類型的 RN 卡片,復(fù)用時(shí)會(huì)運(yùn)行完全不同的離線包代碼,這種情況會(huì)導(dǎo)致 JS 側(cè)重新執(zhí)行渲染邏輯生成全新的視圖,上下滾動(dòng)時(shí)如果每次都出現(xiàn) JS 側(cè)重新渲染,會(huì)極大的影響滑動(dòng)時(shí)性能,造成滑動(dòng)卡頓掉幀,針對這種問題我們對 RN 的 ReactRootView 也做了緩存,

整體架構(gòu)如下:

從圖中可以看到 ViewHolder 中的 container 和 RN 的 ReactRootView 是一對多的關(guān)系,RN 的 ReactRootView 在第一次初始化完成后還是掛在 RN 管理的虛擬視圖樹中,在 RecyclerView 滑動(dòng)切換不同的展示類型時(shí)只需要從 ViewHolder 的 container 中移除不展示的ReactRootView 再重新 add 需要展示的 ReactRootView,不需要 JS 側(cè)重新執(zhí)行,重新 add ReactRootView 之后還需要將當(dāng)前的數(shù)據(jù)再傳入 JS 側(cè)以適配相同樣式的卡片展示不同數(shù)據(jù)的需求。這里面的原理是一般情況下我們一個(gè) RN Bridge 只會(huì)創(chuàng)建一個(gè) ReactRootView,但是查看 RN 源碼,RN 其實(shí)支持一個(gè) RN Bridge 綁定多個(gè) RootView 的能力,代碼如下:

  public void addRootNode(ReactShadowNode node) {
    mThreadAsserter.assertNow();
    int tag = node.getReactTag();
    mTagsToCSSNodes.put(tag, node);
    mRootTags.put(tag, true);
  }

一個(gè) ReactRootView 即一棵視圖樹,RN在更新客戶端視圖時(shí)都會(huì)遍歷所有的 ReactRootView,代碼如下:

  protected void updateViewHierarchy() {
    ....
    try {
      for (int i = 0; i < mShadowNodeRegistry.getRootNodeCount(); i++) {
        int tag = mShadowNodeRegistry.getRootTag(i);
        ReactShadowNode cssRoot = mShadowNodeRegistry.getNode(tag);
        if (cssRoot.getWidthMeasureSpec() != null && cssRoot.getHeightMeasureSpec() != null) {
          ...
          try {
            notifyOnBeforeLayoutRecursive(cssRoot);
          } finally {
            Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
          }
          calculateRootLayout(cssRoot);
          ... 
          try {
            applyUpdatesRecursive(cssRoot, 0f, 0f);
          } finally {
          }
          ...
        }
      }
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }
  }

所以即使使用多個(gè) ReactRootView RN 的渲染邏輯也可以正常執(zhí)行,這里一個(gè) ReactRootView 即對應(yīng) JS 實(shí)現(xiàn)中的一個(gè) Component,我們在運(yùn)行 RN 業(yè)務(wù)代碼會(huì)看到 startApplication 的實(shí)現(xiàn)在 ReactRootView 中,startApplication 傳入的參數(shù)就是 Component,對應(yīng)代碼如下:

public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle initialProperties,
      @Nullable String initialUITemplate) {
      ...
  }
}

到此客戶端側(cè)的重點(diǎn)實(shí)現(xiàn)基本完成了,接下來就是JS側(cè)。

  • JS 側(cè)寫法變化

JS 側(cè)的對于卡片開發(fā)的寫法和正常的 RN 開發(fā)基本相同,唯一的區(qū)別是需要同時(shí)注冊多個(gè) component,客戶端每個(gè)業(yè)務(wù)卡片啟動(dòng)時(shí)只需要啟動(dòng)對應(yīng)的 Component 即可,代碼示例如下:

AppRegistry.registerComponent('hotTopic', () => EStyleTheme(HotTopic));
AppRegistry.registerComponent('musicCalendar', () => EStyleTheme(MusicCalendar));
AppRegistry.registerComponent('newSong', () => EStyleTheme(NewSong));
  • JS 和 Native 通信

至此整個(gè)渲染流程都已經(jīng)介紹完成,卡片已經(jīng)可以正常展示,不過既然RN具有圖靈完備的能力,勢必會(huì)有一些用戶交互導(dǎo)致的UI變化,比如點(diǎn)擊卡片上的 ”叉“ 的不感興趣操作,點(diǎn)擊后需要通知客戶端彈出客戶端的不感興趣組件,多個(gè)卡片對應(yīng)同一個(gè) JS 引擎,JS 和 Native 的通信通道也是復(fù)用的,怎么決定由哪個(gè)卡片來彈出呢,我們的做法是在卡片第一次渲染時(shí)就使用時(shí)間戳的哈希值生成唯一的 key,將這個(gè) key 作為 Native 側(cè)和 JS 側(cè)區(qū)分不同業(yè)務(wù)的唯一標(biāo)識(shí),和具體展示的業(yè)務(wù)卡片關(guān)聯(lián)起來在雙側(cè)都存儲(chǔ)起來,這樣后續(xù)每次通信時(shí)雙側(cè)就可以通過 key 來確認(rèn)通信的對象,確保不會(huì)導(dǎo)致通信混亂。

  • RN 引擎預(yù)熱

在整個(gè) RN 的執(zhí)行周期中離線包加載一般也會(huì)消耗比較多的時(shí)間,所以為了盡可能的提升性能,我們還對頁面卡片對應(yīng)的整個(gè)離線包進(jìn)行了預(yù)熱,即提前將離線包加載到內(nèi)存中并準(zhǔn)備好業(yè)務(wù)邏輯的運(yùn)行時(shí)環(huán)境,預(yù)熱只需要?jiǎng)?chuàng)建好 ReactInstanceManager 并調(diào)用createReactContextInBackground() 即可,調(diào)用后整個(gè)離線包會(huì)被交給 JS 引擎進(jìn)行預(yù)處理,代碼如下:

ReactInstanceManager.builder()
                            .setApplication(ApplicationWrapper.getInstance())
                            .setJSMainModulePath("index.android") 
                            .addPackage(MainReactPackage())
                            ...
                            .build()
                            .createReactContextInBackground()

這里還需要注意的一個(gè)點(diǎn)是代碼調(diào)試能力,采用內(nèi)嵌的方式如果原來頁面已經(jīng)有搖一搖這種手勢, RN 原生的調(diào)試菜單會(huì)無法呼出,這里需要增加額外的交互方式來解決,我們在卡片上增加了一個(gè)懸浮按鈕。

到此整體框架就都已介紹完畢,在框架之外內(nèi)存占用和合理的異常處理也是需要考慮的重點(diǎn)。

內(nèi)存

在整體技術(shù)實(shí)現(xiàn)之外,我們另外關(guān)注的一個(gè)重點(diǎn)就是內(nèi)存占用,我們對以RN Bridge為核心的RN容器內(nèi)存占用進(jìn)行了統(tǒng)計(jì),使用Profiler工具獲取數(shù)據(jù)如下:

 無RN容器(native/java)1 RN容器(native/java)2 RN容器(native/java)3 RN容器(native/java)5 RN容器(native/java)
紅米k30pro 6G148/54.6154/56157/55.7153/56.7208/59.8
谷歌Pixel 2XL 4G137.8/60163/73176/83186/91196/101
紅米k30 8G118/52143/56136/55138/56142/60

整體看來在5個(gè)以內(nèi)RN容器的情況整體內(nèi)存并沒有增加很多,內(nèi)存占用整體在可控狀態(tài),由于此方案采用了一個(gè) RN Bridge 對應(yīng)多個(gè)卡片的方式,所以相當(dāng)于只新增一個(gè)Bridge,對內(nèi)存影響較小,實(shí)際線上運(yùn)行也沒有新增 OOM 問題。

異常處理

  • 出現(xiàn)異常如何處理

不管是 JS 寫法原因還是 ReactNative 本身的穩(wěn)定性原因,總有一定概率會(huì)有異常出現(xiàn),這時(shí)需要合理的邏輯處理保證功能和用戶體驗(yàn)不會(huì)受到比較大的影響,我們當(dāng)前的處理策略是異常監(jiān)聽還是使用 NativeExceptionHandler 來監(jiān)聽 SoftException 和 FatalException,異常時(shí)在統(tǒng)一的回調(diào)中通知上層業(yè)務(wù)(recyclerView 層),然后根據(jù)具體的業(yè)務(wù)情況,由業(yè)務(wù)層統(tǒng)一消除或者重建 RN 容器,保證體驗(yàn)不受影響或者影響較小,以云音樂首頁使用場景為例目前卡片總 PV 約 1 億,錯(cuò)誤率不到萬分之一,整體運(yùn)行情況穩(wěn)定,無相關(guān)用戶反饋。

  • RN版本升級導(dǎo)致和數(shù)據(jù)不兼容如何處理

RN 使用離線包策略,為保證用戶能正常獲取到離線包和保證離線包能快速高效的更新,我們采取了兜底包集成、更新信息服務(wù)端接口搭車等策略,不過受限于用戶的機(jī)型地區(qū)、網(wǎng)絡(luò)狀態(tài)等原因還是存在一定概率的更新不成功,對于這種情況我們將當(dāng)前 RN 離線包支持的卡片信息保存在離線包的配置文件中,通過離線包獲取的接口暴露給業(yè)務(wù)方,業(yè)務(wù)在運(yùn)行離線包前可以根據(jù)配置信息對網(wǎng)絡(luò)請求結(jié)果進(jìn)行過濾,保證新版數(shù)據(jù)匹配舊版的離線包時(shí)不會(huì)導(dǎo)致異常。

未來規(guī)劃

短期內(nèi)我們希望將 RN 動(dòng)態(tài)列表方案結(jié)合我們已有的 RN 低代碼能力,實(shí)現(xiàn)首頁運(yùn)營動(dòng)態(tài)搭建發(fā)布,另一方面主要在性能提升,我們目前還是使用的 RN 0.60.5 版本,JS 的執(zhí)行效率和當(dāng)前版本的多線程框架是我們的最大的瓶頸,之后我們會(huì)在新架構(gòu)上進(jìn)行更多的嘗試。

以上就是React Native 的動(dòng)態(tài)列表方案探索詳解的詳細(xì)內(nèi)容,更多關(guān)于React Native 動(dòng)態(tài)列表的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • react中使用better-scroll滾動(dòng)插件的實(shí)現(xiàn)示例

    react中使用better-scroll滾動(dòng)插件的實(shí)現(xiàn)示例

    滾動(dòng)在很多地方都可以使用,本文主要介紹了react中使用better-scroll滾動(dòng)插件的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • react antd checkbox實(shí)現(xiàn)全選、多選功能

    react antd checkbox實(shí)現(xiàn)全選、多選功能

    目前好像只有table組件有實(shí)現(xiàn)表格數(shù)據(jù)的全選功能,如果說對于list,card,collapse等其他組件來說,需要自己結(jié)合checkbox來手動(dòng)實(shí)現(xiàn)全選功能,這篇文章主要介紹了react antd checkbox實(shí)現(xiàn)全選、多選功能,需要的朋友可以參考下
    2024-07-07
  • 在react項(xiàng)目中使用antd的form組件,動(dòng)態(tài)設(shè)置input框的值

    在react項(xiàng)目中使用antd的form組件,動(dòng)態(tài)設(shè)置input框的值

    這篇文章主要介紹了在react項(xiàng)目中使用antd的form組件,動(dòng)態(tài)設(shè)置input框的值,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • react-router v4如何使用history控制路由跳轉(zhuǎn)詳解

    react-router v4如何使用history控制路由跳轉(zhuǎn)詳解

    這篇文章主要給大家介紹了關(guān)于react-router v4如何使用history控制路由跳轉(zhuǎn)的相關(guān)資料,文中通過示例代碼介紹的的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • React網(wǎng)絡(luò)請求發(fā)起方法詳細(xì)介紹

    React網(wǎng)絡(luò)請求發(fā)起方法詳細(xì)介紹

    在編程開發(fā)中,網(wǎng)絡(luò)數(shù)據(jù)請求是必不可少的,這篇文章主要介紹了React網(wǎng)絡(luò)請求發(fā)起方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-09-09
  • 編寫簡潔React組件的小技巧

    編寫簡潔React組件的小技巧

    這篇文章主要介紹了編寫簡潔React組件的小技巧,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下
    2021-04-04
  • React-native橋接Android原生開發(fā)詳解

    React-native橋接Android原生開發(fā)詳解

    本篇文章主要介紹了React-native橋接Android原生開發(fā)詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-01-01
  • React項(xiàng)目中className運(yùn)用及問題解決

    React項(xiàng)目中className運(yùn)用及問題解決

    這篇文章主要為大家介紹了React項(xiàng)目中className運(yùn)用及問題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • React中常見的動(dòng)畫實(shí)現(xiàn)的幾種方式

    React中常見的動(dòng)畫實(shí)現(xiàn)的幾種方式

    本篇文章主要介紹了React中常見的動(dòng)畫實(shí)現(xiàn)的幾種方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-01-01
  • react函數(shù)組件類組件區(qū)別示例詳解

    react函數(shù)組件類組件區(qū)別示例詳解

    這篇文章主要為大家介紹了react函數(shù)組件類組件區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08

最新評論