React Native 的未来与React Hooks

2019-04-21 15:50:55 浏览数 (1)

近期和一些朋友聊到了 React-Native 的官方重构状态,而刚好近期发布的 0.59.x 系列版本中,上层设计出现了比较大的调整,结合体验之后的状态,就想聊聊 React-Native 的现状、新版本的升级体验、还有新支持的 React Hook 等特性。

本篇并不是源码解析和教程,更多是讨论和记录描述。笔者一直致力于 AndroidReact-NativeFlutter 等大前端开发,有时也会写写 ReactVue,本篇文章也是希望能够和大家交流,可以的话欢迎提出问题或者建议,最后同样希望文章能对你有所启发。

皮一下,React-Native 项目发布4年多了,还没有 1.0 版本么(¬_¬)

一、现状

相信大家对于 React-Native “要凉” 的第一印象,应该是来自于 Aribnb 的 “为什么 Airbnb 放弃了 React Native” ,如文中描述的 React-Native 确实会遇到一些性能瓶颈,但这取决于和谁对比,个人认为 代码是服务于业务的,抛开场景比性能的做法其实并不严谨。 关键还是在于你如何使用,并且官方与社区是否还活跃和优化。

先说我对跨平台的理解: 一套逻辑可以在多个平台运行,更多是避免各平台业务逻辑不统一,而对工作量的减轻是不明显!不明显!不明显的! 同时一个企业项目大了之后,一般也不会局限于一个框架之内。

事实上 Facebook 也并没有放弃 React-Native ,在经历 《Facebook 正在重构 React Native,将重写大量底层》 的官宣之后,“四舍五入”将近一年后的今天,底层重构虽然还没有正式发布,但是近期的新版本 0.59.x 也给出了不错的答卷。

新版本中主要有以下几点:

  • 1、减轻了 React-Native 自身框架,将 webView 、viewPager、netinfo、async-storage 等内置包拆分,通过社区独立维护,并逐步模糊 ReactReact-Native 的界限。
  • 2、更新 JavaScriptCore 、upgrade 和 CLI 工具。
  • 3、支持 React Hooks
  • 4、修复了 FlatList 等列表控件中的诸多问题。

未来版本的重构主要目标有:

  • 1、减轻 JSBridge 的依赖。
  • 2、通过 Fabric UI架构,将 Shadow 层、 UIManagerNativeModule 从 Java 移到 C 中,从而支持 双向的同步和异步渲染与调用

可以看出 0.59 版本中的重构和拆分,都是在为了下一步的重构做准备,更多具体的下一代重构内容分析,可以在京东的 《庖丁解牛!深入剖析 React Native 下一代架构重构》 中查阅,这里就不多赘述了。

同样在携程的项目中: 《携程开源RN开发框架CRN》 文章也表示在第一时间更新到了 0.59.x 版本,现在还会觉得 React-Native “要凉” 了嘛?

题外话 : 如今的编程界里存在各种“党争”,比如前端中 VueReactAngular ,跨平台的 CordovaWeexReact-NativeFlutter 等,而我在考虑选择框架时,一般会从以下几点优先级先后排序:

  • 1、框架的活跃度。
  • 2、你的业务需求复杂度。
  • 3、团队配置和团队成员技术风格。
  • 4、个人对框架的舒适度。

二、React-Native 0.59.x

在选择升级版本之前,我们需要了解 React-Native 中版本是有 0.A.B 的大 A 小 B 版本号设定,而在 React-Native 使用过程中我的一个感受就是:

在做 React-Native 的版本选择或升级时,最好不要选用 0.A.0 版本,比如 0.59.0;我一般会选择大版本之后的小版本迭代,如 0.59.4 版本去升级更新,这样的版本相对更稳定,可以少躺一些问题。

然后 React-Native 的版本升级一直是个头大的问题,我一般会先在自己的开源项目中躺坑,本次在我的开源项目 GSYGithubAPP 中,是从 0.57.8 直接升级到 0.59.4 版本,结果如预期一般并不顺利,而一般 React-Native 的版本升级,带来的问题主要有三类:

1、官方 API 的调整

一般这类问题都比较好解决,官方的更新文档也有详细说明,这次升级中主要是将原本 React-Native 自带的 webViewnetinfoasync-storage 等插件替换到 react-native-community 下提供,并替换一些弃用 API 。

2、第三方库不兼容 :

这也是 React-Native 中比较头疼的问题,因为第三方包的维护参差不齐,基本上如果作者不维护或维护不及时,那就只能自己苦笑动手了,就像本次 GSYGithubAPP 在升级过程中就遇到有:

  • 升级后遇到 realm 库在 Xcode 上的编译错误错误,详细可见 GSYGithubAPP#66 ,虽然问题不大,可自行通过简单本地改库解决,这也是目前项目的升级还未合并到 master 的原因之一。
  • react-native-router-fluxreact-navigation 的升级版本需要相互对应,同时需要增加 react-native-gesture-handler 依赖,并且在 index.js 入口处提前导入来解决一些问题。
  • 各类第三方插件的 Android targetSdksupportSdk 等版本和依赖方式问题。

3、node_module “黑洞”

这类问题属于看人品,比如 GSYGithubAPP 项目是从 0.57 升级到 0.59 的,而 BackAndroid0.58 已经被完全弃用,其中项目刚好存在一个 modal 插件使用了 BackAndroid ,虽然作者也更新了插件做兼容,但是····

