Skip to main content

游牧周记第38期

· 3 min read
Suhe
This site owner

关注

最近听(看)了不少哲学方面的内容,颇有种信息量巨大,需要整理提炼的冲动,我看来应该做笔记,写点啥了。

大问题

哲学。喉咙不清,可能有轻度炎症。 但内容极好,选题精彩,节奏紧凑。 B站

徒梦的学习笔记

哲学对话,非视频! 作为背景听太棒了,台湾的。以2人对话方式展开。 Bilibili 后来发现内容是从其他语音平台搬运的,仅B站就还有类似频道。 原频道还没有找。

# PowerfulJRE

没啥好说的,Joe Rogan的访谈记录, 2000万+订阅。 Youtube 用来练英语听力不错。 B站的思维黑洞Lab,只有2k多粉,收集了大量的这类谈话节目,也不错看。

我的视频

App快速开发上架挑战

7天能完成一个新App的开发和上架吗? 我的实际挑战纪录片B站

阳台种菜

昆明降雨大降温,结果冰菜长得最好。

开发

expo-router和unistyle的冲突

我第一次使用unistyle,这时已经是v3+了,网上和AI掌握的很多资料失效过时。

核心在于执行顺序,要保证最早执行unistyle的configure,只有这个办法了:

// app.json中
"main": "index.ts"

// 新建index.ts(根目录)
import "./src/styles/unistyles"; // 初始化 unistyles 必须最先执行
import "expo-router/entry"; // 启动 Expo Router

不然再怎么配置也没有用。 官网说明

App开发的这些边距尺寸概念

下面这张图能帮你一眼看懂它们各自「量」的是哪一块:

┌────────────────────────────────────────┐
│ Status Bar ← statusBar.height = 20 │
├────────────────────────────────────────┤
│ │
│ APP CONTENT │
│ │
├────────────────────────────────────────┤
│ Navigation Bar │
│ (虚拟三大键/手势指示条) │
│ ← navigationBar.height = 44 │
└────────────────────────────────────────┘
  • insets
    告诉你「内容区域」距离屏幕四周安全距离是多少。
    例如:

    insets = {
    top: 28, // 避开刘海 + Status Bar
    bottom: 40, // 避开 Navigation Bar
    left: 0,
    right: 0,
    ime: 0 // 键盘弹起时为键盘高度
    }

    所以:
    insets.top 通常 ≈ Status Bar 高度 + 刘海高度
    insets.bottom 通常 ≈ Navigation Bar 高度 + 底部圆角高度

  • statusBar
    只量系统状态栏本身(电量、信号、时间那一栏)的 绝对尺寸
    在 iOS 刘海屏和 Android 状态栏高度不同,但 不会 把刘海额外高度算进来。

  • navigationBar
    只量虚拟导航栏(三大键或手势提示条)的 绝对尺寸
    如果设备启用了全面屏手势,这个高度可能是 0。


一句话总结

名称描述
insets内容可安全放置的区域 ↔️ 你需要用它来做自适应布局
statusBar系统状态栏的精确尺寸
navigationBar虚拟导航栏的精确尺寸

布局时 优先用 insets,只有当你要精确知道状态栏或导航栏本身多高时才去看 statusBar / navigationBar

游牧周记第37期

· 11 min read
Suhe
This site owner

美剧

安道尔2

第一季就很好看,个人觉得是星战宇宙中超过曼达洛人的存在,其实也就这2部我看得下去。 这一季开局一般,到了后半截开始起飞,非常有后劲。

异形.地球

看了2集,蛮好的。 太空船和外星生物这次没有掉到纽约或西方主流大城市,轮到泰国了(新暹罗,应该指曼谷吧)。

星期三2

这调调还在。 说实话前段时间看了些演技极差的烂片后,发现这部片子不管怎么扯,所有演员的演技在线。

听小机 Listen Bot

没错,这就是我花了1周开发上架的App。 我的app网站 8月13日凌晨终于审核完成。 主体开发就2天,然后第三天进入IOS发布审核。 play市场1天完成。 appStore审核了4天,所以总计7天。

