expo-updates
expo-updates
expo 对于 React Native 开发者应该不陌生,算是官方推荐的开发 RN 应用的方式之一
其优势在于,一般场景的应用(不依赖复杂 native 模块),使用 expo 可以让开发者忘记 iOS/Android 忘记 Xcode/Android Studio,任何支持 js/ts 的编辑器/IDE 都能完成开发,开发阶段在 expo client 上同步刷新,需要发布则通过 expo build 上传至云端编译打包 apk/ipa ⭐️
"既然这么方便,怎么没见人用"
长话短说:国内不好用
overview
那为什么还要讨论 expo-updates
众所周知,很多国外开发者们早已习惯使用的服务和软件,在国内则往往鲜有人尝试,或是使用国内低配替代版,或是人手一个轮子自娱自乐
拿 expo 举例,微信登录/安卓推送/各类统计,这些国内开发绕不过去的东西,他国是不会在意的,这几乎不可能内置到 expo client (内置了 facebook登录/google登录/FCM/GA),你不支持 => 我不用 => 大家都不用 => 永远不会支持,仿佛两个世界
而事实上,不好用也不是不能用,这两年 expo 官方推出并逐步强化了 bare workflow 的开发方式 (对比于默认的 managed workflow),这使得通过 npx react-native init 创建的应用也可以使用大部分 expo api,谁说国内可怜的开发者就不配用好东西了!
expo-updates 就是其中一个好东西,相比于另一个大家熟知的 OTA 方式 codepush,expo-updates 拥有以下优点:
- ⭐️ 官方支持 self-hosting,想部署在哪都行 (
ping codepush.appcenter.mstimeout) - 功能支持
完善够用且和 expo 配合度更好 - 开发团队专业做 RN,未来可期 (bare workflow 也将支持使用 expo build 服务)
国内网络环境如此之复杂,又恰巧大部分方案都出自毫无同情心的敌国团队,是否支持 self-hosting 则成了关键
调研完所有现存的成熟 OTA 方案后,似乎也就只剩下 expo-updates 能用了
issues
遗憾的是,在尝试接入 expo-updates 过程中,还是遇到了一些小问题
sdkVersion
在不依赖 expo sdk 的 bare workflow 中,理论上只要提供了 runtimeVersion,sdkVersion 应当为可选,但在实际使用过程中,发现并非如此
如果贸然删去 sdkVersion,执行 expo export 会得到:
Cannot determine which native SDK version your project uses because the module `expo` is not installed
原因是 export 命令会读取并校验 app.json 中的 expo 字段,而 sdkVersion 实则为必填项 (与文档和直觉不符)
解决办法: 在 app.json 中固定 sdkVersion 版本为 39.0.0,但不使用该字段进行更新和版本控制
react-native-config
使用 react-native-config 可以用 .env 文件切换环境,并且支持 native code 读取字段
根据文档提示,我们可以在 AndroidManifest.xml 中通过 @string/EXPO_UPDATE_URL 得到值,同时也能在 AppDelegate.m 中通过 [ReactNativeConfig envFor:@"EXPO_UPDATE_URL"] 获得
但令人意外的是,expo export 每次执行后,都会修改 AndroidManifest.xml/Expo.plist 文件,使其与 app.json 中的配置一致
解决办法: 不管他,折腾了一圈后发现,实际上这不重要,因为 export 应当执行在 CI 环境
releaseChannel
说到这里,实际上已经出现了三个概念
- sdkVersion
- runtimeVersion
- releaseChannel
在更新时,三者之间判断的优先逻辑如何,文档中有所描述,但由于 managed workflow 仍然是 expo 默认的开发模式,很多提到 OTA 的部分,缺少了 bare workflow 相关的提示,大概总结为:
- sdkVersion 用于占位,防止 expo export 失败
- runtimeVersion 在 native code 更新后升级版本,app 将只更新 runtimeVersion 相同的包
- releaseChannel 用于区分分发渠道,bare workflow 下并未支持完全,需要自己开发类似 expo publish 的功能,服务端根据 expo-updates 请求头 (Expo-Release-Channel) 返回 manifest
其中 releaseChannel 流程图如下:
解决办法: source code
export
export 命令中,有两个参数需要强调:
- --public-url expo-updates 请求该地址,用于获取更新的信息
- --asset-url assets 地址,bundles/*.js 依赖的资源地址前缀
实际使用中,export 不支持对 iOS/Android 分别 export,每次执行都将完整打包两个平台,而如需同时在线上跑多个 updates (v1.0.1-hotfix v2.0.1-hotfix),则需要 --merge-src-url
expo 将根据参数合并多个目录下的版本,替换 ios/android-index.json 的 object 为 object[],而选择哪个版本进行更新,则在 expo-updates 中判断 (runtimeVersion/releaseChannel)
workflow
- installing react-native-unimodule
- installing expo-updates
- installing react-native-config
- setup runtimeVersion
- EXPO_RUNTIME_VERSION for .env
- runtimeVersion for app.json
expo export --public-url https://cdn.domain.com --output-dir v1.1.0- upload v1.1.0 to cdn.domain.com
- installing react-native-wechat (BREAKING CHANGE)
- update runtimeVersion to 2
expo export --public-url https://cdn.domain.com --merge-src-url v1.1.0 --output-dir dist- upload dist to cdn.domain.com
notes
- 问题基本可以绕过
- 测试阶段使用起来很方便
- 支持动态配置,满足大部分需求
ToDo
- expo-updates-dev-server
- expo-bare
- npx expo-bare export
- npx expo-bare publish
