本文从一个Web开发者的角度简单梳理RN开发的基础知识,对比web开发可能让web同学更快的上手RN开发。
RN
提供的框架组件都有style
属性className
, 使用styleSheet.create({})
或style={{}}
,推荐前者性能更好fontWeight
)TypeScript
声明文件,编辑器会提醒你某个组件有哪些样式RN
的组件样式是有规则的,先把高频样式用会,低频样式查查文档组件样式是有继承关系,分三层
flex
布局 + transform
+ shadow
View
组件样式 继承通用样式, 自身有 backgroundColor
、border
相关、opacity
Text
、Image
)都继承了View
组件样式。因此View
组件的私有样式其实也可以算作通用样式Text
/Image
组件的私有样式,就不能相互通用了。优点: 跨平台、高性能、易上手
Flex
是跨平台的 Web
/Android
/IOS
平台都在用, RN
的布局引擎Yoga
是Android
/iOS
通用的。
Flex
布局代码,最终都会被Yoga
引擎计算为精准的坐标系,然后渲染到屏幕上flexDirection
默认值变为column
flex
没有了auto
none
快捷值RN的样式远没有web强大,不过RN组件非常丰富,弥补了这方面的缺点。
display
只有none
和flex
postion
相关
sticky
需要借助 <scrollview>
的stickyHeaderIndices
属性 来实现fixed
ImageBackground
background
、border
、margin
transfrom
写法有变化 transform: [{translateX:100}]
text-indent
推荐使用StyleSheet
ts// width: 750rpx = 100%
export const radio = (function(logicWidth: number) {
return logicWidth / 750
})(logicWidth);
<SafeAreaView>
包裹页面,使用 <StatusBar>
设置样式StatusBar.currentHeight
, ios 设置默认值 20
react-native-device-info
因为RN的样式比较少,但是RN提供了大量的组件弥补这方面的缺陷
Image组件支持4种加载图片的方法
静态图片资源
静态图片资源的加载原理
网络图片
Android
用的是 Fresco
第三方图片加载组件的缓存机制iOS
用的是 NSURLCache
系统提供的缓存机制。它遵循的是 HTTP 的 Cache-Control 缓存策略Image.prefetch(url)
宿主应用图片
html// Android drawable 文件目录
// iOS asset 文件目录
<Image source={{ uri: 'app_icon' }} />
// Android asset 文件目录
<Image source={{ uri: 'asset:/app_icon.png' }} />
Base64 图片
RN的点按组件经历了三代
Touchable
组件Button
组件Pressable
组件第一代 Touchable
组件
TouchableWithoutFeedback
:用于响应用户的点按操作,但不给出任何点按反馈效果。反馈效果由 4 个扩展类实现;TouchableNativeFeedback
:给出当前原生平台的点按反馈效果,在 Android 中是涟漪(ripple)效果,就是从点击处散水波纹的效果TouchableOpacity
:短暂地改变组件的透明度;TouchableHighlight
:短暂地加深组件的背景色;TouchableBounce
:有 bounce 回弹动画的响应效果。第二代 Button
组
Touchable
组件进行封装,别让开发者纠结选啥组件了TouchableNativeFeedback
组件,在 iOS 上是 TouchableOpacity
组件第三代 Pressable 组件
jsx
// 固定的基础样式
const baseStyle = { width: 50, height: 50, backgroundColor: 'red'}
<Pressable
onPress={handlePress}
style={baseStyle} >
<Text>按钮</Text>
</Pressable>
// 固定的基础样式
const baseStyle = { width: 50, height: 50, backgroundColor: 'red'}
<Pressable
onPress={handlePress}
style={({ pressed }) => [ /* 动态样式 */
baseStyle,
{ opacity: pressed ? 0.5 : 1}
]} >
<Text>按钮</Text>
</Pressable>
onPressIn
: 开始响应事件,用户手指接触屏幕,且该手势被当前组件锁定后触发;onPressOut
:结束响应事件,用户手指离开屏幕时触发。onPress
: 在onPressOut
中,如果距离onPressIn
< 500ms则触发 onPress
onLongPress
ref 的值不会因为组件刷新而重新声明,它是专门用来存储组件级别的信息的。
TextInput是输入框组件,相对于web,它的能力更强,可以定制键盘
一些与键盘优化相关的属性
enablesReturnKeyAutomatically
iOS 独有的属性,默认是false
returnKeyType
textContentType
android autoComplete
keyboardType
可以控制键盘类型<Text>
包裹;image
网络图片必须给定宽高;
<ScrollView>
没有overflow:scroll
swipe
ref
拿到原生组件,调用方法scrollTo(x, y, animated)
FlatList
/ SectionList
后者支持分组,比如城市列表、联系人列表overflow:scroll
想要滚动就需要 <ScrollView>
swipe
ref
拿到原生组件,调用方法scrollTo(x, y, animated)
<ScrollView>
会全部渲染元素,对于长列表就有性能问题<FlatList>
和<SectionList>
会渲染可视区及附近的元素,滚动时销毁远离可视区的元素并创建即将进入可视区的元素。<FlatList>
做说明<FlatList>
的底层实现是 <VirtualizedList>
而<VirtualizedList>
的底层又是<ScrollView>
所以<FlatList>
可以使用<ScrollView>
和<VirtualizedList>
的大部分属性getItemLayout
属性,如果不把列表项高度告知<FlatList>
,<FlatList>
会通过onLayout
的布局回调动态计算,用户是会感觉到卡顿的官方的<FlatList>
方案性能确实有的大幅提升,但性能还不够理想,社区提出了<ReyclerListView>
方案, 它是改改在用(变更离开可视区的元素为即将进入可视区的元素)所以它的性能更好。
<ReyclerListView>
有一定学习成本,先来看一下使用
它有三个必填属性
dataProvider
layoutProvider
rowRenderer
示例
jsxfunction MyList() {
let dp = new DataProvider((r1, r2) => {
return r1 !== r2;
});
useEffect(() => {
fetch('XXX')
.then(res => res.json())
.then(arr => {
dp = dp.cloneWithRows(arr);
})
})
const _layoutProvider = new LayoutProvider(
index => {
if (index % 3 === 0) {
return ViewTypes.FULL;
} else {
return ViewTypes.HALF_RIGHT;
}
},
(type, dimension) => {
switch (type) {
case ViewTypes.HALF_RIGHT:
dimension.width = width / 2;
dimension.height = 160;
break;
case ViewTypes.FULL:
dimension.width = width;
dimension.height = 140;
break;
}
},
);
const _rowRenderer(type, data) {
//You can return any view here, CellContainer has no special significance
switch (type) {
case ViewTypes.HALF_RIGHT:
return (
<CellContainer style={styles.containerGridRight}>
<Text>Data: {data}</Text>
</CellContainer>
);
case ViewTypes.FULL:
return (
<CellContainer style={styles.container}>
<Text>Data: {data}</Text>
</CellContainer>
);
default:
return null;
}
}
return (
<RecyclerListView
onEndReached={fetchNextPage}
dataProvider={dp}
layoutProvider={layoutProvider}
rowRenderer={RowRenderer}
/>
)
}
分析
layoutProvider
获取元素尺寸和类型,rowRenderer
根据类型渲染元素dataProvider
除了数据还要提供比对方面,提高性能因为RecyclerListView
用的是 position:absolute
的绝对定位布局。所以要指定组件的宽高
如果宽高大致确定吗也是可以用的,可以开启forceNonDeterministicRendering
小幅修正布局位置
<RecyclerListView>
貌似很完美,但不支持双列
解决方案1: 需要修改<RecyclerListView>
源码,如这个案例
解决方案2 就是使用<FlashList>
它是对<RecyclerListView>
的二次封装
最后建议,如果有长列表瀑布流需求, 一开始就用<RecyclerListView>
或<FlashList>
, 不然随着业务迭代改造成本很大。
Platform.select({ios, android})
Platform.OS
执行不同逻辑web的e.target.value
=> RNe.nativeEvent.text
导航 react-navigation
/ react-native-navigation
/ 使用原生导航管理RN视图
动画 使用Animated
ScrollView
实现Web
端Swipe
组件的脚标动画 Animated.event
ref
获取到原生组件 调用 setNativeProps
方法measure
measureLayout
)+ 监听滚动 可实现类似Web端的IntersectionObserver
的功能
measure
方法layout
方法在渲染前可以拿到文本元素尺寸(web需要渲染后才知道)JS
包含浏览器API
、BOM
和 ECMA
规范两部份组成 由JS引擎(chrome V8
/ safari JSCore
)提供。RN >=7.0
使用 Hermes
引擎 <7.0
使用 JSCore
XMLHttpRequest
/ fetch
/ WebSocket
没有跨域问题Babel
做了语法转换 内置了API
和Polyfills
require
/ console
setTimeout
、setInterval
、requestAnimationFrame
、setImmediate
__DEV__
本文作者:郭敬文
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!