2017-07-23 23:49:52

React Native小贴士

不要使用yarn和cnpm

对于cnpm, RN的构建系统是自己写的一个local-cli, 它BUG多到干脆不认cnpm那种版本号写在最前面的文件组织格式.

对于yarn, RN进阶一定会用到源码构建, 使用yarn的话每次add新的包时, yarn会自动把RN包里安卓下载好的boost包给清除掉, 然而那几十M的文件全部是放在亚马逊3s服务器上, 因此国内下载速度非常凄惨。

最好的办法是使用npm5.0版本的package-lock配合cnpm的--registry=https://registry.npm.taobao.org后缀进行RN的包管理

合理使用global

RN的global十分有用, 因为RN根本不存在前端的污染全局变量的风险, global只能在你自己的应用内修改,所以用global来挂载一些经常使用到的组件是个相当不错的选择.。

以我写的psnine为例, 总计110个大小组件, 光import语句都写了一千多行, 何必呢。

转变开发思维

移动端开发和WEB开发差别通常比大家想象中的大,比如一个经常被提起来的问题是:嵌套ScrollView无法滑动,然而事实却和提问者的预期相差甚远:以安卓为例,移动端根本不存在WEB端冒泡的机制,因此嵌套滑动在安卓上本来就不存在。你真的见过安卓应用中有一个可滑动的视图中又嵌套了另一个滑动方向一致的视图这种情况么?所以,嵌套ScrollView的需求在最初就不应该被提起来。当然,这个需求也不是不可以做到,但是方法非常脏,不是初学者能做到的。

如果这个问题的提问者稍微搜索一下,会发现安卓有一个NestedScrollView,然而这玩意儿根本不是处理嵌套ScrollView的,而是用来处理material design库中多个新控件的折叠滑动效果。

上一段提到的可以合理使用global也是如此,在WEB端大家都害怕在全局变量上做了啥会有被其他库给覆盖掉的风险,但是在移动端RN开发中,你应用的global可是只能你自己用,怕个啥呢?

再举一个栗子🌰,经常看到有的开源RN仓库中有很多图片,然而这种做法本身就是不专业甚至上线后都能算生产事故的,因为安卓和IOS以及各自的不同机型、不同版本那么多,一张图片到底占了多高多宽在不同机型上是完全不同的,因此不用类似react-native-vector-icons的库来做兼容的应用本身就可以说是有严重缺陷的。关于这个问题,如果读者有做过移动端WEB开发,那么一定在写第一行代码之前就知道有这么一回事,先解决了再写代码,这就是经验带来的问题了。

typescript和RN

RN和typescript搭配时有一些问题,导致效果不是预期那么好,例如,RN的官方typings一直都不全,很多变量都没有定义,导致tslint的报错一大堆;其次,typescript也不直接支持RN特有的.android.js.ios.js后缀,因为它们都需要执行相同的校验流程;最后,typescript的编译速度实在是出乎我的预料地慢,改一个字母需要构建四五秒,导致RN的热重载效率低了好多。

TouchableNativeFeedback的涟漪错位

RN的Text控件也可以触摸并触发onPress事件, TouchableNativeFeedback内部存在Text时RN不知道你要触摸的是Text还是TouchableNativeFeedback, 并且android和IOS本身没有冒泡所以RN只能认为你触摸的是Text,导致涟漪错位,因此只能这样来避免涟漪错位了:

<TouchableNativeFeedback>
  <View pointerEvents='box-only'></View>    
</TouchableNativeFeedback>

上面的其实不是根本原因,根本原始是yoga(RN的跨平台Flex布局实现引擎)有bug,TouchableNativeFeedback中的View采用Flex布局时就会导致涟漪错位了, 有一个鸡肋的办法是将Flex布局改成纯Text控件并使用textAlign等文字布局属性, 但是TouchableNativeFeedback中只使用Text的情况几乎不存在, 因为直接用Text控件的onPress不就完了嘛...

TouchableNativeFeedback降级

安卓中没做版本兼容的只有这个控件, 要求安卓5.0及以上, 低于这个版本的直接抛错崩掉你的app, 可以采用如下方法降级, 这个办法不仅仅兼容了低版本安卓, 还兼容了IOS

import ReactNative, {
  Platform,
  TouchableNativeFeedback,
  TouchableOpacity
} from 'react-native'

let TouchableComponent

if (Platform.OS === 'android') {
  TouchableComponent = Platform.Version <= 20 ? TouchableOpacity : TouchableNativeFeedback
} else {
  TouchableComponent = TouchableOpacity
}