一周上架一个可以赚钱的app?

  1. idea酝酿时间不能算在内,其中最主要的不是核心功能思路,而是落实有没有成熟的同类产品。
  2. 从动手写第一个代码开始算起,这时候遇到第一个问题,包名怎么取?虽然是个英文名,甚至是简写而已,但影响到写代码过程中的思路和体验,最好基本确定,不要coding过程中一直想改,影响心情。
  3. 名称用了4个钟头才定下来,其中90%都是在开启上网功能的ChatGpt上查重和想点子,最后出门溜达一圈自己想出了一个,回家用AI落实没啥太成熟的同行应用,还被夸了一道,就定了。中英文一起。
  4. 这是一个和语音识别有关的应用,AI从我描述的核心功能出发,设计了技术栈总结,结果发现ChatGPT建议的好多是第三方包和服务,反而是国产Kimi推荐了一个靠设备自身识别功能的,非常令人兴奋。
  5. 这时已经是晚上,用新的包名在macbook上建了一个expo最新版(sdk都54+了),就累不动睡了。
  6. 第二天坐公交去五华区图书馆,从10点干到13点,终于把核心的语音识别功能调试成功,高高兴兴回家去,准备开着大屏幕接着写。
  7. 然后就是轻车熟路的AI LLM服务接入,唯一问题是什么时候切入,发现是个非常细节的问题,调试到自己一头雾水的时候,已经凌晨了,还是睡了。
  8. 已经算第三天了,想着继续降低一点体验感要求,就图个能尽快提交市场。首先简化了AI介入为手动,然后UI部分完全套用之前用熟了的自创useStyle框架(各位看我教程都明白),快速搞了个基本能用的版本。
  9. 这时候想到必须今晚提交,那还得有logo(icon)啊,开启免费版ChatGPT,说了想法,结果生成一个还不错的小人,就是手有点怪,让他改,改更怪了,运气好在5张的今日免费额度用完前搞定了。
  10. 基本文案也用AI弄了,当然自己加工时间也不短,差不多耗了2个小时。中英日文版,没错,及时如此紧张的时间,我还是用了多语言。先写简体中文的json,然后在IDE中AI生成其他版本。顺便说一句,这次IDE用回了VSCode,Cursor把人整郁闷了这次放弃。总之AI没有花一分钱。
  11. 然后截图,准备上线。这时候才发现问题来了,Eas网络抽风,死活都没法在线打包,本地也卡得要死,有时试上十多次才能进入正常运行环节,等到地老天荒才能打成一个包。这个过程中也没闲着继续调试App功能,不断发现bug和可以优化的地方,同时请AI检查逻辑错误。就这么边调试边打包,都第n个build了,发现天已经黑了。
  12. 说明一下,现在还没试过任何android的命令,不是说要完全放弃安卓,而是以苹果为目标,抓住快速变现人群才是关键,iOS版上线成功,才有做其他市场的心情。
  13. 这天真累啊,RevenueCat的配置也快速搞定了,虽然只是iOS,这可是我最关心的功能,虽然做了无数次,还是有点担心它抽风。事实证明还好,集成得很快。
  14. App store上架资料准备期间,国内ICP备案的申请也在进行,这时候发现已经是周五凌晨,我理论上已经到第4天了。3天完成的计划被打破。ICP备案如果不加入安卓,以后要补充可能麻烦,于是在基本没调试过android版的情况下,先打包apk,这个漫长的调试啊,整完已经3点am了。累死,睡了。
  15. 第4天,基本都在折腾打包和上架提交,这期间解锁了GitHub Action对接Eas的新技能,打包效率大幅提升,直到Eas鸡贼的免费额度用完,不过iOS版也基本可用了,终于提交了AppStore,发现第一次提交不需要ICP备案也没问题,即使后面出了问题只影响国内市场发布,等等也无所谓。秉承来都来了的原则,把Google Play市场的资料也整了。第一次运行了android版,算是意外的顺利,这次开发过程中比较幸运的一件事,没花多少时间修改安卓UI就ok了。看来安卓要先上线,唯一的问题是我考虑安卓打包签名用本地文件,不想依赖云服务,于是也只能本地打包,那个速度不比eas的ios本地打包快多少。过程中不断发现bug和优化点,打了n个包以后提交测试,然后推进正式版,居然实现了android先上线。这时候又到子夜了,今天结束。
  16. 第5天周末,发现iOS还在待审核状态,ICP备案阿里云方面的工作和工信部那边完成了,管局还没开工,估计要等周一了。今天状态稍微放松点了,不断测试调试,iOS等审核不敢发新版,android连续提交2个版本,包括RevenueCat的集成,Google这边挺折腾的。今晚不用熬夜了,下午同学通知聚会吃菌子,然后就骑单车出门了,算是锻炼一把。
  17. 截至目前iOS还在待审核状态,我个人的经验证明3天上架不太可能,除非只是Google Play,加上iOS的话4-5天算是极限了。至于国内市场,本人可不敢想,作为个人开发者基本放弃了。整个过程中代码时间大约只占一半,Eas打包意外的烦人,大约浪费了我1/4的时间,还有1/4的时间是文案、图片和国内ICP备案等琐事,包括想名字等。

下次看是否出一个审核过程的要点说明。

Expo开发

一个新app从开发到上架,再次归纳

  1. ideas
  2. AI确认名称和技术栈
  3. coding和测试,包括GitHub的代码管理
  4. 集成RevenueCat,如果有其他第三方服务包括广告等,继续
  5. 真机测试,先集中力量搞iOS,毕竟是付费主力
  6. Eas打包测试,配置GitHub Actions触发打包,这一步也可以更靠前到3
  7. AppStore id和证书的申请
  8. AppStore新建App,AI辅助完善多语言文字、icon,搞点截图(注意大小,还有iPad13寸版本)
  9. 这时候如果有国内市场,就要考虑ICP认证了,这件事涉及到Android最好一起做
  10. 不管Android版本测试与否,先打包apk,为了ICP
  11. 阿里云去申请ICP备案,前面一些工作是为了提取信息
  12. 备案要几天,这时候继续提交AppStore,先testFlight,但也不要太细,差不多就提交
  13. 测试Android,差不多了就打包提交GooglePlay,先申请,截图和文字可以先用iOS的
  14. 第一次aab打包,之前建立新的keystore文件签名(下方有详细介绍)
  15. 提交GooglePlay,也是测试一下,发布正式版

新android项目的本地签名

云端的选择有两个,在GooglePlay或Eas服务中设置。

不想依赖云服务,所以最终选择了自己建立keystore文件。 这里发现以前项目的keystore不能复用,不然GooglePlay不批准,也就是说一个app要对应一个不同的签名。

macOs上这样做:

# 确认安装了keytool
keytool -version

# 如果没有,安装java,没错,这玩意是java自带的
brew install openjdk

然后生成keystore文件:

keytool -genkeypair \
-v \
-storetype JKS \
-keystore my-release-key.keystore \
-alias my-key-alias \
-keyalg RSA \
-keysize 2048 \
-validity 10000

参数含义
-keystorekeystore 文件名(会生成在当前目录)
-alias你给密钥取的别名(之后构建要用)
-keyalg加密算法(Android 推荐 RSA)
-keysize密钥长度(2048 够用)
-validity有效期(单位天,10000 天≈27 年)
执行上面命令后,会提示你输入:
  • keystore 密码(必须记住)
  • 姓名、组织、城市、省份、国家代码
  • key 密码(建议和 keystore 密码相同,方便记忆)

生成后可以验证一下:

keytool -list -v -keystore my-release-key.keystore

会提示输入密码,然后显示:

  • SHA1 / SHA256 证书指纹
  • 证书有效期

比较容易犯的错误之一是把alias搞错了,可以这样找回:

keytool -list -v -keystore path/to/my-release-key.keystore

然后你的expo项目中eas.json:

 
{
"build": {
"apk": {
"android": {
"buildType": "apk"
},
"credentialsSource": "local"
}
}
}

local就是指本地的加密配置文件: 建立 credentials.json(不要传到 Git,前面的key store文件也是)

{
"android": {
"keystore": {
"keystorePath": "./my-release-key.keystore",
"keystorePassword": "你的_keystore_密码",
"keyAlias": "my-key-alias",
"keyPassword": "你的_key_密码"
}
}
}

最后打包:

eas build --platform android --profile apk

tailwindcss v4+的theme怎么搞?

查遍全网,只有这个靠谱!AI全部懵了

tailwindcss中global.css的三个层次概念

我遇到的问题:

@layer base {
a {
text-decoration: none;
}
}

这段代码无效,但把内容从@layer base中提到最外层,就ok了。

AI解读原因:

Tailwind CSS 在编译时会@layer base 里的样式权重降到低于 base 层之外的普通 CSS,而浏览器对 <a> 的 UA 样式(text-decoration: underline)又属于「user-agent stylesheet」,权重很低,所以放在最外层能覆盖,放进 @layer base 反而被 UA 样式顶掉。