在更新了插件之后,重新运行后却依旧报错?WTF,而明插件源码已经没有 BackAndroid 的痕迹,那错误哪里来的?

通过 Chrome 的 Debug 查看当前 bundle 源码,最后发现居然真的有BackAndroid 的存在,当时就判断妥妥的缓存问题。

在执行了无数遍的卸载 APP,关闭CLI,删除 node_module 重装后,最终还是通过删除缓存 rm -rf ~/.rncacherm -rf $TMPDIR/* ,再重新安装node_module 运行才解决问题。

  • 总结

其实这也是为什么我说 React-Native 等跨平台开发,其实并没有降低工作量的原因。跨平台解决的是逻辑统一维护,而开发中过程中,很多时候会遇到兼容开发的问题,并且平台之间的适配同样消耗时间。

我相信每个 React-Native 开发人员都十分讨厌满屏幕的红色,所以不知哪一版开始, React-Native 把错误增加了红黑相间的效果(¬_¬)。

三、React Hooks

React Hooks 其实也是我升级到 0.59 的目的之一,因为它确实是一个很有意思的设定。

事实上我并非严格意义上的前端人员,大部分时候我对 CSSES 的了解也不深入,但在 JS 的使用过程中有几个让我印象深刻的:

  • ReduxRedux 的状态管理设计,且由它衍生出的一系列后续和第三方插件,我个人觉得这是 React 当初能快速的风靡的助力之一。
  • HOCES7 Decorators :事实上这应该也包含在 Redux 里, 但是 HOC Decorators 快速实现类似切面编程的效果,这无疑让 Java 开发的我感到亲切。

最后就是本文主角 React Hooks 了,React Hooks 也算是比较新的概念,关于 React Hooks 的我推荐这篇文章: 《【React深入】从Mixin到HOC再到Hook》 ,文中很好的描述了 React 开发风格的发展和对比。

而对于 React Hooks 能在这么早就引入到 React-Native 中,给我的感觉就是 Facebook 团队在致力于模糊 React 开发者在 Web 和 App 之间的边界,同时这也是为了丰富 React 开发者的生态吧。

而对于 React Hooks ,在我的理解上而言,函数式编程可能更贴近“未来”的形态(虽然我并不特别确定),而 React Hooks 确实有着明显的优势:

  • 可以更好的减少我们的代码量。
  • 同时降低代码在生命周期执行过程中造成的阻塞。
  • 自定义 Hooks 可以在一定程度上解耦,增加复用,减少嵌套。
  • 函数式编程的风格让函数功能独立,代码简洁更好阅读。

回归到具体使用, React Hooks 其中最常用默认接口有 :

  • useState 可以让你在函数中快速添加状态
  • useEffect 让你快速添加生命周期处理
  • useImperativeHandle 快速对外暴露接口

这些内置 Hook 可以在一定程度上节省你的代码量,并且提供清晰的状态管理逻辑,同时利用官方的 useReducer ,如下方代码,更可以快速写出一个伪 Redux

代码语言:javascript复制
import React, {Component, useReducer, useRef, useImperativeHandle, forwardRef} from 'react';
import {Text, View, TouchableOpacity,} from 'react-native';

const initialState = {count: 0};

function reducer(state, action) {
    switch (action.type) {
        case 'reset':
            return initialState;
        case 'increment':
            return {count: state.count   1};
        case 'decrement':
            return {count: state.count - 1};
        default:
            return state;
    }
}

export function DemoCounter({initialCount}) {
    const [state, dispatch] = useReducer(reducer, {count: initialCount});
    return (
        <View>
            <Text>Count: {state.count}</Text>
            <TouchableOpacity onPress={() => dispatch({type: 'reset'})}>
                <Text>Reset</Text>
            </TouchableOpacity>
            <TouchableOpacity onPress={() => dispatch({type: 'increment'})}>
                <Text> </Text>
            </TouchableOpacity>
            <TouchableOpacity onPress={() => dispatch({type: 'decrement'})}>
                <Text>-</Text>
            </TouchableOpacity>
        </View>
    )
}

对于 React Hooks ,结合查阅源码和文章简单理解,就在渲染之前利用系列的钩子,而 Hooks 内部利用了数组 ,实现状态数据的顺序更新。

所以官方也表示了,Hooks 不能在循环或者条件判断中使用,这属于一种约定,因为 Hooks 内的数组每次都是顺序的调用的,如果在条件判断中打乱了顺序,将导致游标无法匹配到正确的数据,所以约定了不要在 if 或者 for 中使用 useState 等行为。

关于 React Hooks 相关更详细的干货,推荐查阅:

  • 《react hook的初步研究》
  • 《React hook 不是魔法,是数组》
最后说说编码风格:

无论是 HOCReact HooksRedux 等,其实我觉得都不存在所谓最优解,具体选择使用还是得看业务场景,过度为了设计而设计,杀鸡用牛刀的后果就是很不顺手,而且还容易误伤

如果是个人开发,show 代码亮逼格这无可厚非,但如果是实际团队开发,最好还是需要考虑团队的合作选型,不然你写的代码只有你能维护,估计最后哭的还是自己。

好了,本篇到此结束!(///▽///)

跨平台完整项目与文章:
  • Flutter 开源项目与文章
  • React Native 开源项目与文章
  • Weex 开源项目与文章

完整文章目录在项目首页 ReadMe

其他文章

《移动端跨平台开发的深度解析》

我们还会再见吗?

0 人点赞