技术咨询、项目合作、广告投放、简历咨询、技术文档下载 点击这里 联系博主

# ReactNative和js版本不一致问题分析

前言:在启动 RN 工程的时候很可能会遇到 javascriptreact-native 版本不一致的问题,比如:

console.error: "React Native version mismatch. JavaScript version: 0.61.5 Native version: 0.55.0

其实一眼就能明白就是 js 版本和 native 版本不一致导致的,如果按照工程提示执行watchman watch-del-all && react-native start --reset-cache." 大部分的情况会发现是没有效果的;本文将从如何解决此问题,以及此问题发生的原因两个方面剖析, 废话不多说,先告诉你方案;

# 一、解决方案

通过谷歌发现了有如下几种解决方案:

  1. native 版本更新到和 js 版本一致并重新npm install;(tips: 如果已经一致了,请看下面的方案)

修改android/app/build.gradle下面的 implementation "com.facebook.react:react-native:+"为指定的版本 implementation ('com.facebook.react:react-native:0.61.5') { force = true } // 0.61.5 处变为自己的 JS 工程中的相应版本号

  1. 关闭所有和 react-native 相关的终端,重新在当前目录启动npm run start或者react-native run-android;

原因分析:在运行项目之前,可能本地 node 服务中已经运行了另外一个 RN 项目,而另外那个项目的 js 版本为 0.55.0,要运行的项目 js 版本为 0.61.5。所以就引发了上面的错误。解决办法就是关闭 node 服务,重新运行项目就可以了(react-native run-android 命令启动项目时,如果 node 服务没有启动,会自动启动)node 服务中不能同时运行不同 js 版本的项目

  1. 清空react-native的缓存,重新npm install或者yarn