这里涉及一个Tailwind 的三层概念。 ──────────────

  1. base
    浏览器 UA 样式 + 你的 @layer base {} 代码 → 权重最低(0-0-0)。
    典型用途:重置 <a><h1> 等标签默认样式。

  2. components
    @layer components {} → 权重 0-0-1。
    典型用途:你写的 .btn.card 等工具类。

  3. utilities
    .text-red-500 等原子类 → 权重 0-1-0,足够高,不会被 base/components 顶掉

──────────────

为什么放在 @layer base 里有时“无效”?

• 浏览器 UA 对 <a>text-decoration: underline 权重也是 0-0-0,
但「后出现的规则」会覆盖「先出现的规则」。
• Tailwind 把 base 层放在 最前面,所以
base → UA → 你的最外层 CSS,最终 UA 样式反而在最后生效。
• 因此需要:

  • 把样式写在 Tailwind 之后(最外层),或

  • !important / :where() 提升/降低权重。

什么时候该用 @layer base

• 想整理所有「全局标签重置」到一处,而不是散落各处。
• 不介意权重 0-0-0,或愿意配合 !important / :where()
• 方便 Tailwind 在 PurgeCSS 时知道「这段代码属于 base 层,不要误删」。

简记
@layer base ≠ 高权重,只是一个「分组 + 排序」指令。

那最初的问题这样做,定义就生效了:

@layer base {
a {
text-decoration: none !important;
}
}

游牧周记第36期

· 8 min read
Suhe
This site owner

游戏

Diablo IV的卡死问题

2025之后,就卡顿极其严重,问了无数AI,查了无数资料均无效果。 后来还是Gemini 2.5Pro给出了稍微有用的答案。 去掉一个配置文件就好了。 LocalPrefs.txt,说是在Diablo IV的文档目录下,我建议搜一下。 其实就是彻底把游戏配置归零,然后进入游戏提醒重新配置,你也可以直接跳过。 然后真的就顺了很多,虽然和几年前刚刚安装时比有时候还有点卡。 原理就是作为多年老用户,遗留了很多老的配置在配置文件中,互相可能有冲突了。

Gemini越来越神了,会否成为真正的AI一哥?

expo开发

iOS app如何去掉Apple Pay功能设置

我注意到remove的是com.apple.developer.in-app-payments merchant.com.your.merchant.id 貌似内购,而不是apple pay啊


🧠 结论先讲:

com.apple.developer.in-app-payments 是 Apple Pay(实体支付),不是内购(IAP)!


✅ 区分这两个容易混淆的权限

能力名称键名(Key)功能用途说明
🛒 In-App Purchase (IAP)com.apple.InAppPurchase(隐含于系统)订阅、金币、虚拟物品App Store 管理,Xcode 和 profile 默认支持
💳 Apple Pay(实体支付)com.apple.developer.in-app-payments实体商品支付、Apple Wallet 支付需要配置商户 ID,如:merchant.com.xxx

🔍 你看到的这个字段:

<key>com.apple.developer.in-app-payments</key>
<array>
<string>merchant.com.your.merchant.id</string>
</array>

正是 Apple Pay 的专属字段,它告诉 Xcode:

这个 App 打算使用 Apple Pay,接入商户 merchant.com.your.merchant.id


❗️它不是 IAP(内购)

  • IAP 并不需要你手动加任何 entitlement

  • 只要你的 App ID 开启了 In-App Purchase 能力,并在 App Store Connect 配置商品,就可以正常使用;

  • Apple Pay 才需要这个 entitlement,并配合后台商户配置使用。


✅ 你应该怎么做?

  • ✅ 如果你只使用 IAP(订阅、金币),删除这个字段是完全正确的

  • ✅ 同时在 Apple Developer Portal → App ID → Capabilities 中关闭 Apple Pay;

  • ✅ 用 eas credentials 删除旧 profile,重新生成干净的 App Store profile;

  • ✅ 在 eas build 时加入 --clear-cache,清除旧的 entitlements 残留。


✅ 为什么这个字段出现在 prebuild 后?

Expo 的模板可能默认为支持 Apple Pay 的插件(如 expo-payments-stripe)预留了这个能力。如果你:

  • 用了 expo-dev-client

  • 或者启用了其他支付相关插件(即使没用 Apple Pay)

就有可能在 prebuild 时被注入。

suhe注:不认同这点,以前没出现过,原因还是不详。


✅ 最后确认(只使用 IAP 的最小需求)

项目是否需要 Apple Pay
expo-in-app-purchases❌ 不需要
react-native-iap❌ 不需要
App Store Connect 商品配置❌ 不需要 Apple Pay,仅需要开启 IAP 能力
merchant.com.xxx✅ 仅 Apple Pay 需要,IAP 完全不需要

GitHub结合Eas打包的工作流程

一个人开发项目,而且希望主分支 main 继续承担日常开发任务,同时只在需要 EAS 构建时创建 PR + 贴 label 触发远程打包。这种需求非常清晰,也非常实用,我们可以为你设计一套轻量、流畅、零冗余的 Git 使用流程,专为“单人开发 + Expo GitHub App 构建”场景优化。


✅ 你的目标简化版

需求实现方式
日常开发就在 main 分支上继续直接用 main 写代码,无需每次都建分支
需要远程构建时触发 PR + Expo 构建快速从 main 创建一个构建分支,发 PR,贴 Label,完成后自动删除

🧭 推荐工作流设计(为你量身定制)

✅ Step 1:日常开发保持在 main

你继续在 main 分支做日常开发、调试、测试,无需变动流程。

git checkout main
# 编辑代码...
git commit -am "修复组件显示 bug"
git push origin main

✅ Step 2:需要构建时,创建一个构建专用分支

你只在需要构建的时候,从当前的 main 快速拉个构建用分支(不做额外开发):

git checkout -b build/eas-android-20250803
git push origin build/eas-android-20250803

📌 命名建议:build/eas-平台-日期,比如 build/eas-android-20250803


✅ Step 3:GitHub 上发起 PR(无需修改任何代码)
  1. 打开 GitHub,会自动提示你:

    Compare & pull request
  2. 点击进入 PR 页面。

  3. 在右侧添加 Label,例如:

    eas-build-android:prod_android
  4. 不需要写代码 diff,不需要添加 Reviewer,你自己点就好。


✅ Step 4:Expo GitHub App 识别 Label 并构建
  • 构建过程会在 PR → Checks 里展示;

  • Expo Dashboard 会显示构建进度和下载链接;

  • 你可以继续用电脑干别的事,等它构建完成。


✅ Step 5:构建完成后,合并 PR 并清理分支

如果你希望主分支也保留这次构建记录:

# 在 GitHub PR 页面点击 “Merge Pull Request”

然后回到本地:

