准备
学习 React Native 之前,需要了解一下其他知识,帮助你更快的理解 RN
React:React 中文文档
ES6:ES6 入门教程
环境搭建
本人搭建的是 mac+Android 环境,具体过程参考:React Native 中文网 – 搭建环境。
搭建结束后,运行项目
cd AwesomeProject
react-native run-android
当在手机或模拟器上出现如下页面,则说明配置成功。
我在搭建结束后,出现红屏,报错如下:
Unable to load script.Make sure you're either running a metro server(run 'react-native start')or that
your bundle 'index.android.bundle' is packaged correctly for release.
解决方法我记录在这里:点这里。如果遇到同样的问题可以看下。
语法简介
Hello World
先看下大概长什么样
import React, {Component} from 'react';
import {Text, View} from 'react-native';
export default class HelloWorldApp extends Component {state={ text : 'this is state.name'}
render() {
let pic = {uri: 'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'};
const stateName = this.state.name;
return (<View style={{ flex: 1, justifyContent: "center", alignItems: "center"}}>
<Text>Hello, world!</Text>
<Image source={pic} style={{width: 193, height: 110}} />
<Text>{stateName}</Text>
</View>
);
}
}
基本的用法 Props,State 等 与 React 是一样的,只不过基础组件不同。React 里使用常规的 html 标签,div span 等,使用了一些 RN 自己的原生组件 Image,Text 等。
样式
RN 的样式与 React 写法页基本相同,样式名遵循了 web 的 css 命名,只是按照 JS 语法要求,改成了驼峰命名。
style 属性可以是简单的对象,也可以传入一个数组——在数组中位置居后的样式对象比居前的优先级更高,这样你可以间接实现样式的继承。
当样式复杂时,建议使用 StyleSheet.create 来集中定义组件的样式。看下例子:
import React, {Component} from 'react';
import {AppRegistry, StyleSheet, Text, View} from 'react-native';
export default class LotsOfStyles extends Component {render() {
return (
<View>
<Text style={{color:'yellow',fontSize:'20'}}>just yellow</Text>
<Text style={styles.red}>just red</Text>
<Text style={styles.bigBlue}>just bigBlue</Text>
<Text style={[styles.bigBlue, styles.red]}>bigBlue, then red</Text>
<Text style={[styles.red, styles.bigBlue]}>red, then bigBlue</Text>
</View>
);
}
}
const styles = StyleSheet.create({
bigBlue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {color: 'red',},
});
StyleSheet.create 可以弥补编写复杂样式时,不能使用 css 的不便。
宽高与布局
RN 中宽高可以直接通过 style 指定,与 web 不同的是,RN 中尺寸是无单位的,表示与设备像素无关的逻辑像素点。逻辑像素不清楚的,可以看这里:手机屏幕分辨率术语:逻辑分辨率和物理分辨率。
import React, {Component} from 'react';
import {AppRegistry, View} from 'react-native';
export default class FixedDimensionsBasics extends Component {render() {
return (
<View>
<View style={{width: 50, height: 50, backgroundColor: 'powderblue'}} />
</View>
);
}
}
弹性高宽
在组件样式中使用 flex 可以使其在可利用的空间中动态地扩张或收缩。一般而言我们会使用 flex:1 来指定某个组件扩张以撑满所有剩余的空间。如果有多个并列的子组件使用了 flex:1,则这些子组件会平分父容器中剩余的空间。如果这些并列的子组件的 flex 值不一样,则谁的值更大,谁占据剩余空间的比例就更大(即占据剩余空间的比等于并列组件间 flex 值的比)。
import React, {Component} from 'react';
import {AppRegistry, View} from 'react-native';
export default class FlexDimensionsBasics extends Component {render() {
return (
// 试试去掉父 View 中的 `flex: 1`。// 则父 View 不再具有尺寸,因此子组件也无法再撑开。// 然后再用 `height: 300` 来代替父 View 的 `flex: 1` 试试看?<View style={{flex: 1}}>
<View style={{flex: 1, backgroundColor: 'powderblue'}} />
<View style={{flex: 2, backgroundColor: 'skyblue'}} />
<View style={{flex: 3, backgroundColor: 'steelblue'}} />
</View>
);
}
}
flex box 布局
RN 中 flex box 的使用与 web 上 css 的 flex 布局用法基本一直,不同的是 flex-direction 默认值是 column, 而且 RN 中的 flex 只能是数字,而不是 flex-grow, flex-shrink 和 flex-basis 的简写。
一般来说,使用 flexDirection、alignItems 和 justifyContent 三个样式属性就已经能满足大多数布局需求。
例如:
export default class JustifyContentBasics extends Component {render() {
return (
// 尝试把 `justifyContent` 改为 `center` 看看
// 尝试把 `flexDirection` 改为 `row` 看看
<View style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between',
}}>
<View style={{width: 50, height: 50, backgroundColor: 'powderblue'}} />
<View style={{width: 50, height: 50, backgroundColor: 'skyblue'}} />
<View style={{width: 50, height: 50, backgroundColor: 'steelblue'}} />
</View>
);
}
};
注意:RN 中 flexbox 不需要一定指定 flex:1,只要外层元素高度存在(可以设置 height),设置 flexDirection、alignItems 或 justifyContent,内部元素都会根据样式规则,遵循 Flexbox 布局。
事件响应
RN 事件响应的写法,与 ReactJS 类似。不同的是,移动端手势的响应要比 web 端复杂的多,RN 通过响应者的声明周期,来响应不同触摸事件。
响应者的生命周期
我们通过两个方法去“询问”一个 View 是否愿意成为响应者:
- View.props.onStartShouldSetResponder: (evt) => true, – 在用户开始触摸的时候(手指刚刚接触屏幕的瞬间),是否愿意成为响应者?
- View.props.onMoveShouldSetResponder: (evt) => true, – 如果 View 不是响应者,那么在每一个触摸点开始移动(没有停下也没有离开屏幕)时再询问一次:是否愿意响应触摸交互呢?
如果 View 返回 true,并开始尝试成为响应者,那么会触发下列事件之一:
- View.props.onResponderGrant: (evt) => {} – View 现在要开始响应触摸事件了。这也是需要做高亮的时候,使用户知道他到底点到了哪里。
- View.props.onResponderReject: (evt) => {} – 响应者现在“另有其人”而且暂时不会“放权”,请另作安排。
如果 View 已经开始响应触摸事件了,那么下列这些处理函数会被一一调用:
- View.props.onResponderMove: (evt) => {} – 用户正在屏幕上移动手指时(没有停下也没有离开屏幕)。
- View.props.onResponderRelease: (evt) => {} – 触摸操作结束时触发,比如 ”touchUp”(手指抬起离开屏幕)。
- View.props.onResponderTerminationRequest: (evt) => true – 有其他组件请求接替响应者,当前的 View 是否“放权”?返回 true 的话则释放响应者权力。
- View.props.onResponderTerminate: (evt) => {} – 响应者权力已经交出。这可能是由于其他 View 通过 onResponderTerminationRequest 请求的,也可能是由操作系统强制夺权(比如 iOS 上的控制中心或是通知中心)。
class App extends Component<Props> {onStartShouldSetResponder = () => {console.log("onStartShouldSetResponder");
return false;
};
onMoveShouldSetResponder = () => {console.log("onMoveShouldSetResponder");
return false;
};
onResponderMove = eve => {console.log("onResponderMove", eve);
};
onResponderGrant = eve => {console.log("onResponderGrant", eve);
};
onResponderReject = eve => {console.log("onResponderReject", eve);
};
pressIt = eve => {console.log("onPress");
};
render() {
return (<View style={styles.container}>
<View
onResponderMove={this.onResponderMove}
onStartShouldSetResponder={this.onStartShouldSetResponder}
onResponderReject={this.onResponderReject}
onResponderGrant={this.onResponderGrant}
onMoveShouldSetResponder={this.onMoveShouldSetResponder}
style={{
height: 50,
width: 50,
backgroundColor: "#ccc",
cursor: "pointer"
}}
>
<Text> 点我!</Text>
</View>
)
}
生命周期的执行顺序,可以实际触发试试。
Touchable 系列组件
- 一般来说,你可以使用 TouchableHighlight 来制作按钮或者链接。注意此组件的背景会在用户手指按下时变暗。
- 在 Android 上还可以使用 TouchableNativeFeedback,它会在用户手指按下时形成类似墨水涟漪的视觉效果。
- TouchableOpacity 会在用户手指按下时降低按钮的透明度,而不会改变背景的颜色。
- 如果你想在处理点击事件的同时不显示任何视觉反馈,则需要使用 TouchableWithoutFeedback。
滚动视图与长列表
滚动视图
ScrollView 是一个通用的可滚动的容器,你可以在其中放入多个组件和视图,而且这些组件并不需要是同类型的。ScrollView 不仅可以垂直滚动,还能水平滚动(通过 horizontal 属性来设置)。
export default class IScrolledDownAndWhatHappenedNextShockedMe extends Component {render() {
return (
<ScrollView>
<Text style={{fontSize:96}}>Scroll me plz</Text>
<Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
<Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
<Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
<Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
<Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
<Text style={{fontSize:96}}>If you like</Text>
)
}
ScrollView 内部所有组件都会被渲染,即使这些组件在屏幕的外部。如果要显示较长滚动内容,建议使用性能更好的长列表组件:FlatList。
长列表
长列表常用的组件有 FlatList 和 SectionList。
FlatList 用法
- 用于显示一个垂直的滚动列表,其中的元素之间结构近似而仅数据不同
- 更适于长列表数据,且元素个数可以增删。FlatList 并不立即渲染所有元素,而是优先渲染屏幕上可见的元素。
- FlatList 组件必须的两个属性是 data 和 renderItem。data 是列表的数据源,而 renderItem 则从数据源中逐个解析数据,然后返回一个设定好格式的组件来渲染。
export default class FlatListBasics extends Component {render() {
return (<View style={styles.container}>
<FlatList
data={[{key: 'Devin'},
{key: 'Jackson'},
{key: 'James'},
{key: 'Joel'},
{key: 'John'},
{key: 'Jillian'},
{key: 'Jimmy'},
{key: 'Julie'},
]}
renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 22
},
item: {
padding: 10,
fontSize: 18,
height: 44,
},
})
SectionList 的用法
SectionList 与 FlatList 相似,只不过多了一个分组功能。
export default class SectionListBasics extends Component {render() {
return (<View style={styles.container}>
<SectionList
sections={[{title: 'D', data: ['Devin']},
{title: 'J', data: ['Jackson', 'James', 'Jillian', 'Jimmy', 'Joel', 'John', 'Julie']},
]}
renderItem={({item}) => <Text style={styles.item}>{item}</Text>}
renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.title}</Text>}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
网络请求
React Native 提供了和 web 标准一致的 Fetch API,用于满足开发者访问网络的需求。
Fetch 的第一个参数用来指定请求路径,可选的第二个参数,可以指定 http 一些请求参数。
fetch('https://mywebsite.com/endpoint/', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstParam: 'yourValue',
secondParam: 'yourOtherValue',
}),
});
Fetch 返回一个 Promise 对象,RN 中可以使用 promise 这样获取数据:
fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {return responseJson.movies;})
.catch((error) => {console.error(error);
});
也可以使用 async/await
let response = await fetch('https://facebook.github.io/react-native/movies.json',);
let responseJson = await response.json();
return responseJson.movies;
除了 Fetch 之外也可以使用 frisbee 和 axios 等其他网络库。
React Native 的优缺点
RN 的语法先介绍到这里,详细的用法可以看这个 React Native 中文网。
在具体的使用之前,我们了解下 RN 的优劣势,来判断是否需要用它。
简单介绍下 RN 的优劣势,来决定是否需要用它。
优点
- 调试方便
RN 支持 hotReload 以及 liveReload,在调试界面的时候非常方便,修改代码之后保存,界面就自动跟着变化。也可以使用 chrome,react-devtools 调试。 - 跨平台开发,可以写一套代码,Android 和 ios 并行开发。
- 使用热更新,便于维护快速更迭的模块,跳过漫长的 App 审核。
缺点
-
长列表及动画性能不佳
长列表快速向上滚动时,列表会出现白屏的情况,RN 的动画会出现卡顿问题。这两个问题基本是因为 js 与 native 频繁通信造成的,很难从根本上解决。
- 版本更新快,项目中更新一次 RN,对代码改动较大,很耗时。
- 两个平台还没有完全统一,一些控件在两个平台变现会有差异。
Flutter,比 RN 更好的跨平台方案?
在实际工作中 RN 一般作为原生开发的补充,来开发一些快速迭代的页面,但很难取代原生开发,成为 App 开发的最优解。Flutter 作为目前炙手可热的跨平台方案,能否取代 RN,Weex,还不能确定。首先我们先简单了解下 Flutter。
React-Native、Weex 核心是通过 Javascript 开发,执行时需要 Javascript 解释器,UI 是通过原生控件渲染。Flutter 与用于构建移动应用程序的其它大多数框架不同,因为 Flutter 既不使用 WebView,也不使用操作系统的原生控件。相反,Flutter 使用自己的高性能渲染引擎来绘 制 widget。
它的开发语言是 Dart,Dart 是面向对象的,类定义的,单继承语言,语法类似 c 语言,可以转义为 js。
Flutter 特点:
- 快速开发
由于 Flutter 选用了 Dart 作为其开发语言,Dart 既可以是 AOT(Ahead Of Time)编译,也可以是 JIT(Just In Time)编译,其 JIT 编译的特性使 Flutter 在开发阶段可以达到亚秒级有状态热重载,从而大大提升了开发效率。但是 Flutter 不支持代码的热更新。 - 性能优越
使用自带的高性能渲染引擎(Skia)进行自绘,渲染速度和用户体验堪比原生。 - UI 一致性
Flutter 因为是自己做的渲染,因此在 iOS 和 Android 的效果基本完全一致。React Native 存在将 RN 控件转换为对应平台原生控件的过程,存在一定的差异(如 Button 在 iOS 和 Android 下面显示效果不一样)。 - App 体积
Flutter iOS 空项目 30M 左右,Android 空项目 7M 左右(iOS 需要额外集成 Skia)。React Native iOS 空项目 3M 左右,Android20M 左右(Android 会加入 OKHttp 导致体积增大)。
具体 flutter 介绍可以看下:
移动跨平台框架 Flutter 介绍和学习线路
如何评价 Google 的 Fuchsia、Android、iOS 跨平台应用框架 Flutter?
此外作为 Fuchsia OS 的正统 app framework,Flutter 的应用前景,与 Fuchsia 的推广程度息息相关。可以看下知乎对 Fuchsia 和 Flutter 的讨论:
如何看待 Google 的新操作系统 Fuchsia?
Flutter 在 2019 年会有怎样的表现
总结
React Native 语法和基本用法与 ReactJs 类似,之前用过 React 的,RN 上手很快。虽然 RN 在性能上和跨平台一致性等方面算不上完善,但目前在国内主流大厂,RN 仍是最常用的跨平台解决方案之一。对于日常工作中可能会用到技术,花时间学学还是很必要的。即使 Flutter 真的取代了 RN,Flutter 的 React 风格,也会让熟悉 RN 开发的同学更好上手。
参考
React Native 中文网
react-native 技术的优劣
React Native 官网