作者: meathill

  • 分享自制小工具:Automate GPT – 在 ChatGPT 里批量执行任务

    分享自制小工具:Automate GPT – 在 ChatGPT 里批量执行任务

    其实这个小工具是我很早以前开发的,最近因为有需求,又翻出来修了修,觉得蛮有用的,分享给大家。Automate GPT 是一个浏览器扩展,可以方便我们更好的使用 ChatGPT。

    OpenAI 最近几次发布的新功能还是很强力的,比如近期大热的各种风格化绘图,什么吉卜力啊、卡通3D啊、玩具啊,等等。相信大家都在各种平台上看过,我也试做了一些:

    (更多…)
  • 【系列教程】使用 Vercel Serverless function 连接 APNs 实现 iOS 推送通知(3)时区处理

    【系列教程】使用 Vercel Serverless function 连接 APNs 实现 iOS 推送通知(3)时区处理

    上一篇博客我们分析基于 APNs 实现 iOS Push Notification 的代码,讲解关键环节的关键代码。相信大部份同学看完上一篇就可以复制出自己的消息推送功能。这一篇则是结合我们产品的特殊需求,介绍时区处理,以便给不同时区的用户定时推送消息。有出海和全球化需求的同学应该会需要相关知识。

    (更多…)
  • 【系列教程】使用 Vercel Serverless function 连接 APNs 实现 iOS 推送通知(2)代码解析

    【系列教程】使用 Vercel Serverless function 连接 APNs 实现 iOS 推送通知(2)代码解析

    上一篇文章我们分享了 Push Notification 的基础原理和项目配置,这一篇我们开始看具体的代码。

    (更多…)
  • 【系列教程】使用 Vercel Serverless function 连接 APNs 实现 iOS 推送通知(1)基础知识

    【系列教程】使用 Vercel Serverless function 连接 APNs 实现 iOS 推送通知(1)基础知识

    新年开新坑,写一个小教程,分享一下年前尝试用过 Vercel Serverlss function 通过 APNs 发送通知的经验,虽然能找到不错的教程,但仍然要踩一些坑。希望对大家有帮助。祝大家蛇年大吉,快速成长。

    (更多…)
  • 一人开发会有什么问题?分享两年间挣扎的体会

    一人开发会有什么问题?分享两年间挣扎的体会

    新年反思一人开发的经验教训,为什么我没做到自以为的 100x 程序员?主要分享给其他有一定技术能力想做独立开发的朋友。

    (更多…)
  • 踏破坎坷成大道,斗罢艰险又出发——2024年总结及2025年许愿

    踏破坎坷成大道,斗罢艰险又出发——2024年总结及2025年许愿

    工作之后,感觉每年都过得转瞬即逝。好像刚刚写完 2023 年总结,这下 2024 年也结束了。总之,该总结还是要总结,该展望还是要展望。希望自己每年都有进步吧。

    (更多…)
  • 近期一段调试 React Native 诡异 bug 的经验

    近期一段调试 React Native 诡异 bug 的经验

    熟悉我的同学可能知道,我近期开始尝试用 React Native + Expo 开发移动应用,接触到很多不之前熟悉的东西。当一切顺利的时候就还好,当遇到难以排查的问题时,就需要付出数倍于以往的努力才行。今天就分享近期一段调试 React Native 诡异 bug 的经历。

    需求其实很简单,就是个单选组件。我使用 <ScrollView> 做外部容器,里面有若干<Pressable>,点击哪个就选哪个。现在的问题是:选择之后,会更新 state,造成父组件刷新,继而 <ScrollView> 刷新,然后滚动位置就归零了,感观上就滚动到最上面了。很难受,折腾一晚上没解决……

    (更多…)
  • 升级 Expo v52 小记

    升级 Expo v52 小记

    (草稿箱里躺了半个月,写完赶紧发了)上周查文档,发现 Expo latest 已经升级 52,于是准备找机会把 DailyLift 的依赖升级到最新版本。这周一趁着 kick-off 还没开,尝试升级了一下,很顺利,1.5 小时升级完毕跑起来没什么问题,这里小记一下。

    Expo v52 的主要升级(仅限我用到的)

    1. 新架构。(其实我不知道具体是什么新架构,不过听说有新架构字我觉得非升不可。)
    2. React Native 升级到 v0.76
    3. expo/fetch 可以支持流式传输,这样我们就不再依赖 SSE。不过,考虑到 React 和 React Native 自身的数据传递方式(缓存数据),和低频渲染,我觉得可能对实际开发的提升不大。
    4. expo-audio 重写了录音功能,支持后台录音,现在切换应用时不用担心录音断掉了。
    5. expo-live-photo iOS 下支持 live photo 了,不知道有多大价值,也许再 iOS用户眼里是个很重要的升级吧。
    6. React Native DevTools 这个非常重要,在国内使用不需要翻墙,且支持 Network 面板,升级非常明显。

    主要变更

    1. iOS 基础版本升级到 15.1

    升级步骤

    首先,建议阅读官方的升级指引:https://expo.dev/changelog/2024/11-12-sdk-52。因为不同开发者都有自己的开发习惯,我的升级笔记未必适合所有人,官方的要全乎得多。

    不过有一点,上面说“先把所有依赖升级到最新版“,而我的经验告诉我,React Native + Expo 不能乱升级依赖,否则几乎必挂。所以这里建议大家小心为上,项目能跑就别轻易升级,React Native 的环境太复杂了。

    检查依赖

    使用 npx expo-doctor@latest 检查依赖,确保所有依赖都支持新版本。因为我大部分功能依赖都是从 Expo 找来的,所以这一步基本上没遇到问题。只有少数纯 JS 或者统计之类的代码无法明确得到支持状态。

    升级依赖

    npx expo install expo@^52 --fix

    重复 npx exp-doctor@latestexpo install --check 直到所有的依赖都安装完成并且检查通过。将其它 React Native 无关的仓库,比如 lodash,@supabase/supabase-js,@amplitude/analytics-react-native 等等也都升级到最新版本。

    expo prebuild 重新构建原生开发目录。不过我建议,如果没有修改过原生开发目录里的文件,直接删掉然后重建会比升级更好。至少我这里第一次原址升级失败了,第二次完全重建才搞定。

    调整代码

    升级依赖完成之后,开始调整代码。我这方面没遭遇什么问题,基本上两个版本都顺利兼容。为数不多要修改的地方主要来自于早期对 Expo 不够熟悉而错误的混用了 Expo Stack 和 React navigation Stack。这是另一个话题,等我下次分享出来。

    总结

    整体来说升级过程比较顺畅和平滑,这得益于我之前尽量使用 Expo 推荐的功能库,并且不 hack 代码。所以整个项目比较稳定可靠。

    建议大家在条件允许的情况下都尽快升级到 Expo v52。如果大家遇到什么问题,欢迎留言讨论。

  • 聊聊错误/异常处理

    聊聊错误/异常处理

    又是繁忙的一周。突然发现以前没聊过错误/异常处理,准备分享一下。

    这里我就不细究错误(Error)/异常(Exception)的定义了,大体上程序执行过程中,一旦出现就会阻碍后续代码顺利执行的问题,我们都当它们是错误。我们要保证自己的代码能得到顺利执行,就要防止被它们破坏,就要捕获并处理错误。这就是接下来要讨论的要点。

    有些同学在这个领域搞得不伦不类:要么从来不捕获任何错误;要么所有地方都丢一个 try ... catch ...;要么最外层包一个大 try ... catch ...。这些做法都不对。本文分享我的观点和做法。

    开发时能够不出错,尽量不要出错

    虽然我自己也整天写 bug,并且被自己的 bug 折麽得死去活来,但我还是要说:我们应该在开发阶段尽量避免出现错误,保证我们的代码能够长时间运行不出错。这就需要我们考虑到各种边界情况,提前做出预判,操作前多做检查,避免出错。

    比如,如果使用 node.js 要操作文件,就先判断目标是否存在,目录是否已经创建过。而不是捕获各种可能出现的错误。用户要上传数据给服务器后端处理,就要先尽可能排除掉不可接受的情况,而不是被服务器拒绝后再转达给用户。

    捕获无法在开发阶段排除的错误

    也有一些错误,我们实在无法在开发阶段排除。最常见的就是网络问题。因为我们无法预期用户在怎样的网络条件下使用我们的产品,而现在用户的使用场景又极为丰富:无论是高速运动的火车,还是跨国网络环境,又或是第三方服务突然挂掉,都可能导致网络不畅,而请求失败。

    所以我们必须在发起网络请求时做好错误捕获,并且处理好网络造成的问题。比如:

    1. 告知用户当前状态,正确渲染 UI
    2. 保护好未能保存到服务器上的数据
    3. 提醒用户重试
    4. 提供其它临时保存方案

    简而言之,发起网络请求时没有 try ... catch ... 我在 code review 时是不会通过的。

    开发中见到错误要及时处理

    我见过一些同学的开发环境,跑起来满屏错误,但是好像也能运行,于是他们就不管。这样做是不行的。因为会被抛出的错误大多是未捕获的(Uncaught),它们大概率会破坏到某些代码的执行,没有被影响多半只是因为运气好,没有破坏到当前正在执行的逻辑。早晚还是要遭。

    另外,如果项目遇到了一些奇怪的问题,不知道该怎么解决;同时控制台里有一些奇奇怪怪的报错,那么解决这些报错很可能会带来意想不到的收获。

    总之,不要漏掉错误,不要容忍错误。否则积累技术债。

    要理解错误冒泡的中断机制,尤其是异步操作

    异步回调中的错误处理比较难做,因为回调前后是两个函数栈,所以执行时函数无法捕获到回调时的错误。举个例子吧:

    someAsyncMethod() // 可以直接被外层 try catch 捕获
      .then(() => {
        // 这里如果发生错误,无法被外层捕获
      })
      .catch(e => {
        // 所以我们通常要在最后处理错误
      })

    异步函数由于浏览器的升级,可以在堆栈最底层捕获到上层发生的错误。所以我们可以在底层函数,通常来说也就是执行时的函数捕获错。比如,我们有一个业务函数,要调用一个封装好的请求函数,那么,错误处理就应该放在业务函数里,而不是请求函数里:

    // 业务代码
    async function doDeletePost(id) {
      try {
        await deleteItem('post', id);  
      } catch (e) {
        // 应该在这里处理错误
      }
    }
    
    // 这里虽然是异步函数,请求远程接口,但不需要处理错误
    async function deleteItem(type, id) {
      await fetch(`/api/${type}/${id}, {
        method: 'DELETE',
      });
    }

    另外注意,没有 await 的异步函数就不存在错误捕获,不要写出这样的代码:

    function doDeletePost(id) {
      try {
        deleteItem('post', id); // 没有 await,没有意义
      } catch (e) {
        // 错误处理
      }
    }

    不要封装错误处理,在业务端处理,并提供准确的信息

    有些同学喜欢封装错误处理,我觉得当年的 axios 二次封装难辞其咎。对此我坚决反对,比如:后台同步数据出错,可能不需要告知用户;但是用户主动发起的操作失败就必须告知用户。加载数据失败和删除数据失败,其错误信息也是不一样的,应该准确传达给用户。

    所以,我们应该在业务端发起请求的地方捕获并处理错误,而不是封装一个通用请求类,并且集中处理可能的错误,给出无法区分的信息。

    错误信息要有价值

    有些同学拿到错误之后,直接 alert('出错了。') 这种错误信息对用户、对开发者都没有帮助。以网络请求为例,比较常见的做法是:

    1. 返回的信息首先应该遵守 HTTP 规范,即不同的结果使用不同的响应码。
    2. 返回的响应体包括 code 字段,用来传输错误码,它跟 HTTP 状态码没关系。成功的结果,code=0;错误的结果,code 则使用全局唯一的错误码,方便前后端协同排查。
    3. 错误码可以使用方便理解的规范,比如 XXXX-XXX-X 的形式,从大类到小类再到具体的函数位置等。这样,在任何位置捕获到错误,都可以快速定位到可能出错的环节,进行排查,解决问题。

    总结

    希望大家都掌握好错误处理的方法,能够妥善、快速的解决问题。如果各位对错误处理有一些特殊的理解,或者对错误处理有疑问,欢迎留言讨论。

  • 近期 React Native + Expo 研发笔记

    近期 React Native + Expo 研发笔记

    整理一下近期开发 React Native App 的经验,以 tips 为主,写下来确保将来不会掉在同样的坑里。能够直接从官方文档查到的我就不写了,这里主要记录我踩到的坑。

    技术栈

    先说技术栈,我们目前的技术栈主要是:

    1. React Native v0.74.5,版本受 Expo 限制
    2. Expo v51.0.38
    3. NativeWind 用来写样式,蛮舒服的
    4. Supabase + expo-sqlite + AsyncStorage 用来做各种存储
    5. expo-apple-authentication + @react-native-google-signin/google-signin 用来 SSO
    6. expo-av 用来录音
    7. react-native-sse 用来流式传输

    选择 Expo 是因为 React Native 官方推荐,搭起来环境才发现没那么必要……不过配都配好了,就懒得重新搞了。实际用了之后,感觉还不错,大部分功能都更好用,尤其是各种组件比原始组件要好很多。所以推荐大家需要新组建时先看 Expo SDK:docs.expo.dev/versions/latest/sdk/ 大部分都比原生组件好。实在没有再去找其它 React Native 组件。

    Expo

    Expo 不是必须的,但还是蛮值得使用的。尤其是对 App router 这种基于文件系统的路由体系很熟悉的开发者来说,Expo 很值得。

    Expo 提供构建体系 eas,不过免费版每个月只能构建 15 次 iOS,不够用。所以大部分时间还是本地构建。构建之后,无法直接右键安装,但是可以通过 XCode Devices 安装。

    使用 Expo 需要注意环境变量和组件配置。因为对 .env 的修改、环境配置(app.json)、Logo、字体等的配置不一定会实时生效。所以修改这些不受 React Native 控制之后的东西,都先 prebuild + clean cache,然后再打包。或者直接在项目里手动搜索并修改也可以。

    手机上安装 Expo App 之后可以在 React Native 和 Expo 的网站上预览效果。其中 Android App 可以直接启动摄像头扫二维码;iOS app 没有启动摄像头的选项,我一直以为没法用,后来才发现,直接用照相机扫描二维码,就可以在 Expo 里打开了。

    NativeWind

    NativeWind 让我们可以在 React Native 里使用 TailwindCSS 和 className,非常舒服,非常推荐。

    不过 NativeWind 也有问题。有些组件的 className 不会直接应用在外层容器上,比如 expo-image。而如果不设置 widthheight 的话,<Image> 就不会显示,这会导致我们无法正常使用这个组件。

    还有一些组件,它们由多个视觉组件组成,所以样式也需要分层配置,就有一些诸如 containerWrapperStyle 的属性。这样的组件也无法通过 className 来配置样式。所以我们使用的时候还是要多小心。如果组件渲染不符合预期,那么就看看组件如何应用样式。

    Expo Image

    Expo Image 比起 React Native Image 强很多:

    • 支持 WebP
    • 支持缓存
    • 支持 object-fitobject-position 填充
    • 支持一些我还没有用到的新特性

    非常推荐。但是不支持 NativeWind,请大家记得直接操作其 style 属性。

    FlashList

    <FlashList> 是 Shopify 开发的列表组件,针对大量内容高速滚动做了优化。它提供兼容 <FlatList> 的接口,可以无痛替换,推荐使用。

    使用的时候,需要注意滚动方向。比如 Twitter 这种最新的在上面,就是默认滚动;如果是聊天窗口这样最新的在下面,就要 inverted=true。同时还要注意,inverted=true 之后,Header Footer 的位置也要对调。

    Expo AV

    Expo AV 可以用来录音和播放音频。我们用它来实现 STT。将来会尝试原生 STT。

    需要注意的点不多,主要就是切换应用时,录音会自动停止,并且无法继续。所以我们需要侦听事件,并且做出相应的处理。

    渲染效率

    React Native 不支持非常高频的渲染,比如 Streaming output。在网页上没问题,在 RN 里就会报错:too many renderings。所以,我们需要适当缓存数据,减少渲染频率。我目前是 32 token 渲染一次。

    将来会尝试用 reanimate 之类的库来渲染文字,看看能否得到更好的效果。

    总结

    其实最近遇到的问题很多都跟 React 有关,不过考虑到 React 开发者恐怕不会认为这些“问题”是问题,所以还是写一些文档里找不到但是可能会踩到的坑吧。

    希望对大家有用,如果大家对 React,React Native,React Native Expo 开发感兴趣、有问题的话,欢迎留言讨论。