React Native总结
经过两三个月,随着Android端和iOS端的ReactNative项目上线,想把第一次从0到上线的React Native经验总结一下。选用React Native的原因是因为对JS和React熟悉, 加之项目需要双端快速上线,又只有我一个人可以投入这个项目开发,Native需要时间去熟悉, 所以RN成了第一选择。看别人的总结总把React Native说成一个坑,但遇到问题后, 大部分问题都可以解决实现。如果React Native解决不了的问题也可以通过跟Native做桥接完美实现功能。特别是像国内的一些SDK的RN版本少或者很多年没有维护(如IM等等),需要写原生再去用RN去调用原声代码。
项目结构
刚开始摸索阶段可能是花时间最多的阶段, 最后决定项目由React Native Cli + React Navigation + React Hook来完成。不用Expo的原因是因为后面很难用Native模块去扩展,如果是一些简单的项目可以用Expo去实现。在Navigation里面有React Navigation和React Native Navigation,看了文档以后感觉React Navigation更简洁并且Star数也比较高,所以选择了React Navigation。 在代码风格方面使用React Hook, Hook提供的useContext ,可以完美的代替redux,写法又简洁又大部分的RN文档又是Hook的方式, 所以选择React Hook。
代码结构大概是
. |
最外层几乎是默认的init结构, src目录下assert文件存储图片,icon,动效等等文件。 只需要放iOS的2x和3x的图片。components主要放一些组件,constansts常量如屏幕宽度,高度, 等等,context存储比如AuthContext, LoadingContext 等等全局的一些context。lib存储了一些第三方的library的一些扩展或者重写。navigations主要放了BottomTabNav, AuthNav, AppNav等等路由。request存放请求相关。screens存放每个页面。
页面结构
. |
路由结构还是比较清晰,AppStackNav和AuthStackNav用token去控制进入哪个Nav, 主要注意的是Bottom Tab和 Detai的顺序, Bottom Tab和 DetailScreen需要平级, 才能Push的时候Detail页面盖住Bottom Tab。还有一个问题是TopTabNav。 在这边我是实用react-native-tab-view 来代替 TopTabNav。 如果需要Bottom Tab的动效,可以参考https://blog.csdn.net/hejun041/article/details/101026140 。有一个问题是pop时候如何传给上一个页面数据, 这个问题解决办法是navigate到下一个组件的时候传入一个函数getBackData然后在pop时候调用这个函数。RN会抛出警告ignore掉就好。方法是
LogBox.ignoreLogs([ |
关于StackNavigator的配置, 我的配置是如下图所示, Slide需要统一成iOS方式。然后每个页面的title或者shadow。注意的是gestureEnabled在iOS是true,需要都设置成true。headerBackTitleVisible 设置成false
<AppStack.Navigator |
{ |
样式
React Native主要使用flex布局, 问题不大, 字体大小,宽度等等没有单位采用的是dp, 默认字体不是PingFang SC 需要全局设置。下面是我全局使用的一些样式。
|
关于SafeArea我是用的是react-native-safe-area-context, 它提供上下左右的安全区域, 需要注意的是React Navigation 已经加上了SafeArea所以不用考虑。
关于StatusBar
if (isAndroid) { |
关于fontWeight
'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' |
iOS和android的fontWeight表现不一样, 在android的500,600 不太明显 , iOS600跟android的700差不多。
关于shadow
iOS和android的样式也是不一样的
//ios |
android的阴影很难跟iOS一样去统一, 点9图又很麻烦, 其它方式又很卡, 所以凑合用吧。
关于Button
推荐使用TouchableOpacity自行去实现。 渐变色用react-native-linear-gradient
关于图片
如果需要按比例显示, Image的contain会剧中图片使用react-native-scalable-image 来实现居左或靠上的图片
还有iOS 14以下Image的webp图片显示会有问题, 可能需要转换格式。
关于键盘
是个头疼问题, 采用的是KeyboardAvoidingView。需要注意的是要考虑Nav的 header高度。
<KeyboardAvoidingView style={{ flex: 1, width: SCREEN_WIDTH }} behavior={Platform.OS === "ios" ? "padding" : "null"} keyboardVerticalOffset={Platform.select({ ios: useHeaderHeight() })} > |
关于Splash Screen
使用react-native-splash-screen 或者需要动效的话使用react-native-lottie-splash-screen (注意iOS项目配置Swift转OC)
关于ImagePicker和拍摄
因为没有找到合适的Picker跟项目的流程一样,所以用@react-native-community/cameraroll 和Flatlist自己写了一个ImagePicker以及上面添加一个Preview的页面, 不算很复杂。短按拍摄,长按拍段视频也可以用onPress和onLongPress实现。拍摄进度使用的是 react-native-circular-progress。
关于iOS设备登录和内购
不太复杂, 使用的是 @invertase/react-native-apple-authentication和 react-native-iap, 唯一注意的地方是内购需要防止丢单,需要App监听回调, 跟后端交互以后需要ACK一下。
关于原生交互
一些SDK可能没有提供RN版本的封装, 这时候可能需要写Native代码然后实现桥接。例如使用的腾讯IM, 阿里人脸认证等等, 都使用原生去封装了一下。 原声代码不是很难, 基本上Copy就可以, 关于桥接 RN提供了回调, Promise 还有Emiter等等方式, 封装方式也比较简单, 很容易去实现,官网有具体实现方法。
关于WebView
RN在跟Webview交互的时候可能需要向Webview发送数据或者拿到数据这时候, react-native-webview也提供了这个方法 。injectedJavaScriptBeforeContentLoaded
这个方法会在webview load之前执行, 但是发现有些版本较低或者部分手机无法执行这个方法。 injectedJavaScript
这个方法是都会执行, 但是有些时候还没有执行完webview的init js代码就执行了, 最后没办法 只能使用injectedJavaScript
然后Webview延迟大概500ms-1s去执行, 拿到App的数据。 然后如果Webview向App发送事件使用 onMessage
就可以, 比如在Webview里面按返回执行的逻辑可以在onMessage
拿到事件以及数据在执行。
关于Flatlist
这部分可能是比较麻烦, 而且优化内容比较多的一个地方。 Flatlist提供了下拉刷新, 以及加载更多的功能, 以及renderHeader 等等方法 。 对于item可能需要React.memo
等等方法在state变化时,防止重新渲染, 提高性能(优化放在必要时候)。还有initialNumToRender
如果不去设置这个值每次会render10个数据, 所以当一个屏幕高度内有10+个数据时候会出现先render10个的现象,这时候可以把这个值设置高一些或者设置成数据长度。
总结
经过这段时间的摸索, 感觉React Native还是值得去尝试的, 虽然可能有一些琐碎的问题, 所谓的坑, 但问题都不大,并且也可以跟原生交互。 像对于移动端开发不是很熟悉,但对Javascript, React熟悉的朋友们都值得去尝试, App的体验跟Native相差无几, 可能在项目初期会花的时间比较多,但对于后期的维护来说实在是太方便, 一次改两个平台,太香!!!