React Native 在「元氣閱讀」的實踐

本文做者:閱文集團前端團隊前端

原創聲明:本文爲閱文前端團隊 YFE 成員出品,請尊重原創,轉載請聯繫公衆號 (id: yuewen_YFE) 獲取受權,並註明做者、出處和連接。react

前言

經歷了三個多月的集中開發,閱文集團旗下二次元產品「元氣閱讀」APP 終於在各大應用商店上架了。「元氣閱讀」APP 大部分的功能模塊基於 React Native 開發,整個開發過程前端團隊趟了很多 React Native 的坑,同時也積累了很多實踐心得,與你們一塊兒分享。android

1、業務背景與技術選型

在使用 React Native (如下簡稱RN)以前,和業界大部分團隊同樣, 咱們 APP 的開發模式採用的是客戶端(iOS/Android)內嵌 H5 的 Hybrid 開發模式。一開始,咱們除了採用比較成熟的離線包方案管理靜態資源,在首屏加載體驗上咱們也作了很多優化工做,但發現 H5 線上的體驗和性能數據與原生仍是有很多差距,因此咱們決定引入新方案。ios

RN 和 Weex 已是業界兩個相對成熟的 Hybrid 解決方案,基本能知足咱們的需求:git

  • 用戶體驗:相比於 H5 頁面,RN 和 Weex 在用戶體驗上有了很大的提高,體驗幾乎接近原生
  • 人力成本:相比於客戶端,RN 和 Weex 的一套代碼能夠跑在 iOS、Android 兩端,且代碼重用率也較高
  • 靈活發佈:RN 和 Weex 都擁有熱更新能力

最終咱們選擇了 RN 做爲解決方案,主要是考慮了幾個因素:github

  • 社區現狀:相對 Weex,RN 的社區活躍度和 Facebook React 周邊生態更好
  • 大廠背書:騰訊、京東、百度和攜程都有大型產品在線上跑
  • 團隊現狀:早在 17 年上半年,閱文前端團隊已經選型 React 做爲咱們前臺產品線的主要研發技術棧,且大部分紅員都能駕馭 React

2、應用場景

在「元氣閱讀」APP 中,使用 RN 開發的應用場景達到了 70% 左右。用戶能看到的頁面中,除了書架、註冊登陸和閱讀引擎,其它模塊幾乎都是使用 RN 完成開發,「元氣閱讀」APP 已經屬於國內大型產品中,超大規模的 RN 應用了。歡迎你們在各應用商店(iOSAndroid)搜索「元氣閱讀」下載體驗。redux

▲小說書城

▲小說書城react-native

▲漫畫書城

▲漫畫書城數組

▲元氣圈

▲元氣圈緩存

▲漫畫詳情

▲漫畫詳情

▲排行榜

▲排行榜

▲分類

▲分類

3、導航管理

對於 RN 的開發,導航的前期規劃十分重要,一般在搭建項目時就須要提早考慮。關於導航組件的選擇,react-navigation 是個不錯的選擇,咱們但願 react-navigation 能在業務場景更加通用。

一、統一跳轉規則

Native 與 RN 互跳是最多見的需求。有了統一的 URL,只需維護一份 sitemap 和實現一個 open 接口,就能夠很容易的在 Native 與 RN 中互相跳轉。

react-navigation 是使用 routeName + params 的形式跳轉的,因此須要在調用 router.getStateForAction 以前作一點調整:

// 修正 action: 容許 navigate/push/reset 動做傳 url
if (isPushLikeAction(action) || isReplaceAction(action)) {
  if (isRouteUrl(action.routeName)) {
    // 使用 path-to-regexp 庫來判斷 url 對應的 routeName + params
    const route = parseRouteByUrl(action.routeName) 
    if (route) {
      action.routeName = route.name
      action.params = route.params
    }
  }
}
複製代碼

二、實現 404 跳轉

在 Web 開發中,404 頁面是一個很常見的邏輯,參照上面的方式, RN 能夠這樣實現:

// 修正 action: 當 navigate/push/replace 跳轉到未知 routeName 時,調整爲定義的 notFoundRouteName
if (isPushLikeAction(action) || isReplaceAction(action)) {
  // 修正 action: 提供 404 能力
  if (allRouteNames.indexOf(action.routeName) === -1) {
    const oldAction = { ...action }
    action.routeName = notFoundRouteName
    action.params = { action: oldAction }
  }
}
複製代碼

三、控制頁面生命週期

在項目開發過程當中,常常碰到這樣的需求,回到原來頁面以後要刷新原頁面的數據,好比登陸以後、進入詳情頁完成某操做以後回到列表頁等。

「元氣閱讀」項目剛啓動時 react-navigation 仍是 0.x 版本,只能用 onNavigationStateChange + context 才能讓頁面感知 focus/blur 。1.x 版本以後,咱們能夠經過自帶的 addListener 方法來監聽 didFocus 或 didBlur 事件。

四、優化頁面二次打開

「元氣閱讀」是一個以 RN 爲入口的應用,在正常的使用過程當中,須要頻繁的從 RN 切換到 Native 或從 Native 切換到 RN,這樣就會有多個 RN 頁面(根組件),而第二個根組件在初始化的時候就須要定位到指定頁面,因此和 Native 約定,經過 initialRouteUrlinitialRouteName + initialRouteParams 來告訴 RN 須要定位到什麼頁面:

const navigator = getActiveNavigator() // 須要全局維護一個 Navigator 的堆棧
let nextState = originGetStateForAction(action, state) // 調用原始的 getStateForAction 獲取新的/初始化的狀態

if (navigator) {
  const { initialRouteName, initialRouteParams, goBackOnTop } = navigator.props // 讀取 navigator 的 props

  if (isInitAction(action)) {
    // 支持經過 initialRouteName & initialRouteParams 初始化到相應頁面
    if (initialRouteName) {
      const initialActionPayload = { routeName: initialRouteName, params: initialRouteParams }
      const initialAction = NavigationActions.navigate(initialActionPayload)
      nextState = router.getStateForAction(initialAction, nextState) 
       if (!isTopNavigator() && nextState.index > 0) {
        // 非第一層 RN 實例且有兩個頁面的時候(前面 navigate 到了非一級頁面),保留最後一個頁面
        nextState = {
          ...nextState,
          index: 0,
          routes: nextState.routes.slice(-1),
        }
      }
    }
  } else if (isBackAction(action)) {
    // 在第一層頁面,而且不是是第一個 Navigator,則調用 goBackOnTop 關閉 RN 
    if (isTopScren(state) && !isTopNavigator( ) && typeof goBackOnTop === 'function') { 
      goBackOnTop()
      if (nextState === state) {
        // 防止 Android 的物理返回鍵致使退出 App
        nextState = { ...nextState }
      }
    }
  }
}

return nextState
複製代碼

五、狀態本地存儲

組件 react-navigation 在 2.x 版本新增了狀態本地存儲功能,在 reload 以後能夠直接定位到以前的頁面,可是須要注意兩個點:

  • 在「元氣閱讀」這種多個根組件的業務場景,每個根組件的 rootNavigator 須要有個標記區分,建議以索引區分
  • 在頁面出錯(紅屏)以後,爲了不 reload 仍是停在當前錯誤頁,能夠在 componentDidCatch 裏面清除本地存儲

4、狀態管理與數據持久化

在「元氣閱讀」裏,咱們常常須要緩存用戶的信息、瀏覽過的書詳情信息以及用戶收到的消息等等,這樣用戶在離線訪問「元氣閱讀」時就能避免白屏或異常的狀況,並且還能夠實現「秒開」。

舉個例子,當用戶第一次打開書籍詳情頁的時候,把書書籍詳情的信息緩存下來;第二次再打開的時候,就能夠達到秒開的效果。秒開效果能夠看下圖:

▲跳轉詳情頁

▲跳轉詳情頁

咱們選擇 reduxredux-persist 搭配一塊兒使用,來實現數據共享以及數據持久化緩存。

一、redux

選擇用 redux 主要是實現數據共享的功能。經過 redux 單項數據流的特色,每一步操做都有跡可循,比較容易排查問題。

