跳到主要内容

游牧周记第52期

· 阅读需 5 分钟
Suhe
This site owner

日常

踝关节扭伤

这次非常严重,韧带断了,涉及到住院、手术、长期静养。 学会了几个词汇:R.I.C.E原则, ATFL。

昆明的又一个据点

后新街网红老街,闹中取静;就是冬天室内有点冷;咖啡和点心稍贵但环境好。

关注

爱德华说

B站居然还有个敢说真话的,还不是黑话。 他是东北人。 # 一场集出生率、离婚率、死亡率于一体的东北人口消失魔术 被这个视频带进来,后来发现似乎也就这一部猛点。

Luke Made It

这小伙子的风格挺讨喜的,虽然赛道是非常偏门的程序员日常。 youtube 有时候看这些老外的视频,唯一羡慕的一点是他们能毫无麻烦的使用各种平台和AI服务。

影视

下载美剧的地方还有哪里好?

美剧天堂似乎很久没有更新了,最近追的Pluribus和“欢迎来到德里镇”,好久没更新,然后大结局季的“怪奇物语”即将到来,有点急。 找了一下似乎只有这里:SeedHub。 因为我的要求是下载,目标是appleTV上看大屏幕,否则的话现在B站都足够了。

知识

瞿昙

乔达摩.悉达多的另一个翻译法,我看到这个是老罗讲苏轼阴阳王安石的那篇“圣旨”(王安石赠太傅制)。

开发

expo读取Supabase如何缓存

Claude的建议和kimi差不多,就是用tanstack或自己写本地缓存方法。 kimi说还有React-Query / SWR + supabase-cache-helpers方法。 最后用了Claude的。

LangChain

一直没试过,最近也v1.0+了,我看到它们还有ts/js版本。官方频道有一些nextjs项目案例。 youtube

工具

mole 开源mac清理工具

gitHub

x上关注了作者,他也推广了好久,这次试试吧,毕竟512G的M1 pro用了这么多年,没有其他可以优化空间的办法了。


# 安装
brew install tw93/tap/mole

运行命令:


# You can use either 'mole' or 'mo' command:

  mo                # Interactive menu
  mo clean          # System cleanup
  mo uninstall      # Remove applications
  mo analyze        # Disk space explorer

# To update Mole, use:
mo update

我试了一下:


Clean Your Mac

**☻** First time? Run mo clean --dry-run first to preview changes

➤ System cleanup requires admin access
➤ Password: 

**Authentication failed**, continuing with user-level cleanup

⚙ Apple Silicon | Free space: 14Gi
✓ Whitelist: 5 custom + 2 core patterns active

➤ User essentials

  ✓ User app cache 249 items (30.16GB)
  ✓ User app logs 30 items (86.4MB)
  ✓ Crash reports 45 items (244KB)

➤ Finder metadata

  **☻** Finder metadata protected by whitelist
  **☻** Run mo clean --whitelist to allow cleaning .DS_Store files

➤ macOS system caches

  ✓ Saved application states 33 items (468KB)

➤ Sandboxed app caches

  ✓ Wallpaper agent cache (911.4MB)
  ✓ Media analysis cache 4 items (435.2MB)
  ✓ Sandboxed app caches 98 items (2.93GB)

➤ Browsers

  ✓ Chrome cache (47.6MB)
  ✓ Chrome GPU cache 25 items (10.3MB)
  ✓ Chrome Service Worker cache (681MB cleaned, 0 protected)
  ✓ Edge Service Worker cache (785MB cleaned, 0 protected)
  ✓ Arc Service Worker cache (158MB cleaned, 0 protected)

➤ Cloud storage

  ✓ Nothing to tidy

➤ Office applications

  ✓ Nothing to tidy

➤ Developer tools

  ✓ npm cache
  ✓ npm logs 13 items (192KB)
  ✓ pip cache
  ✓ Homebrew cleanup (1 items)
  ✓ pnpm store cache (1.21GB)
  ✓ Next.js build cache 3 items (26.9MB)
  ✓ Next.js build cache 3 items (41.7MB)
  ✓ Next.js build cache 4 items (280.0MB)
  ✓ Next.js build cache 2 items (12.7MB)
  ✓ Gradle caches 6 items (8.77GB)
  ✓ Ruby Bundler cache (24.8MB)
  ✓ Gradle daemon logs 12 items (1.50GB)
  ✓ Android SDK cache 76 items (5.0MB)
  ✓ Xcode Interface Builder cache (35.0MB)

➤ Development applications

  ✓ Xcode derived data 16 items (56.18GB)
  ✓ iOS device logs 12 items (528KB)
  ✓ Xcode build products 10 items (17.6MB)
  ✓ VS Code logs 40 items (3.6MB)
  ✓ VS Code cache (434.4MB)
  ✓ VS Code extension cache 2 items (824KB)
  ✓ VS Code data cache 8 items (85.3MB)
  ✓ Discord cache (306.6MB)
  ✓ Zsh completion cache (52KB)
  ✓ less history (4KB)

➤ Virtualization tools

  ✓ Nothing to tidy

➤ Application Support logs

  ✓ App logs: Antigravity 2 items (152KB)
  ✓ App logs: Cursor 10 items (13.4MB)
  ✓ App logs: Postman 4 items (604KB)
  ✓ App logs: Shadowbot 38 items (4.0MB)
  ✓ App logs: Trae 7 items (4.4MB)
  ✓ App logs: Trae CN 3 items (26.0MB)
  ✓ App logs: Windsurf 6 items (4.3MB)
  ✓ App logs: discord 5 items (432KB)
  ✓ App logs: mCloud 497 items (4.83GB)
  ✓ App logs: obs-studio 10 items (232KB)

➤ Orphaned app data

  ✓ Found 136 active/installed apps
  ✓ Orphaned WebKit: com.obsproject.obs-studio (420KB)
  ✓ Orphaned WebKit: com.taobao.Aliworkbench (1.1MB)
  ✓ Orphaned HTTP: com.bytedance.douyin.desktop (56KB)
  ✓ Orphaned HTTP: com.cloudflare.1dot1dot1dot1.macos (232KB)
  ✓ Orphaned HTTP: com.cloudflare.1dot1dot1dot1.macos (4KB)
  ✓ Orphaned HTTP: com.electron.ollama (168KB)
  ✓ Orphaned HTTP: com.electron.ollama (4KB)
  ✓ Orphaned HTTP: com.exafunction.windsurf (84KB)
  ✓ Orphaned HTTP: com.obsproject.obs-studio (360KB)
  ✓ Orphaned HTTP: com.pinduoduo.merchantMac (56KB)
  ✓ Orphaned HTTP: com.pinduoduo.merchantMac (4KB)
  ✓ Orphaned HTTP: com.sony.WirelessAutoImport (64KB)
  ✓ Orphaned HTTP: com.taobao.Aliworkbench (56KB)
  ✓ Orphaned HTTP: com.taobao.Aliworkbench (4KB)
  ✓ Orphaned HTTP: com.tencent.meeting.UpdateServer (100KB)
  ✓ Orphaned HTTP: com.tencent.meeting.UpdateServer (4KB)
  ✓ Orphaned HTTP: com.tencent.meeting.services.wmexternal (64KB)
  ✓ Orphaned HTTP: com.todesktop.230313mzl4w4u92 (296KB)
  ✓ Orphaned HTTP: com.todesktop.230313mzl4w4u92 (4KB)
  ✓ Orphaned HTTP: com.trae.app (56KB)
  ✓ Orphaned HTTP: com.xunlei.XLEmbeddedPlayer (88KB)
  ✓ Orphaned HTTP: com.yingdao.yd (56KB)
  ✓ Orphaned HTTP: net.nutstoer.osxapp.onlineinstaller (56KB)
  ✓ Orphaned HTTP: net.yanue.V2rayU (232KB)
  ✓ Found 24 orphaned app resources
  ✓ Cleaned 24 orphaned items (~3.5MB)


➤ Apple Silicon optimizations

  ✓ Nothing to tidy

➤ iOS device backups

find: /Users/alansuhe/Library/Application Support/MobileSync/Backup: Operation not permitted

  ✓ Nothing to tidy

➤ Time Machine failed backups

  ✓ No failed Time Machine backups found
  ✓ Nothing to tidy

➤ Large project dependencies

  **☻** Found **10.8GB** in 12 old node_modules (60+ days)
  **☻** Run mo analyze to see details and manually clean

  ✓ Nothing to tidy


======================================================================

Cleanup complete

Space freed: 108.35GB
Free space now: 148Gi
Files cleaned: 1266614 | Categories: 64 | Protected: 8

