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:

相关文章
相关标签/搜索