git checkout main
git pull origin main
git branch -d build/eas-android-20250803
git push origin --delete build/eas-android-20250803

也可以 不合并,直接关闭 PR 然后删除分支也没关系 —— 因为你本来就在 main 写代码,构建用 PR 只是工具人 😎。


✅ 自动化版本(可选 Shell 脚本)

如果你想一条命令就完成构建流程(不用记这么多命令),你可以用如下脚本:

#!/bin/bash

DATE=$(date +%Y%m%d)
BRANCH=build/eas-android-$DATE

# 确保当前在 main
git checkout main
git pull

# 创建构建分支并推送
git checkout -b $BRANCH
git push origin $BRANCH

echo ""
echo "✅ 分支 $BRANCH 已创建并推送"
echo "👉 请访问 GitHub 页面创建 PR 并添加 label: eas-build-android:prod_android"

保存为 eas-build.sh,加权限:

chmod +x eas-build.sh

然后以后只需运行:

./eas-build.sh

就自动完成创建分支 + 推送,剩下你只需在 GitHub 上贴个 label。


✅ 为什么这个流程适合你(一人开发者)?
特性优点
main 分支持续开发不被强迫换分支、不打断手感
构建过程隔离不会把构建 commit 和构建控制信息混进 main
构建结束即可删PR 不需要合并,临时存在就好
label 构建精准控制不用担心误触发、不用写复杂 CI 文件
GitHub 自动记录构建日志PR 页面和 Expo Dashboard 保留构建历史,可审计

🧩 可选优化项(建议长期使用时加上)
  • .github/PULL_REQUEST_TEMPLATE.md:自动提示自己加 label;

  • .gitignore:确保构建相关的缓存文件不被提交;

  • eas.json 里 profile 明确配置好平台和镜像,便于稳定复用。


✅ 总结

你可以用这个结构保持开发流畅、构建可控:

开发场景操作
正常写代码就在 main
想打包构建main 拉个 build/eas-xxx 分支 → 发 PR → 贴 label
构建完成Merge 或 Close PR,然后删掉构建分支
重复这个流程所有构建 PR 都是干净、独立、不会干扰你 main 的

Eas云端打包pnpm项目的问题

报错情况:

eas打包expo app项目报错:Running "pnpm install --frozen-lockfile" in /Users/expo/workingdir/build/ directory ERROR  packages field missing or empty For help, run: pnpm help install pnpm install --frozen-lockfile exited with non-zero code: 1

之前没有发现过,但近期出现,可能两个原因:

  1. pnpm/eas/expo版本更新;
  2. eas云端服务是gitHub Action触发的; 之前的一个项目遇到过,差点忘了,解决方法就是在pnpm-workspace.yaml中加入:
# 告诉 pnpm 在哪些文件夹里寻找项目
packages:
# 如果您的 Expo 项目就在根目录,并且没有其他包,可以这样写:
- '.'

App 切换到后台的Expo优雅处理方式

首先做一个通用勾子。

// useAppStatePaulseTask.tsx

import { useEffect, useRef } from 'react';
import { AppState, AppStateStatus } from 'react-native';

export function useAppStatePauseTask(onPause: () => void) {
const appState = useRef(AppState.currentState);

useEffect(() => {
const subscription = AppState.addEventListener('change', (nextAppState: AppStateStatus) => {
if (appState.current.match(/active/) && nextAppState.match(/inactive|background/)) {
onPause(); // App 进入后台时触发
}
appState.current = nextAppState;
});

return () => subscription.remove();
}, [onPause]);

}

在页面调用的例子:


// 在 App 切换到后台(如按了 Home 键、切换到其他 App)时停止某些正在进行的任务

useAppStatePauseTask(() => {
handleAiStop(); // 中止 AI
ExpoSpeechRecognitionModule.stop(); // 停止语音识别
});

新作品

没错,我要开发新的app了,灵感来自于到中国的一个澳洲朋友。 和AI有关,和语言有关,先透露到这里。 总之先去AppStore注册,还有RevenueCat把付款机制做了,图标啥的AI赶紧整一个。 对了还有ICP备案啥的。 一切从简从速。

游牧周记第35期

· 5 min read
Suhe
This site owner

龙树谅从不失手

代码

我的App终于更新

学易从3.X 到 4.X时代了,上次大版本升级不知是3年还是几年前的事了? 这次筹划准备了半年吧,中间还在外旅游了几个月,回来后又被Diablo耽搁几周,实际整合代码的时间约3周,最累是发布前几天。 主要变动都在UI上,终于把几张封面图换了。

expo router的android奇异事件

半透明层覆盖,用animation: 'fade_from_bottom', 解决。 AI说:有时动画也会造成灰色背景干扰。可尝试这样的方法,反正不用default。

expo sdk53后android奇异事件

问题:

expo router项目从52升级到53后,代码没变,但android上出现之前正常的顶部显示贴边了,穿过了状态栏。这种现象原因及解法?

Grok回答:

问题原因

在将 Expo Router 项目从 SDK 52 升级到 SDK 53 后,Android 上出现顶部内容贴边、穿过状态栏的问题,主要原因是 Expo SDK 53 引入了对 Android **边到边(Edge-to-Edge)**显示的支持。这一新特性使得应用的内容默认会延伸到状态栏和导航栏下方,而不像 SDK 52 及之前版本那样默认避免与系统栏重叠。以下是具体原因:

  1. 边到边显示的引入
    • SDK 53 默认启用了 Android 的边到边布局,应用内容会绘制在状态栏和导航栏下方,导致顶部内容(如头部标题或导航栏)可能与状态栏重叠。
    • 在 Android SDK 34 及以下版本的设备上,这一问题尤为明显,因为边到边显示的适配可能不完全兼容旧版 Android 系统。
  2. 状态栏透明性(Translucent Status Bar)
    • 如果你在代码中使用了 expo-status-bar 且设置了 translucent 属性为 true,状态栏会变为透明,允许内容绘制在其下方,从而导致重叠问题。
  3. SafeAreaView 或安全区域处理未更新
    • 如果你的应用未正确使用 SafeAreaView 或 react-native-safe-area-context 来处理状态栏的安全区域,升级后内容可能会直接贴边,忽略状态栏的高度。
  4. Expo Router 布局问题
    • Expo Router 的根布局可能在 SDK 53 中默认添加了一个白色背景(或其他默认样式),当启用边到边显示时,内容会填充整个屏幕,可能导致视觉上的重叠问题。
  5. 新架构(New Architecture)的影响
    • SDK 53 默认启用了 React Native 的新架构(New Architecture),这可能影响某些组件(如 StatusBar 或导航相关组件)的行为,尤其是在与边到边显示交互时。

