本文由 简悦SimpRead 转码,原文地址 mattoakes.net
React Native 0.59引入了许多令人兴奋的功能,其中最重要的是Hooks。这篇文章解释了什么是......
升级到React Native 0.59
React Native 0.59是一个重大的更新,带来了许多有用的变化。正常的升级过程是适用的,但是,要想顺利升级,了解哪些变化以及为什么要做这些变化是非常有用的。
正在为升级到最新版本的React Native而烦恼吗?
什么是新的?
让我们来看看这个版本的新内容。这是React Native blog post和full changelog的一个总结。请看一下这些内容的完整细节。
钩子
对开发者来说,最明显的变化是增加了Hooks。钩子让你在组件之间重新使用状态逻辑,而不需要借助于类。它们是一个选择加入的功能,将与你当前的代码并排工作,所以你可以逐步采用它。
你可以在React网站上阅读更多关于钩子的信息。
Android JavascriptCore升级和64位支持
另一个大的变化是升级到Android的JavascriptCore。在Android上,用于运行JS代码的JavascriptCore库被捆绑在你的应用中。在iOS上,它使用一个系统提供的框架。以前的React Native版本在Android上捆绑了一个非常老的库。
升级后的库包括性能的提高和一些以前的多任务功能。它还在安卓上实现了64位支持,从2019年8月1日起,这将成为谷歌应用商店的一个应用要求。在此之前,你需要至少升级到React Native 0.59才能在Android上对你的应用进行修改。
Inline Requires
Facebook正在将他们的一些性能优化带到社区。这从Inline Requires开始。它通过允许Metro捆绑器识别可以轻松加载的模块,来优化你的应用程序的启动时间。
这是一个选择加入的功能,他们正在寻找反馈。你可以在React Native网站上阅读更多关于它的信息。
精益核心
这个版本标志着精益核心工作的开始。React Native资源库非常大且复杂。这使得代码库的非核心部分很难被新的贡献者所接近。这就是为什么非核心组件进入新的社区运行库。这个过程从WebView开始,效果很好。
0.59版本提取了更多的组件和API,并将它们标记为废弃的。现在,你可以像以前一样继续使用它们,但是,我们鼓励你迁移到新的库中。它们都是向后兼容的,通常只需要对导入语句进行修改。
CLI的升级
CLI工具也从主资源库中提取出来了。这导致了许多变化,包括一个改善性能的3倍。还有一个新的upgrade
命令,这将使未来的升级更加容易。
在你升级之前
像往常一样,在执行升级之前,最好先进行一些检查。首先,检查React Native问题跟踪器,看看是否有任何可能影响你的重大问题。你还应该检查你使用的任何库,看看是否有任何兼容性问题。
假设似乎没有任何障碍,你就可以开始升级了。
执行升级
像往常一样,你可以按照 rn-diff-purge
工具中的差异来执行升级。你可以按照这个差异,自己进行修改,但是,我想向你解释这些修改的每一个含义。这将使你更好地理解为什么你需要做这些修改。我将告诉你的是0.58和0.59.5之间的差异。
升级依赖的版本
第一步是升级你的package.json
中的依赖项并安装它们。记住,每个React Native版本都与特定的React版本相关。请确保你也升级它。如果你使用它,你也应该确保react-test-renderer
与React版本匹配,如果你使用它,与React版本匹配。你还应该升级metro-react-native-babel-preset
和Babel版本。
"dependencies": {
"react": "16.8.3",
"react-native": "0.59.0"
},
"devDependencies": {
"@babel/core": "^7.4.3",
"@babel/runtime": "^7.4.3",
"babel-jest": "^24.7.1",
"jest": "^24.7.1",
"metro-react-native-babel-preset": "^0.53.1",
"react-test-renderer": "16.8.3"
},
流程升级
接下来是一个简单的问题。React Native更新了Flow的版本,它在0.59。这意味着你需要确保你的flow-bin
依赖关系被设置为^0.92.0
。在你的.flowconfig
文件的[版本]
部分也应该有相同的版本值。
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
- node_modules/react-native/flow-github/
[version]
- ^0.86.0
+ ^0.92.0
如果你在你的项目中使用Flow进行类型检查,这可能会导致你自己的代码中出现新的错误。我建议你看一下0.86和0.92之间的版本的更新日志,看看是什么原因导致了这些错误。
如果你使用Typescript进行类型检查,那么你可以删除.flowconfig
文件和flow-bin
依赖关系。
如果你根本没有使用类型检查器,那么我强烈建议你考虑使用一个。两种选择都可以,但是,我个人更倾向于使用Typescript。
删除不需要的Lint Ignores
另一个非常简单的问题,大多数人可能会忽略。默认的文件头包含一些用于Facebook内部工具的忽略提示。这些对其他人来说是不需要的,可以删除。
/*
* @format
* @flow
- * @lint-ignore-every XPLATJSCOPYRIGHT1
*/
Android Gradle更改
现在是更多一点的东西。React Native 0.59对Gradle构建工具有各种改变。
构建工具版本
在diff中你会看到的第一个变化是删除了buildToolsVersion
语句。这已经不再需要了,你可以删除这一行。
这是因为从3.0.0开始的Android Gradle插件将默认使用一个最小版本。它将忽略任何在buildToolsVersion
行中指定的较低版本。大多数应用程序将不需要,但是,你可以用这个来指定一个较新的版本。
android {
compileSdkVersion rootProject.ext.compileSdkVersion
- buildToolsVersion rootProject.ext.buildToolsVersion
Java 8声明
接下来是增加了几行,说明该应用通过React Native库使用了Java 8功能。该应用在没有这些东西的情况下构建得很好,并且已经使用Java 8功能有一段时间了。这几行修复了一些Android lint警告,所以你应该包括它们。
在你的android/app/build.gradle
文件的android
部分的某个地方包含这些行。
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
64位ABI拆分
由于React Native 0.59包括对Android的64位支持,我们需要更新处理ABI分割标志的代码。这个标志告诉构建系统为每个架构创建一个单独的APK。这些文件只包括该架构所需的库二进制文件。打开这个标志意味着发送给用户的APK将明显变小。
第一个变化是告诉构建系统,我们想要一个适用于x84-64架构的APK。
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
- include "armeabi-v7a", "x86", "arm64-v8a"
+ include "armeabi-v7a", "x86", "arm64-v8a", "x86-64"
}
}
第二行改变了为每个APK设置版本代码的代码。我们需要这样做,因为ABI中的每个APK都需要一个单独的版本代码,每次应用更新都会增加。这个代码块为每个架构添加了一个不同的常量到你的基本版本代码。然后,每个APK都会得到他们自己独特的值,这些值不太可能重叠。关于更多的细节,请看ABI分割的Android文档。
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
- def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 3]
+ def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 3, "x86-64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
如果你不使用该功能,我建议你做这些修改。然后你就可以在将来启用它而不会有任何问题。
升级后的Gradle版本
这个版本的React Native使用较新版本的Gradle和Android Gradle插件。你需要在android/build.gralde
文件和android/gradle/wrapper/gradle-wrapper.properties
文件中进行版本修改。你也可以删除wrapper
Gradle任务。这不再需要了,因为它重复了Gradle的版本设置。
buildscript {
ext {
- buildToolsVersion = "28.0.2"
+ buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 27
supportLibVersion = "28.0.0"
}
repositories {
google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.2.1'
+ classpath 'com.android.tools.build:gradle:3.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
- task wrapper(type: Wrapper) {
- gradleVersion = '4.7'
- distributionUrl = distributionUrl.replace("bin", "all")
- }
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
- distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
+ distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
Android目标SDK升级
我们还需要将目标SDK从27升级到28。
buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
- targetSdkVersion = 27
+ targetSdkVersion = 28
supportLibVersion = "28.0.0"
}
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
对于Android,有三个SDK版本,我们需要指定。
compileSdkVersion
。这指定了将用于编译我们的应用程序的Android SDK。如果我们想使用Android SDK 28中添加的API,那么至少需要将其设置为28。对于这个版本,请将其设置为28。minSdkVersion
: 这指定了我们的应用程序所支持的最低Android版本。如果你使用更高层次的API,那么你需要提供回落。React Native提供了这些回退,以支持低至API级别16(用户称为4.1)的Android设备。除非你使用一个需要更高的安卓版本的库,否则请将此设置为16。targetSdkVersion
: 这是对我们所针对的SDK的设置。新发布的Android SDKs将包括新的或改变的行为。它们也会包括不针对它们的应用程序的_兼容性模式。这可以避免在新的安卓系统升级中破坏旧的应用程序。随着从27到28的变化,安卓将施加一些新的安全规则。我们将在稍后的时间里更详细地访问这个问题。
你应该阅读有关Android SDK 28的行为变化,以确保你的应用程序的其他部分不受影响。
只调试AndroidManifest.xml
React Native中默认的Android Manifest曾经包括一个SYSTEM_ALERT_WINDOW
权限。这实际上只在 "调试 "时需要。这个改变把权限移到了AndroidManifest.xml
文件中,只用于调试
构建。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" android:networkSecurityConfig="@xml/react_native_config" />
</manifest>
<uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
这是用Gradle的_source set_功能工作的。它从android/app/src
内的多个文件夹中获取文件,并在构建时将它们合并。在这个例子中,对于debug
构建,src/debug
和src/main
的AndroidManifest.xml
文件被合并。对于 "release "的构建,它将只有 "src/main "的文件。
Android调试网络配置
新的调试AndroidManifest.xml
包括一行引用新的react_native_config.xml
文件。这是由于我们开始针对Android SDK 28时行为的改变而需要的。在Android 9+(API 28+)设备上所有的网络连接都需要默认加密。这将导致访问不使用HTTPS加密的Metro打包器的问题。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">localhost</domain>
<domain includeSubdomains="false">10.0.2.2</domain>
<domain includeSubdomains="false">10.0.3.2</domain>
</domain-config>
</network-security-config>
该配置文件关闭了Metro所需的域的安全规则。你可能需要为这个列表添加你自己的域。例如,如果你要连接到一个没有HTTPS的开发API。
你可以在Android网站上阅读更多关于网络安全配置。
AppDelegate
实现了RCTBridgeDelegate
。
现在我们把注意力转向iOS,`AppDelegate'有了些许变化。这是另一个变化,应用程序将在不改变它的情况下建立和运行。这个改变是为了修复获取捆绑URL的时间问题。
你需要修改AppDelegate.h
文件,导入并声明该类实现了RCTBridgeDelegate
协议。
+ #import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>
- @interface AppDelegate : UIResponder <UIApplicationDelegate>
+ @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
@property (nonatomic, strong) UIWindow *window;
@end
然后在AppDelegate.m
文件中,你需要把获取捆绑URL的逻辑移到一个叫做sourceURLForBridge
的方法中。这就是我们实现RCTBridgeDelegate
协议所需的全部内容。
#import "AppDelegate.h"
+ #import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
然后你需要改变didFinishLaunchingWithOptions
的实现,创建并使用RCTBridge
对象。
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
- NSURL *jsCodeLocation;
- jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
-
- RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
- moduleName:@"RnDiffApp"
- initialProperties:nil
- launchOptions:launchOptions];
- rootView.backgroundColor = [UIColor blackColor];
+ RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
+ RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
+ moduleName:@"RnDiffApp"
+ initialProperties:nil];
+ rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
如果你使用的是react-native-navigation
或Code Push,其实现会有所不同。如果你使用的是这两个库中的任何一个,你应该参考如何正确实现它,或者让AppDelegate
保持原样。
添加默认的Metro配置
模板现在包括一个默认的metro.config.js
文件。这个文件是用来配置Metro的,Metro是React Native工具,它捆绑了你的应用程序的Javascript代码。
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false
}
})
}
};
在这个默认值中,有两个布尔值设置,用于新的内联要求的性能优化。默认情况下,它们都是关闭的,所以如果你想使用它们,请把它们变成 "true"。
如果你不打算使用这些功能,也不配置任何其他Metro设置,你就不需要这个文件。它被包含在模板中,以使这些功能的配置更加清晰。
精益核心
如上所述,React Native的一些非核心部分已经从主模块中提取出来,并转移到社区运行仓库。现在,它们在React Native 0.59中仍然可用,但是,你会得到一个关于它们被废弃的警告。你应该为你使用的每一个模块进入新的仓库并遵循迁移说明。
在大多数情况下,API是100%兼容的,你可以直接安装、链接,然后改变你的导入,开始使用新模块。
测试和收尾工作
这些是你需要为所有项目做出的所有改变。还有一些可能会影响一些应用程序的突破性变化。你应该通读更新日志,看看是否有任何东西适用于你的项目。你也应该检查一下你使用的库,看看是否需要升级。
正在为升级到最新版本的React Native而烦恼吗?