Equivalent to ~24 4K movies of storage.

======================================================================

搞掉了108GB,也不知有没有次生灾害风险,待用。

游牧周记第51期

· 阅读需 10 分钟

标签 Tags

爱泼斯坦

读书

太阳王

可能是十多年前买的“科幻”(奇幻?)小说,当时不知为啥没读几页就丢在一边了,这几天睡前又重新拿起来,感觉完全不同了,一直看到发困睡着。 和近些年看佛经和印度神话的东西有关系吗?可能是,毕竟重新拿起来之前我连这本书讲什么都完全没记忆了。 前言是乔治RR马丁写的,当年我连他是谁都不知道(直到冰与火之歌剧集)。

关注

Expo App Awards 我得奖了吗?

youtube 写这个的时候我正在看15小时前的直播录像。 大概一个月前看到参赛通知,我把学易app提交参赛了。 当然没可能中奖了,不然他们早通知我了。据说一共有1200+ app报名。 我发现得奖作品中日本作者还不少,然后还有个学日语的app。 然后虚拟币钱包之类的也挺受欢迎,我没想到。 不知道他们的评选标准是啥。 总之我感觉自己的app不亚于这些😎,可能就是太小众了吧。 另外,好像奖品就是一个3.2kg重的机械键盘? 结果页

据说比较火的2个项目

x上有人刷好久了,开始觉得可能不适合我,但出于好奇,还是看看。

抄一下介绍:

TrendRadar 是一个轻量级的热点聚合工具,主要解决信息过载问题,帮助用户高效获取关心的热点新闻。 BettaFish 则是一个专业的舆情分析系统,通过多智能体协作深度挖掘评论和情感趋势,打破信息茧房,还原全局舆情态势。

追求高效信息获取和日常热点追踪,选择 TrendRadar; 需要深度舆情分析、情感挖掘和趋势预测,选择 BettaFish。 两个地址分别是: TrendRadar:https://github.com/sansan0/TrendRadar BettaFish:https://github.com/666ghj/BettaFish

发现是python的2个需要服务器部署的项目,要尝试还没有试用平台。 我目前没有这个需求,有的也可以问GPT,所以先放弃了。

AI

Google Antigravity

Google Antigravity,全世界都似乎才发现Goolge才是泡沫救星,上一轮25年前就是它幸存下来拯救世界的。 今天推出的这个开发工具,据说免费调用Gemini 3.0pro,没错这玩意也更新了。 我没法用,不管怎么换vpn地区,看来它的方法不仅仅判断ip。

关注

礼明月

礼明月

云南曲靖人,现在大理日结似乎活得挺自在,看以前视频是广东打工赛道的,现在也精准避开了躺平话题。B站8万多粉,有广告接,还不错了。人和内容都很真诚的样子。

我的AI辅助编程实践

这是一个聊天视频的提纲

  1. 网上的vibe coding;我之前的视频,似乎也提到了AI辅助;
  2. 但真实的世界是怎样的呢?名称:一个国内独立开发者的AI辅助Coding现状;
  3. 大模型选择, 那个大模型更适合编程呢,Claude,openai,gemini御三家,有些说国内的GLM 4.6,qwen, kimi;真实情况是,全都用,没事就试一下,工具上恨不得全部绑上,根据情况灵活调用,当然这个情况包括钱包的情况。
  4. 最想用的当然是这个(claude)了,好用,也不贵,还有client工具,但真实情况你懂的,所以为啥我前面的标题要加上国内两个字呢? 还有这个gemini也不错2.5pro曾经惊艳了我,据说3.0快来了,但是地区你看到了,但实际上管得没那么严,也出了个client,还开源;最后我多数时间还是回到ChatGPT,发挥稳定的优等生,免费额度高,页面聊天的话基本用不完,比其他的某些模型良心多了。openai还为我们前端开发者贡献了这个sdk,相信很多朋友在用吧。
  5. 顺便看看openai价格现状。试试能不能订。
  6. 其实如果你真想用国外御三家AI,渠道也是有的,如果只是聊天,可以找聚合的平台比如poe。每天都会有一些免费token赠送,聊胜于无那种。缺点是界面跟不上,还有似乎只能聊天,没有api。
  7. 国产大模型,很幸运的,国产几家大模型今年意识到编程赛道才是刚需,去年字节的trae似乎还只想到做ide,对标cursor呢,现在纷纷对标claude code,比如qwen出了个cli,据说直接抄Gemini的开源client。其实这个活门槛更低。能力方面,纷纷号称仅次于claude最新模型,评分还特别高,实际使用下来怎么样呢,后面说。值得高兴的是,我这几年一直关注的稍显低调的月之暗面kimi,k2模型好评如潮,我至少在聊天界面用得很爽,基本和GPT各占了我一半时间。
  8. web聊天,现在好多讲vibe coding的视频,都直接从开发界面和Agent的终端命令开始了。现实中的情况是,聊天过程几乎耗费我整个AI实践的2/3以上时间,谈设计、谈优化、谈选择,谈技术细节,就是不想让它写代码,有时候为了防止AI忍不住吐出长篇代码,我特 地在每个问题结尾加入文字:“你说呢?”、“你怎么看?”、“请谈谈你的想法和建议“等等。说实话AI在这种场合给出的idea、创意和洞察力有时真的令人惊叹,甚至会拍案叫绝。聊天的最后结果是生成写代码前的各种文档。而且我在实际开发过程中,永远会开着一到2个浏览器窗口,和不少于2个聊天窗口对话,似乎他们才是真正的合作伙伴,Agent那只是我请的听话的员工而已。
  9. Agent,之前我可能在聊天界面就让AI出代码了,2025了显然已经不能这么做,上下文吃不住。接下来就是api调用时刻。今年5月后我似乎就没有用cursor了,不知为啥我就是习惯vscode的界面,它们明明很像;claude code出来后,ide更是成了可有可无的玩意儿。听说cursor 2.0逆风翻盘了,我还没试过,但我觉得最令人佩服的是他们终于开始做自己的大模型了,希望他们有朝一日打败claude。不管怎么说我还是回到vscode,删掉了一堆ai插件,除了删不掉的那个你懂的。
  10. 灵码,有一天试用qwen的cli几小时,烧钱太快放弃了,结果阿里的客服给我打电话,我抱怨一通他们没人性的收费模式后,他推荐这个灵码,说关键是现在免费,一看又是ide,对了还有个插件。装上去是这样了,试下来解决问题过程让人头痛,废话还挺多,我基本上就把它作为免费代码补全工具用。
  11. cli,接下来终于是核心代码工具了,没错,还是claude code,但那只是工具,模型随便配置,因为我又套了个壳claude code router。这小玩意不但可以灵活切换各种模型,还自带UI界面。我立马把国产编程御三家glm4.6,qwen3、kimi k2加上去了。
  12. 费用,大家都明白我实际代码是要用国产模型了,那费用呢?说起来我没怎么关心,因为基本是免费。你没听错,我之前在各个平台都充了值(国内喜欢玩充值,而不是订阅,还有都按token实时扣费),包括聚合平台silicon flow。直到发现这个小平台。
  13. iflow,看似一个实验性的半成品平台,比如今天突然大模型页面又小时了,主办方正事儿不干,做了好多小玩意儿。不知咋想的。你看它还搞了个cli,估计和qwen的cli来源差不多。我为啥用它?因为...免费,至少现在是这样,唯一麻烦的是每7天要换一个apikey。
  14. 完全体,虽然说磨刀不误砍柴工,我始终认为,程序员的价值绝对不体现在文具多,而是趁手。工具的准备和折腾,也不知曾耽搁了多少生产力和创造力。AI时代,我们也不应被漫天的vibe coding工具所晃瞎了眼。希望看我视频的朋友们,都能尽快找到适合的刀子,赶快砍柴..创作去。
  15. 我前段时间,应要求写了一个微信小程序,这玩意我上次接触时,还没带口罩呢,这么多年过去,它的技术栈还是那么让人一言难尽。于是我用claude code + kimi,一小时顺利完成了所有主要功能和界面,剩下3小时折腾企鹅厂的那些烦人的注册啥的。猜猜看写代码之前聊了多久?大概半天吧。总之这个任务一天之内结束了,手写代码量基本为0。

有趣

喉返神经和x86芯片

x上看到的文章,不知是否原创。 一个老程序员看到喉返神经后的感慨