我采用的解决方法:

  • 在根布局文件(通常是 app/_layout.tsx)或相关屏幕组件中,使用 SafeAreaView 包裹内容。
  • 示例代码: ·
import { SafeAreaView } from 'react-native-safe-area-context';
import { View, Text } from 'react-native';
import { Stack } from 'expo-router';

export default function Layout() {
return (
<SafeAreaView style={{ flex: 1 }}>
<Stack />
</SafeAreaView>
);
}

expo模糊效果的问题

本来是另外一个问题引出的。 我用expo-image组件的blurhash来做模糊效果背景图,ios良好,Android问题很多,出现黑色穿透背景等。 突然想起expo-blur的BlurView在android上也是试验性的,多少年了似乎也没有正式支持。 果然ios才是亲儿子,哦不对,expo是ios的乖儿子(自认的?)。 问了ChatGPT,它推荐一个第三方react-native-blur组件,我看两个月前还在更新,star数只有20。问一下AI他们的区别是什么? 回答如下:

iOS 上两者都可以用;但 Android 上 expo-blur 的“模糊”其实是假的,性能差、bug 多,@sbaiahmed1/react-native-blur 才是真正能用、性能好、效果稳定的模糊组件。

如果你愿意配置原生或使用 EAS Build,强烈推荐切换到 @sbaiahmed1/react-native-blur

要么我还是用图片文件算了?

种植

继续跟踪羽衣甘蓝、冰菜、罗勒和其他,还有生菜也长出来了,移栽。 阳台不够大真麻烦啊。

看片

几个月前看到开头那些“正确角色”就被劝退的《杀戮人机》。现在片荒,又拿出来看,硬着头皮撑过前面半截,大脑麻木了一阵就进入状态了。 总的来说很苹果味,轻松还带点悬疑。 亚马逊的《基地》都第三季了,看预告似乎第一反派“骡”出现了,要进入剧情高潮。但我从第一季开始就抑制不住对这片子的恶心,即使跳着看也很少坚持看完一集。作为小说粉的我,实在受不了这个改变和表演的风格。如果是50-70年代科幻间谍风反而我会喜欢。 亚马逊的魔戒前传很多人不喜欢,我还觉得不错,但这部...真就算了。 《黑袍纠察队》是他们不可超越的神作。

《研究院》《倒计时》都看了第一集,感觉节奏好,吸引力还行,但没有追剧冲动。

游牧周记第34期

· 10 min read
Suhe
This site owner

阳台种植

冰菜

没想到冰菜是这么容易发芽的。 但是因为种子实在太小(和粉尘差不多,传说中的芥子是否也如此?),播散时无法间隔,现在想间苗移栽难度很大。

其他苗的移栽也有点操作困难,我是否需要学一下,或者买点工具?

其他

似乎是罗勒和生菜,还有个薄荷我不记得种在哪里了。

开发

关于zustand的调用方法

我的问题是这样:


expo项目中,zustand定义了一组全局状态如下:
type TheStates = {
step: Steps
auto: boolean
result?: ChangingYaoNumber[]
coinsToPay: number
activeSubscriberPayCoins?: boolean
updateStates: (setting: Partial<TheStates>) => void
}

export const useDivinationStates = create<TheStates>()(
set => ({
step: Steps.ASK,
auto: true,
result: undefined,
coinsToPay: C.MIN_DIVINATION_COINS,
activeSubscriberPayCoins: false,
updateStates: statesData => set((state) => ({ ...state, ...statesData }))
})
)
...
UI页面在调用其中多个状态的时候,一般采用以下2种方式:
1. 一句话:
const { auto, step } = useTheStates()
这种方式似乎会造成当其他状态变化,也会重复渲染?
2. 分开写
const auto = useTheState(state=>state.auto)
const step = useTheState(state=>state.auto)
这种方式可以精准管理每个状态,避免重复渲染,但太麻烦。
请简单分析以上方法优缺点,直接输出结果。

另外为了结合两者优点可否这样写:
const { auto, step } = useDivinationStates(state => ({
auto: state.auto,
step: state.step,
}))
这个写法是否会造成组件无限重复渲染?简单分析并直接给结论。

实际上最后那个方式真的造成了重复渲染,我一般用2,但这个问题只是测试一下AI,同时想搞清此问题原理。

各个AI说法不一。

kimi k2

  1. 两种写法的优缺点
  • 写法1:
    优点:最简洁。
    缺点:只要 store 中任何字段变化(如 coinsToPay、result…),组件都会重新渲染,哪怕只用到了 auto、step。

  • 写法2: 优点:每个字段独立订阅,仅当该字段变化时才渲染。
    缺点:代码啰嗦,字段多时需要写很多行。

  1. 你给出的写法

不会导致无限渲染。
Zustand 会对 selector 返回的对象做浅比较。只要 auto、step 的值没变,返回的新对象会被判定为“相等”,不会触发额外渲染。因此它同时保留了写法1的简洁和写法2的按需订阅效果,推荐使用。

豆包

第一个问题都差不多结论,关于第二个问题如下(后面也是这样):

不会导致无限重渲染。这种写法通过自定义选择器只提取所需状态,只有当 auto 或 step 变化时才会触发更新。但需注意确保选择器返回值的引用稳定性(例如避免内联函数导致的浅比较失效)。

最后提到了一个稳定性问题。

Qwen 3

代码模式

分析:

  • 这种方法通过选择器函数来提取所需的状态,类似于方法2中的精确控制,但又保持了一定程度上的简洁性。
  • 它只会对指定的状态进行浅比较,只有这些状态发生变化时才会触发组件的重新渲染。

结论:

这种写法不会造成组件无限重复渲染。只要选择器返回的对象中包含的状态发生变化,组件才会重新渲染,否则不会。因此,这种方法能够有效地结合两种方法的优点,既保证了代码的简洁性,又避免了不必要的重新渲染。

Grok 3

分析:

  • 这种写法使用 Zustand 的选择器(selector)功能,通过 useDivinationStates 的回调函数返回一个对象 auto, step,仅订阅 auto 和 step 两个状态。
  • Zustand 的选择器会比较返回对象的字段(auto 和 step),只有当这些字段的值发生变化时,组件才会重新渲染。
  • 不会导致无限重复渲染,因为 Zustand 的选择器是基于值的浅比较(shallow comparison),只要 auto 和 step 的值不变化,组件就不会重新渲染。
  • 这种方式结合了方式1的简洁性和方式2的精准订阅优势,避免了无关状态变化导致的重复渲染。