if (TouchableComponent !== TouchableNativeFeedback) {
  TouchableComponent.SelectableBackground = () => ({})
  TouchableComponent.SelectableBackgroundBorderless = () => ({})
  TouchableComponent.Ripple = () => ({})
}

Object.defineProperty(ReactNative, 'TouchableNativeFeedback', {
  get: function () {
    return TouchableComponent
  }
})

我真的不知道facebook为啥不自己做掉这个的降级

方便的cookie

安卓中fetch的底层会过一下okhttp, 因此返回的set-cookie字段会被okttp截取掉, 好处是用fetch请求登录后, RN的webview也会自动登录成功(因为webview的请求也是走okhttp), 如果需要这个cookie字段, 设置fetch的withCredentials即可

模拟Table布局

RN是没有Table布局的

如上图, 这种Table布局最重要的特点是, 整体表格的最大宽度是由表格中最宽的一行决定的.

模拟Table布局可以采用View组件的onLayout方法和一个虚拟组件, 在虚拟组件中异步获得最大宽度.

不过这事比较麻烦, 因为在PC端没换行的表格在移动端90%都换行了, 因此很大概率表格宽度就是手机最大宽度.

此时可以简单设置一下表格View的宽度, 不过表格内容会在第一行只有一列的情况下坍缩, 表现为padding和height均不起效(还是flex的锅),overflow也不认这个宽度, 因此得上一个无脑定死宽度的办法:{{ height: 100, maxHeight: 100, minHeight: 100 }}, 这个办法很有效, 之后还会提到它

flatlist的性能

flatlist是RN在0.42开始用纯JS写的高性能长列表组件, 如果说之前RN在长列表里表现为10%, 那么有这个长列表后可以说是90%,

初次使用flatlist时都是开发环境, 会发现flatlist延迟非常严重, 滑动后一般要过几秒才会刷新出最新的条目.性能如此之差的原因有两点:

  • 纯函数组件
    • 一定要把flatlist中的item改为纯函数, 因为flatlist每次刷新都会重新渲染所有item, 不改成纯函数就无法复用原组件, 性能比之前的10%还惨, 博主觉得可以打个1%.
  • dev和prod之间的性能差距
    • flatlist在开发和正式环境下的性能差距非常大, 如果说在开发环境下3秒才刷新, 那么在正式环境下只会花50毫秒, 肉眼几乎无法察觉.

RN的breaking change

RN的breaking change是出了名的. 博主也在这里和大家分享一下我其中一部分升级体验.

  • 0.41升级到0.42
  • 发现react-native-vector-icon编译挂掉了, 试了半天没办法, 看issue有说RN 0.43没问题
  • 好, 再升到0.43!
  • 发现内联图片的宽高居然竟然是反的
    • 可惜没留档, 之前master分支上这个内联图片文件有不少人吐槽
  • 好, 再升到0.44!
  • 发现Navigator组件被删了
  • 呵呵, 安卓就这一个Navigator组件, 从现在开始路由已经没有官方支持的了
  • 换吧, 哼哧哼哧换上react-navigation, 我特么一百个组件啊!!!
  • 哦对了, 这时候已经是我选择升级后的第三天了

从0.32开始, 我没有一次升级能直接跑起来代码, 全部挂掉, 没有意外. 因此前几天看到jsconf上RN的ppt上居然写着随时保持最新版本, 那种心情真是无法言说, 因为最后那个ppt上分享了三个开源RN项目, 其中一个就是我的.

现在我是自己维护一个低版本仓库, 随便贴几个issue

RN现在是个什么状态呢, 看下图的issue

不要以为800多未关闭的issue很多, 关闭了的8400个issue里面至少3000个issue是合理issue(基本能确定是bug甚至是有复现有PR)但是没解决, 直接先被晾半年再说你这个issue不活跃我们关了啊, 为啥我敢肯定至少3000个呢, 因为我都被关了五六个issue了, 看看这个链接, https://github.com/facebook/react-native/issues/9447#issuecomment-316906630, 被晾了整整一年的issue在两天前被关掉, 还是新鲜的呢

我觉得RN这些很惨的问题, 90%的锅要仍在它没测试, 是的, 拿了5万Star能排在Javascript历史前十的项目连代码覆盖率都不敢亮在readme里的项目, 我看到也就这一个项目了

本文链接:https://smallpath.me/post/react-native-tips

-- EOF --