跳到主要内容

游牧周记第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