结论:

  • 新写法 不会造成无限重复渲染。
  • 它是最佳实践,兼具简洁性和性能优化,仅当 auto 或 step 变化时触发渲染,适合大多数场景。推荐使用。

完全是肯定的,对了最早推荐这样写的也是它。

Gemini 2.5 Pro

先Thinking了一阵子

这个写法的意图很好,希望结合前两者的优点。但它存在一个严重的问题。

  • 分析:

    1. 组件每次渲染(或因父组件渲染而重新渲染)时,选择器函数 state => ({ auto: state.auto, step: state.step }) 都会被重新执行。
    2. 这个函数每次执行都会在内存中创建一个全新的对象 { auto: ..., step: ... }
    3. Zustand 在接收到 store 的更新后,会比较选择器上一次返回的值和这一次返回的值是否相等,以决定是否要触发组件渲染。
    4. 默认的比较方式是严格相等 (Object.is,类似于 ===)。由于每次返回的都是一个新对象,它们的内存地址不同,所以 oldObject === newObject 的结果永远是 false
    5. 因此,只要 store 中任何一个状态发生变化(即使不是 autostep),这个 hook 就会认为它所订阅的值发生了变化(因为对象引用变了),从而触发组件重新渲染。
    6. 组件的重新渲染又会再次执行选择器,创建一个新对象... 如果这个组件内部有触发 store 更新的逻辑,就会陷入无限重复渲染的死循环。
  • 结论:
    是的,这种写法会造成不必要的重复渲染,并且在特定情况下可能导致无限循环。 它不仅没有解决方法一的性能问题,反而因为每次都创建新对象而可能表现更差。绝对应该避免这种写法。

第一个提到无限循环的,而且要求绝对避免,态度坚决!!!

ChatGPT 4.1

  • 结论不会造成组件无限重复渲染
  • 原因:Zustand 会对比 selector 返回的对象(此处是 {auto, step}),只有当对象内容变化(即 autostep 变化)时才会触发渲染。不会因为无关状态变化而导致渲染,也不会死循环。 ...
  • 推荐用第三种写法:useDivinationStates(state => ({ auto: state.auto, step: state.step })),既简洁又精准。
  • 不会导致无限渲染,可以放心使用。

GPT居然没分析出来,令我有点意外。

Claude-Sonnet-4

结论:会造成无限重复渲染

原因:
每次组件渲染时,state => ({ auto: state.auto, step: state.step }) 都会创建一个新的对象。由于对象引用不同,zustand认为状态发生了变化,触发重渲染,形成无限循环。

正确的解决方案:

使用 shallow 比较或自定义比较函数:

javascript

复制

import { shallow } from 'zustand/shallow'

const { auto, step } = useDivinationStates(
state => ({ auto: state.auto, step: state.step }),
shallow
)

或者使用 useShallow hook(zustand v4.4+):

javascript

复制

import { useShallow } from 'zustand/react/shallow'

const { auto, step } = useDivinationStates(
useShallow(state => ({ auto: state.auto, step: state.step }))
)

这样既保持了代码简洁,又避免了不必要的重渲染。

第二个发现问题的,并且还给出了建议的写法,要试试!!!

最后我补充测试了Grok 4

这个写法使用了zustand的selector机制,返回一个自定义对象,只订阅auto和step的状态。zustand会使用浅比较(shallow equality)检查selector返回的对象是否变化:

  • 如果auto或step变化,对象内容变化,组件会重新渲染(预期行为)。
  • 如果其他状态(如result)变化,但auto和step不变,selector返回的对象相同,不会触发渲染。
  • 它不会修改store状态,只读取,因此不会导致渲染循环。无限渲染通常源于渲染过程中直接修改状态(如在useEffect中无条件setState),而这里仅是读取。

结论
不会造成组件无限重复渲染。这个写法有效结合了两者的优点:简洁(一行解构多个状态)且高效(只在选定状态变化时渲染),是推荐的优化方式。

马斯克的大力出奇迹,还是没能正确分析!!!

果然代码领域Claude还是老大,Gemini 2.5 Pro也非常令人惊喜!ChatGPT有点拉了我没想到。 国产还需努力。

新版react-native-picker的奇葩问题

android不说了,本来就不用它。 ios上,不管传什么值,永远报错:

Error: Exception in HostFunction: TypeError: expected dynamic type 'string', but had type 'double'

用尽无数方法(AI也用了无数)没法解决。 最后降级,目前版本是"@react-native-picker/picker": "2.11.1",参考expo官方资料降到"2.7.5"后ios正常,但android build出错;只好重新安装:

npx expo install @react-native-picker/picker

结果这次两边都ok了,版本还是:"@react-native-picker/picker": "2.11.1"。 不知这次能稳定多久? 结果第二天编译又出错了,貌似还是缓存问题。 突发奇想,我再升级到最新版呢,于是再装(2.11.1),一切正常了,仿佛没事发生。

reanimated旋转动画哪家AI强

我是指AI编程领域。 对我是要做一个硬币旋转的效果。 其实几个月前我就在设计了,所有AI都问遍了,多多少少都有问题,且无法解决。 截至上次Claude和GPT的还算能用,android完全ok,IOS模拟器上不行,显示效果Bug无法解决。

今天采用Gemini 2.5pro完美解决,虽然过程中也遇到一两次同样错误,但很快就修复搞定了。 其实知道现在我也不知怎么搞定的。😅

Gemini还给出了很优雅的代码分离方式。

JS随机数小问题

actionFn: () => Math.floor(Math.random() * 6) + 1, // 生成1到6的随机数!Math.random() 的取值区间是 左闭右开:

Math.random() 的取值区间是 左闭右开
[0, 1),即 0 ≤ r < 1

因此
Math.random() * 6 的区间是
[0, 6),即 0 ≤ r·6 < 6

Math.floor 只会向下取整,最大只能取到 5(因为 5.999… 向下取整也是 5)。
所以 Math.floor(Math.random() * 6) 的结果范围是 0–5绝不会出现 6

组件设计的styles小tip

在React Native中,style属性通常用于定义组件的样式。关于style?: StyleProp<ViewStyle>;style?: ViewStyle;的区别和前者的好处,主要体现在类型安全和灵活性上。

  1. 类型安全StyleProp<ViewStyle>实际上是一个可以接受多种类型的联合类型,不仅仅包括直接的样式对象(即ViewStyle),还包括数组形式的样式(用于组合多个样式对象)以及nullundefined。这意味着使用StyleProp<ViewStyle>可以更准确地描述style属性能够接受的所有合法输入类型,提供更好的类型检查和错误提示。

  2. 灵活性:通过使用StyleProp<ViewStyle>,你不仅可以传递一个样式对象,还可以传递一个样式的数组。这种方式允许你以一种更加灵活的方式组合不同的样式规则,例如覆盖默认样式或合并多个样式源。这对于动态调整UI或根据状态应用不同样式非常有用。

  3. 支持其他样式类型:虽然这里的讨论集中在ViewStyle上,但StyleProp也可以处理其他类型的样式,如TextStyleImageStyle,增加了代码的复用性和可读性。

