react-native tips
目前flutter当红, react-native显得有些迟暮.
在学flutter过程中, 发现很多思想跟rn都是相通的, 比如:
-
widget
与虚拟dom
. -
flutter context/element
与ref
. -
RenderObject
与native UI
. -
key
控制更新或重建的作用.GlobalKey
可以跨组件传值, 类似于rn的key + ref
. - 相通的布局样式.
-
redux
.
毕竟断断续续做了几年RN, 还是记录下一些不曾总结过的tips, 免得学着新的忘了旧的.
tips
-
1 组件继承于
PureComponent
或自己重写shouldUpdate方法; 组件props或state, 能用常量时用常量. 比如:// 全局定义 const BlankObject = Object.freeze({}); const BlankArray = Object.freeze([]); // 传给子一级的函数, 需要bind this, 提前bind好 // 在contructor里bind constructor(props) { super(props); this.onXXXComplete = this.onXXXComplete.bind(this); } // 或, 直接像下面这样定义 onXXXComplete = () => { // ... }
-
2 组件内, 需要显示的value, 可以先放到
this
下, 而非state
中, 每次值变化时, 判断是否需要更新. 当然, 也可以在shouldUpdate
生命周期函数里做剔除. 例如:
class ComponentA extends PureComponent {
$valueObj = {}; // {key1, key2, key3, ...}
$valueToShowObj = {}; // {key1, key2, key3, ...}
set valueObj({key1, key2, key3, ...}) {
const {key1: oldKey1, key2: oldKey2, key3: oldKey3, ...} = this.$valueObj;
let needUpdate = false;
if (key1 !== oldKey1) {
this.$valueToShowObj[key1] = key1;
needUpdate = true;
}
if (key2 !== oldKey2) {
this.$valueToShowObj[key2] = key2;
needUpdate = true;
}
if (key3 !== oldKey3) {
this.$valueToShowObj[key3] = key3;
needUpdate = true;
}
...
if (needUpdate) {
this.setState(this.$valueToShowObj);
}
}
}
- 3 列表, 或同一级同类型兄弟组件比较多时, 如果有动态更新需求, 比如增/删/交换位置, 使用
key
值, 来提高diff
效率, 降低不必要的销毁重建. - 4 如果有跨层的UI组件操作, 对应组件重建代价又比较高, 或牵连范围比较广, 就要考虑用绝对布局了, 目的就是把视图结构flatten了,
diff
特性才能有效的发挥. 比如, 使用FlatList
做多列展示, 有增加/删除操作, 因为FlatList
是按行排布, 每一行内是分列的, 操作时就有了item的跨行迁移, 基本上全局都会丢弃重建. 如果item带图片, 页面表现就十分感人了. - 5 UI封装动静分离, 粒度足够小. 静态组件尽量用函数而非
class
, 比较轻量. 另外, 父组件的子组件比较多又比较重, 父组件又要操作某个子组件时, 用拿到子的ref去操作, 或者也可以走消息通知. - 6 如果组件有特别频繁的位置/外形变化, 比如关联
scollView
的滚动, 使用Animate
或setNativeProps
, 不要使用state
, 不然性能基本都耗在diff上
了. 方案如下:
/* animate */
height = new Animated.Value(h);
<Animated.View style={{ height: this.height, width: w }}/>
// 修改height
this.height.setValue(newH);
/* setNativeProps */
// 修改width
this.$refView.setNativeProps({ style: { width: newW } });
- 7 如果UI组件在屏幕之外, 在数据变化时最好屏蔽掉这部分UI的刷新. 对于不需要立即显示, 而且还比较重的组件, 可以懒加载. 比如新闻类的多列表页面, 先加载当前要显示的, 其他的用空view占位, 即将进入屏幕时替换为真正的视图. 节省cpu时间, 内存占用和用户流量. 有必要时做好最大并存数限制, 避免内存累计占用过大. 实现这个效果, 一个很简单的方案就是, 封装一个容器view:
class UpdateContainer extends PureComponent {
shouldComponentUpdate(nextProps, nextState) {
return nextProps.shouldUpdate || (nextProps.shouldMount === false && this.props.shouldMount === true);
}
render() {
const { shouldMount, style, children } = this.props;
if (children === null || children === false) {
return null;
}
if (shouldMount) {
React.Child.only(children);
}
// 占位空view
return <View style={style}/>;
}
}
- 9
redux
,connect
到UI组件时, 如果有比较重的数据运算, 可以加缓存, 避免其他节点更新引起的重复运算, 类似的框架有reselect. 思想比较简单, 也可以自己实现. - 10
redux store
尽量不要存储页面状态数据, 或者不需要跨组件保持一致性的数据. 如果有些页面状态需要共享, 可以用消息, 也可以用context
; 还可以自定义一个状态管理类, 维护状态和相关组件的引用的列表, 当状态变化时遍历列表调用setState 或 forceUpdate
. - 11 图片使用跟视图匹配的size. 静态图问UI要. 网络图, 很多图片云服务都支持设置裁剪/填充参数, 可以按需配置. 另外, RN内部是支持用assertId读取相册图片的, 所以可以用
Photos.framework
去读相册, 用的时候url
加ph://
前缀, 如ph://23123
. - 12 视图层级不要太深.
- 13 scrollView的滚动和内部view的拖动类手势冲突的时候, 可以在手势识别时
scrollEnabled = false
, 结束时scrollEnabled = true
, 不过是用setNativeProps来设置, 而非setState. 如果是原生自定义的子视图,也可以添加原生gesture来进行手势管理。
总结
目前就想起这么多, 很多注意的点都内化了, 遇到时知道怎么做更好, 但乍一想记不起来. 一直在学新的技术, 没有停下来总结, 这点很不好, 慢慢改正.
总之, 就是需要在减少重复运算, 重复刷新上多作留意. diff虽然较为高效, 但dom树的比较还是很耗性能的, 能简单比较就排除的刷新, 就不要留给diff去判断.
最后, 虽然使用的是RN, 但要认清视图依然是原生的
, 不要被js
局限住, 有些需求是可以直接通过在原生拿到视图来达成的.
网友评论