怎么说呢,以前看到过一篇文章讲登月火箭和古罗马车轴标准的关联,大家都在说明一个道理:有些事你不去做,就没人会做,你不改变它,可能永远就这样。 喉返神经(recurrent laryngeal nerve,RLN)的解剖图 那条从脑干发出的神经,本应直抵喉部,却偏要绕道下行,深入胸腔一圈,再折返向上。 在鱼类身上,这路径顺理成章;到了哺乳动物,却成了一个死结;而到了长颈鹿体内,它竟要绕行五米之远。 脖子越长,绕路越显荒唐,却无法更改。

intel 8086

intel i9

电脑永远逃不掉那个噩梦:上电后,从 F000:FFF0 开始那个1978年的时刻醒来,假装自己还活在 1MB 的史前时代。 BIOS 再怎么换皮,UEFI 再怎么现代,都得先演一遍“古代仪式”:跑过 8086 的实模式,走过那套段模型、1MB 内存地图和 A0000 显存区,然后才缓缓进入保护模式、长模式,最终装载 Windows 11。

很显然,整个宇宙都是草台班子。

游牧周记第50期

· 阅读需 5 分钟
Suhe
This site owner

标签 Tags

claude skills

Pluribus 同乐者第一季

开发

现在的微信小程序开发是怎样的?

我上次写小程序大约是4年以前。 这次接到一个想法,所以又拿起来试试。 体验是:

  1. 还是得用微信的工具
  2. 默认还是js而不是ts
  3. gitHub似乎不好接入,自带的git没搞明白
  4. 文档还是很不好查
  5. 语法等方面似乎多年没变化 不过幸好有terminal,我试了一下claude cli居然能用。 快速写了个云函数实现的AI Chat。

小程序开发的坑

  1. 不支持导入json文件,不管是require还是import都不行,只有改成js文件export了。
  2. 页面间传参数,中文会乱码,必须先编码,接收端再解码,但我遇到的情况更奇葩,要双重解码才能用,明明就是一个字符串而已。
  3. 调用云函数失败: Error: cloud.callFunction:fail Error: errCode: -504003 | errMsg: Invoking task timed out after 3 seconds ,云函数的默认超时时间3秒,如果连接第三方服务如ai等,就要设置长一点,免得随时报错。这个在云函数面板很深处设置,和env一样。

小程序还不能实现的

  1. AI Chat的流时输出,靠云函数还不行,除非自建服务器。

react的context和zustand用哪个?

发现前几天AI帮我写的一个复杂组件,用了context做状态共享管理,又是provider又是reducer的挺复杂,它可能不知道我装了zustand。 以前我用zustand主要是全局管理,当时一个组件(由一些分组件构成的逻辑链)就没想到用。 这两种到底谁好呢? AI总结:

如果你已经装了 Zustand,就不要再为了局部状态引入 Context
直接创建一个独立的小 store,既高效又简洁。

项目类型状态作用范围推荐方式
Expo / Next.js局部 UI 状态(颜色、开关)Context
Expo / Next.js多组件间共享业务数据(购物车、表单、播放控制)Zustand 局部 store
SSR 项目(Next.js)跨请求安全的全局状态Zustand + 自定义初始化逻辑

但是性能和使用复杂度的双重优势,让人必须选zustand啊。

特点React ContextZustand
设计定位提供全局数据传递的通道状态管理库(比 Redux 轻量)
状态存放Context Provider 内部独立的 store(React 之外)
状态更新触发依赖 React 渲染机制(Provider 的 value 变化会导致消费组件重渲染)基于订阅机制(只更新订阅的组件)
使用复杂度简单(但易导致重渲染)简单且性能更好
TypeScript 支持需要手动声明内置优秀的类型推导
SSR 支持原生(React 内置)需要一点配置(Next.js SSR 支持良好)

个人结论,以后没事就别折腾什么context了,直接zustand搞定一切。 当然据说react 18+对性能有所改善,待观察。

我目前的AI辅助开发模式

显然每个人的工作流程和习惯都是适应出来的,类似生物进化。 这个时代,每天都有人在推荐更好的方式、更好的工具、更好的模型。 我建议你们看一眼就好,没有颠覆性的东西出现前,还是要自己用着趁手才是最好的。 如何找到最适合自己的路线,不在工具层面浪费太多时间和成本呢?

  1. 先按以下思路理清楚几个问题并诚实回答:
  • 不考虑条件限制的话,我想要的最好方式是什么?

答:和OpenAI沟通,整理自己的思路;具体开发过程在Claude code + Claude最好模型辅助下进行;无限制tokens;再加上好用的代码补全。

  • 我面对的硬条件限制是什么?

答:OpenAI的Plus版本但现在没有支付渠道;Claude模型就彻底死心了(国内渠道封死),就算用国内的各种代理,体验还不好(会经常抽风)。

  • 我面对的成本限制是什么?

答:OpenAI的Plus usd $20/月还算合适,价格也不低(aicodemirror.com不花个600+/月的话token根本不够)。

  1. 基于自己的情况,确认次要目标:

答:LLM模型不想花钱(至少少花钱);用Claude Code以追随主流工作方式;要有代码补全。

  1. 做法:
  • iflow.cn使用国产模型,目前是kimi2为主力,工作量小(难度低?)的时候还行,多了会抽风或幻想;
  • claude-code-router(CCR)工具调用Claude code,方便管理和切换模型;
  • vscode中使用千问的灵码插件,作为辅助。(目前看来代码补全还行,但一些偏agent和edit的活,实在是啰嗦又不清楚,界面容易误操作,不太敢用了。)

游牧周记第49期

· 阅读需 6 分钟
Suhe
This site owner

标签 Tags

李新野Elon

剧集

小丑回魂:欢迎来到德里镇第一季

才出了2集,我的感觉是惊为天人。

其实Thing只在国内这么翻译,容易和DC小丑混淆,开始我也是因为这个原因没第一时间看。 五十年代质感,人物性格刻画intense,及时是种族议题都很真实,不做作。 我有点当年看Strange Things的感觉了,虽然很不一样。

顺便说说本周另一部泰拉玛斯卡第一季虽开头还不错,到第三集就有点看不下去了。

开发

为啥国产大模型AI api都不支持response

导致vercel AI SDK用球不成。

supabase的domain jwt cookie问题

之前做了一个用户登录中心,后台是supabase的Auth服务。 原理就是用Cookie存储jwt,实现同主域名下跨站共享登录状态。 目前的做法是自己生成jwt cookie,和supabase的auth不同步。 后来想为啥不用supabase的cooke呢,问了AI都说这样才对。 Gemini甚至说supabase有配置域名的地方,我找半天没见到,只是发现有个收费服务上有这么一句:

Custom domains are a Pro Plan add-on Paid Plans come with free vanity subdomains or Custom Domains for an additional $10/month per domain.

看来这是一个收费服务?

突然想试试Clerk或类似国产服务。

claude-code-router和iflow的坑

我前面的blog似乎说过。 由于iflow给的apikey 7天就过期。 所以要去网站申请新key,然后改。 这里有点坑:

  1. ccr ui打开网页修改基本无效,自己又改回来了。
  2. 直接改~/.claude-code-router/config.json文件,要注意上面那个APIKEY是访问和网页UI打开的密码。下面providers中的apikey才是iflow给的。
  3. 修改后ccr restart或者UI中重启均无效,要ccr stop + start才行。原因呢:
CCR 的 `restart` 实现等同于**先杀再拉起同一个进程**,但**配置在启动时一次性读入内存**,后续热更新文件不会触发重载;  
所以看似“重启”,其实**还是用的旧内存对象**。

Clerk替代之Logto

logto.io的文档丰富,支持next和expo。 看似非常国际化,但问题是“社交连接器”中没有国内的微信/支付宝,只有apple/google/discard,太国际化了点?哦不对它叫做WeChat和Alipay😅。

试用发现配置很简单,但永远报错redirect url不对,查不出原因,放弃。

Clerk替代之Authing

看着也不错。

不替代了就用Clerk

国内打开其网页和面板确实慢,不管了。 根据文档,简单配置了,确实非常成熟简单。

面板中有development和production模式,要启用后者,除了vercel部署和域名解析好之外,还要配置5-6个Clerk自己的域名。 我的应用域名定在iching.suhe.space,选择了Secondary模式。

过程中问了AI:

如果我的各个子应用都想用clerk作为用户登录管理,形式都是XX.suhe.space,同时suhe.space也是一个应用呢?

回答:

你的情况是:

- 你有主域名 `suhe.space` 对应一个主应用。

- 还有多个子应用,例如 `app1.suhe.space`、`app2.suhe.space`,都想用 Clerk 统一管理用户登录。