综上所述,使用style?: StyleProp<ViewStyle>;相比于style?: ViewStyle;提供了更高的灵活性和更强的类型安全性,使得代码更加健壮和易于维护。

游牧周记第33期

· 5 min read
Suhe
This site owner

日常

种植

夏天播种,本来就容易发芽,昆明也没那么热,总的来说合适。 家里阳台太小,确实很不方便。 另一个大点的被封了,通风不畅,关键是阳光不足。 目前要点:

  • 种实用的植物,能吃的:羽衣甘蓝(非常容易发芽),生菜(还没出芽),罗勒(好像长出来了),冰菜(今年新种,还没动静),薄荷(没动静)。
  • 特别的植物:山乌龟(本想做点造型,让其爬水管,但方向难控制,长得乱了,要修剪),一些水培的酸角。同学处挖来的缅茄,即树番茄,叶子超大,就是不长高,据说以后是颗大树。
  • 之前得植物:柠檬如此难以长大,折腾我几年了,死了又活,没有结果;前几天网购了一颗新品种花叶柠檬(泰国柠檬),替代之前死了那颗,这次来自浙江,包装和配土都很好,希望能活。
  • 买了100多L营养土,分10袋运来,家里都没地方摆;有点像打碎点的松针土,混合了一些其他物质,国产品牌(浙江的); 领悟: 人闲下来就是要有个自己的院子。

图书馆

回昆明后第一次去图书馆,选择周围热闹,有公交的五华区图书馆。 有点失望:人多桌椅少,电源插座等设施不方便,寄存柜不是满的就是坏的,二楼阅览室有个能用的。 自习室是少数适合用电脑的地方,但上方的光线直射下来,热得躲不开。 二楼虽然有点不晒的桌椅,但根本没机会抢到位置。

关注

PanSci 泛科学

我关注多年的Youtube频道,主播非常有意思,我是说说话风格,这种用梗和幽默是华语媒体中比较Funny的,和大陆哪些都不一样。

开发

学易app新版本

4.0+已经开发了半年,但都在玩一直没有实际进步。 3.12只更新了一次,主要是上次ICP备案问题,被迫改api网址的事情。 回昆明后,花了很多时间在Diablo IV,好不容易静下来写代码。 这次版本更新,有以下开发要点:

  • 不做大的新功能引入,更在意原有功能的清理和优化;
  • 原有style、多语言、iap和广告等基础通用(可复用)部分,认真进行性能和代码风格优化;
  • 之前的各种组件性能调优;
  • 大量进行细节部分的体验提升,主要是动画效果;
  • 界面有所改变,但大结构基本保持,主要是提升可读性,UI更多在字体、颜色等方便参考Apple自身美学规范;

AI的使用经验:

  • 不付费,Claude唯一值得,但太贵;
  • 不在IDE做大量开发,只做代码补全、插错等辅助工作;
  • 新建组件,提前在免费LLM中做功课,精心编写要求,然后poe中用Claude写核心代码,再到ChatGPT进一步优化;
  • Gemini和Grok辅助;
  • 完成的代码和之前的一些代码,发给ChatGPT,让其进行分析、评估、查错和优化;提出要点,而不是全部自动改写,这一招非常受用;
  • 现在想做的是想让AI尽量帮我处理多语言资源文件的自动翻译和生成,结果还挺不顺。

免费字体

一直在避免加装第三方字体包,原因有三:

  • 版权问题
  • 系统资源占用
  • 系统自动的已经很美观 现在为啥想到呢?因为在设计按钮类动作文字的风格,以前link就用蓝色(和Apple一样,现在也是),其他功能点击则选择橙色,有点怪,发现Apple自己是选用的较粗字体来做区别,其实和正文、标题等不易区分,我也不想再用跳色,想用字体来区分一下。 https://fonts.google.com/ 这里有很多免费字体(包括一些中文的,以及无数其他语言,但同时简繁兼容的还没有),且还有个@expo-google-fonts,适合expo开发。

Expo/ReactNative技巧

  • Pressable的hitSlop 我之前发现,按钮太小,经常手指难以触发。 UI设计要求注意最小触控区域,但不代表视觉按钮尺寸,如按钮大小32pt,但触控区域要求44pt,则可以用这种方式:
<Pressable
onPress={handlePress}
hitSlop={6} // 在每边加 6pt,总触控区域为 32+12=44
style={styles.circleButton}
>
<Ionicons name="add" size={20} color="#000" />
</Pressable>

const styles = StyleSheet.create({
circleButton: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: '#F2F2F7',
justifyContent: 'center',
alignItems: 'center',
},
});

expo router Android tabbar导航奇葩问题

这个问题我问了AI:

expo router项目,在useRouter.push(某些路由)时,在android中,打开的页面同时,不时会有一个覆盖的(透明或半透明)层水平出现,覆盖住整个页面,出发点是一个app/(tabs)中的页面,目标在app下的各个tsx,如果没有在app的子目录中,此现象不会出现,如果在子目录中,就会出现。ios没有这个现象,请分析,并给出解决办法?

这次ChatGPT没能搞定,反而是Grok 3找到办法了。

结合 transparentModal 和自定义样式,确保页面全屏显示并覆盖 Tab Bar。

只要将原来的目标子目录presentation从'modal'改成'transparentModal'即可。 然后发现其实是在页面下方有个底色层,这样改虽然消失了,但整体也变得透明了。 要么就在android相应页面container中加上backgroundColor,结果也不行,还是透明的。

再检查发现,问题出在tabs设置的tabBarBackground上,去掉就没这个问题;而且这个TabBarBackground是特别设计的,用到了BlurView和绝对定位,这个在Android本来就不稳,先去掉了,现在至少正常。

游牧周记第32期

· 2 min read
Suhe
This site owner

Coding

react-native-skia

关注

  • Reactiive Youtube又一个讲ReactNative的,更新不多,关注量不大。 我主要看他的动画讲解,包括最近在接触的skia。

  • Dan’s React Native Lab Youtube另一个ReactNative创作者,关注更少,但主题很实用。

  • 渤海小吏 B站378万关注,百大之一。有些视频播放量近500万。 主打西游记和中国古代历史。 历史部分我还没看,毕竟B站太多。 西游记讲解信息量大,片子长,当然重点是角度特别,细讲原著细节和解密分析,然后还有长篇大论的做人做事道理(潜规则)。 作为背景听不错。

