> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# 场景实践

#### 优化原生和热更包体积

##### iOS 原生包优化(ipa)

对于同一份 archive（其版本号、编译时间和内置 bundle 已固定，不会受导出方式所影响），可以用不同选项多次导出 ipa，选择其中最小的上传到 pushy 服务器作为热更基准包。

![bitcode](/static/image/exportipa.91920cd4bf.png)
##### Android 原生包优化(apk)

apk 的优化主要考虑两个方向：

- [启用 proguard 压缩混淆源码](https://reactnative.cn/docs/signed-apk-android#%E5%90%AF%E7%94%A8proguard%E6%9D%A5%E5%87%8F%E5%B0%91apk%E7%9A%84%E5%A4%A7%E5%B0%8F%EF%BC%88%E5%8F%AF%E9%80%89%EF%BC%89)。但这一步可能导致一些使用反射的代码运行时报错，启用后需要充分测试每个页面和功能，以及需要阅读一些第三方关于 proguard 的特别设置说明。
- 更新 react-native-update-cli 到 v2.22.7 以上，会自动抽取原生包的最小所需部分上传。

##### 热更新包优化(ppk)

热更新包的主要内容是 js 包和其所引用的静态资源（主要是图片）。

- js 包成分分析。可以借助一些第三方工具（如[react-native-bundle-visualizer](https://github.com/IjzerenHein/react-native-bundle-visualizer)）来分析 js 文件中哪些占比较大，是否可以用其他库替换等（如 dayjs 替换 moment，lodash-es 替换 lodash）。
- 图片优化。
  - 在保证体验的情况下，使用一些工具对图片进行裁剪压缩。
  - 如不需要图片的透明像素，可以考虑将 png 格式转为 jpg 格式。
  - 还可以进一步考虑压缩比更高的图片格式，例如 webp 格式（需要第三方原生插件例如[react-native-webp-format](https://github.com/Aleksefo/react-native-webp-format#readme)），heif 格式（iOS 11 和 Android 10 以上原生支持）等。这里有个图片格式对比可供参考 [https://compare.rokka.io/\_compare/#heif=40\&jpeg=80\&webp=80\&av1=40\&width=800](https://compare.rokka.io/_compare/#heif=40\&jpeg=80\&webp=80\&av1=40\&width=800)。

#### 有很多渠道包需要热更，如何操作比较方便？

1. 如果渠道包的`js代码和初始资源`有差别（无论多么细微的差别都会生成不同的 jsbundle），那么只能单独生成 apk，分别上传和绑定。可以考虑写一些脚本自动调用 cli 来执行批量操作。
2. 如果渠道包的`js代码和初始资源`完全一致，可以考虑使用[Flavor 构建](https://developer.android.com/studio/build/build-variants?hl=zh-cn)，或其他一些动态生成渠道包的方案（比如[腾讯的 VasDolly](https://github.com/Tencent/VasDolly)，[美团的 walle](https://github.com/Meituan-Dianping/walle)等），这样所有的渠道包基于同一个基础 apk 生成（因而会有相同的编译时间戳和 jsbundle）。这样可以只用上传一个基础 apk，对此 apk 的热更操作可以对所有渠道包生效。
3. 如果您是`高级版`、`专业版`或`大客户VIP版`客户，也可以考虑在管理后台的应用设置中启用`忽略编译时间戳`。此选项仅对比版本号，不校验时间戳，可提供更宽容的热更策略，但可能消耗更多流量。

#### 如何支持 aab 格式的原生包？

将 react-native-update-cli 更新到 v2.6.0 以上版本，即可使用 pushy parseAab 和 pushy uploadAab 命令来支持 aab 格式的原生包。

如果同一个 Android 版本既需要上架 Google Play 的 AAB，也需要给其他渠道分发 APK，建议不要分两次构建。请在项目根目录的`package.json`中配置一个 npm script，让 CI 和本地发布都通过同一个入口，在同一次 Gradle 调用中同时执行`assembleRelease`和`bundleRelease`。如果已有`scripts`字段，只需要追加其中一项：

```json
{
  "scripts": {
    "package:android:release": "cd android && ./gradlew clean assembleRelease bundleRelease"
  }
}
```

```bash
$ npm run package:android:release
```

这样生成的`android/app/build/outputs/apk/release/app-release.apk`和`android/app/build/outputs/bundle/release/app-release.aab`来自同一次 release 构建，编译时间戳保持一致。之后按渠道分发对应格式即可：Google Play 使用 AAB，直装包或第三方渠道使用 APK。如果项目使用 flavor，请把 npm script 中的任务名改为实际 variant，例如`assembleProdRelease`和`bundleProdRelease`。

#### 测试与回滚

自 v10.11.2 版本开始，可以使用以下两种快捷扫码方案来测试热更，而无需提前进行绑定：

![testqrcode](/static/image/testqrcode.24c22c1ebe.png)
- 首先请添加测试用的 [DeepLink](/docs/getting-started.md#%E6%B7%BB%E5%8A%A0%E6%B5%8B%E8%AF%95%E7%94%A8%E7%9A%84-deep-link) 功能

先参考上述链接在原生端添加启用测试用的 [DeepLink](/docs/getting-started.md#%E6%B7%BB%E5%8A%A0%E6%B5%8B%E8%AF%95%E7%94%A8%E7%9A%84-deep-link)，重新打包。JS 代码中无需任何改动，只需在上述界面中勾选“使用 Deep Link”，填入您应用的协议名，例如"pushy://"，然后使用系统相机或系统内置的扫一扫功能扫码（注意不能使用微信扫码），即可自动调起应用并触发更新。

- 若应用自带扫码功能

请参考 [parseTestQrCode](/docs/api.md#function-parsetestqrcodeqrcode-string) 方法的说明。

<details>
<summary>若您的应用不具有上述两项功能，或 pushy 版本低于 v10.11.2，则可以参考如下测试方式（不推荐）</summary>

先发布一个**测试包**，再发布一个除了版本号以外均完全相同的**正式包**。

例如，假设我们有一个正式包，版本为`1.6.0`，那么可以修改版本号重新打包一个`1001.6.0`，以一个明显不太正常的版本号来标识它是一个测试版本，同时后几位相同，可以表明它和某个正式版本存在关联（内容/依赖一致）。

在每次往发布包发起热更新之前，先对**测试包**`1001.6.0`进行更新操作，基本测试通过之后，再在网页后台上将热更包重新绑定到**正式包**`1.6.0`上。如果在测试包中发现了重大问题，你就可以先进行修复，更新测试确认通过后再部署到正式线上环境。这样，可以最大程度的避免发生线上事故。

</details>

若您用来测试的原生包当前已经有发布的热更版本且更新策略是立即应用，那么和扫码测试之间可能产生策略冲突，比如扫码更新后又立即强制更新到发布版本，对测试带来不便。此时可以考虑在扫码更新之后，利用 beforeCheckUpdate 回调临时禁止更新检查，直到下次启动再恢复。

```js

// isFirstTimeDebug 这个标志位需要 v10.37.0+ 版本，表示当前是扫码热更后的第一次启动
import { isFirstTimeDebug } from 'react-native-update/src/core';

const pushyClient = new Pushy({
  beforeCheckUpdate: () => {
    if (isFirstTimeDebug) {
      // 如果当前是扫码热更后的第一次启动，则跳过热更检查
      return false;
    }
    return true;
  },
})
```

万一确实发生线上事故需要回滚的话，先立即对原生包或者整个应用设置暂停热更，然后更改绑定到之前正常的版本，或者利用版本控制系统回滚代码到正常的状态，然后重新生成热更包并推送。

#### 元信息(Meta Info)的使用

在发布热更新版本时，或者在网页端，你可以编辑版本的元信息。这是一段在检查更新时可以获得的字符串，你可以在其中按你所想的格式（一般建议用[JSON 格式](https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/JSON)）保存一些信息。

比如我们可以在元信息中约定字段标志`silent`，表示需要静默更新。当我们上传热更包填写 metainfo 时，以[JSON 格式](https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/JSON)输入：

```json
{ "silent": true }
```

> 请注意，我们并不对输入做任何格式校验和约束，请自行校验输入是否正确。

此时在客户端检查更新时，能获取到我们刚刚输入的元信息，但它并不具备任何功能，只是一个字符串而已。所以我们其实需要**预先**在更新流程中加入对应的处理逻辑：

```js
// 调用 useUpdate() 获取 updateInfo
if (updateInfo.expired) {
  // ... 原生包版本过期，下载或跳转下载页面
} else if (updateInfo.upToDate) {
  // ... 没有更新，弹提示或忽略
} else {
  // 有更新，一般来说我们在这里给用户弹窗提示，让用户选择是否更新
  // 那么静默更新的本质其实就是不弹窗，直接执行，所以可以在这里加入额外的判断流程
  // ...
}
```

我们在原有的更新流程中加入元信息的读取和判断：

```js
let metaInfo = {};
try {
  // 注意 JSON 输入有可能有错误，需要用 try 语句来避免应用被带崩
  metaInfo = JSON.parse(updateInfo.metaInfo);
} catch (e) {
  // 异常处理，忽略或上报？
}

if (metaInfo.silent) {
  // 如果热更包携带有 silent 字段，不询问用户，直接执行更新
  switchVersion();
} else {
  // 否则还是走之前的询问流程
  // Alert.alert('提示', '检查到新的版本.......
}
```

又比如，可能某个版本包含一些重要的公告内容，所以还可以在上面插入一个公告字段等等。如何使用元信息，完全取决于您的想象力！
