一、react native基础
一、标签
View
- 相当于以前web中的div
- 不支持设置字体大小,字体颜色等
- 不能直接放文本内容
- 不支持直接绑定点击事件(一般用TouchableOpacity来代替)
Text
文本标签
- 文本标签 可以设置字体颜色、大小等
- 支持绑定点击事件
TouchableOpacity
可以绑定点击事件的块级元素
- 相当于块级的容器
- 支持绑定点击事件
onPress
- 可以设置点击时的透明度
<TouchableOpacity activeOpacity={0.5} onPress={this.handlePress} ></TouchableOpacity>
Image
图片标签
- 渲染本地图片时
<Image source={required('../girl.jpg')} />
- 渲染网络图片时必须加入宽度和高度
<Image source={{ uri: 'http://XXX.jpg' }} style={{ width: 200, height: 300 }} />
- 在android上支持GIF和webP格式图片
默认情况下android是不支持GIF和webP格式的,你需要在andriod/app/build.gradle文件中根据需要手动添加以下模块:
dependencies: {
// 如果你需要支持android4.0(API level 14)之前的版本
implementation 'com.facebook.fresco:animated-base-support:1.3.0'
// 如果你需要支持GIF动图
implementation 'com.facebook.fresco:animated-gif:2.0.0'
// 如果你需要支持webP格式,包括webP动图
implementation 'com.facebook.fresco:animated-webp:2.1.0'
implementation 'com.facebook.fresco:webpsupport:2.0.0'
// 如果只需要支持webP格式,不需要动图
implementation 'com.facebook.fresco:webpsupport:2.0.0'
}
ImageBackground
一个可以使用图片当作背景的容器,相当于以前的div+背景图片
<ImageBackground source={...} style={{ width: '100%', height: '100%' }}>
<Text>Inside</Text>
</ImageBackground>
TextInput
输入框组件
- 可以通过
onChangeText来获取输入框的值
二、语法
插值表达式
import React from 'react';
import { View, Text } from 'react-native';
const Index = () => {
rerurn (
<View>
<Text>{ "开心" }</Text>
<Text>{ 123 }</Text>
</View>
)
}
export default Index
组件
- 函数组件
-
- 没有state(可以通过hooks)
- 没有生命周期(可以通过hooks)
-
- 适合简单的场景
import React from 'react';
import { View, Text } from 'react-native';
const Index = () => {
let num = 100;
setInterval(() => {
num++;
}, 1000)
return <View>
<Text>{ num }</Text>
</View>
}
- 类组件
-
- 适合复杂的场景
- 有state
-
- 有生命周期
import React from 'react';
import { View, Text } from 'react-native';
class Index extends React.Component {
state = {
num: 100
}
// 组件挂载完毕 类似vue中mounted
componentDidMount() {
alert('发送异步请求‘)
}
render() {
setTimeout(() => {
// 修改state
// 不能直接修改 this.state.num = 1000, 错误!!!
this.state({
num: 1000
})
}, 1000)
return <View>
<Text>类组件: { this.state.num }</Text>
</View>
}
}
状态state
属性props
父子传递数据的关键
拓展:插槽,类似于vue中的slot,利用的是props.children
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class Index extends Component {
render() {
return (
<View>
<BigText fontColor="red">测试</BigText>
</View>
)
}
}
class BigText extends Component {
render() {
// 通过props来接收负组件传递的数据
return <Text style={{ color: this.props.fontColor }}>
{ this.props.children }
</Text>
}
}
export default Index
调试
分为两种方式
- 使用谷歌浏览器来调试(iOS中使用command+D)
-
- 使用谷歌浏览器即可
- 不能查看标签结构
-
- 不能查看网络请求
- 使用rn推荐的工具
react-native-debugger来调试
-
- 可以查看标签结构
- 不能查看网络请求
- 想要查看网络请求
-
- 找到项目的入口文件
index.js - 加入以下代码即可
- 找到项目的入口文件
GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest
react-native-debugger使用方法:
- react-native-debugger下载地址:github.com/jhen0409/re…
- 启动命令:
open "rndebugger://set-debugger-loc?host=localhost&port=8081"(默认端口号为8081,可以根据自己启动的项目端口号进行更改),再利用command+D打开操作栏,选择Debug with Chrome启动即可。command+d打不开时,可以选择Simulator -> Device -> shake。或者Simulator -> Hardware -> Shake Gesture。
事件
绑定事件需要特别注意this的指向问题,可以总结为以下方式:
- 使用箭头函数
- 通过bind重新绑定this
- 匿名函数
- 利用构造函数
import React from 'react';
import { View, Text } from 'react-native';
class Index extends React.Component {
{/* 使用构造函数和bind */}
constructor() {
super();
this.handlePress = this.handlePress.bind(this)
}
state = {
num: 100
}
handlePress() {
this.setState({
num: 1000
})
}
// 使用箭头函数
handlePress = () => {
this.setState({
num: 1000
})
}
render() {
return (
<View>
<Text onPress={this.handlePress}>{ this.state.num }</Text>
{/* 通过bind重新绑定this */}
{/* <Text onPress={this.handlePress.bind(this)}>{ this.state.num }</Text> */}
{/* 匿名函数 */}
{/* <Text onPress={() => this.handlePress()}>{ this.state.num }</Text> */}
</View>
)
}
}
生命周期
生命周期指: react组件从创建到销毁的整个过程中会自动触发的函数
主要的生命周期
- constructor
-
- 组件被实例化的时候触发,一般用作对组件做初始工作,如设置state等
- render
-
- 组件开始渲染时触发
- 组件被更新时触发,state和props发生改变时触发
- componentDidMount
-
- 组件挂载完毕,可以发送异步请求获取数据
- componentWillUnmount
-
- 组件被卸载时触发
- 一般用在清除定时器或者取消订阅等
\
二、rn常见错误
1、运行项目报错:phasescriptexecution failed with a nonzero exit code
问题描述
- 使用Xcode进行代码编写,在启动项目时会一直提示报如下错误。
错误原因
- 出现这个问题的主要原因是工作区已满,导致代码编译出现错误。解决问题的方法大致两种。
-
- 进入Xcode的代码工作缓存区文件夹进行手动清理(容易出错,不建议)
- 直接用Xcode提供的清理方法,Xcode清理完工作区后会自动将工作文件连接(不会出错,推荐)
解决方法
- 首先在 Product -> Scheme 中选择当前项目的主代码模块
- 选择 Product -> Clean Build Folder 清理工作区
- 之后再重新编译项目代码就可以了
注意:如果本地存在8081端口号被占用,可以利用命令行重新设置新的端口号启动项目即可。
2、iOS运行项目报错: "No bundle URL present"
解决方法
- 修改代理shadowsocks VPN,调整自动代理
- 配置host,在本地的host配置中添加
127.0.0.1 localhost即可
- 报错也可能是因为文件错误,可以打开Xcode查看是否有错误,进行解决。模拟器中不会出现明显的错误提示。
三、mobx相关
1、基本用法
1、安装依赖
mobx核心库mobx-react方便在react中使用mobx技术的库
@babel/plugin-proposal-decorators让rn项目支持es7装饰器语法的库
yarn add mobx mobx-react @babel/plugin-proposal-decorators
2、在babel.config.js中添加以下配置
plugins: [
["@babel/plugin-proposal-decorators", { 'legacy': true }]
]
3、新建文件 mobx/index.js 用来存放全局数据
import { observable, action } from 'mobx';
class RootStore {
// observable 表示数据可监控 表示是全局数据
@observable name = 'hello';
// action 行为 表示 changeName 是个可以修改全局共享数据的方法
@action changeName(name) {
this.name = name
}
}
export default new RootStore();
4、在根组件中挂载
通过 Provider 来挂载和传递
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import rooStore from './mobx';
import { Provider } from 'mobx-react';
class Index extends Component {
render() {
return (
<Provider rootStore={rootStore}>
<Sub1></Sub1>
</Provider>
)
}
}
export default Index
5、其他组件使用
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { inject, observer } from 'mobx-react';
@inject('RootStore') // 注入 用来获取全局数据的
@observer // 当全局发生变化了 组件的重新渲染 从而显示最新的数据
class Sub1 extends Component {
changeName = () => {
// 修改全局变量
this.props.rootStore.changeName(Date.now());
}
render() {
return (
<View>
<Text onPress={this.changeName}>{ this.props.rootStore.name }</Text>
</View>
)
}
}
export default Sub1
2、踩坑经历
1、使用mobx之后数据更新而页面没有重新渲染
- 问题描述:react项目中,使用mobx,通过action方法修改状态后,值能打印出来,发生了变化。但是页面并没有重新渲染,还是显示原来的值。
- 解决方法:需要在store仓库中添加
makeOvervable
import { observable, action, makeObservable } from 'mobx';
class RootStore {
constructor() {
makeObservable(this)
}
// es7的装饰器语法 Object.defineProperty
@observable name = '悟空';
@action // 行为,修改名称
changeName(name) {
this.name = name;
}
}
export default new RootStore();
四、项目
1、初始化项目
npx react-native init tanhuajiaoyou // 创建项目
// 使用react-navigation作为路由,安装依赖
// @ant-design/react-native 作为UI组件库
- 像素计算公式
-
- 设计稿的宽度 / 元素的宽度 = 手机屏幕 / 手机中元素的宽度
- 手机中元素的宽度 = 手机屏幕 * 元素的宽度 / 设计稿的宽度
- 利用navigation跳转路由的方法
this.props.navigation.navigate('');
2、用到的第三方组件
- teaset
- react-native-svg-uri
- react-native-datepicker
- react-native-picker
- react-native-image-crop-picker
- 高德地图:react-native-amap-geolocation(使用方法参照官方文档)
- react-native-tab-navigator
- react-native-image-header-scroll-view(顶部图片吸顶效果)
- react-native-deck-swiper(需要安装react-native-view-overflow)
- react-native-linear-gradient
- react-native-image-zoom-viewer
- aurora-imui-react-native react-native-fs(聊天UI库)
- react-native-scrollable-tab-view(同时安装@react-native-community/viewpager)
3、React Native中使用svg
- react-native-svg使用
npm install react-native-svg --save
yarn add react-native-svg
// link native code
react-native link react-native-svg
以上操作,其实就是将raect-native-svg的依赖安装到客户端中,进行了上面的操作,基本已自动完成安装依赖,但是在部分ios上会存在问题,如遇到问题可以去react-native-svg查看解决方案
- react-native-svg-uri使用
react-native-svg-uri支持在RN中通过一个URL或者静态文件渲染svg,同时也支持传入svg字符串,将其渲染。
npm install react-native-svg-uri --save
yarn add react-native-svg-uri
// 确保已安装 react-native-svg,若安装依赖,执行
react-native link react-native-svg
-
- 使用方法
import SvgUri from 'react-native-svg-uri';
// 使用svg类型的图片
const TestSvgUri = () => (
<View style={styles.container}>
<SvgUri
width="200"
height="200"
source={require('./img/homer.svg')}}
/>
</View>
);
// 直接使用svg字符串
// 如果图标没显示,可能需要填充颜色 fill="#rgb"
const svgString = '<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10609"><path d="M938.666667 374.328889L529.066667 68.266667c-10.24-7.964444-23.893333-7.964444-34.133334 0l-409.6 306.062222c-6.826667 5.688889-11.377778 13.653333-11.377777 22.755555V921.6c0 15.928889 12.515556 28.444444 28.444444 28.444444h307.2c15.928889 0 28.444444-12.515556 28.444444-28.444444V716.8c0-40.96 32.995556-73.955556 73.955556-73.955556s73.955556 32.995556 73.955556 73.955556v204.8c0 15.928889 12.515556 28.444444 28.444444 28.444444h307.2c15.928889 0 28.444444-12.515556 28.444444-28.444444V397.084444c0-9.102222-4.551111-17.066667-11.377777-22.755555z m-45.511111 518.826667h-250.311112V716.8c0-71.68-58.026667-130.844444-130.844444-130.844444S381.155556 645.12 381.155556 716.8v176.355556h-250.311112V411.875556l381.155556-284.444445 381.155556 284.444445v481.28z" p-id="10610" fill="#c863b5"></path></svg>'
const TestSvgUri = () => (
<View style={styles.container}>
<SvgUri
width="200"
height="200"
svgXmlData={svgString}
/>
</View>
);
4、基于Token实现身份验证和权限管理
- 在用户登录之后,将用户信息存储到mobx中,同时将用户信息存储到本地缓存中AsyncStorage(AsyncStorage已被废弃,利用react-native-async-storage代替)
- 在路由页面对用户做权限校验
什么是token
- http是一种无状态的协议,也就是http没法保存客户端的信息,没办法区分每次请求的不同;
- token是服务器生成的一串字符,作为客户端请求的令牌,当第一次登录后,服务器会分发token字符串给客户端。后续的请求,客户端只需要带上这个token,服务器即可知道是该用户的访问。
- 使用token,可以实现:权限管理、身份验证、防止同一账号异地登录。
token的验证过程
- 客户端:用户名和密码请求登录
- 服务端:收到请求,验证用户名和密码,验证成功后,分发一个token返回给客户端
- 客户端:将token存储,例如放在Cookie里或者LocalStorage里,后续每次请求,带上此token
- 服务端:收到请求,验证token是否正确,验证成功返回请求数据
5、iconfont字体图标
android版本
- 在字体图标网站上下载字体
- 然后拷贝ttf后缀的文件到
android/app/src/main/assets/fonts中,如果没有assets文件夹可以新建一个,并且在app/src/build.gradle中添加配置:
project.ext.vectoricons = [
iconFontNames: ['iconfont.ttf']
]
- 然后给
Text标签设置
<Text style={{ fontFamily: 'iconfont', color: 'red; }}>{ '\ue82b' }</Text>
6、项目开发中遇到的问题
问题一:使用react-native-image-crop-picker出现闪退问题
- 问题描述:在ios模拟器中,使用react-native-image-crop-picker包无法调用相册,出现闪退问题
- 问题分析:因为iOS10对隐私权限的管理更为严格 ,比如访问的摄像头、麦克风等硬件,都需要提前请求应用权限、允许后才可以使用,或者现在要提前声明
- 解决方法:
-
- 在ios文件夹下有一个info.plist文件,如图所示:
-
- 在该文件下,添加如下键值对,表示可以访问相册权限
<key>NSPhotoLibraryUsageDescription</key>
<string>photoLibraryDesciption</string>
-
- 添加完成之后需要重新启动,执行yarn ios命令,即可获取相册的权限
问题二、打开调试工具之后会影响接口请求
- 问题描述:打开调试工具之后,会影响网络请求,导致网络请求失败
- 原因分析:打开调试工具之后,调试工具可能会对网络请求进行拦截处理,导致一些请求失败
- 解决方法:在发送网络请求的时候最好关闭调试工具,只是用控制台即可
问题三、使用react-native-image-header-scroll-view包报错
- 问题描述:按照react-native-image-header-scroll-view的官方文档来引入会报错,如下所示:
- 原因分析:上述报错是说,元素类型无效:期望字符串(对于内置组件)或类/函数(对于复合组件),但得到:undefined。您可能忘记从定义组件的文件中导出组件,或者您可能混淆了默认导入和命名导入。就是使用的组件并没有被原组件导出。
- 解决方法:发现官方文档的示例是之前老版本的引入方式,现在的1.0.0以上的版本,采取了新的导入方式,如下所示:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { pxToDp } from '../../../utils/stylesKits';
import { ImageHeaderScrollView } from 'react-native-image-header-scroll-view';
class Index extends Component {
render() {
return (
<View style={{ flex: 1 }}>
<ImageHeaderScrollView
maxHeight={pxToDp(130)}
minHeight={pxToDp(44)}
headerImage={require("../../../imgs/BgImg.jpeg")}
renderForeground={() => (
<View style={{ height: 130, justifyContent: "center", alignItems: "center" }}>
<Text>touch me!</Text>
</View>
)}
>
<View style={{ height: 1000 }}>
<Text>交友</Text>
</View>
</ImageHeaderScrollView>
</View>
);
}
}
export default Index
问题四、非路由页面的跳转问题
问题描述:非路由页面使用this.props.navigation.navigate(XXX)没有效果
原因分析:因为非路由页面的props中不存在navigation,所以使用上述方法跳转时不生效
解决方法:如下所示
import { NavigationContext } from '@react-navigation/native';
class Index extends Component {
static contextType = NavigationContext;
// 此时利用this.context.navigate(XXX)可以进行跳转
this.props.context.navigate('Detail');
}
export default Index