在寫 redux 的時候,可能你們以爲會須要寫不少樣板代碼。在這裏推薦一下 redux-actions 這個庫,可以幫助咱們減小一些代碼量。下面簡單的舉一下例子:

// 常見的寫法
export default (state = {}, action) => {
 switch (action.type) {
  case INCREASE:
   return {...state, total: state.total + 1}
  case DECREASE:
   return {...state, total: state.total - 1}
  default:
   return state
 }
}

// 經過 handleActions 方法
import { handleActions } from 'redux-actions'

export default handleActions({
 [INCREASE]: state => {
  ...state,
  total: state.total + 1
 },
 [DECREASE]: state => {
  ...state,
  total: state.total - 1
 }
}, initialState = {})
複製代碼

二、redux-persist

redux-persist 會訂閱 store,一旦 store 發生變化,就會觸發存儲操做。這樣當咱們操做 store 的時候,數據也就會更新到本地了。

在開發項目的時候可能會發現,咱們在 store 中共享的數據有一些多是不須要被緩存到本地的。好比說搜索結果頁,由於每次搜索的關鍵字不同,結果也是不同的,這樣的數據被緩存到本地就沒有意義。那咱們怎麼來控制一些數據不被緩存到本地呢?

redux-persist 支持配置黑白名單,意思是只持久化白名單中的數據或者不持久化黑名單中的數據。這樣就能夠根據需求來配置黑白名單,從而決定哪些數據須要被緩存到本地,哪些數據不須要被緩存。例如:

import { createStore, applyMiddleware, combineReducers } from 'redux'
import { persistReducer } from 'redux-persist'
import thunkMiddleware from 'redux-thunk'
import storage from 'redux-persist/lib/storage'

const rootPersistConfig = {
 storage,
 key: '***',
 blacklist: ['***'] // 黑名單
}

const enhancer = applyMiddleware(thunkMiddleware)
export const store = createStore(persistReducer(rootPersistConfig, rootReducer), enhancer)
export const persistor = persistStore(store)
複製代碼

5、性能優化

A compelling reason for using React Native instead of WebView-based tools is to achieve 60 frames per second and a native look and feel to your apps. Where possible, we would like for React Native to do the right thing and help you to focus on your app instead of performance optimization, but there are areas where we're not quite there yet, and others where React Native (similar to writing native code directly) cannot possibly determine the best way to optimize for you and so manual intervention will be necessary. We try our best to deliver buttery-smooth UI performance by default, but sometimes that just isn't possible.

在 RN 文檔裏看到一段關於性能的解讀,裏面提到:「目前在某些場合 RN 還不可以替你決定如何進行優化(用原生代碼寫也沒法避免),所以人工的干預依然是必要的」,咱們確實在性能優化上花費了很多精力。

一、首屏優化

運行過 RN 項目的同窗不難發現,咱們第一次進入 RN 頁面時會有一個短暫的白屏,快至幾十毫秒,慢至 1 到 2 秒,白屏時間取決於終端的性能,在低端安卓機子表現最差,並且退出後再進入,仍然會有這個白屏。咱們實施了幾個優化策略:

1)預加載 Bundle

在客戶端啓動時,就開始對 RN 的 bundle 進行預先加載,咱們發現這樣操做後,白屏操做的時間縮短了很多,特別是安卓設備。但這還不是最完美的,咱們仍然會看到很短暫的白屏。

2)優化閃屏邏輯

因爲大部分 APP 必定是先有閃屏,而後才進入首頁。咱們徹底能夠利用這個業務場景,讓 RN 程序躲在閃屏下加載,直到加載完畢,經過 Bridge 通知客戶端把閃屏關閉,這樣就比較巧妙地解決了白屏的問題。

▲before
▲before

▲after
▲after

二、交互優先

當 JavaScript 線程中同時作不少事情時,很容易就會致使線程掉幀,表現爲頁面卡頓、動畫切換緩慢,咱們可使用「交互優先」的原則去作優化。

1)優先執行用戶可感知的操做:如頁面場景切換

例如,頁面轉場這個場景。咱們就能夠把頁面邏輯放在 InteractionManager.runAfterInteractions 的回調中執行,這樣能夠優先保證轉場動畫的執行,而後纔是咱們的頁面邏輯,很好的規避了轉場卡頓的問題。

