游牧周记第46期
标签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'
原因:
-
next/router是 Pages Router(旧版路由)的 hook,只在pages/目录下使用。 -
next/navigation是 App Router 的新 hook,适用于app/目录的'use client'组件。 -
两者 API 有差异:
-
next/navigation的useRouter()只有少量方法: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" 页面中:
-
初始渲染:
-
Next.js 先在服务器(Node.js)端执行一遍组件(SSR),生成静态 HTML。
-
这时 React 并没有运行
useEffect(),因为它只在浏览器端运行。
所以:console.log('app user ---', user)在 SSR 阶段输出一次(
user还是null)。
-
-
客户端挂载阶段(Hydration):
-
浏览器端 React 接管 HTML(Hydration),
-
再执行
useEffect(), -
这时发出 fetch 请求并调用
setUser(data)。
-
-
问题是:
-
控制台的
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.icoapp/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.png | app/ 根或同级段 | 自动优化、自动生成 <link>、支持双主题 |
页面里手写的 <img src="..."> 装饰图 | public/images/xx.png | 不走构建优化,纯静态,CDN 缓存友好 |
组件里 import Logo from './logo.png' | 与组件同级 | Webpack 会加哈希,缓存击穿风险低 |
结论:示例把“品牌图标/OG 图”放 app/ 是 故意用新特性,不是错误;其他纯装饰静态资源仍应归 public。
