React-Native踩坑指南

1,166 阅读5分钟

执行 expo init AwesomeProject 的时候需要在普通命令行下执行,不要用 git bash。

不要使用 chocolatey 安装 node,有图为证:

微信截图_20211008220800.png

npx react-native init 无反应:

参考:blog.csdn.net/weixin_4604…

abd devices offline

重新打开 "USB调试" 即可。

报错 Package signatures do not match the previously installed version

com.android.ddmlib.InstallException: INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package signatures do not match the previously installed version

卸载应用,重新跑命令:

"设置" → "应用和服务" → Application Name → 卸载,然后跑命令:

npx react-native run-android

警告 failed to connect to development server using adb reverse react native

运行命令:

adb reverse tcp:8081 tcp:8081

全局安装 react-devtools 注意:

推荐使用 npm 安装,而不用 yarn:

npm install -g react-devtools

使用 react-devtools 调试 rn 需注意:

除了跑命令 react-devtools ,还应执行一下命令(更正端口号):

adb reverse tcp:8097 tcp:8097

使用模拟器调试 rn:

  1. 连接模拟器:
adb connect addr:port
  1. 查看连接是否成功
adb devices
  1. 跑项目
npx react-native run-android

参考:juejin.cn/post/684490…

使用 adb 连接蓝叠:www.cnblogs.com/rogunt/p/13…

Image

当你使用@2x和@3x图时,也只需要:

<Image source={require('./img/check.png')} />

这样它就会自己识别机器去匹配相应的图片。

动态引图片写法注意:

// GOOD
<Image source={require('./my-icon.png')} />;

// BAD
var icon = this.props.active
  ? 'my-icon-active'
  : 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />;

// GOOD
var icon = this.props.active
  ? require('./my-icon-active.png')
  : require('./my-icon-inactive.png');
<Image source={icon} />;

使用静态图片它会自动适应宽高,使用网络图片则需要自己手动设置宽高。

背景图片使用 ImageBackground:

<ImageBackground source={require('./xx.jpg')} style={{width: 300, height: 200}}>
  <Text>Inside</Text>
</ImageBackground>

absolute 局中:

.foo {
	position: 'absolute';
	top: -10;
	marginHorizontal: 'auto';
}

react navigation:

api 整理:

navigation.navigate 指定跳转页

navigation.goBack 返回上一页

navigation.push 将路由 push 到 history 里,可相同页面跳多次

navigation.popToTop 弹出所有路由,直到第一个路由

使用 createNavigationContainerRef 来进行 navigation 操作:

参考:reactnavigation.org/docs/naviga…

Deep Linking

什么是 Deep Linking?

参考:www.zhihu.com/question/51…

rn 的原生事件发射器(EventEmitter):

import { DeviceEventEmitter } from 'react-native'
// 常用 api
DeviceEventEmitter.emit(xx)
DeviceEventEmitter.addListener(xx, callback)
DeviceEventEmitter.removeAllListeners(xx)

rn 调用 Android 原生方法:

参考:www.jianshu.com/p/27d87c616…

Execution failed for task ':app:checkDebugAarMetadata'

vim ./android/build.gradle

repositories 下加个 jcenter(),allprojects.repositories 下也加个 jcenter()

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext {
        buildToolsVersion = "30.0.2"
        minSdkVersion = 21
        compileSdkVersion = 30
        targetSdkVersion = 30
        ndkVersion = "21.4.7075529"
    }
    repositories {
        google()
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:4.2.2")
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        mavenCentral()
        mavenLocal()
        maven {
            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
            url("$rootDir/../node_modules/react-native/android")
        }
        maven {
            // Android JSC is installed from npm
            url("$rootDir/../node_modules/jsc-android/dist")
        }
        
        jcenter()

        google()
        maven { url 'https://www.jitpack.io' }
    }
}

参考:stackoverflow.com/questions/6…

react-native-swiper 使用异步数据会使得左右按钮失去作用?

虽然不知道为什么,但要在 外层添加判断:list?.length > 0 再渲染 Swiper。

安卓无法显示 gif:

// 需要与 react native 版本对应的 fresco 才可以
// 查看版本可以通过:
// gradlew :app:androidDependencies
implementation 'com.facebook.fresco:animated-gif:2.5.0'

react-navigation中 push, navigate, goback, pop 的区别:

www.cnblogs.com/nangezi/p/1…

TextInput 文字显示不全:

paddingVertical: 0

图片显示不全:

resizeMode 设置为 contain

页面 navigate 并不会销毁,只有 back 才会销毁

在进入下一页时,useEffect 的 return 函数并不会执行,其只有在 back(返回上一页) 时才会执行。这小可爱跟 react 和 小程序的套路完全不一样好吧,react 和 小程序在进入下一页,都是会执行销毁操作的,这个小可爱竟然还需要 useIsFocused 来 hack,佛了,佛了。

react-native 的 useIsFocused 销毁页面执行时机在下一个页面的 show 之后(坑!!!)

比方说,两个页面,分别都有如下代码:

// A
useEffect(() => {
		if (isFocused) {
			console.log('A ----------> show')
		}
    return () => {
			console.log('A ----------> destory')
		}
  }, [isFocused])

// B
useEffect(() => {
		if (isFocused) {
			console.log('B ----------> show')
		}
    return () => {
			console.log('B ----------> destory')
		}
  }, [isFocused])

那么,当从 A navigate B, 然后 B navigate 其他页面,会有如下打印:

A ----------> show
B ----------> show
A ----------> destory
B ----------> destory

这个真的是很扑街,下个页面的 show 竟在上个页面的 destory 之前,因此,removeAllListeners 操作最好指定清除事件名,不然所有都清除很容易把下个页面的全清了,如果下个页面有 listener 将无法顺利监听到!!!

DeviceEventEmitter 的 removeSubscription 已弃用的替代方案:

remove,yyds

View 的 measure 方法:

viewRef.current?.measure((x, y, width, height, pageX, pageY) => {
	// width 宽
	// height 高
	// pageX 离左边的距离
	// pageY 离顶部的距离
  console.log('measure: ', x, y, width, height, pageX, pageY)
})

TouchableWithoutFeedback 有阻止父层事件功效:

<TouchableOpacity onPress={handleParentPress}>
	<TouchableWithoutFeedback onPress={handlePress}></TouchableWithoutFeedback>
</TouchableOpacity>

以上,调用 handlePress 时,不会触发 handleParentPress

多图加载过慢原因:

在多图加载的场景里,经过实践,iOS 不管怎么折腾,表现都比较好,但是 Android 就容易出幺蛾子。下面我们就详细说说 Android 端如何优化图片。

在一些场景里,Android 会内存爆涨,帧率直接降为个位数。这种场景往往是小尺寸 Image 容器加载了特别大的图片,比如说 100x100 的容器加载 1000x1000 的图片,内存爆炸的原因就是上面说的原因。

那么这种问题怎么解决呢?Image 有个 resizeMethod 属性,就是解决 Android 图片内存暴涨的问题。当图片实际尺寸和容器样式尺寸不一致时,决定以怎样的策略来调整图片的尺寸。

  • resize小容器加载大图的场景就应该用这个属性。原理是在图片解码之前,会用算法对其在内存中的数据进行修改,一般图片大小大概会缩减为原图的 1/8。
  • scale:不改变图片字节大小,通过缩放来修改图片宽高。因为有硬件加速,所以加载速度会更快一些。
  • auto:文档上说是通过启发式算法自动切换 resize 和 scale 属性。这个启发式算法非常误导人,第一眼看上去还以为是会对比容器尺寸和图片尺寸采用不同策略。但我看了一下源码,它只是单纯的判断图片路径,如果是本地图片,就会用 resize,其他都是 scale 属性,所以 http 图片都是 scale 的,我们还得根据具体场景手动控制。

错误: 程序包android.support.annotation不存在import android.support.annotation.Nullable;

更改对应的java文件:

- import android.support.annotation.Nullable;
+ import androidx.annotation.Nullable;

解决 Modal 之上不能 toast

使用了 Modal ,toast 的无法覆盖在 Modal 之上,导致 toast 被遮罩挡住了,解决:

在 Modal 下方添加 RootSiblingParent 即可

<Modal>
	<RootSiblingParent>...</RootSiblingParent>
</Modal>

react-native-navigation navigate机制

调用 navigate 以后,会 push 一层路由,当重复调用时,能进行复用,举个例子:

A → B → C → B

以上第二次跳 B 时,能复用上次的 B,并且弹出后面的路由(C),所以当 B 返回是,会返回到 A。