2)初始化頁面儘可能渲染少許組件

當咱們呈現一個頁面給用戶時,必定是要在最短期內讓用戶感受到頁面已經展示完畢了,因此咱們在初次展現頁面時,能夠優先顯示固定的佔位信息,配合 loading 或骨架圖佈局不肯定的部分,與此同時咱們纔在背後默默的發起請求(碰到複雜頁面,則可拆分多個異步請求),總之整個過程是先保證頁面可見,再逐步完整。

三、長列表優化

▲組件的子樹
▲組件的子樹

這是一個組件的子樹。對其中每一個組件來講,SCU 代表了 shouldComponentUpdate 的返回內容,vDOMEq 代表了待渲染的 React 元素與原始元素是否相等,最後,圓圈的顏色代表這個組件是否須要從新渲染。

在 React 中若是隻是一次這樣的組件子樹渲染,並不會有太大的性能問題。但若是對於分頁長列表這種須要成百上千次的渲染場景,會花費很大的開銷在 vDOM 的生成和 Diff 上,而這也直接致使了長列表在 RN 中嚴重的性能問題。那咱們須要作些什麼加以改進呢?先來看看這張組件更新渲染的流程圖:

▲組件更新流程
▲組件更新流程

當一個組件的 state 或者 props 改變時,就進入了生命週期函數 shouldComponentUpdate,而當 shouldComponentUpdate 返回的是 true ,就會調用 render 方法生成 Virtual Dom,隨後和舊的 Virtual Dom 進行比對,最終決定是否更新。因此從中咱們明顯地看出 SCU 和 Virtual Dom 的 Diff 是影響 Dom 更新的關鍵所在,爲此咱們分別針對這兩點作了優化:

1)控制好 shouldComponentUpdate 的更新邏輯

從上圖也能夠看出若是 shouldComponentUpdate 返回的是 false,那程序就能夠直接跳過生成 Virtual Dom 以及以後的 Diff,這對於一個大列表的場景是至關可觀的優化,例如目前咱們有一個 1000 條數據的列表,在下拉加載 20 條新數據時,若是沒有利用 shouldComponentUpdate 進行控制,會把以前的 1000 條數據也 render 一遍,而在 shouldComponentUpdate 中控制好更新邏輯,就只須要 render 最新的那20條,是否是很大的提高!不過使用 shouldComponentUpdate 要格外當心,你必定要考慮到全部影響更新的邏輯。否則會出現真正須要更新的時候卻也沒能更新。

來看一個具體的例子,場景是 APP 中的分類列表頁,咱們在每個列表項的 render 中打印 log,統計進入 render 的次數。首先來看看 shouldComponentUpdate 不作任何處理的狀況,也就是 shouldComponentUpdate 始終返回的是 true:

shouldComponentUpdate (nextProps, nextState) {
  return true
}
複製代碼

▲before
▲before

再看看咱們在 shouldComponentUpdate 中以圖片的 uri 地址過濾掉沒必要要的渲染項以後的狀況:

shouldComponentUpdate (nextProps, nextState) {
  if (nextProps.imgSrc.uri === this.props.imgSrc.uri) {
    return false
  } else {
    return true
  }
}
複製代碼

▲after
▲after

從圖中左邊的控制檯很明顯的看出,過濾後不論加載到哪一頁,都只是渲染最新的20條,減小了大量沒必要要的渲染。再比較一下在相同條件下二者加載一千條數據的時間:

結果也是顯而易見,並且在操做過程當中發現未使用 shouldComponentUpdate 的狀況下,越日後會越慢,到 1000 條數據時,再加載新數據所要等待的時間簡直沒法忍受。

2)在數組遍歷時,增長惟一標識的 key 值

若是更新是不可避免的,那隻能想辦法去提升 Virtual Dom 的 Diff 效率。咱們能夠在遍歷數組時給每一項加上惟一的 key 值,這樣在 Diff 階段,能夠準確知道要操做的子組件,提升 Diff 的效率。

四、動畫優化

