近期和一些朋友聊到了 React-Native
的官方重构状态,而刚好近期发布的 0.59.x 系列版本中,上层设计出现了比较大的调整,结合体验之后的状态,就想聊聊 React-Native
的现状、新版本的升级体验、还有新支持的 React Hook 等特性。
本篇并不是源码解析和教程,更多是讨论和记录描述。笔者一直致力于 Android
、React-Native
、Flutter
等大前端开发,有时也会写写 React
和 Vue
,本篇文章也是希望能够和大家交流,可以的话欢迎提出问题或者建议,最后同样希望文章能对你有所启发。
皮一下,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 等内置包拆分,通过社区独立维护,并逐步模糊React
和React-Native
的界限。 - 2、更新 JavaScriptCore 、upgrade 和 CLI 工具。
- 3、支持 React Hooks 。
- 4、修复了
FlatList
等列表控件中的诸多问题。
未来版本的重构主要目标有:
- 1、减轻 JSBridge 的依赖。
- 2、通过 Fabric UI架构,将
Shadow
层、UIManager
、NativeModule
从 Java 移到 C 中,从而支持 双向的同步和异步渲染与调用 。
可以看出 0.59 版本中的重构和拆分,都是在为了下一步的重构做准备,更多具体的下一代重构内容分析,可以在京东的 《庖丁解牛!深入剖析 React Native 下一代架构重构》 中查阅,这里就不多赘述了。
同样在携程的项目中: 《携程开源RN开发框架CRN》 文章也表示在第一时间更新到了 0.59.x 版本,现在还会觉得 React-Native
“要凉” 了嘛?
题外话 : 如今的编程界里存在各种“党争”,比如前端中
Vue
、React
、Angular
,跨平台的Cordova
、Weex
、React-Native
、Flutter
等,而我在考虑选择框架时,一般会从以下几点优先级先后排序:
- 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
自带的 webView
、netinfo
、 async-storage
等插件替换到 react-native-community 下提供,并替换一些弃用 API 。
2、第三方库不兼容 :
这也是 React-Native
中比较头疼的问题,因为第三方包的维护参差不齐,基本上如果作者不维护或维护不及时,那就只能自己苦笑动手了,就像本次 GSYGithubAPP
在升级过程中就遇到有:
- 升级后遇到
realm
库在 Xcode 上的编译错误错误,详细可见 GSYGithubAPP#66 ,虽然问题不大,可自行通过简单本地改库解决,这也是目前项目的升级还未合并到master
的原因之一。 react-native-router-flux
与react-navigation
的升级版本需要相互对应,同时需要增加react-native-gesture-handler
依赖,并且在index.js
入口处提前导入来解决一些问题。- 各类第三方插件的 Android
targetSdk
和supportSdk
等版本和依赖方式问题。
3、node_module “黑洞” :
这类问题属于看人品,比如 GSYGithubAPP
项目是从 0.57 升级到 0.59 的,而 BackAndroid
在 0.58 已经被完全弃用,其中项目刚好存在一个 modal
插件使用了 BackAndroid
,虽然作者也更新了插件做兼容,但是····
在更新了插件之后,重新运行后却依旧报错?WTF,而明插件源码已经没有 BackAndroid
的痕迹,那错误哪里来的?
通过 Chrome 的 Debug 查看当前 bundle
源码,最后发现居然真的有BackAndroid
的存在,当时就判断妥妥的缓存问题。
在执行了无数遍的卸载 APP,关闭CLI,删除 node_module
重装后,最终还是通过删除缓存 rm -rf ~/.rncache
和 rm -rf $TMPDIR/*
,再重新安装node_module
运行才解决问题。
- 总结
其实这也是为什么我说 React-Native
等跨平台开发,其实并没有降低工作量的原因。跨平台解决的是逻辑统一维护,而开发中过程中,很多时候会遇到兼容开发的问题,并且平台之间的适配同样消耗时间。
我相信每个
React-Native
开发人员都十分讨厌满屏幕的红色,所以不知哪一版开始,React-Native
把错误增加了红黑相间的效果(¬_¬)。
三、React Hooks
React Hooks
其实也是我升级到 0.59 的目的之一,因为它确实是一个很有意思的设定。
事实上我并非严格意义上的前端人员,大部分时候我对 CSS 和 ES 的了解也不深入,但在 JS 的使用过程中有几个让我印象深刻的:
- Redux :
Redux
的状态管理设计,且由它衍生出的一系列后续和第三方插件,我个人觉得这是React
当初能快速的风靡的助力之一。 - HOC 和 ES7 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
。
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 不是魔法,是数组》
最后说说编码风格:
无论是 HOC
、 React Hooks
、Redux
等,其实我觉得都不存在所谓最优解,具体选择使用还是得看业务场景,过度为了设计而设计,杀鸡用牛刀的后果就是很不顺手,而且还容易误伤。
如果是个人开发,show 代码亮逼格这无可厚非,但如果是实际团队开发,最好还是需要考虑团队的合作选型,不然你写的代码只有你能维护,估计最后哭的还是自己。
好了,本篇到此结束!(///▽///)
跨平台完整项目与文章:
- Flutter 开源项目与文章
- React Native 开源项目与文章
- Weex 开源项目与文章
完整文章目录在项目首页 ReadMe
其他文章
《移动端跨平台开发的深度解析》
我们还会再见吗?