这种情况下在 Clerk 的设置里一般建议这样处理:

---

### 1. 主应用(Primary)

- 绑定 **`suhe.space`**,选择 **Primary application**。

- Clerk 会把这个域名作为主应用的默认域名,管理用户、会话、邮箱验证等。


### 2. 子应用(Secondary)

- 每个子应用绑定自己的子域名,比如:

- `app1.suhe.space` → Secondary application

- `app2.suhe.space` → Secondary application

- 子应用依赖主应用的用户数据库,但可以独立处理前端登录/注册。

- Clerk 会自动共享登录状态(如果你配置了 cookie 跨子域)。


---

### 3. Cookie / 跨子域登录配置

为了实现用户在子域间共享登录状态,需要在 Clerk 配置里设置:

- **Cookie domain** = `.suhe.space`(注意前面有点号)

- 这样登录状态可以跨子域共享。


---

✅ 总结:

- `suhe.space` → Primary

- `*.suhe.space` 子域 → Secondary

- 配合 cookie 域配置,就能实现多子应用统一登录管理。

nextjs和expo能共用多语言json文件吗

我一直是这么认为的,而且在近期的项目中也使用了。 直到发现二者模板占位符语法完全不同

环境插值语法示例
next-intl / FormatJS (ICU){name}"Hello {name}"
i18next / react-i18next / Expo 常用库{{name}}"Hello {{name}}"
我现在是expo的标准,但发现next还没法通过配置来调整。
看来以后只能反过来了。

更理想 的方向 —— 如果可以让 Expo 端也兼容 {} 单花括号 (ICU),那就不用在 Web 端做任何 hack 或转换,直接一份 JSON 到处用。

Expo(React Native)可以支持 {} 单括号语法
只要你用的是 i18nextreact-i18next(绝大多数 Expo 项目都是这样),
你只需要 调整 interpolation 配置 即可。

interpolation: {
prefix: '{',
suffix: '}',
}

其他

第一次自己交医保

本来经过前段时间的咨询,啥都准备好了。 结果打开云南税务局的小程序,还是蒙圈了。 UI逻辑反人类。 干脆去官渡区政务中心问问,结果发现停车很难,或者用10块买断一天的场地。 3、4楼都是来办医保社保的,比当年房管局还火爆。 其中一个小伙子坐镇中间,大妈们排队让他操作这个小程序,这哥们不会就是开发方代表吧~ 我实在没机会问他,在旁边自己摸索一下,结果搞定了,然后走人。 去官渡区图书馆坐了一下,没有五华区感觉好(当然跟成都比都是渣渣)。 最近似乎新流感来了,我感觉有点不适,几年来第一次戴上口罩。

游牧周记第48期

· 阅读需 6 分钟
Suhe
This site owner

标签 Tags

陈志

影视

近期没啥好的剧...电影就看了一站再战,科恩兄弟加昆汀的即视感,但内容节奏确实不错,2h44m一点不觉得拖沓,全程精神十足(我可是看漫威都要睡着的人)。创作者似乎讽刺了所有方,其实没有明说自己的观点,现在要表态好难。

关注

硅谷101,以前听过他们的博客,但真的没有视频效果好。内容选题制作都很棒,算是中文同类频道的高水平。因为有很多嘉宾会话、现场采访,内容也需要图表啥的来配合,所以不适合纯音频。 (以前blog推荐过...不知为啥algolia搜不出来。)

思考

自学转码可行吗

某人的视频推荐:https://csdiy.wiki/。 其实是一本电子书,markdown做的网站,我看了它的技术是用Material for MkDocs, python的,算了。 项目在gitHub,我觉得可以当一个贡献者。 他最近的提法似乎和他之前提倡转码有点矛盾,因为不是科班出身始终在求职市场和行业中低人一等,这点没法回避甚至被强调,其实是非常实际的,经过的都懂。 我当年因为稀缺才进入这个行业,后来招人确实也以能力为准,后来人多了,想法也就有点变了。 但我深信这个行业是少数的非科班可以做得很好的领域,因为现在科班也学不到啥真本事(懂得都懂),主要是弯路走得少,以及意识和氛围带来的加成和优势。

有点像看麻瓜进入魔法行业的感觉,虽然也有赫敏这样的努力型人才。

颠覆导游解说行业的想法

方向是对的,但时机错过了

智能景点解说app都写到一半了,才想去来去看看微信小程序,人家不但成熟而且免费。 这件事给我几个启示:

  • 自己的体验未必靠谱,出去玩了2年,都没想过看看现成产品和服务?
  • 这么多产品了,说明这不是一个伪需求。
  • AI的市场分析有时很不靠谱,覆盖面很窄。
  • 不要只关注App,我总是忘了国内环境要看微信小程序。
  • 巅峰一个行业的想法,方向是对的,格局也是对的,这样才能让人有持续目标和动力。
  • 自己也想用才做,这点至少在动力和产品研究层面是对的。 不管怎么说,我写了一周代码的这个玩意,先停下来。 至少其中的ai和树形结构部分是可以复用的,还不错。

开发

松耦合多项目共享资源的前端开发

我想同步开发“学易”的网站(nextjs)和app(expo),其中有很多共享资源。 目标是尽量降低耦合度,各自开发特性和流程不变,极其灵活。 同时大量的资源如多语言,style常数,环境变量等共享,并同步更新。 现在拿下一个版本的学易来做实验。 新建一个目录yi,在其下建立一个目录share,放locale,style,assets等资源。 在根目录下执行expo和next的新建项目命令。 开发时,还是进入yi目录,打开ide。 但测试运行时,要cd进入具体的app目录,执行pnpm dev等。 vercel的部署就不靠github了,免得修改mobile app时被误触发,这次使用vercel cli。

后来我放弃了。

新的策略:

  1. npmjs包,包含多语言,常量,utils。
  2. 大量的json数据改用云数据库。

nextjs的双层缓存技巧

目标是既要控制内部调用量,又要限制对外(supabase)调用频率。 一句话就是限制网站内外部对网站api的调用频率。

  1. 内部调用api缓存