合理運用動畫對於 APP 的體驗提高有很大幫助。但咱們在應用動畫時發如今有些場景會出現卡頓、掉幀的現象,本質緣由是因爲 JavaScript 是單線程的,若是線程中在跑一些比較重的任務,就可能會對動畫的性能出現影響。下面介紹幾種辦法,把動畫這件事儘可能交於原生:

1)使用 LayoutAnimation

針對一次性動畫,建議使用 LayoutAnimation,它利用了原生的 Core Animation,使動畫不會被 JS 線程和主線程的掉幀所影響。

2)使用 setNativeProps

setNativeProps 方法可使咱們直接修改基於原生視圖組件的屬性,而不須要使用 setState 來從新渲染整個組件樹。避免了渲染組件結構和同步太多視圖變化所帶來的大量開銷。

3)使用原生驅動的方式

在 Animated 動畫設定中,添加 useNativeDriver 字段,並設爲 true,這樣就能夠把動畫的執行交由原生處理。

6、發佈更新

現在因爲互聯網高速傳播的特效,事物發展的速度愈來愈快,產品快速迭代、試錯的能力就顯得尤其關鍵,做爲開發者,對咱們的挑戰就是如何讓開發完成的功能快速上線,下面來看看咱們是怎麼作的:

一、發佈

咱們選擇 Jenkins 做爲自動化部署方案。經過配置在 Jenkins 中打包腳原本實現自動打包,把 RN 的 bundle 包打到指定的位置,這樣就不用每次打包以前再手動打包了,大大提升了效率。

二、熱更新

因爲 Native 端發佈一次新版本的成本比較大,RN 的熱更新能力就成爲了很大的亮點。只須要把最新的 bundle 包發佈到服務器,就可以讓用戶手中的 app 自動下載遠端的 bundle 包,而後無感知的更新,可謂是特別的方便。

咱們通過調研,最終選擇了微軟的 CodePush。它提供給 RN 和 Cordova 開發者直接部署移動應用更新給用戶設備的雲服務,並且還開源了 RN 版本。具體接入的教程能夠查看官方網站,這裏就不一一贅述了。下面主要講幾個須要注意的點:

1)註冊 app

在 CodePush 上註冊 app 的時候,須要區分 iOS 和 Android,例如 appName-iosappName-android,在發佈的時候須要在不一樣的平臺分開發布。

2)key 的配置:Staging 和 Production

在註冊 app 的時候,會返回一套 deployment key,分別爲 Production 和 Staging 環境(後續也能夠自定義 deployment key 名稱),在集成 CodePush SDK 的時候會用到。Production 對應生產環境的 key,Staging 對應測試環境的 key。這樣就能夠分別更新不一樣環境的包。若是想要查看 app 的 deployment key 表,可使用下面的命令:

code-push deployment ls <appName> -k
複製代碼

3)RN 接入 CodePush

RN 端接入 CodePush 很是簡單,只須要在根文件中加入幾行代碼就能夠了。CodePush 傳參的時候能夠根據環境的不一樣作不一樣的配置。代碼大體爲下面這樣:

import React, { Component } from 'react'
import codePush from 'react-native-code-push' // 引入 codePush
       
  const codePushOptions = __DEV__ ? {
    updateDialog: true, // 顯示更新彈窗
    installMode: codePush.InstallMode.IMMEDIATE // 當即更新(會打斷用戶操做)
  } : {
    // 下次 app 從後臺切換到前臺時檢查更新,並下載最新的包
    checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
    // 下次重啓的時候更替換成最新的包
    installMode: codePush.InstallMode.ON_NEXT_RESTART
  }
       
  @codePush(codePushOptions)
  export default class App extends Component {
    render() {
      ...
    }
  }
複製代碼

checkFrequencyinstallMode 是可配置的,具體的配置能夠根據需求來決定。

4)版本控制

在熱更新的時候須要控制版本號,默認是當前安裝包的版本(三位數版本號),若是須要指定版本號的話,能夠在執行熱更新命令的時候加上 -t,後面跟須要更新的版本號就好了。

7、異常監控