日常

丙烯画

Acrylic画,工具买全了,开始实践。 比我想象的好上手,比小时候水粉水彩的痛苦经历体验好很多,当年咋没这个。 覆盖力强是一方面,另外画布比纸张确实好多了。

阳台种植

去年种的菜发芽的只有生菜和羽衣甘蓝,都吃了。 今年准备还是按照沙拉目标做,羽衣甘蓝播种第三天就发芽了(现在是7月),然后生菜和冰菜的种子在路上。 没地方搞土,只好买了120L的国产大牌子营养土,价格是美乐棵的一半多点(也不算便宜)。

游牧周记第31期

· 3 min read
Suhe
This site owner

关注

Cool History Bros

youtube频道 印尼华人用英文制作的高质量Youtube历史类动画影片,信息准确丰富,画面水平极高,还非常幽默。 评论区好评如潮,但都有一个疑问,为啥没人看?到现在关注量也只有15万。 是历史题材大家不感兴趣吗? 是中国历史(其实还有很多其他国家历史)太冷门? 对比同类型影片,应该都不是。 可能的原因之一是有人吐槽他的英文口音,但我听下来感觉非常正常而清晰(估计母语者不这么想)。 哎,至少他是少数把中文名词读准了的创作者之一,另外有几个长期在华语文化圈生活的西方人制作的类似的中国历史影片,也有这样的优势,但内容和画面差太远,结果也比他关注和点击率高很多。

最佳拍档

youtube频道 号称科技类娱乐频道,主播是大陆人。 解读很多高品质的名人演讲和对话视频,很轻松的做法,但质量上乘,取材有深度有品位。 纳瓦尔那期自不必说,可以反复看。 最近一期彼得提尔的关于创新停滞和对近年LLM AI的冷静观点,颇为心有戚戚焉。 我看这个频道的原因,一是为了省时间,二是英文不够好没法看原片。

影视

鱿鱼游戏Season3(大结局?)

快速看完了,有些地方快进的,有些太生硬、套路和尴尬的场景就跳过你懂的。 其实游戏设计部分剧情和部分演员的表演真不错,整体符合预期。 但整体上确实过于平庸,特别是警察线等周边路线,还有嘉宾啥的都搞得非常无聊(韩国人对外国文化的理解还是有点刻板简单)。 结局也挺无语的,感觉铺垫不够,有点失败。 最精彩的反而是最后几个人渣的演出,太真实了,东亚人可能不陌生,西方人可能觉得有点破底线。 应该韩国这边不会拍续集了,要拍也会换卡司换场地(比如片尾暗示的美国)。

另一部韩剧(John Wick like?)

无赦之仇 本来剧情还算紧凑,符合韩片的爽剧感觉,但没有了一拳超人(对,就是说馬東錫的那些)那种幽默感。 非要搞得深沉,还加点阴谋论,整个气氛不伦不类。 导演明显是看了太多John Wick,但把男主搞成一个有自杀和自虐倾向的一拳超人。 和鱿鱼游戏类似,最大的亮点是各个反派配角,两个黑老大演得不错,有点像之前看的美剧“黑帮领地”那感觉,包括挑起事端的无脑黑二代,都挺不错,不算单薄刻板。 最后结局证明了我的推断,男主有自杀倾向,而且是完全没必要的。

这两部都有类似的调调,韩国人近些年的心理映射? 一是求死求虐待; 二是主角以不动脑子为傲,永远板着个脸迎着刀子慢腾腾硬上,不把观众急死他不舒服似的。 三是不给坏人死得太轻松,暴力镜头不太遮遮掩掩的,这点比好莱坞棒。

游牧周记第30期

· 2 min read
Suhe
This site owner

关注

罗胖的文明之旅

很多年没关注这货了,以前订过他的微信号还买了不少书(别说他推荐的书不错),天天说经济会好得不可思议。 后来看到他就闪,直到后来无聊需要当游戏背景,点开了这个新的文明之旅。 据说是致敬资治通鉴,按编年的方式展开,每集一年,总体来说内容扎实,但不一定有趣,风格还是和以前一样有点啰里八嗦,但时不时有点冷知识和引用的精彩见解和观点(这些就是醍醐味了)。 总体来说深度足够,信息量大且较准确,可以只听(不必看),比如开车或打游戏时。

博物馆系列

  • 尋寶隊長SIGMA 不光是字幕,而且讲解经常犯极其低级的错误,每一期风格和声音都有所不同,又不像AI。 但内容确实够翔实有趣,可供参考,也不会(完全)回避大运动那些年的破坏和近几十年的民间盗墓和哄抢问题。

影视

地下忍者

真人版电影,现在B站有搬运。 原来有心理准备,应该非常难看,结果...比我想象的更加恶心,导演和编剧在想啥。 预算原因特技和装备大缩水倒是可以理解,但剧情和梗,不摆了。 也有好的地方: 教室打斗的部分场景, 几个角色选角:九郎、铃木、新七人众的二代小哥(最佳)、真主角中忍,都还行。

漫画发展越来越有意思了,花泽是神。

游牧周记第29期

· 2 min read
Suhe
This site owner

自驾游总结

2025年的自驾游历经两个月告一段落,我们已经回到昆明。 现在把一路的账目做个总结:

时间

2025年4月13日-2025年6月13日,刚好两个月。

补充:其实应该从1月底昆明出发算起,还经过了之前没去过的昭通部分地区,但在成都家里时间待得太长了。

费用

总计:23652.29 去除日常购物、水电气和医药等,以下是旅游相关清单。

住宿

6380.63

加油

3838

过路

2928.95

公交地铁

225.85

共享单车

185.5

打车

136.99

交通:7483.79

门票

2539.65

纪念品

679.49

餐饮

5660.4

历程

共计12个省,26+城市。 自驾车程8052公里。

四川

成都(出发)

陕西

汉中 西安 铜川 延安

山西

运城 曲沃(经) 临汾 (永胜寺) 太原 (北格镇) (佛光寺) 代县 大同

河北

石家庄 邯郸(经)

河南

安阳 洛阳 郑州

山东

曲阜 (灵岩寺) 济南 烟台 威海 (荣成) 青岛

江苏

宿迁

安徽

合肥

湖北

武汉

湖南

岳阳

贵州

思南 贵阳

云南

昆明

补充

云南

昭通

行程增加900公里。 相应费用约1000。