react-native初探

1,519 阅读11分钟

一、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

调试

分为两种方式

  1. 使用谷歌浏览器来调试(iOS中使用command+D)
    1. 使用谷歌浏览器即可
    2. 不能查看标签结构
    1. 不能查看网络请求
  1. 使用rn推荐的工具 react-native-debugger来调试
    1. 可以查看标签结构
    2. 不能查看网络请求
  1. 想要查看网络请求
    1. 找到项目的入口文件 index.js
    2. 加入以下代码即可
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