秋流到冬尽 春流到夏

0%

React Native 版本校验

背景

我们业务的 SDK 包含 ReactNative 方案,所以必须依赖 React Native 的 AAR,但是最近对接的客户有自己依赖的版本,和我们使用的版本不同,所以需要我们的 React Nativ 代码运行在他们使用的版本上。

问题

当 React Native 版本和 Native 版本不一致时,会弹出红框提示
{% note danger %}
React Native version mismatch.
JavaScript version:0.xx.x
Native Version:0.xx.x
{% endnote %}

解决方法

根据提示在源码里搜索发现,来自于Libraries/Core/ReactNativeVersionCheck.js这个文件里,根据文件注释可以看出是专门做版本校验

ReactNativeVersionCheck.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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`.',
);
}
};

可以看出如果是 path 兼容版本则不会弹框提示,非兼容版本时会弹框警告,如果要想去掉弹框提示,则直接注释掉这个校验代码,再使用命令行重新打包 js 代码即可。

探究

根据上面代码可以看出,根据命名大概可以猜出ReactNativeVersion是 js 侧的版本号, Platform.constants.reactNativeVersion是 Native 侧的版本号。
从 ReactNativeVersionCheck 文件的导包可以看到他们的定义目录

ReactNativeVersionCheck.js
1
2
import Platform from '../Utilities/Platform';
const ReactNativeVersion = require('./ReactNativeVersion');

通过搜索源码发现 ReactNativeVersionCheck.js->Platform.android.js->NativePlatformConstantsAndroid.js(PlatformConstants)->AndroidInfoModule.java->ReactNativeVersion.java 在这里我们发现了定义的地方

ReactNativeVersion.java
1
2
3
4
5
6
7
public class ReactNativeVersion {
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
"major", 0,
"minor", 0,
"patch", 0,
"prerelease", null);
}

但是都是 0 还未赋值,看到有这样的注释* @generated by scripts/bump-oss-version.js,原来是通过脚本动态赋值的。
查看脚本发现

bump-oss-version.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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',
);

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

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',
);

分别给 js,Android,iOS 赋值的代码。到这里我们就了解了 React Native 版本校验的逻辑。通过脚本动态生成三端的版本号。

疑惑

在 bump-oss-version.js 里发现这段代码,感觉挺难理解为啥要做这样的限制。

bump-oss-version.js
1
2
3
4
5
6
7
8
9
// - check that argument version matches branch
// e.g. 0.33.1 or 0.33.0-rc4
let version = argv._[0];
if (!version || version.indexOf(versionMajor) !== 0) {
echo(
`You must pass a tag like 0.${versionMajor}.[X]-rc[Y] to bump a version`,
);
exit(1);
}