JSX
- Browser.js:作用是将 JSX 语法转为 JavaScript 语法
- JSX 中不能使用 if else 语句,但可以使用 conditional (三元运算) 表达式来替代
- 注释需要写在花括号中:
{/*注释...*/}
- JSX 允许在模板中插入数组,数组会自动展开所有成员
- 要渲染 HTML 标签,只需在 JSX 里使用小写字母的标签名,要渲染 React 组件,只需创建一个大写字母开头的本地变量
- class->className for->htmlFor
- 使用
{}
的情况:表达式,变量,对象,字典1
2
3style={styles.mainStyle}
var str = 'hello'
<Text>{str}</Text>
1 | //第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象 |
- 使用()的情况:返回组件
1
2
3
4
5
6render(){
return (
<View>
</View>
)
}
判断平台进行不同操作
1 | const instructions = Platform.select({ |
CSS布局
- 使用驼峰命名法
- style属性可以是一个普通的 JavaScript 对象
- 建议使用StyleSheet.create来集中定义组件的样式
- 后声明的属性会覆盖先声明的同名属性
创建一个样式表
1 | const styles = StyleSheet.create({ |
使用一个样式表
1 | <View style={styles.container}> |
- margin:第一个组件比较特殊,参照父组件,与父控件之间的间距。其他组件间距,相对于上一个组件
- padding:设置子控件与当前控件的位置,想设置自己的子控件相对自己的位置的时候使用
- 注意marginRight和width冲突,如果设置了width,marginRight无效
position - absolute:绝对定位,相对父组件
使用:当想把一个已经设置了宽度的控件快速的放在左下角,右下角的时候
- relative: 相对定位,相对自己的原始位置
使用:当想相对自己做出一些改变的时候,采用相对定位,比如相对自己,往下移动一点
FlexBox布局
1.flexDirection:row | row-reverse | column | column-reverse
决定了子元素在主轴方向的排列方式(此样式设置在父元素上),默认值是column1
2
3
4
5<View style={{flex: 1, flexDirection: 'column'}}>
<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>
2.justifyContent(no flex:1):flex-start | flex-end | center | space-between | space-around | space-evenly
决定了子元素在主轴方向的对齐方式(此样式设置在父元素上),默认值是flex-start1
2
3
4
5
6
7
8
9<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>
3.alignItems(nowrap,align-self):flex-start | flex-end | center | stretch
决定了子元素在次轴方向的排列方式(此样式设置在父元素上),默认值为 stretch,子组件在侧轴方向被拉伸到与容器相同的高度或宽度次轴:与主轴垂直的轴,若主轴方向为row,则次轴方向为column
要使stretch选项生效的话,子元素在次轴方向上不能有固定的尺寸
1
2
3
4
5
6
7
8
9
10<View style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'stretch',
}}>
<View style={{width: 50, height: 50, backgroundColor: 'powderblue'}} />
<View style={{height: 50, backgroundColor: 'skyblue'}} />
<View style={{height: 100, backgroundColor: 'steelblue'}} />
</View>
4.alignSelf:auto | flex-start | flex-end | center | stretch
决定了元素在父元素的次轴方向的排列方式(此样式设置在子元素上),其值会覆盖父元素的alignItems的值。默认值为 auto,继承它的父容器的alignItems属性。如果没有父容器则为 “stretch”。
5.aligncontent(wrap):stretch | flex-start | flex-end | center | space-between | space-around
6.flexWrap:wrap | no-wrap
决定子控件在父视图内是否允许多行排列,显示不全时是否自动换行。默认为nowrap
7.flex:子控件在主轴中占据几等分,任意数字,所有子控件flex相加,自己flex占总共多少,就有多少宽度.
props,state
- props:一般用于自定义组件,大多数组件在创建时就可以使用各种参数来进行定制,用于定制的这些参数就称为props(属性)。
- 在父组件中指定,而且一经指定,在整个组件的生命周期中都不再改变。通过this.props.name访问。
this.props.children表示组件的所有子节点
state:以后想修改某个属性,就修改界面,就需要用state。
- constructor中声明(ES6),在setState中修改数据
this.props 表示那些一旦定义,就不再改变的特性,而 this.state 是会随着用户互动而产生变化的特性。
通过state 来更新和修改数据,而子组件只能通过 props 来传递数据
定义state:1
2
3this.state = {
num:1,
};
修改state:1
2
3this.setState({
num : number
})
或者1
2
3this.setState({name: e.target.value}, function(){
console.log(this.state.name);
})
- 一切界面变化都是状态state变化
- state的修改必须通过setState()方法
- this.state.likes = 100; // 这样的直接赋值修改无效!
- setState 是一个 merge 合并操作,只修改指定属性,不影响其他属性
- setState 是异步操作,修改不会马上生效
父子组件传值
父传子
- props:this.props.name
- ref:this.refs.son.receiveMsg(“msg”)
子传父
- 方法回调
父组件:定义一个处理接收到值的方法,把这个方法传递给子组件,并且绑定this
子组件:通过this.props拿到这个方法调用无关联组件间传值
- 通知:组件1传值给组件2
组件1:1
2
3<Text onPress={()=>{
DeviceEventEmitter.emit('NotificationName',123);
}}></Text>
组件2:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18componentDidMount() {
this.age = 10;
this.age = 20;
console.log(this.age);
// 定义属性,但是对这个属性的修改不会触发render
this.lister = DeviceEventEmitter.addListener('NotificationName',(value)=>{
this.setState({
value:value
});
})
}
componentWillUnmount() {
this.lister.remove();
}
组件生命周期
1 | export default class RNDemo extends Component { |
实例化
0.defaultProps:给props设置默认值
1.constructor:初始化state(只调用一次)
2.componentWillMount:即将加载组件调用,render前(只调用一次)
3.render:渲染组件
4.componentDidMount:组件加载完成调用,render后(只调用一次)
- 除了 render ,其他方法只调用一次
运行
props改变
5.componentWillReceiveProps:props改变调用state改变
6.shouldComponentUpdate:props/state改变调用,可控制是否刷新界面
7.componentWillUpdate:组件即将更新调用(调用this.setState会循环)
8.render
9.componentDidUpdate:组件更新完成(调用this.setState会循环) - 绝对不要在componentWillUpdate,componentDidUpdate中调用this.setState方法,否则将导致无限循环调用,在componentWillReceiveProps,shouldComponentUpdate可以
销毁
10.componentWillUnmount:组件即将销毁,可移除观察者,清空数据等
propTypes
- 可以实现类型检查,当传入错误的属性值,会报警告,但是不会报错
- 用PropTypes定义属性,外界使用的时候,会有提示。
- 必须要用static修饰,否则无效
- static:用来定义类方法或者类属性,定义类的方法和属性,生成的对象就自动有这样的属性了。
- 只能用于React框架的自定义组件,默认JS是没有的,因为它是React框架中的。
类型检查:当传入错误的属性值,会报警告,但是不会报错
导入:import React, { Component,PropTypes } from 'react';
- 定义属性
1
2
3
4static propTypes = {
name:PropTypes.string,
age:PropTypes.number
}
1 | propTypes: { |
设置初始化值
1 | static defaultProps = { |
基本组件
View
###TouchableOpacity:默认点击区域是所有子控件的区域,因为默认一个组件的尺寸由子控件决定
- activeOpacity:0-1 1-不透明
- 点击事件:onPress|onLongPress|onPressIn|onPressOut
- disabled:true|false
1
2
3
4<TouchableOpacity activeOpacity={0.7}>
<View style={styles.childSytle}>
</View>
</TouchableOpacity>
Text
- numberOfLines
- selectable:true|false 是否允许长按选择文本
- suppressHighlighting:true|false 是否允许按下时有灰色阴影
- onPress:文字点击事件 onPress={()=>{}}
- color/fontSize/fontWeight/lineHeight/textAlign
Button
- 不可设置样式,一般用text自定义
- color/disabled/onPress/title
TextInput
- 默认无边框
- autoFocus/blurOnSubmit/editable/maxLength/multiline/placeholder
- keyboardType/returnKeyType/clearButtonMode
- clearTextOnFocus/enablesReturnKeyAutomatically
- clear()
- 事件:onBlur/onFocus/onChange/onChangeText/onEndEditing/onKeyPress/onSubmitEditing
Image
iOS中的2x,3x图片同样适用,如果存在a@2x.png与a@3x.png,写a.png即可。
同时兼容iOS和安卓平台的图片,如果存在a.ios.png与a.android.png,同样写a.png即可。 - source/defaultSource
本地资源加载
1
<Image source={require('./images/add.png')}/>
通过url的方式加载图片,必须设置图片尺寸,否则不显示,iOS端可以添加缓存策略(default/reload/force-cache/only-if-cached)
1 | <Image source={{uri:'http://xxxxxxxx'}} style={{width: 90, height: 90}}/> |
本地图片存放路径
1.放在与index.ios.js同目录下的Img文件夹
2.iOS:可以存放到iOS项目中,打开iOS项目,存入到assets文件中
Android:可以存放到安卓项目中,必须放入drawable目录
- blurRadius
- resizeMode:cover|contain|stretch|repeat|center
- 事件:onLoad/onLoadStart/onLoadEnd/onProgress/onError
ScrollView
- horizontal/showsHorizontalScrollIndicator/showsVerticalScrollIndicator/alwaysBounceHorizontal/alwaysBounceVertical
- automaticallyAdjustContentInsets
- bounces/bouncesZoom/contentInset
- scrollEventThrottle/stickyHeaderIndices(与horizontal={true}冲突)
- 事件:onScrollBeginDrag/End/onMomentumScrollBegin/End/onScrollAnimationEnd/onScroll/scrollEventThrottle
自定义类
1.自定义类1
export default class Person{}
2.继承extends1
class HomeView extends Component
3.使用:var p = new Person();
ScrollView
- 不能通过scrollView获取,因为在RN中,滚动的时候,不会给scrollView组件的contentOffset属性赋值,只能通过nativeEvent事件获取
1
2
3
4
5
6
7
8_onScroll(e) {
// console.log('滚动的时候调用');
// var scrollView = this.refs.scrollView;
// console.log(scrollView.props.contentOffset);
var nativeEvent = e.nativeEvent;
console.log(nativeEvent.contentOffset);
}
ListView
0.ListViewDataSource1
2
3
4
5
6构造函数可以接受下列四种参数(都是可选):
getRowData(dataBlob, sectionID, rowID);
getSectionHeaderData(dataBlob, sectionID);
rowHasChanged(prevRowData, nextRowData);
sectionHeaderHasChanged(prevSectionData, nextSectionData);
1.创建数据源,给数据源设置数据
使用state保存数据源
不分组使用:cloneWithRows
1 | constructor(props) { |
分组使用:cloneWithRowsAndSections
1 | constructor(props) { |
2.实现数据源方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47<ListView dataSource={this.state.dataSource}
renderRow={this._renderRow.bind(this)}
renderHeader={this._renderHeader.bind(this)}
renderFooter={this._renderFooter.bind(this)}
renderSeparator={this._renderSeparator.bind(this)}
//renderSectionHeader={this._renderSectionHeader.bind(this)}
/>
_renderRow(rowData, sectionID, rowID, highlightRow) {
return (
<View>
<Text>{rowData}</Text>
</View>
);
}
_renderHeader() {
return (
<View>
<Text>头部视图</Text>
</View>
)
}
_renderFooter() {
return (
<View>
<Text>尾部视图</Text>
</View>
)
}
_renderSeparator(sectionID, rowID, adjacentRowHighlighted) {
console.log(sectionID,rowID,adjacentRowHighlighted);
return (
<View style={{height:1,backgroundColor:'black'}}></View>
)
}
//如果是组视图,设置组数据
_renderSectionHeader(sectionData, sectionID) {
return (
<View>
<Text>{sectionID}</Text>
</View>
)
}
FlatList
SectionList
默认情况下每行都需要提供一个不重复的key属性。你也可以提供一个keyExtractor函数来生成key。
把这个属性添加到 1
2
3
4
5keyExtractor = {this._extraUniqueKey}
_extraUniqueKey(item ,index){
return "index"+index+item;
}
导航Navigator
如果找不到Navigator,安装Navigator所在的库:1
2npm install react-native-deprecated-custom-components --save
//yarn add react-native-deprecated-custom-components
项目导入:1
import {Navigator} from 'react-native-deprecated-custom-components'
Tabbar
TabBarIOS
- 只可用于iOS平台
- badge可设置未读消息数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<TabBarIOS style={{flex:1,alignItems:'flex-end'}}>
<TabBarIOS.Item
title='消息'
icon={require('./Img/home.png')}
badge={3}
onPress={()=>{
this.setState({
selectIndex:0
})
}}
selected={0==this.state.selectIndex}
>
<View>
<Text>首页</Text>
</View>
</TabBarIOS.Item>
</TabBarIOS>
TabNavigator
- 可用于iOS和安卓
1.安装1
npm install react-native-tab-navigator --save
2.导入1
import TabNavigator from 'react-native-tab-navigator';
3.使用
- TabBarIOS:
icon={require('./Img/home.png')}
- TabNavigator: renderIcon是传入一个函数,这个函数返回一个Image组件,如果是通过url加载,一定要记得设置尺寸,否则不显示
1
renderIcon={() => <Image source={require('./Img/indexicon_hone_after@3x.png')} style={styles.iconStyle}/>}
1 | <TabNavigator}> |
主流架构搭建
ReactNavigation
1 | //推荐yarn |
安装图片组件:1
npm install --save react-native-vector-icons
如果报错 Unrecognized font family ‘Ionicons’:1
react-native link
createStackNavigator
createStackNavigator(RouteConfigs, StackNavigatorConfig);
createStackNavigator函数返回一个React组件,我们可以直接从App.js导出它以用作我们App的根组件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import {createStackNavigator} from 'react-navigation';
import Tabbar from './Tabbar'
import LoginPage from './LoginPage'
const RootStack = createStackNavigator({
//底部导航页面
Tabbar: {
screen: Tabbar,
navigationOptions: {
header: null //顶部导航很多都会自己自定义,这里就为空
}
},
//登录页面
Login: {
screen: LoginPage,
},
Details: {
screen: DetailsPage
navigationOptions: {
title: '详情'
}
}
});
export default RootStack;
切换路由:
1 | this.props.navigation.navigate('Details') 将新路由推送到堆栈导航器(如果它尚未在堆栈中),否则它将跳转到该屏幕 |
传递参数
通过将params放入一个对象作为navigation.navigate函数的第二个参数。
- 将参数传递给下一个路由:
this.props.navigation.navigate('RouteName',{/ * params go here * /})
- 获取前一个路由传递的参数:
this.props.navigation.getParam(paramName,defaultValue)
,还可以使用this.props.navigation.state.params直接访问params对象。 - 更新navigationOptions:this.props.navigation.setParams({name: params})
配置标题栏
设置标题
1
2
3static navigationOptions = {
title: 'Home',
};在标题中使用params
1
2
3
4
5
6static navigationOptions = ({navigation,navigationOptions }) => {
console.log(navigation)
return {
title: navigation.getParam('name', 'A Nested Details Screen'),
};
};调整标题样式
1
2
3
4
5
6
7
8
9
10
11static navigationOptions = {
title: 'Home',
headerStyle: { //标题栏样式
backgroundColor: 'red',
},
headerTintColor: '#fff', //标题文字和按钮颜色
headerTitleStyle: { //标题文字样式
fontWeight: 'bold',
// color:'blue' 如果这里也写了标题文字的颜色,那么会覆盖headerTintColor的颜色,即标题为蓝色
},
};跨屏幕共享通用navigationOptions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35export default class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home',
/* No more header config here! */
};
/* render function, etc */
}
------------------分割线-------------------
const Tabbar = createStackNavigator({
Home: {
screen: HomePage
},
Mine: {
screen: MinePage
}
});
Details: {
screen: DetailsPage
}
}, {
/* 主屏幕的标题配置现在在这里 */
navigationOptions: {
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}
});titleView,navigationItem,back
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21static navigationOptions = {
//用自定义组件替换标题
headerTitle: <LogoTitle/>,
//navigationItem
headerRight: (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="blue"
/>
),
//back
headerBackImage: <Image
source={require('../../image/title.png')}
style={{width: 30, height: 30}}
/>
//
//headerBackTitle: "title"
};
createBottomTabNavigator
1 | import { createBottomTabNavigator } from 'react-navigation'; |
网络请求
- fetch: 发送请求
- then: 回调函数
- response: 请求到的json数据
- catch: 捕获异常
发送GET请求:1
2
3
4
5
6
7
8fetch('http://192.168.1.143:8080') // 发送GET请求
.then((response)=>response.json()) // 请求成功,把请求数据转换为 JSON
.then((json)=>{ // 获取JSON数据做事情
console.log(json)
})
.catch((error)=>{ // 请求失败或者处理JSON数据失败,调用
console.log(error)
})
发送POST请求有三种方式:
- application/x-www-form-urlencoded: 参数是普通的参数拼接
- application/json: 参数是json格式
- application/form-data: 文件上传
request模块
本地存储 AsyncStorage
- 异步的
- 只能存储字符串
- 把数据保存到沙盒中的Documents中,并生成manifest.json文件
- save/read/delete
1
2
3AsyncStorage.setItem('object',JSON.stringify(object),(error)=>{});
AsyncStorage.getItem('object',(error,result)=>{});
AsyncStorage.removeItem('object',(error)=>{});
引导页
1.第一次进入界面,写个属性,记录下第一次加载。
2.每次启动,获取之前是否保存过第一次加载的属性,如果加载过,就显示主页,没加载过,就显示引导页
模块导出
- 自定义组件:继承自Component,必须使用class定义类
- 自定义类:可以不使用class定义
自定义组件导出
1.ES6/5
- ES6:一般使用默认组件,支持import导出
- ES5:一般使用非默认组件,支持require导出
2.默认组件/非默认组件 默认组件
1
2
3
4//定义
export default class XXX extends Component{}
//外部使用
import XXX from '../Custom/XXX'非默认组件
非默认组件:需要加入{}1
2
3
4//定义
export class XXX extends Component
//外部使用
import {XXX} from '../Custom/XXX'
自定义类
没有对象属性的自定义类,同组件的定义和导出方式
1 | //定义 |
有对象属性的自定义类
一般用function定义类,用module.exports(也可以export default)导出
1.定义类的两种方式
class定义类:在类中声明任何属性和方法,都会自动生成对象的属性和方法
1
export default class XXX {}
function定义类:要想定义属性和方法,属性和方法前面必须添加this
1
2
3
4function XXX(data) {
this.data = data;
this.func = function () {}
}
2.导出自定义类的两种方式
module.exports
1
module.exports = XXX;
export default
1
export default function XXX() {}
3.外部引用1
import XXX from '../Custom/XXX'
自定义类的继承–call方法
call方法
1.交换方法调用1
func1.call(func2); //调用func1
2.交换方法调用者1
c1.log.call(c2); //c2调用c1的log方法
3.通过function自定义类没有继承功能,需要通过call方法自己实现
本质:交换方法调用者
项目中经常遇到的问题
主头文件问题
1.创建主头文件1
2
3
4
5
6
7
8
9import Common from '../Common/GroupListView'
...
// 这句话的意思:把当前文件作为一个模块导出,模块里面有这些子组件
// 以后导入这个模块的时候,就能获取了这个模块里面的东西.
module.exports = {
GroupListView,
...
};
2.使用头文件1
2
3import Common from 'Common'
// 创建行模型
var item0 = new Common.GroupListView();
动画
Animated
Animated封装了四个可以动画的组件:View、Text、Image和ScrollView。
- 配置动画
- 组合动画
parallel
sequence
stagger
delay - 联动多个动画值
- 插值:interpolate({inputRange:[0,1],outputRange:[0,100],})
- 跟踪动态值:leader,follower,ValueXY
- 输入事件:Animated.event
- 使用原生动画驱动:
useNativeDriver:true
LayoutAnimation
1 | LayoutAnimation.spring(); |
定时器
- setTimeout,clearTimeout
- setInterval,clearInterval
- setImmediate,clearImmediate
- requestAnimationFrame,cancelAnimationFrame
requestAnimationFrame():用来执行在一段时间内控制视图动画的代码
setTimeout/setInterval/setImmediate:稍后执行代码,可能会延迟当前正在进行的动画
runAfterInteractions():稍后执行代码,不会延迟当前进行的动画1
2
3InteractionManager.runAfterInteractions(() => {
// ...需要长时间同步执行的任务...
});
*** 注意:在unmount方法中清除定时器
setNativeProps
微信登录
安装react-native-wechat
1 | npm install react-native-wechat --save |
自动关联
1 | react-native link |
配置项目
导入react-native-wechat
1 | import * as WeChat from 'react-native-wechat';//首先导入react-native-wechat |
从微信开放平台申请
1 | componentDidMount() { |
在需要触发登录的时候调用
1 | WeChat.sendAuthRequest("snsapi_userinfo"); |
调试
iOS模拟器上,cmd+D
为摇晃手势