try {
const response = await fetch(`${PUBLIC_ENV.BASE_URL}/api/hg/by-hgi/${hgi}?lang=${lang}`, {

next: {
revalidate: REVALIDATE_SECONDS // 一小时
}
});
// ...

但此措施仅限制了网站UI发起的访问频率。 如果有人(包括app)直接调用api呢。

  1. api route的缓存设计 route.ts文件中加入两行。

// 缓存,Next.js 15 自带「段级缓存」——给路由加一条导出即可

export const dynamic = 'force-static';
export const revalidate = 86400; // 一天

就可以实现了,太神奇。

  • 第 1 次访问:走 Supabase,结果自动被 Next.js 缓存到 内存 + CDN(Vercel)
  • 缓存时间内再访问:直接返回缓存,0 数据库查询
  • 缓存时间后:后台静默重新拉取,用户无感知(stale-while-revalidate)。 这是目前 Next.js 里最零成本的接口缓存方案。

整体看来,缓存生效逻辑:

  1. 首次请求:
  • fetch缓存:未命中 → 调用API
  • API路由:未命中 → 查询Supabase
  • 结果:两层都存储缓存
  1. 1小时内的请求:
  • fetch缓存:命中 ✅ → 直接返回
  • API路由不会被调用
  1. 1小时后,1天内的请求:
  • fetch缓存:过期 → 调用API
  • API路由:命中 ✅ → 返回静态缓存(不查Supabase)
  1. 1天后的请求:
  • 两层缓存都过期 → 重新查询Supabase

nextjs的loading

page.tsx是服务端组件的话,没法在一堆await动作下设置state,并加入loading组件。 其实nextjs建议这样,在page.tsx同级目录加入loading.tsx页面,内容大体如下,就搞定了。


export default function Loading() {

return (

<div className="flex flex-col w-md animate-pulse">

<div className="h-8 w-20 bg-gray-200 rounded mb-4" />

<div className="h-6 w-32 bg-gray-200 rounded mb-2" />

<div className="space-y-2">

{Array.from({ length: 6 }).map((_, i) => (

<div key={i} className="h-10 bg-gray-200 rounded" />

))}

</div>

</div>

);

}

animate-pulse, 这个是骨架loading效果的关键。

游牧周记第47期

· 阅读需 12 分钟
Suhe
This site owner

标签

NeocloudCoreweave, Nebius

影视

流人 season 5

这一季还是那么好看,而且变成轻松喜剧了,毕竟上一季有点沉重,算是喘口气的作品。

开发

expo+RevenueCat的促销码问题

用RevenueCat一年半以上了,没想到过测试促销码吗? 我现在才发现这个问题是因为有老用户提出。 后来找遍网络都没人提及,官方似乎也很模糊(回去再看看docs)。 AI多数含混不清,只有kimi板上钉钉地说没有解决方案。 太奇葩了,这么牛的平台居然不支持。 其实兑换成功后,dashboard上确实看到了这笔交易,说明rc能正常读取的,只是app端如何解决似乎无办法? 以前我用的https://github.com/hyochan/react-native-iap也非常简单就搞定了啊,几乎无感的设计,于是我又去看了看,这个组件还在不断更新中,可能哪天我要回归?

附上kimi的回答,我现在非常欣赏它。

把 docs.revenuecat.com 全站翻了一遍,结论可以一句话先说完:
RevenueCat 2025 年依旧「看不到」任何通过 App Store 手动兑换的 Promo Code(一次性内购那 100 个码)。
官方只支持「订阅类 Offer Codes」,而它们跟 Promo Code 完全是两套东西。下面把散落在各文档/社区帖里的关键信息按话题汇总,给你一份「官方拼图」。


1. Promo Code(消费类)在 RevenueCat 里的真实表现

  • 交易会被 当成原价购买 写进 Charts & CustomerInfo,价格、take-home 比例都不变,没有任何字段标记它是 promo
  • 仪表盘里 offer_code = null 就表明 不是 Offer Code;Promo Code 不会出现在这里。
  • 苹果/Google 的 API 不会把「用了 promo code」这件事告诉 RevenueCat,所以 SDK、webhook、ETL 全程都 无法区分
  • 官方在 iOS Subscription Offers 文档里直接把「In-App Purchase Promo Codes」列为 ⚠️ Not recommended,理由:
    – 只能 100 个/6 个月
    – 不能商用
    – Revenue 不准
    – 不支持 presentCodeRedemptionSheet

2. 订阅侧 Offer Codes ≠ Promo Code

  • Offer Codes 必须先在 App Store Connect 建「订阅优惠」,再在 RevenueCat Dashboard 绑定同一个 Subscription Price ID
    消费类 IAP 没有 Price ID,走不通这套流程
  • 用户用 Offer Code 兑换后,RevenueCat 会把首笔交易记成 $0offer_code 字段会写进 CustomerInfo & webhook;续费按正常价格追踪——这些福利只对订阅生效

3. 社区反复出现的「我该怎么追踪 Promo Code 用户?」

  • 官方员工统一答复:
    追踪不了
    – 真想归因,只能自己维护「码-用户」映射,然后 Granted Entitlements 发奖。
  • 有人尝试用 App Store Connect 里看到的「Subscriber ID」去撞 RevenueCat original_app_user_id,发现 两边 ID 对不上,完全无法关联。

4. 官方给出的可行替代方案

方案是否支持消费类RevenueCat 能否识别备注
① 原生 Promo Code100/6 个月,Revenue 不准,不推荐
② Offer Codes❌(仅订阅)需订阅产品 + Price ID
③ Granted Entitlements自建码系统,调用 /promotional API 发奖
④ 后端直接解锁内容不走商店,不走 RevenueCat,需自记账

5. 结论与行动指南

  1. 如果你做的是一次性内购(金币、解锁关卡等),放弃用苹果原生 Promo Code 做归因——RevenueCat 2025 年依旧拿不到任何标志位。
  2. 想让 RevenueCat 图表、CustomerInfo、webhook 都能区分「促销用户」,只能自建兑换系统
    – 前端输入码 → 后端验证 → 调用 RevenueCat Granted Entitlements API 发权益;
    – 权益事件会带 store=PROMOTIONAL,方便后续分析。
  3. 若坚持要用原生 Promo Code,也请务必在后台 手动维护「已发码名单」,用户进 App 后通过 syncPurchases() 把交易拉下来,再靠自己映射发奖,RevenueCat 侧仍显示原价

再看perplexity的回答:

Revenurecat 2025最新版确实支持苹果的Promo Codes(优惠码)功能,特别是通过Apple的Offer Codes(优惠码)实现定制化的促销和试用。根据RevenueCat的官方文档,Promo Codes可以在App Store Connect中配置,用户可以通过这些优惠码获得折扣或试用期,支持在应用内直接兑换 。revenuecat

支持情况

  • 优惠码配置:可以在App Store Connect中预先创建,并通过RevenueCat的Customer Center自动展示和应用 。revenuecat+1

  • 兑换方式:用户可以通过苹果提供的兑换页面(包括在应用内弹出兑换页面或跳转到App Store)完成Promo Code的兑换 。revenuecat

  • 内购设计:建议在设计时,利用Apple的Offer Codes实现针对特定用户群或优惠的个性化促销,确保符合苹果的政策,并可以通过RevenueCat自动管理订阅状态。revenuecat+1

设计建议

  • 促销码的申请:在苹果商店后台通过App Store Connect配置优惠码(Offer Codes)。

  • 应用内兑换流程

    • 通过Purchases.shared.presentCodeRedemptionSheet()弹出兑换界面(建议在用户操作需要兑换时调用),

    • 或者跳转到苹果的兑换链接(https://apps.apple.com/redeem?)让用户在Safari或App Store应用中完成兑换。revenuecat

  • 配合RevenueCat自动匹配:兑换成功后,RevenueCat会自动检测到新交易,并同步用户的订阅状态。revenuecat

关于主要消费类内购

  • 如果是订阅类的内购,建议在设置时使用苹果的Offer Codes形成优惠策略,结合RevenueCat的自动订阅管理,确保优惠和促销体验流畅。

  • 设计过程中,应确保Promo Codes的创建、兑换流程符合苹果政策,避免因违规操作被拒 。revenuecat

总体而言,Revenurecat 2025支持苹果Promo Codes,设计上需在苹果后台配置优惠码,在App中引导用户兑换,利用RevenueCat自动同步订阅信息,实现无缝体验。


总而言之,revenuecat好好做它的订阅简化工作就行,普通的消费类产品,就用react-native-iap去玩。下一个版本我准备这样改。

Offer code 新尝试

react-native-iap(现在还是用expo-iap)专门处理消费类产品的offer code(对于iOS就是promo codes),其他的仍然用revenueCat。

现在发现一个现象,真机测试时,expo-iap的监听,要等约20秒才能获得充值完成信息。 如果此时报错“无法连接/重试”,多等等似乎还是会有监听到,但关闭CodeRedemptionSheet似乎就不行了。 如果不用内置CodeRedemptionSheet,在appStore充值等于打水漂(Gemini说是因为:App Store 的沙盒测试环境和真实的 App Store 是完全隔离的,有道理)。

testFlight也没有什么改善。

最后的方法是直接审批上线测试,发现一切似乎正常了,从兑换完成到监听成功,约有5秒延迟,但没有丢失的现象。

以上均指iOS,GooglePlay的促销代码至今没有用过,反正我有自己的代码销售体系(见之前creem.io的周记)。

Claude Code的国内用法

不只是修改配置,调用国内大模型。 还要在大模型中方便选择和切换。 于是有了开源项目:claude-code-router. (这个作者正在找寻杭州的的AI Agent相关开发工作。) 这个项目除了自己cli外,还有个内置的web ui。 但第一次要自己写config.json这一点有些折腾人,一个字也不能写错,文档不是太清楚。

到这里还没完,README中提到:

Now you can use models such as GLM-4.5Kimi-K2Qwen3-Coder-480B-A35B, and DeepSeek v3.1 for free through the iFlow Platform.
You can use the ccr ui command to directly import the iflow template in the UI. It’s worth noting that iFlow limits each user to a concurrency of 1, which means you’ll need to route background requests to other models.
If you’d like a better experience, you can try iFlow CLI.

于是引出了iFlow这个平台(及其cli工具),上面有很多大模型,好像都是免费,我原本准备用siliconFlow的,发现很多收费不便宜(而且都是流量计费,不比国外的便宜)。

为什么能这么做?我问了Kimi:

claude-code-router 是一个「零配置」反向代理 + 动态路由网关,让你把 Claude Code(只认 Anthropic 格式)无缝对接到 任意 OpenAI/Anthropic 兼容端点,并且支持:

  • 多模型负载均衡

  • 自动重试 / 降级

  • 免费额度聚合(核心卖点)

一句话:iflow 把「免费」当拉新手段,且机房资源自建,烧得起;对路由器作者来说,免费池越深,项目吸引力越大,所以文档里 80% 示例都以 iflow 为默认上游。

风险与注意

  1. 免费池随时可能缩紧
    iflow 用户协议留了一条「平台有权调整免费额度」,路由器作者已做「自动感知下线」,但仍建议关键业务留 1 个付费 key 做兜底。

  2. 数据合规
    iflow 目前未披露等保/SOC 报告,金融、医疗、政府项目慎用。

  3. 速率突发
    免费节点不保证 SLA,大促/新模型开放日容易出现 30-60 s 的排队,路由器会降级,但体感仍比直连慢。


结论速记

  • claude-code-router = 免费额度聚合器 + 智能降级网关

  • iflow.cn 之所以被力推,是因为「模型多、额度大、不限速、续命玩法多」

  • 个人/原型阶段,用路由器+iflow 基本可以实现「0 元跑 Claude Code」;生产环境务必加至少 1 个付费上游做兜底。

kimi所说的这些免费额度和原因等,我在iflow官网均为见到。 但确实也没找到额度限制和实际使用的收费情况,只有个token记录页面,对一个expo app老项目init一下生成CLAUDE.md就用了差不多60k tokens。

最后提一下,Silicon Flow官方也支持快速配置集成进Claude Code的,这里

供应商转换器(Transformers)

在配置上面的集成国产大模型过程中,遇到的概念。 大体如下:

在 CCR(claude-code-router)UI 里看到的「供应商转换器」其实就是一个请求/响应格式转换器
它的作用只有一句话:
把 Claude Code 发出的“Claude 原生格式”请求,转成目标厂商能识别的格式;再把厂商回包转成 Claude 能解析的格式。


  • Claude Code 本体只认 Anthropic 的 /v1/messages 接口格式(message create 那套)。

  • 市面上绝大多数中转站或国内平台只提供 OpenAI 格式/v1/chat/completions)或 Gemini 原生格式/v1beta/models/…)。

  • 如果不做转换,直接转发就会 404 或报 “model not found”。

