React Native热更新 #
概述 #
热更新允许在不重新发布应用的情况下更新 JavaScript 代码和资源。CodePush 是微软提供的开源热更新服务。
安装配置 #
安装 CLI #
bash
npm install -g code-push-cli
注册账号 #
bash
code-push register
创建应用 #
bash
code-push app add MyApp ios react-native
code-push app add MyApp android react-native
安装 SDK #
bash
npm install react-native-code-push
cd ios && pod install
iOS 配置 #
在 ios/MyApp/Info.plist 中添加:
xml
<key>CodePushDeploymentKey</key>
<string>YOUR_DEPLOYMENT_KEY</string>
Android 配置 #
在 android/app/src/main/java/com/myapp/MainApplication.java 中:
java
import com.microsoft.codepush.react.CodePush;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new CodePush("YOUR_DEPLOYMENT_KEY", MainApplication.this, BuildConfig.DEBUG)
);
}
};
}
基本使用 #
包装组件 #
tsx
import codePush from 'react-native-code-push';
const App = () => {
return (
<NavigationContainer>
{}
</NavigationContainer>
);
};
const codePushOptions = {
checkFrequency: codePush.CheckFrequency.ON_APP_START,
installMode: codePush.InstallMode.IMMEDIATE,
};
export default codePush(codePushOptions)(App);
配置选项 #
tsx
interface CodePushOptions {
checkFrequency: codePush.CheckFrequency;
installMode: codePush.InstallMode;
mandatoryInstallMode: codePush.InstallMode;
minimumBackgroundDuration: number;
updateDialog: UpdateDialog | false;
rollbackRetryOptions: RollbackRetryOptions;
}
const options: CodePushOptions = {
checkFrequency: codePush.CheckFrequency.ON_APP_START,
installMode: codePush.InstallMode.ON_NEXT_RESTART,
mandatoryInstallMode: codePush.InstallMode.IMMEDIATE,
minimumBackgroundDuration: 0,
updateDialog: {
title: '更新可用',
mandatoryUpdateMessage: '必须安装更新',
optionalUpdateMessage: '有新版本可用,是否安装?',
optionalIgnoreButtonLabel: '稍后',
optionalInstallButtonLabel: '安装',
mandatoryContinueButtonLabel: '继续',
},
};
export default codePush(options)(App);
检查频率 #
| 选项 | 说明 |
|---|---|
| ON_APP_START | 应用启动时检查 |
| ON_APP_RESUME | 应用恢复时检查 |
| MANUAL | 手动检查 |
安装模式 #
| 选项 | 说明 |
|---|---|
| IMMEDIATE | 立即安装 |
| ON_NEXT_RESTART | 下次重启安装 |
| ON_NEXT_RESUME | 下次恢复时安装 |
手动控制 #
手动检查更新 #
tsx
import codePush from 'react-native-code-push';
const checkForUpdate = async () => {
const update = await codePush.checkForUpdate();
if (update) {
console.log('有新版本可用');
console.log('版本:', update.appVersion);
console.log('大小:', update.packageSize);
console.log('是否强制:', update.isMandatory);
} else {
console.log('已是最新版本');
}
};
下载并安装更新 #
tsx
const downloadAndUpdate = async () => {
const update = await codePush.checkForUpdate();
if (!update) {
return;
}
update.download(progress => {
console.log(`下载进度: ${progress.receivedBytes} / ${progress.totalBytes}`);
}).then(newPackage => {
newPackage.install(codePush.InstallMode.IMMEDIATE).then(() => {
console.log('安装完成');
});
});
};
同步更新 #
tsx
const syncUpdate = async () => {
const result = await codePush.sync(
{
installMode: codePush.InstallMode.IMMEDIATE,
updateDialog: true,
},
(status) => {
switch (status) {
case codePush.SyncStatus.CHECKING_FOR_UPDATE:
console.log('检查更新中...');
break;
case codePush.SyncStatus.AWAITING_USER_ACTION:
console.log('等待用户确认...');
break;
case codePush.SyncStatus.DOWNLOADING_PACKAGE:
console.log('下载中...');
break;
case codePush.SyncStatus.INSTALLING_UPDATE:
console.log('安装中...');
break;
case codePush.SyncStatus.UP_TO_DATE:
console.log('已是最新版本');
break;
case codePush.SyncStatus.UPDATE_INSTALLED:
console.log('更新已安装');
break;
case codePush.SyncStatus.UNKNOWN_ERROR:
console.log('未知错误');
break;
}
},
(progress) => {
console.log(`下载进度: ${progress.receivedBytes} / ${progress.totalBytes}`);
},
);
return result;
};
部署环境 #
查看部署 #
bash
code-push deployment ls MyApp
创建部署 #
bash
code-push deployment add MyApp Staging
code-push deployment add MyApp Production
获取部署密钥 #
bash
code-push deployment ls MyApp -k
切换环境 #
tsx
import codePush from 'react-native-code-push';
const getDeploymentKey = () => {
if (__DEV__) {
return 'STAGING_KEY';
}
return 'PRODUCTION_KEY';
};
export default codePush({
deploymentKey: getDeploymentKey(),
})(App);
发布更新 #
发布到 Staging #
bash
code-push release-react MyApp ios
code-push release-react MyApp android
发布到 Production #
bash
code-push release-react MyApp ios -d Production
code-push release-react MyApp android -d Production
发布选项 #
bash
code-push release-react MyApp ios \
--deploymentName Production \
--description "修复登录问题" \
--mandatory \
--targetBinaryVersion "1.0.0"
| 选项 | 说明 |
|---|---|
| –deploymentName | 部署环境 |
| –description | 更新描述 |
| –mandatory | 强制更新 |
| –targetBinaryVersion | 目标版本 |
| –rollout | 灰度比例 |
灰度发布 #
bash
code-push release-react MyApp ios --rollout 20
逐步扩大:
bash
code-push patch MyApp ios --rollout 50
code-push patch MyApp ios --rollout 100
回滚更新 #
回滚到上一版本 #
bash
code-push rollback MyApp ios
code-push rollback MyApp android
回滚到指定版本 #
bash
code-push rollback MyApp ios --targetRelease v2
查看历史 #
查看发布历史 #
bash
code-push deployment history MyApp Production
查看更新指标 #
bash
code-push deployment history MyApp Production --displayAuthor
最佳实践 #
版本兼容 #
使用 targetBinaryVersion 确保更新兼容:
bash
code-push release-react MyApp ios --targetBinaryVersion "1.0.0 - 1.0.9"
强制更新 #
对于关键修复,使用强制更新:
bash
code-push release-react MyApp ios --mandatory
灰度发布 #
先小范围测试再全量发布:
bash
code-push release-react MyApp ios --rollout 10
code-push patch MyApp ios --rollout 50
code-push patch MyApp ios --rollout 100
监控更新 #
tsx
const syncWithAnalytics = async () => {
const result = await codePush.sync(
{updateDialog: true},
(status) => {
analytics.track('codepush_status', {status});
},
(progress) => {
analytics.track('codepush_progress', {
received: progress.receivedBytes,
total: progress.totalBytes,
});
},
);
analytics.track('codepush_result', {result});
};
完整示例 #
tsx
import React, {useState, useEffect} from 'react';
import {
View,
Text,
Button,
StyleSheet,
Alert,
Modal,
ProgressBarAndroid,
} from 'react-native';
import codePush from 'react-native-code-push';
const App = () => {
const [updateModal, setUpdateModal] = useState(false);
const [downloadProgress, setDownloadProgress] = useState(0);
const [syncStatus, setSyncStatus] = useState('');
useEffect(() => {
checkForUpdate();
}, []);
const checkForUpdate = async () => {
const update = await codePush.checkForUpdate();
if (update) {
Alert.alert(
'更新可用',
`发现新版本 (${update.appVersion}),是否更新?`,
[
{text: '稍后', style: 'cancel'},
{text: '更新', onPress: () => downloadUpdate(update)},
],
);
}
};
const downloadUpdate = async (update) => {
setUpdateModal(true);
setSyncStatus('下载中...');
try {
await update.download(progress => {
const percent = (progress.receivedBytes / progress.totalBytes) * 100;
setDownloadProgress(percent);
});
setSyncStatus('安装中...');
await update.install(codePush.InstallMode.IMMEDIATE);
setSyncStatus('安装完成');
setTimeout(() => {
codePush.notifyAppReady();
codePush.restartApp();
}, 1000);
} catch (error) {
setSyncStatus('更新失败');
Alert.alert('错误', error.message);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>CodePush 热更新示例</Text>
<Button title="检查更新" onPress={checkForUpdate} />
<Modal visible={updateModal} transparent animationType="fade">
<View style={styles.modalContainer}>
<View style={styles.modalContent}>
<Text style={styles.statusText}>{syncStatus}</Text>
<ProgressBarAndroid
styleAttr="Horizontal"
indeterminate={false}
progress={downloadProgress / 100}
style={styles.progress}
/>
<Text style={styles.progressText}>
{Math.round(downloadProgress)}%
</Text>
</View>
</View>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 32,
},
modalContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalContent: {
backgroundColor: '#fff',
padding: 24,
borderRadius: 12,
width: '80%',
alignItems: 'center',
},
statusText: {
fontSize: 18,
marginBottom: 16,
},
progress: {
width: '100%',
height: 10,
},
progressText: {
marginTop: 8,
fontSize: 14,
color: '#666',
},
});
const codePushOptions = {
checkFrequency: codePush.CheckFrequency.MANUAL,
};
export default codePush(codePushOptions)(App);
总结 #
CodePush 热更新要点:
- 安装配置:正确配置 iOS 和 Android
- 检查更新:自动或手动检查
- 下载安装:支持进度显示
- 部署环境:Staging 和 Production
- 发布更新:使用 CLI 发布
- 灰度发布:逐步推广更新
- 回滚更新:快速回滚问题版本
热更新是 React Native 的重要特性,可以快速修复问题和发布新功能。
最后更新:2026-03-28