React native 问题/技巧记录 [不定期更新]

[以下问题适用于 RN 6.0 以上版本,其他版本未测试,不定期更新]

方法数大于 65536


cannot fit requested classes in a single dex file
The number of method references in a .dex file cannot exceed 64K

严格来讲,这是 android 的问题,RN 默认设置触发了这个错误,可在 multidex 找到答案,简单说一下

Android 5.0(API级别小于21)以下不支持多个 dex (android 打包后的库文件),而单个 dex 默认情况下最多只能有 65536 个方法,所以 app 的依赖较多的话,就会大于这个数,就出现这个问题。而RN默认为16(android/build.gradle 中的 minSdkVersion),【根据 更新记录,RN 在 0.64 之后不再支持 API 16-20,默认 minSdkVersion 改为 21,所以不再需要做任何处理】,如果必须要兼容低版本 Android,则只能使用较低版本的 RN,解决方法如下

1: 不兼容 5.0 了,修改 minSdkVersion 版本为 21,该方案也许还要等几年 (衰
2: 想办法减少依赖,不现实
3: 做一些配置修改,具体见: multidex
4: 启用R8压缩,在RN中就是设置 android/app/build.gradleenableProguardInReleaseBuilds=true

启用 R8 生成 apk 会特慢,所以建议是 debug 时使用方案1或方案3,release 尝试使用方案4,但考虑到一般情况下,项目都是在做增量,所以哪怕是启用R8优化,总有一天会还是会超过 65536,还是老老实实用方案 3 吧。可以将 android/app/build/outputs 目录下生产的 apk 拖到 这个网站 查看总方法数

方案 3 在官方文档中已经写的很清楚了,这里记录一下,省的以后翻官方文档了。RN 使用了 androidx,如果是没有使用 andoridx 的项目, 会略有不同,请查阅官方文档

  1. 修改 android/app/build.gradle
android {
    defaultConfig {
        multiDexEnabled true  // 开启 multiDex 支持
    }
}
dependencies {
    implementation 'androidx.multidex:multidex:2.0.1' // 引入 multiDex 库
}
  1. 修改 android/app/src/main/[project]/MainApplication.java
// 修改
import android.app.Application;
// 为
import androidx.multidex.MultiDexApplication;

// 修改
public class MainApplication extends Application implements ReactApplication {
// 为
public class MainApplication extends MultiDexApplication implements ReactApplication {

RN 开启 R8 debug 问题


Requested enabled DevSupportManager, but DevSupportManagerImpl class was not found or could not be created

该问题 答案,在 android/app/proguard-rules.pro 中添加

-keep class com.facebook.react.devsupport.** { *; }
-dontwarn com.facebook.react.devsupport.**

启用 R8 优化,你可能碰到的不止这一个问题,尤其是安装插件多的时候,指不定哪个地方就暴露问题了。要特别小心的检查,如果插件明说了需要添加哪些混淆规则,一定要加上

Expiring Daemon because JVM heap space is exhausted

开启R8,在优化混淆的编译过程中需要内存,默认的不够用就出现这个情况了,一个简单的 答案

// 在 android/gradle.properties 添加
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8


// 在 android/app/build.gradle 添加
android {
    ...
    dexOptions {
       javaMaxHeapSize "3g"
    }
   ...
}

生成 apk 太大


默认配置是生成了一个全平台兼容的 apk,内部包含了 arm/x86 等的 lib,只需配置 android/app/build.gradle

修改后,将为各平台生成不同的 apk,不出意外,包体积大概为原来的 1/4

// 该值 改为 true
def enableSeparateBuildPerCPUArchitecture = true

android {
    splits {
        abi {
            ...
            // 若仍需要一个全平台兼容apk,这里改为 true 即可
            universalApk false 
        }
    }
}

babel 优化


RN 的编译用的 babel,配置文件为项目根目录的 babel.config.js,使用 react init project 载入的项目都会有这个。这与一般的 babel 项目没什么不同,所以可以利用 babel 插件对 rn 项目进行优化。

提前备注:若配置 babel 插件后未生效,可尝试

watchman watch-del-all
yarn start --reset-cache

一、自定义别名

对于安装好的第三方应用,可以很舒服的 import * from "moudule",但对于自己项目的源码,引入路径就成了可能就是这样子

import helper from './../../utils/helper'

我们可以使用 babel 的 module-resolver 为了让自己的代码引入也更加清爽,使用方法比较简单

安装 yarn add babel-plugin-module-resolver

module.exports = {

  // 添加以下配置, 在 alias 中配置别名
  plugins: [
    ['module-resolver', {
      // 这里根据自己项目用的, 也可能是 .ts | .native | .tsx 等
      extensions: [".ios.js", ".android.js", ".js", ".json"],
      alias: {
        '@pages': './src/pages',
        '@res': './src/res',
        '@utils': './src/utils',
      },
    }],
  ],

};

弄好这个,在项目中就可以 import helper from '@utils/helper', 嗯,舒服多了。

二、console 优化

开发中常用 console debug 或 依赖的第三方组件也会有 console,这些在 product 模式下是影响性能的一个因素,并且还会增加最终打包 js bundle 的体积,增加 app 运行时的内存占用。

使用 remove-console 插件在编译时移除 console 语句。

安装 yarn add babel-plugin-transform-remove-console

module.exports = {

  // 添加以下配置
  plugins: [
    "transform-remove-console"
  ],

};

三、propTypes 优化

具体原因可参见这个 issue,去除 propTypes 可减小代码体积,甚至可以提升性能。目前发现了 babel-plugin-transform-react-remove-prop-types 这个拓展,还没来得及测试。

vscode 配置


  1. 不使用 vscode 开发 java,感觉有点用但又不够用,还是 android studio 好用,就在插件中找 java 扩展,把他给禁用了,省的不小心点开个 Java 文件他就自动编译,还编译不好

android Studio 配置


  1. avd 模拟器默认放到系统用户目录了,磁盘不够用了,在 系统环境变量中添加 ANDROID_SDK_HOME 指定一个目录,将会做为 avd 存储目录,原来的目录可以删除掉。

相册访问


1,react-native-cameraroll

官方组件,但新版 rn 已从核心中移除该插件,若仍需要,可手动安装

yarn add @react-native-community/cameraroll

该扩展主要用于保存图片,另外提供了一个无界面读取图片列表 api,若有心,其实可以用 jsx 做一个界面出来,但对于界面问题,其实有更好的选择

2,react-native-image-picker

yarn add react-native-image-picker ( 详细 安装文档 )

该扩展自带选择图片的UI界面,同时具有拍摄功能,可以用于 上传或拍摄 的应用场景

3,react-native-image-crop-picker

yarn add react-native-image-crop-picker

该扩展与2类似,但在提供了 UI 界面的同时,额外提供了一个 图片裁剪 的功能

-------权限--------

以上扩展需要使用相册/相机/麦克风,所以需要添加权限

android: 修改 android/app/src/main/AndroidManifest.xml


     
     
    

iOS,修改 ios/[project]/Info.plist, 添加以下键值对

...

     ....
    NSPhotoLibraryUsageDescription
    需要您的同意, $(PRODUCT_NAME) 才能访问相册
    NSPhotoLibraryAddUsageDescription
    需要您的同意, $(PRODUCT_NAME) 才能保存图片

    NSCameraUsageDescription
    需要您的同意, $(PRODUCT_NAME) 才能访问相机
    NSMicrophoneUsageDescription
    需要您的同意, $(PRODUCT_NAME) 才能使用麦克风录制视频

PNG 自动优化


android 使用 gradle 编译,默认会优化 png 文件,这本来是个好事,但在使用热更,尤其是增量更新,这反而成了坏事了,因为自动优化会改变文件 hash,导致后续版本无法正确比对文件,生成增量

禁用该功能参见 官方文档,在 app 的 build.gradle 添加

android {
    …
    buildTypes {
        release {
            crunchPngs false
        }
    }
}

js 环境


这个略坑,需要特别注意。

开启 debug 的情况下使用 chrome v8 引擎,实际运行使用的是 js 环境可能就是 JavaScriptCore 引擎,测试没有问题不代表实际没问题,比如 atob btoa 这对 base64 互转的函数,在 v8 下是 ok 的,但在 rn 的 js 环境下可能是没有的。

为什么说可能,一是因为 JavaScriptCore 也可能升级,二是 android 可以定制引擎,比如 rn 其实自带了一个 hermes 引擎,社区实现的 react-native-v8 引擎。

总之一句话:在使用一些比较新的 js global 对象时,要注意测试关闭 debug 是否仍然可运行。

android 状态栏


假设要做一个全屏的应用,使用沉浸式状态栏效果比较好,此时整个屏幕都可以使用,那么页面高度设置为屏幕高度即可(若顶部有重要内容,还要考虑刘海屏的问题,可能不合适);但沉浸式在 android 5.0 以下不支持,不用额外安装插件,即可差异化实现。

import { Platform, Dimensions, StatusBar, StyleSheet, View} from 'react-native';

const {width:screenWidth, height:screenHeight} = Dimensions.get('window');
const viewHeight = Platform.OS === 'android' && Platform.Version < 21 
  ? screenHeight - StatusBar.currentHeight : screenHeight;

const styles = StyleSheet.create({
  wrapper: {
    width:screenWidth, 
    height:viewHeight,
  },
});

export default class extends Component {
  render() {
    return (
      
    )
  }
}

android 路径大全


  1. file://

绝对路径,通常为 APP 私有目录 或 系统共用目录

  1. content:// / android.resource://

文件符,这类路径背后其实映射了一个 file:// 路径,通常为跨 APP 共享文件的路径,比如 APP 读取 相册这个APP 的文件,相册的文件是私有的,直接给路径,不安全,系统层面不让读取,在用户授权的情况下,相册会生成一个 content:// 文件路径给其他 APP 使用。

  1. asset://

android/src/main/assets 目录下的文件,RN 默认是没这个目录的,需要的话可以自行创建一个,通常用来存放一些非常规类型的资源文件,比如静态 dat 文件,PDF 获取其他文档文件,一般用的较少,因为此类文件通常会联网获取。

该目录下文件会被打包到 APK 中,不能使用 RN 的热更进行替换。

  1. res://

android/src/main/res 目录下文件,与 asset:// 类似,一般存放图片类型文件,会被 android 索引为资源,即 drawable,但也可以放一些非常规文件到 android/src/main/res/raw, 此类文件也会被 android 索引.

在原生层面使用这类文件时,通常是直接调用文件名即可,无需前缀、后缀,所以这也就要求该目录下文件不可能重名,另外对于 drawable,可以根据 android 的规范针对不同分辨率创建多个目录,存放同名文件,运行时会自动选取最优路径。

在 RN 中, 使用 require('./file') 这种方式调用的文件在最终编译后,会被自动打包进 drawableraw,在 Debug 模式下, require('./file') 在 RN 内部会解析为 http://,指向运行时的调试地址,可以动态更新;对于图片,可使用 @2x 这种格式对应不同分辨率;

由于使用了 require, 所以 RN 可以在中间决定指向的根目录,并提供了自定义指向的接口,若未定义,则指向 android/src/main/res/,所以这种方式的文件可以很方便的进行热更。

require 本地文件


在 js 文件使用 require(filepath) 来引用本地文件,最常用的是 Image 组件,但可能有第三方拓展也需要这么用,比如 webview / video 等;有可能出现 js 错误的问题,这是因为 RN 对引用本地文件做了限制,仅支持某些后缀,具体支持的后缀可参见 这个文件,如果这些后缀不满足要求,可根据该 文档 进行设置。

即在 rn 项目根目录的 metro.config.js 配置 (该配置会完全覆盖 metro 默认值,要全量配置)

module.exports = {
  resolver: {
    assetExts: [
      'png',
      'gif',
      ....
      'm3u8',
      'dat'
    ],
  },
  ...
};

中文名崩溃


RN 0.71 Android 命名中文崩溃,参考 问题,该问题出现在 Debug 版本,主要是依赖的 FLIPPER 导致,只需修改 android/gradle.properties

FLIPPER_VERSION=0.145.0

你可能感兴趣的:(React native 问题/技巧记录 [不定期更新])