昨天突然惊坐起,公司好多大佬都把项目升级到 React Native 0.63.2 了,我他喵现在这个项目还是 React Native 0.60.5 这不太符合我这个爱搞事的 Boy 的风格吧。说干就干,首先我们看一下升级的方法
之前请教过我大哥(带我手把手写代码的前端大佬,对我贼好,于是我就变成小迷弟了。哈哈哈,当然公司还有很多大哥哥小姐姐都对我很好,我是群弟),现在升级无非是两种方法,第一种就是 init 一个新的 RN 版本包,然后把代码搬过去,还有一种就是在原有基础上进行改动升级(其实就是把有差异的地方一个一个改)
首先开始之前先感谢大哥和刘公子的耐心指导和升级项目的指导(很多地方我直接抄了,哇哈哈哈哈哈哈哈…)
其实两种方法差不多,最后都是相同效果替换差异不同。但是我还是想尝试一下第二种方法,因为之前升级到 0.60.5 的时候已经用过第一种创建新的项目然后搬代码的方法了。
React Native 社区有提供一个开源的升级对比工具 React Native Upgrade Helper ,用这个工具就能很方便对比知道版本直接差异了。我们一个个修改差异就能完成升级,当然这个工具只是告诉你 React Native 升级的一些需要修改的地方,自己对接的第三方库还是需要自己适配和踩坑的。
我们先看看这 React Native Upgrade Helper 工具,GitHub 地址:https://github.com/react-native-community/upgrade-helper,当然我们可以直接利用它提供的在线升级帮助网页:https://react-native-community.github.io/upgrade-helper/
当我们选中对应的版本之后会出现需要更改的代码文件,首先看到的是各种相关的官方升级博客链接,这里看看这些提示的链接将有助于你对整个升级的理解,当然不看直接按照下面提示的有变化文件一个个改也是完全 Ojbk 的。最后是一句提示这个一定要注意,大概意思就是:下面全部代码中 RnDiffApp
和 rndiffapp
都属于占位符,请使用你的 APP 相关名称代替(这个 APP 相关名称就是字面意思,你自己所使用的名字,等于提示你抄答案不要把别人名字也抄上去了)你也可以点击上面的设置图标将这个占位符设置成你自己的实际 APP name
这里我就不一步步操作改了,要改的文件也不算很多,这个工具给的提示还是很全的。
继续我记录几个在踩坑中遇到的问题吧。
本来卡挺久的,刚开始是卡启动屏,因为 JS 这边没有跑起来所以无法调用启动屏的关闭方法,于是我在原生中将开启启动屏的方法关闭了,我们使用的是 react-native-splash-screen 包,在 MainActivity.java 的 onCreate 方法中注释掉 SplashScreen.show(this, R.style.SplashScreenTheme); 调用。因为刚升级可能有很多包有兼容问题导致 Js 编译或者运行出错,如果开启启动屏的话将会不能关闭导致一直卡启动。
上面这个问题的话是我们使用了 global ,在引入的时候必须先放到 import { AppRegistry } from 'react-native'; 之前
紧接着开始处理部分包兼容的问题了
[Thu Sep 2020 38.935] ERROR Invariant Violation: ART has been removed from React Native. It can now be installed and imported from '@react-native-community/art' instead of 'react-native'. See https://github.com/react-native-community/art
[Thu Sep 2020 38.935] LOG Running "myapp" with {"rootTag":1}
[Thu Sep 2020 38.936] ERROR Invariant Violation: "diudie" has not been registered. This can happen if:
* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called.
我刚开始一直纠结与 ERROR Invariant Violation: "myapp" has not been registered. 后来大哥说你先解决上面的 @react-native-community/art 兼容问题,我才恍然大悟。
@react-native-community/art 问题主要是说,现在 ART 已经不从 react-native 中导出了,这里先看看自己有没有使用 ART 组件,如果有的话请装 @react-native-community/art 组件然后替换成使用 @react-native-community/art 组件。
如果自己没有使用或者已经处理过之后确信自己没有引用了,那么我们开始检查第三方依赖,我这边出现的使用 react-native-progress 的话升级到 ^4.1.2 以上。
实在找不到的话我们祭出 grep 搜索大法,将 node_modules 文件夹扫一遍
grep "import.*ART.*react-native" ./node_modules/ -Rn -l
import.*ART.*react-native 就是一句正则,表示我要找全部的 ART 从 react-native 中导出的组件代码
找到组件之后如果是自己写的组件更新就好了,如果是别人写的开源组件去提交 issue 然后自己可以先 Fork 一份修复一下提交 Pull request 。
修复方法就是改成
import { Shape as ARTShape, Path as ARTPath } from 'react-native';
// 从上面的导入方法改为如下,可能有部分差异,但是大致如此
import { Shape as ARTShape, Path as ARTPath } from '@react-native-community/art';
我这边还遇到一个 ViewPagerAndroid 的问题,也是用 grep 来找到出问题的包,先看看这个第三方依赖有没有更新如果有的话更新没有的话先自己处理一下。然后联系维护者更新或者提交 Pull request。像这种名字比较长且有特色的话直接搜索就是了就可以不用正则,当然用正则更好咯。
grep "ViewPagerAndroid" ./node_modules/ -Rn -l
修复方法,记得在第三方的依赖包中装 @react-native-community/react-native-viewpager 哈,上面的其他 @react-native-community 组件也是一样的不要没装就开始使用了。
import { ViewPagerAndroid } from 'react-native';
// 从上面的导入方法改为如下,可能有部分差异,但是大致如此
import { ViewPagerAndroid } from '@react-native-community/react-native-viewpager';
开始整 IOS 包,首先看看 react-native-community/upgrade-support 提供的React Native 0.62 upgrade (Xcode) 升级操作指南,https://github.com/react-native-community/upgrade-support/issues/13
一般来说按照上面操作是没问题的,但是我就是不一般的操作了。这里记录几个我踩到的坑
第一个是 Create Bridging Header 创建的弹窗没有出现,一检查发现我这个项目之前不知道啥时候已经创建过 Bridging Header 文件了。
但是跑模拟器还是一直如下图报错
ld: warning: directory not found for option '-L“/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/iphonesimulator '
ld: warning: Could not find or use auto-linked library 'swiftCoreFoundation'
ld: warning: Could not find or use auto-linked library 'swiftCompatibility50'
ld: warning: Could not find or use auto-linked library 'swiftCore'
ld: warning: Could not find or use auto-linked library 'swiftQuartzCore'
ld: warning: Could not find or use auto-linked library 'swiftCoreGraphics'
ld: warning: Could not find or use auto-linked library 'swiftUIKit'
ld: warning: Could not find or use auto-linked library 'swiftDarwin'
ld: warning: Could not find or use auto-linked library 'swiftObjectiveC'
ld: warning: Could not find or use auto-linked library 'swiftDispatch'
ld: warning: Could not find or use auto-linked library 'swiftFoundation'
ld: warning: Could not find or use auto-linked library 'swiftMetal'
ld: warning: Could not find or use auto-linked library 'swiftCompatibilityDynamicReplacements'
ld: warning: Could not find or use auto-linked library 'swiftCoreImage'
ld: warning: Could not find or use auto-linked library 'swiftSwiftOnoneSupport'
Undefined symbols for architecture x86_64:
"protocol descriptor for Swift.ExpressibleByFloatLiteral", referenced from:
protocol conformance descriptor for __C.YGValue : Swift.ExpressibleByFloatLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"associated conformance descriptor for Swift.ExpressibleByFloatLiteral.Swift.ExpressibleByFloatLiteral.FloatLiteralType: Swift._ExpressibleByBuiltinFloatLiteral", referenced from:
protocol conformance descriptor for __C.YGValue : Swift.ExpressibleByFloatLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"associated type descriptor for Swift.ExpressibleByFloatLiteral.FloatLiteralType", referenced from:
protocol conformance descriptor for __C.YGValue : Swift.ExpressibleByFloatLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"associated conformance descriptor for Swift.ExpressibleByIntegerLiteral.Swift.ExpressibleByIntegerLiteral.IntegerLiteralType: Swift._ExpressibleByBuiltinIntegerLiteral", referenced from:
protocol conformance descriptor for __C.YGValue : Swift.ExpressibleByIntegerLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"method descriptor for Swift.ExpressibleByFloatLiteral.init(floatLiteral: A.FloatLiteralType) -> A", referenced from:
protocol conformance descriptor for __C.YGValue : Swift.ExpressibleByFloatLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"protocol descriptor for Swift.ExpressibleByIntegerLiteral", referenced from:
protocol conformance descriptor for __C.YGValue : Swift.ExpressibleByIntegerLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"associated type descriptor for Swift.ExpressibleByIntegerLiteral.IntegerLiteralType", referenced from:
protocol conformance descriptor for __C.YGValue : Swift.ExpressibleByIntegerLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"method descriptor for Swift.ExpressibleByIntegerLiteral.init(integerLiteral: A.IntegerLiteralType) -> A", referenced from:
protocol conformance descriptor for __C.YGValue : Swift.ExpressibleByIntegerLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"value witness table for Builtin.Int32", referenced from:
full type metadata for __C.YGUnit in libYogaKit.a(YGLayoutExtensions.o)
"protocol witness table for Swift.Float : Swift._ExpressibleByBuiltinFloatLiteral in Swift", referenced from:
associated type witness table accessor for Swift.ExpressibleByFloatLiteral.FloatLiteralType : Swift._ExpressibleByBuiltinFloatLiteral in __C.YGValue : Swift.ExpressibleByFloatLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"__swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements", referenced from:
__swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements_$_YogaKit in libYogaKit.a(YGLayoutExtensions.o)
(maybe you meant: __swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements_$_YogaKit)
"_swift_getForeignTypeMetadata", referenced from:
type metadata accessor for __C.YGValue in libYogaKit.a(YGLayoutExtensions.o)
type metadata accessor for __C.YGUnit in libYogaKit.a(YGLayoutExtensions.o)
"protocol witness table for Swift.Int : Swift._ExpressibleByBuiltinIntegerLiteral in Swift", referenced from:
associated type witness table accessor for Swift.ExpressibleByIntegerLiteral.IntegerLiteralType : Swift._ExpressibleByBuiltinIntegerLiteral in __C.YGValue : Swift.ExpressibleByIntegerLiteral in YogaKit in libYogaKit.a(YGLayoutExtensions.o)
"__swift_FORCE_LOAD_$_swiftCompatibility50", referenced from:
__swift_FORCE_LOAD_$_swiftCompatibility50_$_YogaKit in libYogaKit.a(YGLayoutExtensions.o)
(maybe you meant: __swift_FORCE_LOAD_$_swiftCompatibility50_$_YogaKit)
"Swift.Float.init(Swift.Double) -> Swift.Float", referenced from:
static (extension in YogaKit):CoreGraphics.CGFloat.% postfix(CoreGraphics.CGFloat) -> __C.YGValue in libYogaKit.a(YGLayoutExtensions.o)
(extension in YogaKit):__C.YGValue.init(CoreGraphics.CGFloat) -> __C.YGValue in libYogaKit.a(YGLayoutExtensions.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
找了好多 issue ,发现还是有部分小伙伴踩到这个坑的… 由于看的资料比较杂乱我这里就直接大概讲一下我检查和设置的地方,至少我这样设置是能够 Build 通过的。
首先就是创建一个 Swift File 文件了,这个我就直接引用 https://github.com/react-native-community/upgrade-support/issues/13 中的步骤了。
选择你的项目,点击 New File…
选择 Swift File 并且创建,文件名随便你
如果出现如下提示的话选择 Create Bridging Header ,如果没有出现这个提示?恭喜你和我一样需要手动创建(不要急等会我会说怎么手动创建的,我在这里也踩了很久的坑)
然后他们的教程中是说可以删掉这个 Swift 文件了,但是我建议先不要删(因为 GitHub 上有人说删掉会出现问题,我没有去验证删掉会不会出现问题但是我是没删的)
接着设置 LIBRARY_SEARCH_PATHS 这个没啥问题
添加如下 3 条路径,他的教程是带了双引号的,后来 Github issue 有人说不带引号会好点(我的没带引号)
$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)
$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
$(inherited)
添加 RUNPATH_SEARCH_PATHS
这里需要添加 2 条路径
$(inherited)
/usr/lib/swift
现在设置一下 Library Search Paths,一定要注意这里是 3 条路径不能写到一条中,我刚开始就是放在一起,搞的我后来找了好久才找到这个问题
$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)
$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
$(inherited)
最后我们回到上面创建 Bridging Header,如果当时没有弹出 Create Bridging Header 的提示的话,我们就需要手动创建了,首先还是选到自己的项目点击 new File…
这次我们选择创建 Header File
这里注意文件名设置成 diudie-Bridging-Header,(diudie 是我的项目名,请根据你的项目名替换)然后勾选你的项目
选中打开你项目的 Build Settings 搜索 Objective-C Bridging Header ,将你刚刚创建的 diudie-Bridging-Header 文件名设置上去
最后,Clean Build Folder
并重新 Bulid 然后 Run。
我最后还踩到一个坑
Showing All Errors Only
Undefined symbol: _swift_getFunctionReplacement
Undefined symbol: _swift_getOrigOfReplaceable
Undefined symbols for architecture x86_64:
"_swift_getFunctionReplacement", referenced from:
_swift_getFunctionReplacement50 in libswiftCompatibilityDynamicReplacements.a(DynamicReplaceable.cpp.o)
(maybe you meant: _swift_getFunctionReplacement50)
"_swift_getOrigOfReplaceable", referenced from:
_swift_getOrigOfReplaceable50 in libswiftCompatibilityDynamicReplacements.a(DynamicReplaceable.cpp.o)
(maybe you meant: _swift_getOrigOfReplaceable50)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
解决方案的话来自 GitHub issue:https://github.com/react-native-community/upgrade-support/issues/25
打开你项目的 Dead Code Stripping 就好了,
想必看到这里的小伙伴应该已经将 React Native 0.63.2 新包跑起来了,好了那我今天的踩坑就到这里了。