学习路线建议:ES5/6 -> React -> React Native -> Redux + React-Navigation等
- ES5 :https://wangdoc.com/javascript/
- ES6:http://es6.ruanyifeng.com/
- React/ React Native :官方文档即可 - https://reactnative.cn/ 、https://react.docschina.org/docs/getting-started.html
- redux:https://cn.redux.js.org/docs/react-redux/
- react-navigation:https://reactnavigation.org/docs/zh-Hans/getting-started.html
1、RN环境配置与运行
- 按照官网进行环境配置;
- 配置完成,初始化项目:react-native init 项目名称(注意 - 可以cd到自己目录,比如D:/RN目录下,然后执行该命令,即可在该目录下初始化自己的RN项目了,不要在系统或其他需要权限的目录,因为这会导致一些奇怪的问题发生)
- 运行项目:react-native run-android
- 学习的时候,如果高版本的RN常见运行有问题,可尝试低版本。项目开发建议使用最新版本RN
- 安装第三方库,使用npm有时候莫名报错,可使用yarn尝试安装
2、调试
开发RN推荐开发工具VsCode。
有两种方式可以进行RN的调试,任选其一即可。(调试要确保在同一个网段内进行,切记)
- 使用VSCode进行调试
a)需要安装,安装React Native Tools插件。
b)安装完成之后,通过react-native run-android命令,将项目运行到真机跑起来。
c)配置调试选项,如下图:
d)唤出开发者菜单,配置当前的ip和端口;
e)点击调试按钮即可进行调试;
- 使用Chrome浏览器调试
项目跑起来之后,唤出开发者菜单,打开Debug Js Remotely即可。注:如果在Chrome中调试,出现跨域等问题,试着将ip改成localhost即可。
3、React相关
组件是根据props进行渲染的。state是组件的内部状态,目前,React Native 可以使用redux进行状态的管理。
JSX --> 通过Babel转义为纯JS代码 --> 虚拟DOM(DOM DIFF) --> 真实DOM(执行完了render方法)
React Native 是React 在原生移动应用平台的衍生产物,那两者主要的区别是什么呢?其实,主要的区别在于虚拟DOM映射的对象是什么?React中虚拟DOM最终会映射为浏览器DOM树,而RN中虚拟DOM会通过 JavaScriptCore 映射为原生控件树。
JavaScriptCore 是一个JavaScript解释器,它在React Native中主要有两个作用:
- 为JavaScript提供运行环境。
- 是JavaScript与原生应用之间通信的桥梁,作用和JsBridge一样,事实上,在iOS中,很多JsBridge的实现都是基于 JavaScriptCore 。
而RN中将虚拟DOM映射为原生控件的过程中分两步:
- 布局消息传递; 将虚拟DOM布局信息传递给原生;
- 原生根据布局信息通过对应的原生控件渲染控件树;
至此,React Native 便实现了跨平台。 相对于混合应用,由于React Native是原生控件渲染,所以性能会比混合应用中H5好很多,同时React Native是Web开发技术栈,也只需维护一份代码,同样是跨平台框架。
3.1、组件属性验证
组件的属性可以接受任何值,字符串、对象、函数等等都可以。有时,我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。组件类的PropTypes属性,就是用来验证组件实例的属性是否符合要求。
React.PropTypes从Reactv15.5开始被移入了prop-types组件库。
import PropTypes from 'prop-types';
class Greeting extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired//必传且是String类型
}
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
或者
Greeting.propTypes = {
name: PropTypes.string.isRequired//必传且是String类型
};
3.2、默认属性
可以通过defaultProps用来设置组件属性的默认值。
class MyTitle extends React.Component{
static defaultProps = {
shortName:'MyTitle'
};
render(){
return (<h1>{this.props.shortName}</h1>);
}
}
ReactDOM.render(<MyTitle/>,document.getElementById('root'));
3.3、ref属性
组件并不是真实的DOM节点,而是存在于内存之中的一种数据结构,叫做虚拟DOM。只有当它插入文档以后,才会变成真实的DOM。根据React的设计,所有的DOM变动,都先在虚拟DOM上发生,然后再将实际发生变动的部分,反应在真实DOM上,这种算法叫做DOM diff,它可以极大提高网页的性能表现。
有时,需要从组件获取真实DOM的节点,这是就要用到ref属性。
class Alert extends React.Component {
showAlert() {
alert('alert');
}
render() {
return null;
}
}
class MyTitle extends React.Component {
onClick = () => {
this.refs.alert.showAlert();
};
render() {
<div>
<h1 onClick={this.onClick}>click me</h1>
<Alert ref='alert' />
</div>
}
}
需要注意的是,由于this.refs.[refName]属性获取的是真实的DOM,所以必须等到虚拟DOM插入文档以后,才能使用这个属性,否则会报错。上面的代码,通过组件指定click事件的回调函数,确保了只有等到真实DOM发生Click事件之后,才会读取this.refs.[refName]属性。
3.4、组件的生命周期
注意:下述方法即将过时,在新代码中应该避免使用它们:
图一:比较常用的生命周期图
图二:完整的生命周期图
注:强烈建议你不要创建自己的组件基类。在React组件中,代码重用的主要方式是组合而不是继承。具体生命周期的详细说明:参阅官档。
3.5、render方法
render方法是必须的。当被调用时,其会检查this.props和this.state并返回以下类型中的一个:
- react元素:通常是由JSX创建。该元素可能是一个原生DOM组件的表示,如<div/>或者是一个你定义的复合组件。
- 字符串和数字:这些将被渲染为DOM中的text node。
- Portals:由ReactDOM.createPotral创建。
- null:什么也不渲染;
- 布尔值:什么也不渲染。(通常存在于return test && 写法,其中test是布尔值)
返回null或者false时,ReactDOM.findDOMNode(this)将返回null。
render()函数应该是纯粹的,也就是说该函数不修改组件的state,每次调用都返回相同的结果,不读写DOM信息,也不和浏览器交互(例如通过使用setTimeOut)。如果需要和浏览器交互,在componentDidMount中或者其他生命周期方法中做这件事情。保持render()纯粹,可以使服务器渲染更加切实可行,也使组件更容易被理解。
注:若shouldComponentUpdate返回false,render函数将不会被调用。
4、React Native组件的生命周期
4.1、布局基础
1)父视图属性
- flexDirection: enum('row', 'column ', 'row-reverse', 'column-reverse') - 默认column
- flexWrap:enum('wrap','nowrap') - 是否换行
- justContent:enum(‘flex-start’, 'flex-end', 'center', 'space-between', 'space-around') - 主轴(x轴)上子元素排列方式
- alignItems:enum('flex-start', 'flex-end', 'center', 'stretch') - 次轴(y轴)上,子元素的排列。stretch,弹性元素被在侧轴方向被拉伸到与容器相同的高度或者宽度。
2)子视图属性
- alignSelf:定义了flex容器内被选中项目的对齐方式。注意:alignSelf属性可重写灵活容器的alignItems属性。
- flex
3)其他属性
- borderWidth、borderColor、borderRadius等
- 定位:
a)absolute:
生成绝对定位的元素,元素的位置通过'left'、'top'、'right'、'bottom'属性进行规定。
b)relative(默认):
生成相对定位的元素,相对于其正常位置进行定位。因此,left:20会向元素的left增加20像素。
5、react-navigation
最权威的学习文档 - 官方文档 - https://reactnavigation.org/docs/zh-Hans/getting-started.html
提供“页面跳转(stackNavigator)”、“顶部Tab(可滚动)”、“底部Tab”、“侧滑抽屉效果”等功能。
以createStackNavigator为例(其余的):
RouteConfigs
(必选):路由配置对象是从路由名称到路由配置的映射,告诉导航器该路由呈现什么。
RouteConfigs支持三个参数screen、path以及navigationOptions;
screen(必选):指定一个 React 组件作为屏幕的主要显示内容,当这个组件被createStackNavigator加载时,它会被分配一个navigation prop。
path(可选):用来设置支持schema跳转时使用;
navigationOptions(可选):用以配置全局的屏幕导航选项如:title、headerRight、headerLeft等;
StackNavigatorConfig
(可选):配置导航器的路由(如:默认首屏,navigationOptions,paths等)样式(如,转场模式mode、头部模式等)。
从react-navigation源码中可以看出StackNavigatorConfig支持配置的参数有10个。
function createStackNavigator(routeConfigMap, stackConfig = {}) {
const {
initialRouteKey,
initialRouteName,
initialRouteParams,
paths,
navigationOptions,
disableKeyboardHandling,
getCustomActionCreators
} = stackConfig;
...
routeConfig的screen既可以是一个Page,也可以是一个Navigator。比如:
export const AppStackNavigator = createStackNavigator({
HomePage: {
screen: HomePage
},
Page1: {// 动态配置
screen: Page1,
navigationOptions: ({ navigation }) => ({
title: navigation.state.params.name + '页面名'
})
},
Page2: {
screen: Page2,
navigationOptions: {//静态配置
title: 'this is page2'
}
},
Page3: {
screen: Page3,
navigationOptions: (props) => {
const { navigation } = props
const { state, setParams } = navigation
const { params } = state
return ({
title: params.title ? params.title : 'this is page3',
headerRight: (<Button
title={params.mode === 'edit' ? '编辑' : '保存'}
onPress={() => setParams({ mode: params.mode === 'edit' ? '' : 'edit' })
}
/>)
});
}
},
Bottom: {
screen: AppBottomNavigator,
navigationOptions: {
title: '底部'
}
},
Top: {
screen: AppTopTabNavigator,
navigationOptions: {
title: '顶部'
}
}
});
-----------------------------------------------------------------
import { createAppContainer } from 'react-navigation'
import { AppStackNavigator } from './navigators/AppNavigator'
AppRegistry.registerComponent(appName, () => createAppContainer(AppStackNavigator));
一个不错的react-native 矢量图标库:https://oblador.github.io/react-native-vector-icons/
一个不错的入门博文:http://www.devio.org/2018/05/15/navigator-to-react-navigation/
关于react-navigation必须知道的一点:
a)当导航器中的配置的页面被打开时,它的props会收到一个navigation属性
,navigation属性
是整个导航环节的关键一员。
navigation包含一下功能:
- navigate:跳转到其他界面;
- state:屏幕的当前state;
- setParams:改变路由的params;
- goBack:关闭当前屏幕;
- dispatch:向路由发送一个action;
可以通过this.props.state.params来获取通过setParams()
,或navigation.navigate()
传递的参数。
<Button
title={params.mode === 'edit' ? '保存' : '编辑'}
onPress={() =>
setParams({mode: params.mode === 'edit' ? '' : 'edit'})}
/>
<Button
title="Go To Page1"
onPress={() => {
navigation.navigate('Page1',{ name: 'Devio' });
}}
/>
const {navigation} = this.props;
const {state, setParams} = navigation;
const {params} = state;
const showText = params.mode === 'edit' ? '正在编辑' : '编辑完成';
注意:navigation.setParams改变的是当前页面的Params,如果要改变其他页面的Params可以通过NavigationActions.setParams完成。
注意:一个navigation有可能没有navigate、setParams以及goBack,只有state与dispatch,所以在使用navigate时要进行判断,如果没有navigate可以使用navigation去dispatch一个新的action。
- SafeAreaView可以适配全面屏 - 仅支持iOS;
6、列表相关
FlatList与SectionList的底层实现都是VirtualizedList,因此类似于Android中的RecyclerView,存在Item的复用机制,是一个高性能的列表渲染组件。老版本的ListView由于没有这种复用机制,性能相对较差,基本废弃使用。
VirtualizedList - 支持滚动加载(onEndReached)、下拉刷新(onRefresh/refreshing)、可见性配置(onViewableItemsChanged/viewabilityConfig实现)、滑动方向、更智能的Item及section separators支持、Multi-column(借助numColumn)、对Flow更加友好、添加scrollToEnd、scrollToIndex和scrollToItem方法的支持;
FlatList - 高性能的列表组件;SectionList - 支持头部分组的列表组件;SwipeableList - 支持侧滑操作的列表组件;
未完待续.......