@
epiloguess 关于第一点,我确实测试的是非 canary ,我以为这个 canary 是 unstable 的,就没想这个是正式版 haha 。。
---
关于和 PPR 有什么关系:我当时就是感觉如果假设页面上只有两个调 API 的组件,其中一个 A 组件调 API 的时候用 cache ,另一个 B 调 API 的时候用 dynamic ,那就相当于 PPR ,因为 A 组件在不 revalidation 的情况下就一直是静态的了,而 B 组件会变化,这和我心里想的部分预渲染的定义相符。可能到教程里的例子,就是把除了 noStore 的,其他的 API 调用都用 cache 包起来,就相当于实现了 canary 里的 PPR 的功能了,即默认其他的都是静态。
也可能是 cache 的文档里有 revalidation 这个术语,让我想起了教程里 chapter 10 有这句话(看,这里也有 revalidation ):
> Partial Prerendering leverages React's [Concurrent APIs](
https://react.dev/blog/2021/12/17/react-conf-2021-recap#react-18-and-concurrent-features) and uses [Suspense](
https://react.dev/reference/react/Suspense) to defer rendering parts of your application until some condition is met (e.g. data is loaded).
>
> The fallback is embedded into the initial static file along with other static content. At build time (or during **revalidation**), the static parts of the route are *prerendered*, and the rest is *postponed* until the user requests the route.
所以我以为它们也许是 通 的。
---
> 你需要确保的是,如果函数都加上 unstable_cache 了,你这个问题应该就不会出现了吧。
这个我觉得应该是不会了,毕竟我当时测试的时候,加上了 unstable cache 之后,如果没有设定 revalidation 的话,不管怎么操作,都不会 rerender 了,就像是一个动态页面里独立出来了一个静态的玩意儿。所以用了这个应该是绝对不会出来这个问题。
---
> \> 关于第 5 点,我不知道你是怎么测试的,但是我测试的情况下,如果 API 方法里使用了 noStore ,然后把这个方法作为 cache 的 fallback 的话,确实不会退出静态生成。build 的时候显示的 tree 也显示页面仍然是 static ,
>
> 你这个是在什么条件下测试的?
是非 canary 下测试的呀,就是 noStore 只加在 revenue api 方法上,然后用 `cache(()=>getRevenue(), ['revenue'])`。发现 router 树 dashboard 后面的符号是 圆圈。而且用页面的时候 revenue 也没有被调用(除非有 revalidation )。
> 在正式版/canary without ppr 中,
> 这个 noStore 是不是 cache 的 callback,都没有关系,只要你这个路线中,任意一个地方出现了 noStore,next 在 build 的时候,就等价于路由段配置中的 force-dynamic 或者 fetch 中的 no-store,相当于退出静态渲染,改用动态。
我刚刚去把版本换成 canary without ppr 测试了一下,我用了 noStore 在 API 方法里,然后使用 cache 把 API 包裹起来,生成树还是静态的(注意我没有使用你上面回答里的推荐做法,即把 noStore 放在组件里,放在 cache 方法上方,不知道是不是这里有误解。。)。
```
Route (app) Size First Load JS
┌ ○ / 226 B 98.9 kB
├ ○ /_not-found 871 B 87.8 kB
├ ○ /dashboard 293 B 92.2 kB
├ ○ /dashboard/customers 139 B 87 kB
├ ○ /dashboard/invoices 1.65 kB 95.3 kB
└ ○ /dashboard/invoices/create 172 B 93.8 kB
+ First Load JS shared by all 86.9 kB
├ chunks/23-51b06a4d0afaaa6e.js 31.3 kB
├ chunks/fd9d1056-f593fbcc7b74c7aa.js 53.6 kB
└ other shared chunks (total) 1.91 kB
○ (Static) prerendered as static content
```
而且我测试的时候又遇到个怪问题(为什么我老是遇到怪问题。。。):我在用 canary 版本 build 我做到后面 chapter 的代码的时候,发现 /dashboard/invoices 这个 route 变成 dynamic 的了,然后我慢慢注释一些我觉得可疑的东西,想试试是什么把它变成 dynamic 的。
结果我发现是这行代码。。。(下面有 CONFUSION 的):
```tsx
export default async function Page({
searchParams,
}: {
searchParams?: {
query?: string;
page?: string;
};
}) {
// ! CONFUSION: why once use this line, '/dashboard/invoices' became dynamic ??
const query = searchParams?.query || '';
// const currentPage = Number(searchParams?.page) || 1;
// const totalPages = await fetchInvoicesPages(query)
return (
<div className="w-full">
<div className="flex w-full items-center justify-between">
<h1 className={`${lusitana.className} text-2xl`}>Invoices</h1>
</div>
<div className="mt-4 flex items-center justify-between gap-2 md:mt-8">
<Search placeholder="Search invoices..." />
<CreateInvoice />
</div>
{/* <Suspense key={query + currentPage} fallback={<InvoicesTableSkeleton />}>
<Table query={query} currentPage={currentPage} />
</Suspense>
<div className="mt-5 flex w-full justify-center">
<Pagination totalPages={totalPages} />
</div> */}
</div>
);
}
```
我真是惊了个呆 o_o ....,这个不是我自己定义的一个 prop 吗。为什么会把 route 变成 dynamic 的。。。
关于上面两点,我 commit 了代码( commit message is: canary test ) push 了,你可以跑跑看。(另外我发现我之前没 push 数据库配置。。。我打开你 fork 的仓库跑起来直接数据出不来。。现在我 push 上去了)
---
> \> Next.js 有一个内置的数据缓存(data cache),可以在传入的服务器请求和部署中保留数据获取的结果。这是可能的,因为 Next.js 扩展了本机 fetch API 以允许服务器上的每个请求设置自己的持久缓存语义。
这部分不懂,动态组件是 PPR 的概念对吧?意思是动态组件也可以调用这个内置的数据缓存吗?如果是这样的话,那 sql 不也是基于 fetch 的吗?意思是通过恰当的配置,可以让一个动态组件每次读的是缓存的数据?
动态组件的情况下,并不意味着我们不能用缓存吗?
>
https://github.com/orgs/vercel/discussions/4696>
> Your response are cached because `@vercel/postgres` uses `fetch` for `sql` and `fetch` is cached by default in Next.js (
https://nextjs.org/docs/app/building-your-application/caching#data-cache). You can use [Segment Config Options](
https://nextjs.org/docs/app/building-your-application/caching#segment-config-options) to control how caching works.
>
> I have created an example at
https://github.com/vercel-support/167147-postgres-cache-nextjs/tree/main/app/api where you can compare [the default (cached) response](
https://167147-postgres-cache-nextjs-nsnjr8w00.preview.vercel-support.app/api/default) with [one that's fully dynamic](
https://167147-postgres-cache-nextjs-nsnjr8w00.preview.vercel-support.app/api/no-cache) .
没仔细看它里面贴的网站,但是他这里说 sql fetch 是默认缓存的(因此造成了不修改请求代码的情况下会返回 stale data ),这好像和下面的 revalidate 0 冲突啊,revalidate 0 不是 no-cache 的意思吗?
> 一个悲伤的事情是,正式版的静态渲染和 revalidate 0 配合工作良好,ppr 却不行,这也就意味着,对于带有数据库查询的组件,你并不能 ppr 它们变成完全静态的
我 pull 了你的仓库,试了一下,确实是你说的这样。。
只要调了 sql ,而且没有 noStore 就会报错,除非使用 cache 。
我这里也报关于 validate 0 的问题,我也认为应该是 ppr 和它冲突了。
---
其实我就是想说 callback 的,我自己打错了,我看到你说这句,我回去看我写的那么多个 fallback 我也懵逼了,然后在 Suspense 组件那里看到同名 prop 了,疑似被记忆注入替换了 hoho 。