# 缓存清除
rm -rf ~/.rncache
rm package-lock.json
sudo rm $TMPDIR/*
sudo rm -rf $TMPDIR/*
watchman watch-del-all
watchman watch-del-all
rm yarn.lock
rm -rf node_modules/
rm -rf ios/build

# 重新install
npm install

  1. 如果上述三种方法还存在问题,请查看自己的宿主工程react-native的版本是否和当前工程的版本一致

    此种情况比较特殊:可能你当前运行的 jsbundle 是动态加载的,其宿主工程本身就是一个react-native的项目,而宿主工程的版本是 0.55.0 那么新工程如果使用 0.61.5 则 还是会报错 native 的版本不一致

# 二、react-native版本检查分析

# 1、寻找报错位置

通过搜索关键字 React Native version mismatch 可以发现检测的最终代码在 Libraries/Core/ReactNativeVersionCheck.js 中:

import Platform from "../Utilities/Platform";
const ReactNativeVersion = require("./ReactNativeVersion");

exports.checkVersions = function checkVersions(): void {
  const nativeVersion = Platform.constants.reactNativeVersion;
  if (
    ReactNativeVersion.version.major !== nativeVersion.major ||
    ReactNativeVersion.version.minor !== nativeVersion.minor
  ) {
    console.error(
      `React Native version mismatch.\n\nJavaScript version: ${_formatVersion(
        ReactNativeVersion.version
      )}\n` +
        `Native version: ${_formatVersion(nativeVersion)}\n\n` +
        "Make sure that you have rebuilt the native code. If the problem " +
        "persists try clearing the Watchman and packager caches with " +
        "`watchman watch-del-all && react-native start --reset-cache`."
    );
  }
};

查看源码可以发现其JavaScript version主要来源于ReactNativeVersion.version而 Native 版本来自于Platform.constants.reactNativeVersion 判断版本是否一致主要是看以上两个是否匹配。

# 2、ReactNativeVersion.version

在同目录下存在一个ReactNativeVersion.js文件,

exports.version = {
  major: 0, //主版本
  minor: 61, //小版本
  patch: 5, //补丁
  prerelease: null,
};

# 3、Platform.constants.reactNativeVersion

可以在Utilities目录中找到,Platform.android.js;

//关键内容在
 get constants() {
    if (this.__constants == null) {
      this.__constants = NativePlatformConstantsAndroid.getConstants();
    }
    return this.__constants;
  },

并同步找到NativePlatformConstantsAndroid文件


import type {TurboModule} from '../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';

export interface Spec extends TurboModule {
  +getConstants: () => {|
    isTesting: boolean,
    reactNativeVersion: {|
      major: number,
      minor: number,
      patch: number,
      prerelease: ?number,
    |},
    Version: number,
    Release: string,
    Serial: string,
    Fingerprint: string,
    Model: string,
    ServerHost: string,
    uiMode: string,
  |};
  +getAndroidID: () => string;
}

export default (TurboModuleRegistry.getEnforcing<Spec>(
  'PlatformConstants',
): Spec);


查看得知reactNativeVersion 是通过 TurboModuleRegistry.getEnforcing('PlatformConstants') 获取到的;在工程下全局搜索PlatformConstants得知: 在/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java文件中可以看到reactNativeVersion是通过 ReactNativeVersion.VERSION得到的

  @Override
  public @Nullable Map<String, Object> getConstants() {
    HashMap<String, Object> constants = new HashMap<>();
    constants.put("Version", Build.VERSION.SDK_INT);
    constants.put("Release", Build.VERSION.RELEASE);
    constants.put("Serial", Build.SERIAL);
    constants.put("Fingerprint", Build.FINGERPRINT);
    constants.put("Model", Build.MODEL);
    if (ReactBuildConfig.DEBUG) {
      constants.put("ServerHost", getServerHost());
    }
    constants.put(
        "isTesting", "true".equals(System.getProperty(IS_TESTING)) || isRunningScreenshotTest());
    constants.put("reactNativeVersion", ReactNativeVersion.VERSION);
    constants.put("uiMode", uiMode());
    return constants;
  }

在与AndroidInfoModule同目录下存在 ReactNativeVersion.java

public class ReactNativeVersion {
  public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
      "major", 0,
      "minor", 61,
      "patch", 5,
      "prerelease", null);
}

可以看出, Platform.constants.reactNativeVersion 是在 java 侧定义的,最终在原生代码中,我们在 build.gradle 文件中引用的 com.facebook.react:react-native:+ 则包含了这部分代码。

# 4、js 和 java 的版本在哪里设置?

可以看出,在 js 侧有个版本号,同时在 java 侧也有个版本号,两者会在启动的时候进行判断,如果不相同就会抛出错误。

jsjava 是两个依赖,js 部分在 package.json 中进行依赖,java 部分在 android/app/build.gradle 中依赖,两者必须匹配才能很好的工作,所以有了上述的检查工作

# 5、项目初始化之后版本号如何自动设置

查看 RN 源码得知,java 端的版本的模板在scripts/versiontemplates/ReactNativeVersion.java.template (opens new window)中,通过执行scripts/bump-oss-version.js (opens new window)将版本号注入到模板中

fs.writeFileSync(
  "ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java",
  cat("scripts/versiontemplates/ReactNativeVersion.java.template")
    .replace("${major}", major)
    .replace("${minor}", minor)
    .replace("${patch}", patch)
    .replace(
      "${prerelease}",
      prerelease !== undefined ? `"${prerelease}"` : "null"
    ),
  "utf-8"
);

同理 js 的版本的模板在scripts/versiontemplates/ReactNativeVersion.js.template (opens new window)中,并通过 scripts/bump-oss-version.js (opens new window)替换

fs.writeFileSync(
  "Libraries/Core/ReactNativeVersion.js",
  cat("scripts/versiontemplates/ReactNativeVersion.js.template")
    .replace("${major}", major)
    .replace("${minor}", minor)
    .replace("${patch}", patch)
    .replace(
      "${prerelease}",
      prerelease !== undefined ? `'${prerelease}'` : "null"
    ),
  "utf-8"
);

# 总结

在开发环境,RN 启动阶段,会对 js 和 java 两边的版本号进行比较,匹配后才开始真正的系统启动流程。这么设计的原因很可能是和 RN 版本原因;因为 RN 版本更新比较快,很多代码及 API 都是和版本相关的; 保持版本一直的好处可以避免很多因为版本不一致导致的兼容性问题。

【未经作者允许禁止转载】 Last Updated: 2/4/2024, 6:06:40 AM