前言
1、对于0 基础的同学建议
看下 19 年写的这篇会更详细点,本文不会特别细:code-push-server的私有化部署
2、首先要理解
app 库:提供打包命令,项目下载,项目更新
客户端 cli:文件上传 ,本身不打包,调用 app 的脚本打包。
服务器:文件派发,起到文件托管的作用。
备注:客户端cli 与服务端是配套的,他俩负责文件的派发,所以不管你 rn 项目版本怎么迭代,或者说用 weex、uniapp、h5 、windows项目,只要服务器能跑,只要你别动它,它都兼容。
3、我的配置:
app 库:“react-native-code-push”: “9.0.1”
cli:code-push-cli@2.1.9
4、成果预
不重启、实时更新代码与资源演示
一、app 库
1、新建项目
下文所有的是基于你是新建的 rn 项目,如果是旧项目自行适配
# 已失效
react-native init demo --verbose --version 0.75.3
# 用这个
npx react-native init AwesomeProject --version 0.75.3
2、导库
所有的 code-push 客户端库都是 fork 微软的,这里直接上微软最新的
"react-native-code-push": "9.0.1"
app 库使用revopush 的也可以 (试过了没毛病):@revopush/react-native-code-push
"@revopush/react-native-code-push": "1.0.0"
3、android 配置
此处与旧版一致,没有什么改动。
当然你使用三方的 (如你自己 fork 的或者@revopush/react-native-code-push)记得对应导包目录改下即可。
- build.gradle改动
不管使用哪个,最新版 Gradle 未及时适配,修改如下:
目录:node_module/react-native-code-push/android/build.gradle
apply plugin: "com.android.library"
def DEFAULT_COMPILE_SDK_VERSION = 26
def DEFAULT_BUILD_TOOLS_VERSION = "26.0.3"
def DEFAULT_TARGET_SDK_VERSION = 26
def DEFAULT_MIN_SDK_VERSION = 16
android {
namespace "com.microsoft.codepush.react"
compileSdkVersion rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEFAULT_COMPILE_SDK_VERSION
buildToolsVersion rootProject.hasProperty('buildToolsVersion') ? rootProject.buildToolsVersion : DEFAULT_BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion rootProject.hasProperty('minSdkVersion') ? rootProject.minSdkVersion : DEFAULT_MIN_SDK_VERSION
targetSdkVersion rootProject.hasProperty('targetSdkVersion') ? rootProject.targetSdkVersion : DEFAULT_TARGET_SDK_VERSION
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
}
defaultConfig {
consumerProguardFiles 'proguard-rules.pro'
}
}
dependencies {
implementation "com.facebook.react:react-native:+"
implementation 'com.nimbusds:nimbus-jose-jwt:9.37.3'
}
- MainApplication修改 JS 入口文件
import com.microsoft.codepush.react.CodePush
override fun getJSBundleFile(): String? {
return CodePush.getJSBundleFile()
}
- value/strings.xml 新增
这 2 个 key 是 react-naive-code-push/android 会自动从代码中读取,也可以通过 js CodePush.sync 传入
<string moduleConfig="true" name="CodePushDeploymentKey">wS0x2UeVrgtXk40D9J754knkNr254ksvOXqog</string>
<string moduleConfig="true" name="CodePushServerUrl">http://yourServerUrl:3000</string>
- app/build.gradle
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle";
dependencies {
implementation project(':react-native-code-push')
}
- Setting.gradle
include ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
客户端的代码向来改动很频繁,后续维护如果对不上建议熟读源码(非常重要)或者看官方的各端集成方式
4、ios 配置
- AppDelegate.m 改动
sourceURLForBridge 方法下return [self bundleURL];改成条件判断
#if DEBUG
return [self bundleURL];
#else
return [CodePush bundleURL];
#endif
- CodePush.podspec改动
目录:node_module/react-native-code-push/CodePush.podspec
修改如下,注意此版本较低可以自行优化下配置:
require 'json'
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
Pod::Spec.new do |s|
s.name = 'CodePush'
s.version = package['version'].gsub(/v|-beta/, '')
s.summary = package['description']
s.author = package['author']
s.license = package['license']
s.homepage = package['homepage']
s.source = { :git => 'https://github.com/Microsoft/react-native-code-push.git', :tag => "v#{s.version}"}
s.ios.deployment_target = '7.0'
s.tvos.deployment_target = '9.0'
s.preserve_paths = '*.js'
s.library = 'z'
s.source_files = 'ios/CodePush/*.{h,m}'
s.public_header_files = ['ios/CodePush/CodePush.h']
# Note: Even though there are copy/pasted versions of some of these dependencies in the repo,
# we explicitly let CocoaPods pull in the versions below so all dependencies are resolved and
# linked properly at a parent workspace level.
s.dependency 'React'
s.dependency 'SSZipArchive', '~> 2.1'
s.dependency 'JWT', '~> 3.0.0-beta.7'
s.dependency 'Base64', '~> 1.1'
end
- Info.plist
添加服务器地址配置
<key>CodePushDeploymentKey</key>
<string>Ej9L9PeVzEY29cu1GLzKXN5WjQt54ksvOXqog</string>
<key>CodePushServerURL</key>
<string>http://yourServelUrl:3000</string>
如果你的热更新是http,还需要将NSAllowsArbitraryLoads配置改为true,并且设置你的域名为例外
备注:此域名不用带端口号与http://
<dict>
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>你的域名</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
5、RN 升级网址
此处与 code-push 无关,仅适用于你有 RN 版本改动需求:RN 升级对比
二、热更新服务器
1、为什么选 lisong 而非microsoft
通过下图可以看到lisong 的 start 还是远远高于 microsoft,这是因为 microsoft的并不支持开发者完全自由部署(依赖于微软的appcenter,当然现在不知道),lisong 是fork microsoft 的完善成开发者自由部署
使用微软code push和私有化部署code-push-server的过程
2、mysql
- mysq使用8.0以下版本
因为 8.0 mysql 使用了caching_sha2_password 插件,废弃了mysql_native_password,而 lisong 的服务代码使用的是旧版插件,否则你的mysql报错:Plugin ‘mysql_native_password’ is not loaded`
- 旧版 mysql安装
如果你的服务器上已经存在高版本 mysql,建议通过 docker 安装
docker pull mysql:5.7.23
默认端口号是 3306,这里换成 3307,同时 code-push-server 中也要指向 3307 端口
添加初始化密码:新增容易环境变量,密码的字段是MYSQL_ROOT_PASSWORD
- 如果你可以直接 3306 端口安装旧版mysql,修改密码
// 以管理员方式打开 mysql
mysql -u root -p
// mysql命令行中输入然后回车(注意 mysql的命令都是以分号;结尾)
ALTER USER 'root'@'localhost' IDENTIFIED BY '1234567890';
// 退出 mysql
exit;
- 服务器进程常驻
nohup ./bin/www &!
回车
logout
- 杀死常驻进程
// 模糊查找
ps -ef | grep www
kill -9 你的进程
更多命令参考:linux后台进程常驻
3、渐进式发布
微软的渐进式发布理念
1、发版流程:先打Staging 包 => 发灰度包=> 灰度包扩大比例 => Staging 包推送到Production
备注:不建议直接打Production 包,建议用验证好的包一步步推送到用户设备
2、紧急 bug 修复:可直接打Production 包
三、安装 Code Push CLI
新版 使用appcenter
1、登录不上
code-push cli 最新版修改了登录接口,导致无法登录 lisong 搭建的服务器,所以我们只能使用 2.1.9 版本
npm i code-push-cli@2.1.9 -g
2、app bundle打包失败
code-push cli@2.1.9 无法打包高版本 rn,因为 react-native 打包的 cli 改了
新版:/node_modules/react-native/cli.js
旧版:/node_modules/react-native/local-cli/cli.js,对应的cli.js 源码
'use strict';
var cli = require('@react-native-community/cli');
if (require.main === module) {
cli.run();
}
module.exports = cli;
对此我们的解决方案有 3 种,都可以
1、改code-push-cli@2.1.9 源代码
这种我还没尝试,理论上没问题,因此第1种与第2种本质上是一个,就是让 cli 与 app 代码对上。
2、项目的react-native添加local-cli/cli.js
上文已经贴过代码了,亲自尝试无问题
3、cli 从code-push 换成code-push-standalone
打开微软的最新代码 main 分支code-push-server下的 cli 目录 ,按照提示走即可(亲自尝试无问题),
弊端:所有的 code-push 命令要换成code-push-standalone ,且部分命令有改动需要换一下,有一点适应成本
3、code push常用命令
// 账户相关
code-push login 登陆
code-push loout 注销
code-push register http://yourServerUrl:3000/ 注册
code-push whoami 查看当前会话登录账号信息
code-push access-key ls 列出登陆的token
code-push access-key rm <accessKye> 删除某个 access-key
// app操作相关
code-push app add <appName> <platform> react-native 在账号里面添加一个新的app
code-push app remove 或者 rm 在账号里移除一个 app
code-push app rename 重命名一个存在 app
code-push app list 或则 ls 列出账号下面的所有 app
code-push app transfer 把app的所有权转移到另外一个账号
code-push promote sunny635533/DigitalMine-Android Staging Production 推送到另一个部署环境
// 应用信息相关
code-push deployment add <appName> 部署
code-push deployment rm <appName> 删除部署
code-push deployment rename <appName> 重命名
code-push deployment ls <appName> 列出应用的部署情况
code-push deployment ls <appName> -k 查看部署的key
code-push deployment history <appName> <deploymentName> 查看历史版本
code-push deployment list rnDemo 查看密钥
// 发布
code-push release-react <appName> <platform> -t 版本 -d 环境 --des 描述 -m true (强制更新)
code-push promote <源部署名称> <目标部署名称> --rollout 20% //灰度
code-push rollout <部署名称> --disable //撤销灰度
// 清除历史部署记录
code-push deployment clear <appName> Production or Staging
// 回滚
code-push rollback <appName> Production --targetRelease v4(codepush服务部署的版本号)
code-push app add 在账号里面添加一个新的app
code-push app remove 或者 rm 在账号里移除一个app
code-push app rename 重命名一个存在app
code-push app list 或则 ls 列出账号下面的所有app
code-push app transfer 把app的所有权转移到另外一个账号
code-push app ls 查看已注册的应用
code-push deployment ls appName -k 查看应用key
code-push login 登陆
code-push loout 注销
code-push access-key ls 列出登陆的token
code-push access-key rm <accessKye> 删除某个 access-key
code-push app add <appName> <platform> react-native 在账号里面添加一个新的app
code-push app remove 或者 rm 在账号里移除一个 app
code-push app rename 重命名一个存在 app
code-push app list 或则 ls 列出账号下面的所有 app
code-push app transfer 把app的所有权转移到另外一个账号
code-push deployment add <appName> 部署
code-push deployment rm <appName> 删除部署
code-push deployment rename <appName> 重命名
code-push deployment ls <appName> 列出应用的部署情况
code-push deployment ls <appName> -k 查看部署的key
code-push deployment history <appName> <deploymentName> 查看历史版本
code-push deployment clear <appName> Production or Staging 清除历史部署记录
code-push release-react <appName> <platform> -t 版本 -d 环境 --des 描述 -m true (强制更新) 发布
示例:
code-push release-react ReactNativeDemo android -m true -d Staging --des "test" -t "1.0.0"
code-push release-react ReactNativeDemoIos ios -m true -d Staging --des "test" -t "1.0.0"
code-push-standalone release-react ReactNativeDemo android -m true -d Staging --des "test" -t "1.0.0"
code-push release-react XMAndroid android -m true -d Staging --des "test" -t "2.3.953"
四、答疑
1、图片使用方式
正常使用 source 即可,src 是用在 uri 上的不要混淆,具体支持能力看 react-native-code-push文档
// 支持绝对路径方式
import test from "@assets/test.png"
// 支持相对路径方式
import test from "../../assets/test.png"
<Image source={test}/>
2、热更新下载是 diff还是整包
code-push-server 同时支持整包、diff 包,会按照一定命中算法下发。
3、第一次是否需要把未更新的jsbundle上报上去
需要,第一次热更新你需要把未更新的包和第一次更新的包都发上去,内部需要做 diff 命中
4、测试更新异常
注意项目的版本号要对得上,如果热更新的-t 版本号是 1.0.0 ,那么 app 的版本号也要是 1.0.0,不能写成 1.0
5、code-push更新后重新打开回退问题
如果没有调用 CodePush.sync 方法,则需要手动调用CodePush.notifyAppReady。
此外检查下你的原生 AppDelegate 页面 jsbundle 的加载是不是漏了使用 CodePush 的 bundle
return [CodePush bundleURL];
参考:https://github.com/microsoft/react-native-code-push?tab=readme-ov-file
6、热更新应用上架应用市场
因此不存在热更新应用无法上架应用市场的说法,参考:链接