做项目确实不能自己一个人埋头干,很容易被一个很小的问题给卡壳。React Native的热更新问题,我花了近一周都没完全搞定,结果同事帮忙查看,被卡的一个问题其实就是文件夹名称不对而已。

先说一下,我们的项目是Expo进行Prebuild手动打包的,我引入了expo-updates,怎么请求怎么报:{"code":"ERR_UPDATES_CHECK"} Error: Call to functionExpoUpdates.checkForUpdateAsynchas been relected.>Caused by: Failed to check forupdate 这种含糊不清的错误(注:后来发现是android端模糊,ios端会更详细)。

于是尝试npx create-expo-app my-app开空项目,发现空项目直接跑expo-updates居然不用改src下的代码就能自动进行热更新。具体参考:OTA Updates With Expo - Pagepro

然后我又尝试把项目prebuild,自行打包跑官方的热更新,其实也是能顺利跑通的(苹果我没测),需要修改些配置,具体参考:React Native OTA Updates with Expo EAS: Step-by-Step Guide & Best Practices - DEV Community

安卓苹果需加包名,runtimeVersion必须是具体版本,请求头requestHeaders得加channel-name,安卓的E:\temp\ota-app\android\app\src\main\AndroidManifest.xml也要配置channel-name(这个参数是使用expo官方热更新才需要配置)。

{
  "expo": {
      ...
    "ios": {
      "bundleIdentifier": "com.caice.expoota"
    },
    "android": {
      "bundleIdentifier": "com.caice.expoota"
    },
    "runtimeVersion": "1.0.0",
    "updates": {
      "url": "https://u.expo.dev/2b99efc1-4531-4a16-907a-90f24d20e0ad",
      "enabled": true,
      "fallbackToCacheTimeout": 0,
      "checkAutomatically": "ON_LOAD",
      "requestHeaders": {
        "expo-channel-name": "preview"
      }
    }
  }
}
    <meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
    <meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="@string/expo_runtime_version"/>
    <meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
    <meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
    <meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://u.expo.dev/2b99efc1-4531-4a16-907a-90f24d20e0ad"/>
    <meta-data android:name="expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY" android:value="{&quot;expo-channel-name&quot;:&quot;your-channel-name&quot;}"/>

接着是搭建本地服务,用的https://github.com/expo/custom-expo-updates-server,可是不知道哪里配的不对,连它的示例都无法正常使用。

后来用空项目跑通了,修改了两个点,一个是它创建的时间戳文件夹名在windows系统中需要由"expo-publish": "./scripts/publish.sh -d 1/$(date +%s)"改成"expo-publish": "bash -c './scripts/publish.sh -d 1/$(date +%s)'",否则会成生"$(date"这样错误的文件夹。还有一点便是里面的1,我的空项目版本号是1.0.0,而它测试用例其实那个1就是版本号,不一致的话也是跑不通的,我就是被这个问题卡了半天。还以为是chartGPT里说的生成的dist里没有android-index.json / ios-index.json,其实并不需要。chartGPT还是不能太信任,像之前还告诉我需要配置expo_updates_config.xml,也是完全错误的方向,就没有任何文档有提过这东西。

最后就是把这空项目的热更新 拿到我们正式项目里跑了。所以问题其实不在代码里,之前的Updates.checkForUpdateAsync报错完全可以无视掉,在于我们项目加了太多web端不可用的插件,而默认跑npx expo export,那些web端不兼容的牛鬼蛇神就冒出来了。并且npx expo export --platform XXX是只支持打一个端的包,连续打另一个就会把之前的清空。只要在app.json里配置platforms: ["ios", "android"],就可以做到不打web,最终成功实现热更新功能。


import * as Updates from 'expo-updates';

  const checkUpdates = async () => {
    // 热更新测试代码,不用浪费时间在以下代码上,alert在手机里也是可以用的
    try {
      const update = await Updates.checkForUpdateAsync();
      if (update.isAvailable) {
        alert('更新可用,开始下载...');
        await Updates.fetchUpdateAsync();
        alert('更新下载完成,将在下次启动时生效');
        // 如果需要立即重载
        await Updates.reloadAsync();
      } else {
        alert('没有可用更新');
      }
    } catch (error) {
      alert('检查更新失败:' + error);
    }
  };

总结一下,想要原本已经prebuild的expo项目跑本地服务实现热更新其实不难,具体流程如下:

  1. npm install expo-updates,引入上述Updates的测试代码(非初始页里也能跑,但在_layout里跑会出错)
  2. 参考官方文档Install expo-updates in an existing React Native project - Expo Documentation,在Android和Ios里增加相应配置
  3. 如有需要,可在app.json里配置platforms: ["ios", "android"],屏蔽掉web端的麻烦事
  4. 先gradlew assembleRelease一个apk,安装到手机上,打开APP看看,检查更新失败报错不用管
  5. 修改原项目中任意可见内容,用于后续热更新的测试
  6. 搭建本地服务expo-updates-server,把里面的“expo-updates-client”改成项目文件名,localhost改真实IP(手机能直接访问)
  7. 运行服务中的yarn expo-publish(特别需注意我提到的的文件夹名称问题)
  8. 运行服务yarn dev,让服务启动(start需要秘钥我没研究)
  9. 再次打开APP,它就会自动热更新了,变成修改之后的内容

我以为就此结束了,结果ios又出现问题,它没有任何的报错,所有接口都能顺利跑通,也能识别到是否真的有热更新并成功运行代码。但!它就是没有显示新内容……没有任何的异常,过程全对,却看不到结果。

代码又删改测试了半天,原来是"expo-native-wechat"的registerApp调用了原生模块与热更新产生了冲突,只要把热更新的代码setTimeout延迟执行就可行了。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