咱們藉助了騰訊 Bugly 平臺進行線上異常的監控。Bugly 平臺能爲開發者提供異常上報與運營統計功能:

  • Bugly 會上報運行錯誤、崩潰和卡頓的異常,並提供相應的數據統計和告警機制,使咱們能夠儘量快地感知到線上異常,掌握用戶側總體的運營穩定性和流暢度的狀況;
  • Bugly 平臺提供日誌上報功能,能夠協助定位問題;
  • Bugly 平臺也能夠針對版本,機型,系統,來對比異常數據的變化。

例如,下圖是對 Crash 率的統計:

Crash 還能夠根據系統、設備和 APP 版本等維度來細化分析。

還能夠統計最影響用戶的 Top 問題:

8、一些坑和小貼士

在幾個月的開發過程當中,咱們遇到了很多坑,也發現了一些好用或者沒有被注意到的小技巧,下面和你們分享其中的一部分:

一、坑

1)Image 組件在 Android 上潛在的內存泄漏 Bug

在安卓中,加載一張尺寸遠大於容器的圖片,內存會忽然猛漲,在這張圖上下滑動,程序就直接由於內存不足而崩潰瞭如何解決呢?其實辦法也很簡單,只須要設置 Image 組件的 resizeMethod 屬性爲 resize 便可,以下圖:

▲Image的resizeMethod屬性說明
▲Image 的 resizeMethod 屬性說明

2)使用 InteractionManager.runAfterInteractions 時的注意事項

咱們知道 InteractionManager.runAfterInteractions 的回調是須要完成動畫後才執行,咱們的程序中發現過這樣一個的 bug,在點擊某個按鈕後,就怎麼也進不到 runAfterInteractions 的回調中。通過排查,原來是咱們執行了一個無限循環的動畫(loading 效果),而且沒有關閉,因此就永遠進不到 runAfterInteractions 的回調了。因此你們在開發中碰到循環動畫要注意處理。

3)使用 FlatList 列表出現頁面跳動問題

FlatList 有一個叫 getItemLayout 的優化屬性,若是你是個定高的列表項,設置這個屬性能夠大大提升列表渲染的效率。而後咱們遇到的問題是,在高度不確實的時候,也設置了這個屬性,致使最終渲染時實際高度和咱們預設的值不一致,出現了跳動。因此,若是不肯定高度,千萬別設置 getItemLayout 屬性。

▲滑動不暢,會發生跳動
▲滑動不暢,會發生跳動

▲正常滑動
▲正常滑動

4)短期內重複點擊出現多個相同頁面的問題

這不僅僅是 RN 的問題,各端應該都沒法避免。因此一般在各類技術棧的導航庫中都對此進行了修復,咱們剛開始的預期就是 React Navigation 在內部確定解決了這個問題,但發現實際上並無。因而咱們就對 React Navigation 的跳轉作了一次加強,思路是判斷下個路由的地址和上個路由一致,那就不予處理:

▲重複跳轉
▲重複跳轉

▲解決後
▲解決後

function isInCurrentState (state, nextState, routeName) {
  if(nextState && nextState.routeName === routeName && !deepDiffer(state.params, nextState.params)) {
    return true
  }
  if(nextState && nextState.routes) {
    return isInCurrentState(state.routes[state.index], nextState.routes[nextState.index], routeName)
  }
  return false
}

const nextState = originGetStateForAction(action, state)

// 避免重複跳轉
if (nextState && action.type === StackActions.PUSH) {
  if(isInCurrentState(state, nextState, action.routeName)) {
    return state
  }
}
複製代碼

二、小貼士

1) iOS 模擬器中你可能不知道的兩個選項

  • 打開虛擬鍵盤:咱們在開發輸入相關場景時,iOS模擬器默認未開啓鍵盤,須要在 HardWare->Keyboard->Toggle Software Keyboard 進行開關;
  • 慢動畫開關:不少同窗碰到這個問題,不知道點了哪一個按鍵後,模擬器中的任何操做都得比無比緩慢,各類重啓、清緩存都無效。這是因爲咱們不當心觸發了快捷鍵,打開了慢動畫模式,能夠在 Debug->Slow Animations 中關閉(快捷鍵是 command+T)。

