美文网首页
React native 问题/技巧记录 [不定期更新]

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

作者: 马六甲的笔记 | 来源:发表于2019-11-30 10:44 被阅读0次

    [以下问题适用于 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 个方法,而RN默认为16(android/build.gradle 中的 minSdkVersion),所以 app 的依赖较多的话,就会大于这个数,就出现这个问题了,解决方法

    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

    <manifest ...>
         <!--  添加以下权限 -->
         <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    </manifest >
    

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

    ...
    <dict>
         ....
        <key>NSPhotoLibraryUsageDescription</key>
        <string>需要您的同意, $(PRODUCT_NAME) 才能访问相册</string>
        <key>NSPhotoLibraryAddUsageDescription</key>
        <string>需要您的同意, $(PRODUCT_NAME) 才能保存图片</string>
    
        <key>NSCameraUsageDescription</key>
        <string>需要您的同意, $(PRODUCT_NAME) 才能访问相机</string>
        <key>NSMicrophoneUsageDescription</key>
        <string>需要您的同意, $(PRODUCT_NAME) 才能使用麦克风录制视频</string>
    </dict>
    

    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 (<View style={styles.wrapper}>
          <StatusBar backgroundColor="transparent" translucent/>
        </View>)
      }
    }
    

    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'
        ],
      },
      ...
    };
    

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/uzogwctx.html