因此 CCR 内置了 3 种现成的「供应商转换器」:

  1. Anthropic
    几乎不改包,仅替换 key、base_url,适用于官方 Claude 或已经“原生兼容 CC”的渠道。

  2. OpenRouter / OpenAI
    claude 的 message create 请求转成 OpenAI 的 chat completions 格式;回包再把 choices[0].message.content 映射回 content 数组。

  3. Gemini
    把请求转成 Gemini 的 generateContent 格式,并处理候选回复、安全过滤等字段。


在 UI 里怎么选

  • 如果你的地址以 /v1/chat/completions 结尾 → 选 OpenRouter(或 OpenAI)。

  • 如果以 /v1beta/models/ 结尾 → 选 Gemini

  • 如果平台声明“已支持 Claude Code 原生接口” → 选 Anthropic

我遇到的全是OpenAI格式。

哪些模型支持webSearch

CCR的配置中会有专门webSearch设置,但哪些模型是支持的呢? 在Silicon Flow中有时提到网页浏览、搜索等字样,但不清晰。 kimi这样回答的,但我现在非常不信任它:

截至 2025-10 月,官方自带“联网搜索(webSearch)”能力、且国内开发者可直接调用的模型只有下面几类,其余都需要自己在网关层再包一层 Bing/Google 搜索 API 才能算“支持”。

  1. OpenAI 官方

    • gpt-4.1

    • gpt-4o
      其余(o3 / o3-mini / o4-mini 等)均不支持 web-search 工具,会直接报错 Hosted tool 'web_search_preview' is not supported

  2. Google Gemini 系列

    • gemini-2.5-pro

    • gemini-2.5-flash
      在 CCR 里只要把 webSearch 路由指到这两个模型即可生效,官方已内置实时搜索插件

  3. 百度智能云千帆

    • ERNIE-X1-Turbo-32K
      通过入参 web_search=true 开启联网,搜索源为百度自家索引
  4. 智谱 GLM-4-Flash-250414
    官方文档写明“实时网页检索”能力,且模型本身免费,但搜索端有 QPS/额度限制

  5. Ollama 生态
    0.4.x 版本后提供统一的 Web-Search API,凡是跑在 Ollama 上的模型(qwen2.5、llama3.3、gemma2 等)只要上下文够长(≥32 k)都可“一键”获得联网能力;不过搜索服务需要自己在后台配 Bing/Google Key


一句话总结
真正“原生”就带联网搜索、且国内直接能调用的:
gpt-4.1 / gpt-4o、gemini-2.5-pro/flash、ERNIE-X1-Turbo、GLM-4-Flash-250414,以及 Ollama 0.4+ 上的所有模型(需自配搜索 Key)。
除此之外的“国产大模型”要想联网,都得自己再包一层搜索 API。

游牧周记第46期

· 阅读需 6 分钟
Suhe
This site owner

标签Tags

F1美国站

开发

学易app参加评奖

Expo App Awards 又有一个新的app点子,去expo网站才发现在搞评奖,就报名一下。

creem付款开发小记

没法用stripe,就只能找creem了。 开发挺简单,就是文档太差。

live/test模式的设计不好好搞清楚,还有点容易乱。

首先是虚拟物品购买(非订阅)

跳转结果页面

做充值码ok。

前端主动查询

app用,没法子,只能这样,而且手动。

webhook

可惜不能调用app内部链接。 不然怎么要用前面的前端自主查询。

Jwt的库 jose

GPT推荐。

next/router还是next/navigation

AI回答: 在 Next.js 13+(App Router) 下,正确的是:

import { useRouter } from 'next/navigation'

原因:

  1. next/routerPages Router(旧版路由)的 hook,只在 pages/ 目录下使用。

  2. next/navigationApp Router 的新 hook,适用于 app/ 目录的 'use client' 组件。

  3. 两者 API 有差异:

    • next/navigationuseRouter() 只有少量方法:push(), replace(), refresh(), back()

    • next/router 提供更多事件和状态,但不适合 App Router。

结论

  • 你的项目是基于 App Router(app 目录),应该使用:
import { useRouter } from 'next/navigation'
  • 如果你在 pages 目录下开发旧式页面,才用 next/router

localStorage和cookie的选择

目前涉及多个网站共用user登录中心的问题,差不多就是SSO。 AI分析大概这样:

策略存储位置子域可否访问安全性推荐程度
localStorage 保存 JWT❌ 不共享⚠️ 可被 JS 访问,容易被 XSS 攻击不推荐跨域用
cookie(HttpOnly + Secure✅ 可通过设置 Domain=.suheapp.org 共享✅ 高✅ 推荐
URL 参数传递 JWT(?token=xxx临时共享⚠️ 容易被泄露⚠️ 仅限回调临时用途
所以cookie是最好的?

nextjs的奇葩问题

代码如下:

'use client'



import { useEffect, useState } from 'react'

import { PUBLIC_ENV } from '@/lib/env/env.client'



export default function HomePage() {

const [user, setUser] = useState<{ sub: string; email?: string } | null>(null)

console.log('app user ---', user)

const [loading, setLoading] = useState(true)



useEffect(() => {

const fetchUser = async () => {

try {

const res = await fetch('/api/verify-jwt')

console.log('res ---', res)

const data = await res.json()

if (res.ok && data) setUser(data)

} catch (err) {

console.error(err)

} finally {

setLoading(false)

}

}



fetchUser()

}, [])



if (loading) return <div className="p-4 text-center">Loading...</div>



return (

<div className="p-4 text-center">

{user ? (

<>

<h1 className="text-2xl font-bold mb-4">Welcome, {user.email || user.sub}</h1>

<a

href={`${PUBLIC_ENV.USER_WEB_URL}/sign-out?redirect_url=${encodeURIComponent(window.location.origin)}`}

className="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700"

>

Sign Out

</a>

</>

) : (

<a

href={`${PUBLIC_ENV.USER_WEB_URL}/sign-in?redirect_url=${encodeURIComponent(window.location.origin)}`}

className="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700"

>

Sign In

</a>

)}

</div>

)

}

很简单的一个useEffect渲染。 然而console.log('res ---', res)永远不执行,console.log('app user ---', user)永远是null,但实际UI上已经显示获得的user信息,为啥?

ChatGPT发现了它的强项,解释得很精彩,但也很令人困惑:

非常棒的观察 👍
这其实是一个 React + Next.js hydration(同构渲染) 的“错觉问题”,不是你的逻辑错。
下面我来帮你完整解释发生了什么 👇


🧠 现象总结

  • 页面 UI ✅ 能正确显示出用户信息(说明 setUser() 确实生效了)。

  • 但你在控制台看到:

    • console.log('res ---', res) 没出现;

    • console.log('app user ---', user) 永远是 null

    • ✅ 但渲染结果里 user 有值!

听起来很矛盾,对吧?其实完全合理。


🧩 真相:Hydration(服务端渲染 + 客户端挂载)

在 Next.js 的 "use client" 页面中:

  1. 初始渲染

    • Next.js 先在服务器(Node.js)端执行一遍组件(SSR),生成静态 HTML。

    • 这时 React 并没有运行 useEffect(),因为它只在浏览器端运行。
      所以:

      console.log('app user ---', user)

      在 SSR 阶段输出一次(user 还是 null)。

  2. 客户端挂载阶段(Hydration)

    • 浏览器端 React 接管 HTML(Hydration),

    • 再执行 useEffect()

    • 这时发出 fetch 请求并调用 setUser(data)

  3. 问题是:

    • 控制台的 console.log('app user ---', user) 是在初次渲染阶段打印的(只执行一次);

    • setUser 会触发组件重新渲染,但不会重新执行 effect 或之前的 console.log

    • 所以控制台仍然显示那行 user: null,但 React 实际状态早已更新。


✅ 验证方法

把日志加在渲染体里,而不是外层:

return (
<div className="p-4 text-center">
{console.log('render user:', user)}
{user ? (
<h1>Welcome, {user.sub}</h1>
) : (
<div>Please sign in</div>
)}
</div>
)

这次你会看到:

render user: null
render user: { sub: 'abc123' }

说明状态确实更新,UI 正常响应,只是之前的 console.log 没触发重新打印。


✅ 总结一句话

你看到的“console.log 不执行 / user 永远是 null”,
其实是因为 React 的渲染分两阶段(SSR + Hydration),日志只打印在初次渲染阶段。
UI 已经拿到正确的 state 并重新渲染,这是 React 正常行为。


好久没做网站了,受不了啊!

nextjs图片资源放哪里问题

研究一个官方案例:https://github.com/vercel/next.js/tree/canary/examples/with-supabase 发现favicon.ico, og-image.png等图片都在app目录下,我问kimi为啥不放在public中,它的回答:

现状

  • app/icon.ico app/opengraph-image.png 直接放 app/ 目录——这是 Next.js 13 新约定
    放在 app 根或任意段里的 icon / opengraph-image / twitter-image 会被 自动优化为响应式、带哈希的文件,并输出到 .next/static/media/无需手写 <link rel="icon">支持暗色/亮色双图标

  • 普通装饰性 PNG/JPG(按钮背景、装饰图)目前仓库里几乎没有;如果出现,仍推荐丢进 public/,然后 /images/xx.png 引用,保持缓存与哈希策略简单。

规则速记

文件类型放哪里原因
favicon.ico / apple-icon.png / opengraph-image.pngapp/ 根或同级段自动优化、自动生成 <link>、支持双主题
页面里手写的 <img src="..."> 装饰图public/images/xx.png不走构建优化,纯静态,CDN 缓存友好
组件里 import Logo from './logo.png'与组件同级Webpack 会加哈希,缓存击穿风险低

结论:示例把“品牌图标/OG 图”放 app/故意用新特性,不是错误;其他纯装饰静态资源仍应归 public

游牧周记第45期

· 阅读需 6 分钟
Suhe
This site owner

创业

海外收款有个人新渠道

在一个微信公众号看到的。 creem.io 据说是:

目前支持个人注册的海外支付工具只有 Creem

据说可以用支付宝收款,注意不是用户支付宝付款哈,不然爽了(那就不是为了出海,而是可以兼顾国内用户,替代stripe了那是)。

准备花点时间把yi.suhe.space的支付和用户功能集成起来(和app结合?怕审核不过,那就只有开发网页版的了...)。

payout账号的审核,网上说有点看运气,也比较容易踩坑,要事先把网站啥的准备到位,我直接试了申请,第二天成功过审,12小时左右吧,也有人说几分钟就可以的,但是如果不过,可能要等一个月。

截至周五,第一个虚拟商品,学易app的卦金正式测试通过,集成安卓app上线了。

日常

该自己缴医保了

之前的周记说过这个事,现在终于要自己缴纳了。 离职至今2年了。 我的医保(安宁)已经处于暂停参保状态(小程序查的) 电话和安宁医保确认,失业保险金停了。 然后联系官渡医保局,只需要20日前“云南医保”小程序办理灵活就业登记,下个月开始在“电子税务局”小程序缴费即可(云南医保小程序也有入口)。 选择单建统筹(之前提过)。 然后提交就没反应了,又打电话发现提出去了,等人工审核。 之后要求每月1-10日内缴费(不能多缴,可以选择方式),无提醒,必须自己记住。

目前的阳台种菜

昆明连续阴雨降温,周五终于出了太阳。 发现最重要的是光照问题,其次是土壤肥力。 冰菜为例,长得最好的是外阳台,但不怎么晒的部位。 泰国花叶柠檬开花了,买的时候就有好多小果子。 生菜和芝麻菜盆移到室内阳台,但还是光相对好的地方。 这些老是倒伏的我赚到书房阳台(暴晒)放一段时间看能掰回来不。

开发

axios的proxy问题

提交creem请求,有时会出现404错误,BAD REQUEST啥的,很难查出,后来发现似乎和客户端有vpn proxy有关,果然关掉就好了。

AI介绍说axios可以绕过proxy,只需要参数配置如下:

xios.post({
url: 'http://example.com',
proxy: false // 关键:禁用 axios 的代理支持,强制直连(仅在 Node 中有效)
});

但原则上“没有用。浏览器受其自身代理设置限制,JS 无法绕过。”,搞不明白了。 我在测试似乎有效,当然只是在服务端,可能没有浏览器限制的问题。

关于ngrok反向代理服务

creem.io的webhook测试过程中提到。我请ChatGPT介绍一下:


🧩 什么是 ngrok

ngrok 是一个广泛使用的 反向代理与内网穿透服务,可将本地运行的服务(如本地开发的 Web 应用、API、Webhook 接口等)通过一个安全的公网 URL 暴露到互联网。它常用于开发、测试、演示或临时部署场景中。


⚙️ 工作原理

ngrok 的核心原理是 反向代理隧道(reverse proxy tunnel)

  1. 开发者在本地运行一个 ngrok 客户端(ngrok 命令行工具)。

  2. 客户端与 ngrok 云端服务器建立一条 加密的持久连接(TLS隧道)

  3. ngrok 云端服务器分配一个公网访问地址(如 https://abcd1234.ngrok.io)。

  4. 当外部用户访问该地址时,请求会通过隧道转发到本地运行的服务端口(如 localhost:8080)。

这样就实现了从公网到本地的安全访问,而无需修改防火墙或路由器设置。


🚀 基本用法

安装 ngrok 后,可以通过简单命令启动隧道,例如:

ngrok http 8080

执行后,ngrok 会显示一个公网 URL,如:

Forwarding  https://abcd1234.ngrok.io -> http://localhost:8080

你可以直接通过这个地址访问本地的 Web 应用。

常见应用场景包括:

  • 调试第三方 Webhook(如微信、GitHub、Stripe 回调)

  • 本地项目演示或测试

  • 临时搭建小型网站或API服务

  • 远程访问本地管理面板或数据库界面(需谨慎安全配置)


☁️ 支持平台

ngrok 支持多种平台,包括:

  • 操作系统:Windows、macOS、Linux

  • 容器与云环境:Docker、Kubernetes、AWS、GCP 等

  • 编程接口:提供 REST API、Web 控制台以及可编程 SDK

此外,官方提供 ngrok agentngrok cloud edge 等企业级组件,可用于更复杂的部署与集成。


💰 收费与版本

ngrok 提供 免费版与付费版

版本特点
免费版随机生成的临时子域名,支持基本 HTTP/HTTPS 隧道
Pro 版可自定义子域名(如 myapp.ngrok.io),支持 TCP 隧道、更多并发连接
Business / Enterprise支持自有域名、访问控制、流量分析、团队协作与 SLA 保证

付费计划一般按月或按年订阅,可在官网 https://ngrok.com 查询最新价格与功能详情。


🔐 总结

ngrok 是一个高效、安全、跨平台的反向代理工具,通过加密隧道让开发者能快速将本地服务暴露到公网。它大大简化了测试与集成过程,是现代 Web 开发与云原型设计中不可或缺的辅助工具。


creem的开发问题

文档只有英文,且不易读,资料不全似的。 提供了一个案例,半年没更新,算是给点文档补充吧。 目前仍然不支持多语言。 提供了SDK,我之前没看到,因为文档案例中多是axios,我还专门安装了axios。 测试过程还是有些疑惑,在dashboard可以设置成test mode,但不知会不会同时影响正式系统。 也不知道在正式mode(live)时,test的东西还可以测试不? 还需要继续研究。 下一步准备专门做一个web store项目。

游牧周记第44期

· 阅读需 2 分钟
Suhe
This site owner

标签

sora2

k签证

AI

Sora 2

这玩意号称会颠覆tiktok,我的ios账号是美区,可找到安装,然后vpn地址必须调整到非hk ip,接下来就是邀请码,找了好久,最后加入一个微信群,终于有可以用的了。 做了第一个视频:猫哥谈福报。 邀请码:VEMCRE

智谱的Coding模型

这几天都在说高端大气的Claude 4.5,但同时跟随另一个号称差不了多少的智谱# GLM-4.6,有一个DeepSeek? 模型是开源(当然普通电脑就算了),网页端免费,api收费但便宜。 我看了一下开发方是:北京智谱华章科技股份有限公司

关注

人民公园说AI

这帮家伙有海外华人或国内从业者,虽然没有请过任何重量级嘉宾,谈话风格也不够高大上,但当背景听还是挺放松的,抓热点很快,有热情,算是言之有物吧。 youtube

硅谷101

一个高品质的频道,内容当然就是美国财经创新领域的东西,女主落落大方,个人感觉比已经解决破音问题的小Lin好多了。 探讨较深,有很多嘉宾的参与,有时候较长,他们也有播客。 youtube

兴趣

F1直播哪里看

前几天突然决定培养一下F1赛车的兴趣,新加坡已经开赛,接下来阿布扎比,我该找个直播来源。 结果发现国内很难。 首先CCTV 5 (+)只播正式赛,且不稳定(也就是随时可能被其他比赛和内容替代)。 据说广东的五星体育和上海那边有些本地台会转播,但没上星,其他地方看不到。 然后是收费的腾讯体育等,我也没法在新买的电视上看,毕竟只接了机顶盒和apple TV。 当然F1 app等也要注册收费。 后来发现youtube有直播,👌,但是没有中文解说的(英文、日文都有),算是一个小缺憾吧。

游牧周记第43期

· 阅读需 5 分钟
Suhe
This site owner

日常

阳台种植

新买了一些东西:

  • 2颗来自德宏的树莓苗,一颗黑色一颗红色,但只是苗,我分不出,分别用两个盆栽上,看来品质还行。
  • 一袋(10斤)云南森林腐叶土,纯黑色,希望可以弥补有些植物酸性不足和肥少的问题(以前买的营养土似乎太寡淡了...)。
  • 苦楝油和皂液,混装加水后用,但最近似乎没发现红蜘蛛和虫害了。

Diablo IV s10

最近这个游戏打得有点频繁了,s9一直玩亡灵,但没啥突破,感觉冲层无力,最大的成就是最后几天得到一个4星的堕狱头盔,但赛季也结束了。 9月24日,s10上线,当天又以熟悉的亡灵试玩,一口气冲到55。现在大家都说新赛季德鲁伊最好玩,于是第二天改小德,2天打到凌晨,60(153)级,早中期的拍拍装配齐,感觉攻击力确实可以,但会暴毙,最大的问题是我们玩惯了纯招单手逛街的人,觉得有点腰酸背痛了,等凑齐装备,下次改渡鸦流派,看会否轻松点。

出行

高铁去老挝计划

原计划国庆假期后触发,后来又有点急想再提早两天。 办签证有2个方法,在网上电子签或到昆明的领事馆办。

领事馆据说最近热门到要排队,要实现拍摄合适的2寸白底照片,带上身份证等一堆证件,估计300多块钱/人手续费。 那是不是电子签好点呢?我开始也这么想,于是上了https://laoevisa.gov.la,填写了一些内容,上传以前的证件照和护照照片,每人收费36usd(看似便宜很多),正要提交,突然想起一个问题:如果填写有误,可以修改吗,会不会退钱?没地方问就问了GPT,结果是否的,听说照片要求6个月以内拍的,一些文字细节也不能出错,我真的担心可能过不了,那就打水飘了。 考虑良久,还是下楼找个小店拍照了,多年前去泰国也是这家拍的签证照。 原计划第二天就去领事馆,后来突然想改晚点去(比如11月),主要还是因为今年出游较多,有点倦怠了,琅勃拉邦一个地方对我的吸引力有限(真有趣的反而是高铁),旅行目标还不明确,就再等等吧。

开发

Unistyles的theme切换太懒了怎么办

上周解决了整体theme切换的问题,这周发现复杂点的app,部分页面和组件不更新,特别是tabs中和list中,ChatGPT给了个直接又简单的方案。如下:

问题其实是 react-native-unistyles v3+ 在 Expo SDK 54 里切换主题时,部分组件没有重新刷新。
这是因为 UnistylesRuntime.setTheme() 并不会强制触发整个 React 树的 re-render,尤其是被缓存的 screen / VirtualizedList item。


1. 为什么会发生

  • Tabs 场景:React Navigation 默认会缓存 tab screen,不会因为 theme 改变而自动 unmount/remount。

  • FlatList 场景:子项渲染由 renderItem 控制,如果 item 的 key 和 props 没变,React 不会 re-render。

  • UnistylesRuntime 的限制:调用 setTheme 实际只是更新内部 runtime theme,只有订阅 hook(useStyles, useTheme)的地方才会触发 re-render。


2. 常见坑

  1. 你用了 UnistylesRuntime.setAdaptiveThemes(true/false),但如果之前的组件挂载时拿到的 theme 是“老的”,它不会自动更新。

  2. React Navigation tab 屏幕缓存(默认 lazyunmountOnBlur: false),不会重新构建。

  3. FlatList 的 item 如果不依赖 theme 作为 extraData,不会刷新。


3. 解决方案

✅ 方法 A:确保 theme 改变能触发 re-render

const theme = useTheme(); // Unistyles hook

<FlatList
data={data}
renderItem={renderItem}
extraData={theme.name} // 强制当 theme.name 变化时刷新
/>

✅ 方法 B:给 Navigation 强制刷新

React Navigation 可以利用 key 来强制重新挂载:

<Tab.Navigator
key={theme.name} // 当 theme 变化时,强制整个 Tab.Navigator 重建
>
...
</Tab.Navigator>

或者在 _layout.tsx 根节点:

const theme = useTheme();

return (
<Stack key={theme.name}>
{/* theme 改变时整个 Stack 重新渲染 */}
</Stack>
);

✅ 方法 C:避免滥用 setAdaptiveThemes

  • 如果只想手动切换主题,用 UnistylesRuntime.setTheme(...) 就够了,不需要先关再开 adaptive。

  • adaptive 主要给 系统 theme 跟随 用。


4. 推荐写法(_layout.tsx)

useEffect(() => {
devLog('[Root Layout] new theme setting:', settingTheme)

if (settingTheme === 'system') {
UnistylesRuntime.setAdaptiveThemes(true)
} else {
UnistylesRuntime.setAdaptiveThemes(false)
UnistylesRuntime.setTheme(settingTheme as keyof UnistylesThemes)
}
}, [settingTheme])

然后在 Navigation 根加:

const theme = useTheme();

return (
<Stack key={theme.name}>
{/* screens */}
</Stack>
)

👉 总结:

  • FlatList 要用 extraData={theme}

  • Navigation 要用 key={theme.name} 来强制刷新缓存的 screen。

  • 不要频繁调用 setAdaptiveThemes,只在系统跟随模式时启用。


巧妙使用key参数,高! 控制不用太细,在根_layout.tsx或相应的总体导航组件加入即可。 我个人在(tabs)_layout.tsx加入。

skia组件在expo SDK 54后无法响应

升级expo到SDK 54后,发现skia绘制的组件,在外包裹上pressable或touchable,均无法响应点按。ChatGPT没有找到佐证材料,自己编了些理由,但居然得到了正确的方法。

它这样说的:

这个问题其实不是你代码本身的锅,而是 **Expo SDK 54 升级后 react-native-skia的事件穿透机制变了**。简单说就是: Skia 画布默认会「吃掉」触摸事件,导致外层的Pressable/Touchable 都接收不到点击。

✅ 解决方案

1. 禁用 Canvas 的触摸拦截

Canvas 加上:

<Canvas style={{ flex: 1 }} pointerEvents="none"> {/* 绘制 */} </Canvas>

这样 Canvas 就不会「吃掉」点击事件,父级 Pressable 就能响应。

于是就ok了。