[ReactNative翻译]升级到React Native 0.59

427 阅读12分钟

本文由 简悦SimpRead 转码,原文地址 mattoakes.net

React Native 0.59引入了许多令人兴奋的功能,其中最重要的是Hooks。这篇文章解释了什么是......

升级到React Native 0.59

React Native 0.59是一个重大的更新,带来了许多有用的变化。正常的升级过程是适用的,但是,要想顺利升级,了解哪些变化以及为什么要做这些变化是非常有用的。

正在为升级到最新版本的React Native而烦恼吗?

什么是新的?

让我们来看看这个版本的新内容。这是React Native blog postfull 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文件中进行版本修改。你也可以删除wrapperGradle任务。这不再需要了,因为它重复了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/debugsrc/mainAndroidManifest.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而烦恼吗?


www.deepl.com 翻译