2)原來 RN 和原生的通訊也能夠是同步的

咱們知道 RN 和原生的通訊是異步的,但若是是一些全局的常量(環境變量、版本信息等),其實能夠以同步的方式在啓動 RN 時直接掛在 NativeModules 上,這樣使用起來就很方便。

3)Image 組件一些值得關注的屬性

  • defaultSource(iOS Only):正常咱們要實現一個默認圖功能,須要先給圖片設置默認圖連接,而後在圖片下載成功的回調裏再改變狀態,替換默認圖。這個屬性就幫你作好了這些,惋惜的是隻支持 iOS。
  • getSize:當咱們要獲取圖片的寬高,而後再處理圖片相關邏輯,就能夠用這個 API。
  • prefetch:對圖片強制緩存。
  • queryCache:這個 API 能夠獲取到圖片是否緩存,若是已緩存,則下發是在硬盤仍是內存。對於要處理一些緩存邏輯仍是頗有用的,不過要注意的是雖然官方沒有標註 Android Only,咱們只在 Android 獲取成功過,iOS 並沒成功。

4)Text 組件裏一些值得關注的屬性

  • allowFontScaling(iOS Only):這個屬性用來控制是否跟隨系統字體大小。若是你的APP佈局會由於設置字體而失控,能夠考慮開啓,不過此屬性只支持 iOS,安卓須要其它方法解決。
  • selectable:這個屬性能夠用來開啓文本的複製、粘貼功能。

5)FlatList如何實現一行多列

FlatList 提供了一個叫 numColumns 的屬性,你只須要設置一行的列數,即可輕鬆實現一行多列的佈局以下圖:

▲一行三列的佈局

▲一行三列的佈局

6)調試工具

推薦使用 react-native-debugger,它集成了 Chrome 的 DevTools 以及 react-devtools ,還支持 Redux 的相關調試,能夠說是很強大了。

7)性能檢測

能夠經過客戶端自帶的軟件進行性能檢測。iOS 推薦 Xcode 自帶的 Profile;Android 推薦 Android Studio 自帶的 Android Profiler

9、總結

雖然 RN 目前還存在着一些不足,但經過「元氣閱讀」項目實踐,結果證實在人力、性能和效率上,RN 是符合咱們預期的。對於 RN 在業務場景的最佳應用,咱們也總結了幾點:

  • 重運營場景:有運營需求的場景,適合用 RN 實現,如書城頁、福利頁等
  • 快速迭代場景:功能未健全,產品需迭代試錯的功能場景,適合用 RN 實現,如元氣圈、小說書城、漫畫書城等
  • 固定信息展現場景:固定內容的信息展現頁面,適合用 RN 實現,如排行榜、本大人、一級分類頁等
  • 長列表場景
    • 因爲 RN 列表的渲染機制限制,圖+文長列表裏有大量未知尺寸的圖片,不太建議 RN 實現,如相似元氣圈、微信朋友圈場景,暴力滑屏列表有概率出現閃白;
    • 大量已知尺寸的圖片長列表、純文字長列表,性能還能夠接受

一個頁面用 Native 仍是 RN 來實現,除了考慮各端團隊人員配比,業務場景也是一個重要的考慮因素。譬如新項目中,做品詳情頁用 Native 或 RN 實現都能達到驗收目標,但考慮到做品詳情頁產品場景已經很成熟,且有很多模塊與核心閱讀頁有較多的交互,對體驗要求也特別高,咱們與終端團隊一致選擇 Native 來實現。

寫在最後

近期 Airbnb、Udacity 團隊紛紛表示棄用 RN,筆者認爲你們大可沒必要爲此憂心忡忡。Airbnb 列舉的條例,其中很多項是可優化,或者結論是有待考究的;另一些也有公司內部自身存在的問題。最近 Facebook 團隊宣佈正在努力打造一次大的升級,其中提到的對線程模型、異步渲染和橋接的優化方向,也讓咱們十分期待,咱們有理由相信 RN 的將來會更好,也但願能經過這篇分享有更多的同窗加入 RN 的你們庭,共同打造更好的 RN 生態。

更多分享,請關注YFE:

相關文章
相關標籤/搜索