原生项目升级助手
编辑页面
查看你需要对原生项目进行的所有更改的逐文件差异,以将它们升级到下一个 Expo SDK 版本。
For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.
如果你管理自己的原生项目(以前称为 bare workflow),要升级到最新的 Expo SDK,你必须对原生项目进行一些更改。找出哪些原生文件发生了变化,以及需要在每个文件中更新什么内容,可能是一个复杂的过程。
以下指南提供了用于比较你项目当前 SDK 版本和你想升级到的目标 SDK 版本之间原生项目文件差异的 diff。你可以根据项目所使用的 expo 包版本,使用它们对项目进行修改。本页中的工具与 React Native Upgrade Helper 类似。不过,它们是围绕使用 Expo 模块及相关工具链的项目设计的。
想要完全避免升级原生代码?请查看 Continuous Native Generation (CNG),了解 Expo Prebuild 如何在构建前生成你的原生项目。
升级原生项目文件
一旦你升级了 Expo SDK 版本和相关依赖,请使用下面的 diff 工具了解你需要对原生项目做出的更改,并使其与当前 Expo SDK 版本保持一致。
选择你的起始 SDK 版本和目标 SDK 版本以查看生成的 diff。然后,通过复制粘贴或手动修改项目文件,将这些更改应用到你的原生项目中。
From SDK version:
To SDK version:
Native code changes from SDK 54 to 55
android/app/build.gradle
MODIFIED
| 11 | 11 | react { |
| 12 | 12 | entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim()) |
| 13 | 13 | reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() |
| 14 | hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc" | |
| 14 | hermesCommand = new File(["node", "--print", "require.resolve('hermes-compiler/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/hermesc/%OS-BIN%/hermesc" | |
| 15 | 15 | codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() |
| 16 | 16 | |
| 17 | 17 | enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean() |
android/app/src/main/AndroidManifest.xml
MODIFIED
| 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | |
| 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
| 2 | xmlns:tools="http://schemas.android.com/tools"> | |
| 2 | 3 | |
| 3 | 4 | <uses-permission android:name="android.permission.INTERNET"/> |
| 4 | 5 | <!-- OPTIONAL PERMISSIONS, REMOVE WHATEVER YOU DO NOT NEED --> |
| 5 | 6 | <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> |
| 6 | 7 | <uses-permission android:name="android.permission.VIBRATE"/> |
| 7 | 8 | <!-- These require runtime permissions on M --> |
| 8 | <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> | |
| 9 | <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> | |
| 9 | <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/> | |
| 10 | <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/> | |
| 10 | 11 | <!-- END OPTIONAL PERMISSIONS --> |
| 11 | 12 | |
| 12 | 13 | <queries> |
| 19 | 20 | </queries> |
| 20 | 21 | |
| 21 | 22 | <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme" android:supportsRtl="true"> |
| 22 | <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true"> | |
| 23 | <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode|smallestScreenSize" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true"> | |
| 23 | 24 | <intent-filter> |
| 24 | 25 | <action android:name="android.intent.action.MAIN"/> |
| 25 | 26 | <category android:name="android.intent.category.LAUNCHER"/> |
android/app/src/main/java/com/helloworld/MainApplication.kt
MODIFIED
| 6 | 6 | import com.facebook.react.PackageList |
| 7 | 7 | import com.facebook.react.ReactApplication |
| 8 | 8 | import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative |
| 9 | import com.facebook.react.ReactNativeHost | |
| 10 | 9 | import com.facebook.react.ReactPackage |
| 11 | 10 | import com.facebook.react.ReactHost |
| 12 | 11 | import com.facebook.react.common.ReleaseLevel |
| 13 | 12 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint |
| 14 | import com.facebook.react.defaults.DefaultReactNativeHost | |
| 15 | 13 | |
| 16 | 14 | import expo.modules.ApplicationLifecycleDispatcher |
| 17 | import expo.modules.ReactNativeHostWrapper | |
| 15 | import expo.modules.ExpoReactHostFactory | |
| 18 | 16 | |
| 19 | 17 | class MainApplication : Application(), ReactApplication { |
| 20 | 18 | |
| 21 | override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper( | |
| 22 | this, | |
| 23 | object : DefaultReactNativeHost(this) { | |
| 24 | override fun getPackages(): List<ReactPackage> = | |
| 25 | PackageList(this).packages.apply { | |
| 26 | // Packages that cannot be autolinked yet can be added manually here, for example: | |
| 27 | // add(MyReactNativePackage()) | |
| 28 | } | |
| 29 | ||
| 30 | override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry" | |
| 31 | ||
| 32 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG | |
| 33 | ||
| 34 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED | |
| 35 | } | |
| 36 | ) | |
| 37 | ||
| 38 | override val reactHost: ReactHost | |
| 39 | get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost) | |
| 19 | override val reactHost: ReactHost by lazy { | |
| 20 | ExpoReactHostFactory.getDefaultReactHost( | |
| 21 | context = applicationContext, | |
| 22 | packageList = | |
| 23 | PackageList(this).packages.apply { | |
| 24 | // Packages that cannot be autolinked yet can be added manually here, for example: | |
| 25 | // add(MyReactNativePackage()) | |
| 26 | } | |
| 27 | ) | |
| 28 | } | |
| 40 | 29 | |
| 41 | 30 | override fun onCreate() { |
| 42 | 31 | super.onCreate() |
android/gradle/wrapper/gradle-wrapper.properties
MODIFIED
| 1 | 1 | distributionBase=GRADLE_USER_HOME |
| 2 | 2 | distributionPath=wrapper/dists |
| 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip | |
| 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip | |
| 4 | 4 | networkTimeout=10000 |
| 5 | 5 | validateDistributionUrl=true |
| 6 | 6 | zipStoreBase=GRADLE_USER_HOME |
android/gradlew
MODIFIED
| 1 | 1 | #!/bin/sh |
| 2 | 2 | |
| 3 | 3 | # |
| 4 | # Copyright © 2015-2021 the original authors. | |
| 4 | # Copyright © 2015 the original authors. | |
| 5 | 5 | # |
| 6 | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | 7 | # you may not use this file except in compliance with the License. |
ios/HelloWorld/AppDelegate.swift
MODIFIED
| 1 | import Expo | |
| 1 | internal import Expo | |
| 2 | 2 | import React |
| 3 | 3 | import ReactAppDependencyProvider |
| 4 | 4 | |
| 5 | @UIApplicationMain | |
| 6 | public class AppDelegate: ExpoAppDelegate { | |
| 5 | @main | |
| 6 | class AppDelegate: ExpoAppDelegate { | |
| 7 | 7 | var window: UIWindow? |
| 8 | 8 | |
| 9 | 9 | var reactNativeDelegate: ExpoReactNativeFactoryDelegate? |
| 19 | 19 | |
| 20 | 20 | reactNativeDelegate = delegate |
| 21 | 21 | reactNativeFactory = factory |
| 22 | bindReactNativeFactory(factory) | |
| 23 | 22 | |
| 24 | 23 | #if os(iOS) || os(tvOS) |
| 25 | 24 | window = UIWindow(frame: UIScreen.main.bounds) |
ios/Podfile
MODIFIED
| 7 | 7 | def ccache_enabled?(podfile_properties) |
| 8 | 8 | # Environment variable takes precedence |
| 9 | 9 | return ENV['USE_CCACHE'] == '1' if ENV['USE_CCACHE'] |
| 10 | ||
| 10 | ||
| 11 | 11 | # Fall back to Podfile properties |
| 12 | 12 | podfile_properties['apple.ccacheEnabled'] == 'true' |
| 13 | 13 | end |
| 14 | 14 | |
| 15 | ENV['RCT_NEW_ARCH_ENABLED'] ||= '0' if podfile_properties['newArchEnabled'] == 'false' | |
| 16 | 15 | ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] |
| 17 | ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false' | |
| 18 | ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false' | |
| 16 | ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' | |
| 17 | ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' | |
| 18 | ENV['RCT_HERMES_V1_ENABLED'] ||= '1' if podfile_properties['expo.useHermesV1'] == 'true' | |
| 19 | 19 | platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' |
| 20 | 20 | |
| 21 | 21 | prepare_react_native_project! |
package.json
MODIFIED
| 2 | 2 | "name": "expo-template-bare-minimum", |
| 3 | 3 | "description": "This bare project template includes a minimal setup for using unimodules with React Native.", |
| 4 | 4 | "license": "0BSD", |
| 5 | "version": "54.0.50", | |
| 5 | "version": "55.0.26", | |
| 6 | 6 | "main": "index.js", |
| 7 | 7 | "scripts": { |
| 8 | 8 | "start": "expo start --dev-client", |
| 11 | 11 | "web": "expo start --web" |
| 12 | 12 | }, |
| 13 | 13 | "dependencies": { |
| 14 | "expo": "~54.0.33", | |
| 15 | "expo-status-bar": "~3.0.9", | |
| 16 | "react": "19.1.0", | |
| 17 | "react-native": "0.81.5" | |
| 14 | "expo": "~55.0.14", | |
| 15 | "expo-status-bar": "~55.0.5", | |
| 16 | "react": "19.2.0", | |
| 17 | "react-native": "0.83.4" | |
| 18 | 18 | } |
| 19 | 19 | } |
| 20 | 20 |