大纲
react-native-scrollable-tab-view在安卓模拟器上报错
react-native-echarts在安卓模拟器上不显示
react-native-echarts中的echarts源码版本过低,造成andorid柱状图点击主要图消失?下载最新版本修改源文件!
Animated动画组件
hooks中如何实现一个动画( rn中 )
Dimensions
PixelRatio
手势响应系统
PanResponder
Image,ImageBackground,Animated.Image
AppState
StyleSheet
FlatList
获取和清除缓存 react-native-http-cache
获取组件(或元素)的宽高等信息 => (两种方法)
react-native-debugger如何调试http请求,NetWork面板请求不显示问题
复习:
async函数
cookie
hooks中如何实现一个动画
例子1:
const List = () => {
const left = new Animated.Value(-300) // left的初始值,注意 Value是大写
useEffect(() => {
Animated.timing(left, { // 注意区分 timing,decay衰退,spring弹簧
toValue: 110, // 终值
duration: 1000
}).start() // start开始动画
})
return (
// view的动画组件
1111111
)
}
例子2:
- 使用 useRef 实现
- useRef 返回一个可变的ref对象,返回的对象将在组件的整个生命周期内存在
- useRef.current 初始化为传递的参数
const List = () => {
const left = useRef(new Animated.Value(-300)).current
useEffect(() => {
Animated.timing(left, {
toValue: 110,
duration: 1000,
useNativeDriver: false // 是否启用原生动画
}).start()
})
return (
1111111111111
)
}
------------------------------------
例3:综合案例
import React, {useRef} from 'react';
import {Animated, PanResponder, Dimensions, StyleSheet} from 'react-native';
const {height} = Dimensions.get('window'); // 获取手机的宽和高
/**
* @param {children} DOM reactElement
* @param {initialTop} number 竖直方向初始值
*/
/* eslint-disable no-unused-vars, react/jsx-props-no-spreading */
function CardSheet({children, initialTop}) {
const refCardAnimated = useRef(new Animated.Value(initialTop)).current;
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true, // 开始触摸时询问
onStartShouldSetPanResponderCapture: () => true, // 父级获权,捕获
onPanResponderRelease: (evt, gestureState) => {
// 手势释放时触发
// evt 原生的nativeEvent对象、gestureState手势对象
// dy -向上 +向下
// dy 表示从触摸操作开始时的累计纵向路程
Animated.timing(refCardAnimated, {
toValue: gestureState.dy < 0 ? 0 : initialTop,
duration: 50,
}).start();
},
});
return (
{children}
);
}
const styles = StyleSheet.create({
container: {
width: '100%',
position: 'absolute',
overflow: 'scroll',
zIndex: 99999,
backgroundColor: 'white',
},
});
export default CardSheet;
https://reactnative.cn/docs/panresponder/#docsNav
https://blog.codecentric.de/en/2019/07/react-native-animated-with-hooks/
(6) PanResponder
- 将多点触摸操作协调成一个手势
- 它在原生事件之外提供了一个新的
gestureState
对象
(1) gestureState对象
onPanResponderMove: (event, gestureState) => {}
- event: nativeEvent 对象
- gestureState:
- dx - 从触摸操作开始时的累计横向路程
- dy - 从触摸操作开始时的累计纵向路程 !!!!!!!!!!!! 注意:dy 是有正负的,向上是负数,向下是正数 !!!!!!!!!!!!!
- vx - 当前的横向移动速度
- vy - 当前的纵向移动速度
- d:是 distance 路程的缩写
- v: 是 velocity 速度的缩写
(2) PanResponder.create()
- 返回一个 panHandlers 对象
(5) 手势响应系统
(1) 响应者生命周期
1. View要成为触摸事件响应者,有两个询问的方法:
- onStartShouldSetResponder: (evt) => true -- 在 ( 开始触摸 ) 的时候询问
- onMoveShouldSetResponder: (evt) => true -- 如果View不是响应者,在 ( 开始移动 ) 的时候再次询问
2. 如果View返回true,并开始成为响应者,则下列事件可以被触发:
- onResponderGrant: (evt) => {} -- view要开始响应触摸事件了 ( grant: 是允许的意思 )
- onResponderReject: (evt) => {} -- 响应者现在“另有其人”而且暂时不会“放权”,请另作安排。
- onResponderTerminationRequest: (evt) => true
- 有其他组件请求接替响应者,当前的 View 是否“放权”?返回 true 的话则释放响应者权力。
- Terminate: 是中止,结束的意思
- onResponderTerminate: (evt) => {} -- 响应者权力已经交出。
- 这可能是由于其他 View 通过onResponderTerminationRequest请求的
- 也可能是由操作系统强制夺权(比如 iOS 上的控制中心或是通知中心)
3. evt 是一个合成事件
- nativeEvent
- changedTouches - 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
- locationX - 触摸点相对于当前元素的横坐标
- locationY - 触摸点相对于当前元素的纵坐标
- pageX - 触摸点相对于根元素的横坐标
- pageY - 触摸点相对于根元素的纵坐标
4. 优先级 ( 捕获ShouldSet事件处理 )
- onStartShouldSetResponder 和 onMoveShouldSetResponder 是以冒泡的方式调用,所以最初点击的DOM最新调用
- 如果父元素想优先于子元素响应的话,可以在捕获期拦截,在下面的事件中返回true,并在对应的函数中做处理
- onStartShouldSetResponderCapture: (evt) => true,
- onMoveShouldSetResponderCapture: (evt) => true,
(7) Animated动画组件
(1) 两种类型的值
- Animated.Value() 用于单个值
- Animated.ValueXY() 用于矢量值
(3) 三种动画类型
- Animated.decay() 以指定的初始速度开始变化,然后变化速度越来越慢,直至停下 (decay是衰退的意思)
- Animated.spring() 弹簧效果 (spring是弹簧,春天的意思)
- Animated.timing() easeinout函数
(4) 启动动画
- start()用来启动动画
(5) 启动原生驱动
- 在动画配置中指定 useNativeDriver:true 使用原生动画驱动
(6) 动画组件
- 注意:组件必须经过特殊处理才能用于动画
- 特殊处理指:把动画值绑定到属性上,并且在一帧帧执行动画时避免 react 重新渲染和重新调和的开销,此外还得在组件卸载时做一些清理工作
- 可以直接使用动画的组件:
- Animated.View
- Animated.ScrollView
- Animated.Text
- Animated.Image
(8) Image,ImageBackground,Animated.Image
- Animated.Image 可以直接使用的动画组件,注意使用该组件无需引入Image组件,是Animated的属性
- ImageBackground 相当于web中的背景图片,可以在ImageBackground组件内添加任意多个子集
(9) AsyncStorage
- 不推荐使用AsyncStorage,使用 react-native-storage 代替
- Deprecated:不推荐使用,反对
- AsyncStorage是一个简单,异步,持久的key-value存储系统,是全局的,可用用来代替localstorage
react-native-storage
- backend:后端
(1) storage.js
import Storage from 'react-native-storage';
import AsyncStorage from '@react-native-community/async-storage';
const storage = new Storage({
size: 1000,
storageBackend: AsyncStorage, // for web: window.localStorage
defaultExpires: 1000 * 3600 * 24,
enableCache: true,
sync: {
},
});
// add to global environment
// notice: should global import
global.storage = storage;
export default storage;
(2) 全局引入
- 在项目入口index.js中引入
- import '..../storage.js';
- 因为该模块只需要执行,而不用引入任何模块,所以直接import '路径/文件名' 即可 ( 即保证该文件执行过,才能挂载到global对象上 )
https://github.com/sunnylqm/react-native-storage
中文 https://github.com/sunnylqm/react-native-storage/blob/master/README.zh-CN.md
(10) AppState
- AppState 应用是在前台还是后台,在状态变化时通知您
- AppState 通常在处理
推送通知
的时候,用来决定内容和对应的行为
AppState
(1) AppState.currentState
- AppState.currentState 返回应用的当前状态,会一直保持更新
- 在应用启动过程中,AppState.currentState可能为null,知道AppState从原生代码得到通知
(2) 状态类型 status
- active 前台
- background 后台,分为几种情况
- 在别的应用中
- 停留在桌面
- 对 Android 来说还可能处在另一个Activity中(即便是由你的应用拉起的)
- [iOS] inactive - 此状态表示应用正在前后台的切换过程中,或是处在系统的多任务视图,又或是处在来电状态中。
(3) 事件
- change
- focus (仅android, 用户和应用进行交互时触发)
- blur (仅android,用户下拉通知抽屉时触发)
(4) 方法
- addEventListener()
- removeEventListener()
---------------------------------
(5) 实例
---------------------------------
import React, { useState, useEffect } from 'react';
import { AppState } from 'react-native';
const statusObj = { // 支持的三种状态
active: '应用正在前台运行',
background: '应用正在后台运行',
inactive: '应用切换中'
}
export const useAppStatus = () => {
const [appStatus, setAppStatus] = useState(AppState.currentState) // AppState.currentState属性
const listenStatus = (nextAppState) => {
if (nextAppState in statusObj) {
console.log(statusObj[nextAppState]);
setAppStatus(statusObj[nextAppState])
}
}
useEffect(() => {
AppState.addEventListener('change', listenStatus) // AppState.addEventListener 事件
return () => {
AppState.removeEventListener('change', listenStatus) // AppState.removeEventListener 事件
}
}, [])
return appStatus
}
(11) StyleSheet
marginVertical:上下两边的margin
marginHorizontal:左右两边的margin
transform: [{translateX: number}]
- StyleSheet 提供了一种类似 CSS 样式表的抽象。
StyleSheet
(1) 方法
- StyleSheet.create(obj)
- StyleSheet.flatten(style) // 展平数组中的样式对象,或者样式对象,相同属性后面会覆盖前面的属性
- StyleSheet.compose(style1, style2) // 组合
(2) 属性
- StyleSheet.hairlineWidth // 最细的线条,一像素边框
- 可以用来实现一像素边框
- 这一常量始终是一个整数的像素值(线看起来会像头发丝一样细),并会尽量符合当前平台最细的线的标准。
- 如果模拟器缩放过,可能会看不到这么细的线。
- StyleSheet.absoluteFill
transform https://reactnative.cn/docs/transforms/#docsNav
(12) 获取和清除缓存
- react-native-http-cache
import * as CacheManager from 'react-native-http-cache';
const formatCache = cache => {
let cacheSize = Math.floor(cache / 1024);
console.log(cacheSize, 'cacheSize');
switch (true) {
case cacheSize / 1024 > 1: {
cacheSize = `${Math.floor(cacheSize / 1024)} MB`;
break;
}
default: {
cacheSize = `${cacheSize} KB`;
break;
}
}
console.log('cache-size =>', cacheSize);
return cacheSize;
};
export const getCacheSize = async () => { // 获取缓存大小,注意getCacheSize()返回的是promise
const cache = await CacheManager.getCacheSize();
return formatCache(cache);
};
export const clearCache = async () => { // 清除缓存
const res = await CacheManager.clearCache(); // clearCache()返回的也是promise
console.log('清除缓存的返回值 =>', res);
return res;
};
安卓报错修改路径:node_modules\react-native-http-cache\android\src\main\java\cn\reactnative\httpcache
gitbub https://github.com/reactnativecn/react-native-http-cache
安卓按readme配置后报错:https://www.jianshu.com/p/b99d3a46947f
修改方法:
//FileCache cache1 = ImagePipelineFactory.getInstance().getMainDiskStorageCache();
FileCache cache1 = ImagePipelineFactory.getInstance().getMainFileCache();
//FileCache cache2 = ImagePipelineFactory.getInstance().getSmallImageDiskStorageCache();
FileCache cache2 = ImagePipelineFactory.getInstance().getSmallImageFileCache(
安卓报错修改路径:node_modules\react-native-http-cache\android\src\main\java\cn\reactnative\httpcac
(13) 获取组件(或元素)的宽高等信息
(1)方法一
- 在 View 组件中有 onLayout 事件
- {nativeEvent: { layout: {x, y, width, height}}}
- 通过 e.nativeEvent.layout 获取 x, y, width, height
- 注意:这个事件会在布局计算完成后立即调用一次,不过收到此事件时新的布局可能还没有在屏幕上呈现,尤其是一个布局动画正在进行中的时候。
- 如果遇到合成事件异步访问相关的警告,使用 event.persist()
- 缺点:只在初次渲染的时候才会触发这个函数,而且此种方法获取的是组件相对于父组件的位置坐标。
const onLayout = (e) => {
e.persist();
setCopyRightWidth(Math.floor(e.nativeEvent.layout.width))
}
(2)方法二
- UIManager, findNodeHandle
import { UIManager, findNodeHandle } from 'react-native';
this.myComponent=ref} />
UIManager.measure(findNodeHandle(this.myComponent),(x,y,width,height,pageX,pageY)=>{
//todo
})
http://erichuang.top/2018/05/21/ReactNative/React%20Native%E4%B9%8B%E8%8E%B7%E5%8F%96%E7%BB%84%E4%BB%B6%E4%BD%8D%E7%BD%AE%E4%B8%8E%E5%A4%A7%E5%B0%8F/
https://www.jianshu.com/p/20c63afc04b8
(14) react-native-debugger如何调试http请求
react-native-debugger如何调试http请求 ---- NetWork面板请求不显示问题
解决办法:
- 路径: node_modules\react-native\Libraries\Core\setUpXHR.js
- 注释掉: // polyfillGlobal('XMLHttpRequest', () => require('../Network/XMLHttpRequest'));
https://www.cnblogs.com/zhangtao1990/p/10016559.html
(1) react-native-scrollable-tab-view
解决方案:
(1) 解决方案:
yarn add @react-native-community/viewpager
react-native link @react-native-community/viewpager
(2) 关于link
添加包含原生代码的库需要几个步骤:
1. npm install 某个带有原生依赖的库
2. react-native link 某已安装的具体库名(注意:现在rn不需要link了,rn会自动link, 不需要手动link)
经过以上两步,现在原生依赖就成功地链接到你的 iOS/Android 项目了。
(3) 单词
community:是社区的意思
unpack: 打开,取出
issues https://github.com/ptomasroos/react-native-scrollable-tab-view/issues/1050
(2) react-native-echarts在安卓模拟器上不显示
- "react-native": "0.61.5",
- "react-native-webview": "^7.5.2",
- "native-echarts": "git+https://github.com/ideacome-frontend/react-native-echarts.git",
react-native-echarts在安卓模拟器上不显示
1. 安装 react-native-webview
- npm install react-native-webview -S
2. 将node_modules\native-echarts\src\components\Echarts下的tpl.html复制到android\app\src\main\assets目录下,
assets需要自己新建文件夹
3. 修改react-native-echarts的源码
- 在node_modules\native-echarts\src\components\Echarts下的index.js文件进行修改:
- 将Webview组件的source配置项修改为:
- source={Platform.OS === 'ios' ? require('./tpl.html') : {uri:'file:///android_asset/tpl.html'}}
import { View, StyleSheet, Platform } from 'react-native';
this.props.onPress ? this.props.onPress(JSON.parse(event.nativeEvent.data)) : null}
/>
https://www.cnblogs.com/lude1994/p/10647842.html
de
(3) react-native-echarts中的echarts源码版本过低,造成andorid柱状图点击主要图消失?下载最新版本修改源文件!
1. 下载最新版本的echarts,建议定制化下载,并开启代码压的缩
2. 找到RN中的native-echarts源码,修改native-echarts/src/components/Echarts/中的echarts.min.js和tpl.html
3. tpl.html文件中的script标签中加载的就是echarts的源码,直接拷贝最新版的echarts.min.js替换掉
4. 还要拷贝tpl.html文件到andorid/app/src/main/assets/tpl.html,替换掉这里的tpl.html
// 如果没有assets文件夹自己新建一个
(4) Dimensions
- dimensions: 是尺寸,范围的意思
const {height, width} = Dimensions.get('window');
(5) PixelRatio
PixelRatio
- pixel:像素
- pixelRatio:像素比
- PixelRatio.get()
- PixelRatio.getFontScale()
- PixelRatio.getPixelSizeForLayoutSize()
(1) PixelRatio.get()
- 返回设备的像素密度 // 1 2 3 3.5
(2) PixelRatio.getFontScale()
- 返回字体缩放比例
- 该比例用于计算绝对字体的大小
(3)PixelRatio.getPixelSizeForLayoutSize()
- 将布局尺寸转换为像素尺寸,一定是返回一个整数数值
- dp -> px
复习 async函数
- async函数返回一个promise对象,可以使用then方法添加回调函数
- 一旦遇到await就会先返回,等异步操作执行完后,才会执行函数体后面的语句
async 函数语法
(1) 基本语法
- async 函数返回一个promise对象
- async 函数内部 return 语句的(返回值),会成为 then 方法回调函数的 (参数)
- async 函数内部抛出错误,会导致返回的promise对象变成reject对象,可以被catch函数捕捉到
(2) 状态变化
- async 函数返回的promise对象,必须等到async函数内部所有await命令后面的promise对象执行完才会发成改变,除非遇到return或者抛出错误
即:只有async函数内所有异步操作执行完成后,才会只执行then方法指定的回调函数、
await命令
- 如果 await 命令后是一个 promise 对象,返回该对象的结果,如果不是promise对象,就会直接返回后面对应的值
- 如果 await 命令后是一个thenable对象(即定义了then方法的对象),await会将其等同于promsie对象
- await命令 后面的promise对象如果变成reject状态,reject的参数会被catch捕获
- 任何一个await命令后面的promise对象变为reject状态,整个async函数就会中断执行
- 需求:如果希望前一个异步操作失败,也不要中断后面的异步操作
- 两种方法
- 1.
const go = async () => {
try {
await Promise.reject('出错了')
} catch(e) {
}
return await Promise.resolve('还是执行了') // 前面的promise报错了,但是该promise仍然执行了
}
go().then(res => console.log(res, 'res'))
- 2.
async function f() {
await Promise.reject('出错了')
.catch(e => console.log(e));
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// 出错了
// hello world
使用注意点
- await命令后的promise对象可能是reject,所以最好使用try...catch保证能捕获错误,并且不会影响其他的await命令后的promise
- await命令只能用在async函数中,用在普通函数中就会报错
- async可以保留运行堆栈
(1)
const a = () => {
b().then(() => c());
};
- 上面b执行时,a并没有中断执行而是继续执行,当b执行完时,可能a已经执行完
- 所以b或者c函数中如果运行出错,则错误堆栈将可能不包括a
---------
(2)
const a = async () => {
await b();
c();
};
- 上面b执行时,a是中断执行的,a的上下文环境仍然保留
- 所以b或者c出错,错误堆栈将包括a
复习 cookie
- cookie是服务器保存在浏览器的一小段文本信息
- cookie的大小不能超过 4kb
- 浏览器每次向服务器发送请求都会携带上cookie
cookie主要用来分辨两个请求是否来自同一个浏览器,和保存一些状态信息
cookie
1. cookie包含的信息
- cookie的名字
- cookie的值
- 到期时间
- 所属域名,默认是当前域名
- 生效的路径,默认是当前网址
- 总结下是 key value expires max-age domain path secure http-only
如:
- cookie中有 www.example.com
- 如果根路径是 / ,==========> 表示该cookie对该域名的根路径和它的所有子路径有效
- 如果根路径是 /forums,=====> 表示cookie只有在访问 www.example.com/forums及其子路径时才有效
- forum:是论坛的意思
2. 浏览器可以设置不接受cookie,也可以设置不向服务器发送cookie
- window.navigator.cookieEnabled
- document.cookie
- window.navigator.cookieEnabled => 浏览器是否打开cookie功能,boolen值
- document.cookie => 返回当前网页的cookie,可读写
3. 浏览器对cookie的大小和数量限制
- 数量:不超过30个,单个域名的cookie数量不应超过30个
- 大小:不超过4kb
4. 共享cookie的条件
- 两个网址的域名和端口相同,就可以共享cookie
- 不要求协议相同
5. cookie由http协议生成,也主要供http协议消费
6. 服务器如何在浏览器中------------------------------------------------------------------------> 服务器保存cookie
- 需要http回应头中,放置 set-Cookie 字段
- Set-Cookie
- Set-Cookie:foo=bar // 在浏览器保存一个名为foo的cookie,值是bar
- Set-Cookie: =; Domain=; Secure; HttpOnly
Set-Cookie除了cookie的值,还可以附加多个cookie的属性,没有顺序要求
- 服务器如果希望在浏览器保存 Cookie,就要在 HTTP 回应的头信息里面,放置一个Set-Cookie字段。
- 注意:HTTP回应可以包含多个 set-Cookie 字段
7. 服务器如何修改已经存在的 cookie -------------------------------------------------------------> 服务器修改cookie
- 必须满足四个条件:( key,domain,path,secure ) 都要匹配
- 只要有一个属性不同,就会产生一个全新的 cookie,而不是替换原来的 cookie
8. 浏览器发送cookie ---------------------------------------------------------------------------> 浏览器发送cookie
- HTTP的头字段中设置 Cookie 字段
- Cookie: foo=bar
- Cookie字段可以包含多个cookie,用分号隔开
9. 服务器收到浏览器发来的cookie,有两点是无法知道的
- Cookie的各种属性
- 那个域名设置的cookie,是一级域名还是二级域名等
10. cookie的属性 ------------------------------------------------------------------------------> cookie的属性
- Expires
- Max-Age
- Domain
- Path
- Secure
- HttpOnly
Expires
- 指定一个具体的到期时间,时间到了后,浏览器就不会向服务器发送cookie
- 值是 UTC 格式,可以使用Date.prototype.toUTCString()进行格式转换
- 注意:如果Expires设置null,或者不设置,cookie只在当前会话有效,关闭浏览器后,cookie就会被删除
- 注意:上面要 Expires 和 Max-Age 都未设置时,cookie在是会话cookie
- 注意:浏览器是根据本地时间,决定cookie是否过期的
Max-Age ----------------------------------------------------> 优先级 ( Max-Age > Expires ),同时设置,将采用 Max-Age
------------------------------------------------------------> 都未设置,则为会话cookie,只在当前会话有效
- 指定从现在开始,cookie存在的秒数,单位是秒
- 保存一年即 360 * 24 * 60 * 60
Domain
- 指定浏览器在发出HTTP请求时,哪些域名会附带这个cookie
- 未指定,则默认是当前url的一级域名
Path
- 指定浏览器在发出HTTP请求时,哪些路径要携带这个cookie
Secure
- 指定浏览器只有在加密协议HTTPS下,才能将这个cookie发送到服务器
- 如果当前协议是HTTP,则浏览器会忽略服务端发来的Secure属性
HttpOnly
- 指定该cookie无法通过js脚本拿到
- 注意是该cookie,响应头中可以存在多个Set-Cookie字段,即多个cookie
- ( Document.cookie,XMLHttpRequest对象,Request API 都拿不到cookie )
11. Document.cookie
- 读写当前网页的cookie
- 读取 --------------------------------> 返回当前网页的 ( 所有cookie )
- 写入 --------------------------------> 一次只能写入 ( 一个cookie )
- 浏览器向服务器发送cookie,-------------> 用一行 ( 全部发送 )
- 服务器向浏览器设置cookie,-------------> Set-Cookie是一行设置 ( 一个cookie )
- document.cookie读写行为的差异(一次可以读出全部 Cookie,但是只能写入一个 Cookie),与 HTTP 协议的 Cookie 通信格式有关
- 写入cookie时要注意:
- Expires是 UTC 时间,可以通过 Date.prototype.toUTCString() 进行日期格式化转换
- Max-Age单位是 秒
- Path是绝对路径
- Domain必须是当前发送cookie的域名的一部分
12. 删除cookie的唯一办法
----------------------------------------------------------------> 删除cookie的唯一办法是设置 Expires 为一个过去的时间