V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  epiloguess  ›  全部回复第 4 页 / 共 6 页
回复总数  101
1  2  3  4  5  6  
ssr 对我来说挺好用啊,整体将 react 的组件树扁平化
服务器渲染的内容可以在 react.js 下载之前就显示出来,server action 可以在 js 加载之前就能向服务器提交操作
服务端组件和客户端组件的搭配,可以让服务器只将必要的序列化的数据传给客户端组件,昂贵的计算逻辑也可以交给客户端组件来做
我觉得 nextjs+react 提供了一个清晰明了的网络边界,这是我最喜欢的部分
它指示了什么内容应该在服务器上渲染,什么内容应该在客户端上渲染,和 spa 的目标是不一样的
nextjs 更适合被称为同构渲染框架而不是 ssr,它是包括 csr+ssr 的
我觉得是一种合理的进化
当然这些概念十几年前就有了,如何将它和现代前端框架融合才是工作的重点.技术是螺旋上升的,如果从俯视的角度来看,确实是个圈,但不能否认这个圈越来越大了
有一个叫做 tmm 的 支持自动刮削 不过也没有那么自动 不知道你试过没有
225 天前
回复了 vinciarts 创建的主题 程序员 国内用 Svelte + Supabase + Vercel 的全栈多吗
@willx12123 NPM 的数据经过处理了,为了和 CNPM 做比较,实际上,NPM 上工作日的时候一天下载量大概在 17W 左右.大概是 react 的 1/24,vue 的 1/5. 用的人还是不少的. 但是国内用的就比较少了,大概是 vue 的 1/32,而且没有形成稳定的工作日-休息日间隔,说明很多都是业务爱好,没有投入生产
225 天前
回复了 vinciarts 创建的主题 程序员 国内用 Svelte + Supabase + Vercel 的全栈多吗
svelte 不能说很多,只能说...
https://npmstats.com/package/svelte
226 天前
回复了 coffeygao 创建的主题 健康 在不剧烈运动的情况下我怎么能快速减肥?
减肥不用剧烈运动,反而是要选择你能坚持最久的运动,如果你把每天的热量缺口目标设置在 500 大卡,在不控制饮食的情况下,你只需要每天走 10000 步就能达成目标,大概需要一个半小时,如果控制饮食就更快了.每天 500 大卡,一个月大概可以瘦 2-4 公斤
文科学这些东西是给你赋能,就赋能来说,py js 很适合,不是让你拿爱好挑战别人的饭碗 就算是前端没个全身心半年也学不明白达不到初级开发的水平
我 12450H,用 win11,任务栏经常冻结
express 挺好的,就是万年不更新,也不是自带 battery ,nest 其实用的人不多,只有 express 的 1/10 ,选技术也要考虑一下以后招人难不难
https://npmstats.com/tags/node.js%20framework
syncthing
个人首页似乎有点卡
229 天前
回复了 weijancc 创建的主题 Vue.js 升级 Vue3, 开发体验非常糟糕
下载量对比可以来我这看 https://npmstats.com/package/vue
最近给 nextjs 提 pr 的时候,就不小心把别人的 commit 加进去了还 push 了,给 pr 加了一对额外的标签还 request 了不需要的人,尴尬的要死,要是早点看到帖子就好了
更正:
倒数第二个水平线
---
那我们思考一下访问会访问什么

会调用 patch 过的 fetch,传入的 revalidate 是 0,走到刚才那个逻辑,如果没有提前设置 isUnstableNoStore 为 true,就会 else{
cache reason = 'auto cache'
}
--

传入的是 revalidate undefined ,不是 0 写错了

---

关于 debug npm link 应该也可以 不过我没怎么用过

毕竟我不是天天 debug..............

你也可以不用这么麻烦,毕竟你都用 canary,直接修改本地某个版本的包就行,反正注意一下别的项目不再用了就可以,比如只修改.50

---
翻了一下最开始的评论,还行,对的比错的多,笑死.

计划整理一下所有回答的内容,可以给后来者一点明确的参考,毕竟我们这帖子太乱了.

不过可能还要几天,毕竟总结的越早,错误的地方也就越多.
@lazyczx 这个文件下不了呀

关于 debug,因为我一直用的都是 pnpm,node_module 里面都是 pnpm 处理的硬链接
如果是 npm,每个项目都不共用吧,除非装 global

不管怎么说,理论上,直接修改本地文件应该就可行吧,比如说你 import 了 noStore 这个函数,这个函数在本地就是以 js 格式存在的,是源码处理过后的,在源码中是以 ts 的格式存在的

> 我做的时候,用的是 npm link ,下了 nextjs 仓库然后 link 替换掉了 dashboard 项目里的 next ,但是运行命令的时候提示 next 找不到,于是我全局安装了 next ,但是运行各种命令都报错,报什么 m...interface 什么找不到,我去 next 里 npm i 了也没用(虽然好像默认是不需要自己跑过去 npm i 的吧?)。

所以我不太懂你这一步 ,npm link 是干嘛的...

如果想快速 debug,最方便的应该就是直接修改你当前项目里面 node_module 目录下的 next 包里面的函数值,这种方式一般也叫 heck

如果想从源码 debug,需要 git clone next 的仓库,一边修改一边测试,修改完之后 build,再替换你本地用的 next 包.

这样就比较麻烦了,特别是如果你不熟悉项目的情况下..

---
最后补充一下我昨天的内容
下面是我打的一部分断点,我查了一下 patch-fetch 的 commit,勉强搞明白了到底是怎么回事.
这是没有 noStore 的时候的断点
我们都知道 nextjs 扩展了 fetch,具体实现,
next build 的时候,首先就会对每个路由,patch 原生的 fetch,它并不在乎路由里面有没有 fetch 的调用,这样就比较麻烦了
实现的效果就是,以后在渲染这些路由的时候,在调用 fetch 的时候,调用的就是 patch 过的 fetch,而不是原生的 fetch


call patchFetch from entry-base
Function patchFetch at patch-fetch.js being called [Function: g]
urlPathname from patchFetch Begining / // 所以断点日志的前三部分,就是不同的 urlPathname
revalidate from patchFetch Begining undefined
isUnstableNoStore from patchFetchBegin false

call patchFetch from entry-base
Function patchFetch at patch-fetch.js being called [Function: g]
urlPathname from patchFetch Begining /_not-found
revalidate from patchFetch Begining undefined
isUnstableNoStore from patchFetchBegin false


call patchFetch from entry-base
Function patchFetch at patch-fetch.js being called [Function: g]
urlPathname from patchFetch Begining /dashboard
revalidate from patchFetch Begining undefined
isUnstableNoStore from patchFetchBegin false



// 这是 fetch 函数在 next 中的定义
// fetch(`https://...`, { next: { revalidate: false | 0 | number } })
// 完成上面的工作之后,next/webpack,开始渲染页面/页面中的组件

rendering...RevenueChart component
Fetching revenue data... //已经在 try...catch 里面了,下一步就是获取 data

using patchFetch fetch //调用 patch 过的 patch
// 这时候我们能看到 input 和 init,分别是 fetch 的 url 和 option,option 也就是第二个参数
// 这里也就是说,@vercel/postgre 中的 sql`` 是通过 fetch 调用的
// 值得注意的是,如果没有配置 option 里面的 option.next.revalidate ,这个值默认是 undefined,sql``是没有配置的
input https://ep-blue-star-a4zoprb8-pooler.us-east-1.aws.neon.tech/sql
init {
method: 'POST',
body: '{"query":"SELECT * FROM revenue","params":[]}',
headers: {
'Neon-Connection-String': 'xxxx',
'Neon-Raw-Text-Output': 'true',
'Neon-Array-Mode': 'true'
}
}

isRequestInput false
curRevalidate undefined
fetchCacheMode undefined
isUsingNoStore false
revalidate undefined
isUsingNoStore false
cacheReason
revalidate false
cacheReason auto cache



rendering...RevenueChart component
Fetching revenue data...
input https://ep-blue-star-a4zoprb8-pooler.us-east-1.aws.neon.tech/sql
init {
method: 'POST',
body: '{"query":"SELECT * FROM revenue","params":[]}',
headers: {
'Neon-Connection-String': 'xxxx',
'Neon-Raw-Text-Output': 'true',
'Neon-Array-Mode': 'true'
}
}
using patchFetch fetch
isRequestInput false
curRevalidate undefined
fetchCacheMode undefined
isUsingNoStore false
revalidate undefined
isUsingNoStore false
cacheReason
revalidate false
cacheReason auto cache


Fetched revenue data...
Fetched revenue data...


---

所以错误是怎么产生的?
你会发现,RevenueChart 被渲染了两次,假设我们现在有另一个组件,其中调用了 noStore()
一开始,为路线 patchFetch
然后渲染组件 RevenueChart,调用 patch 过的 fetch,没有问题
然后渲染组件 with noStore(),noStore()函数会调用 markCurrentScopeAsDynamic,也就是说,会标记当前 scope 作为动态渲染,因此,有 noStore 的组件,在 build 过程中,不会调用后面的 try..catch 块的里面的 await sql``


---
这里补充一下 scope 的知识
关于 https://nextjs.org/docs/messages/ppr-caught-error
里面提到过
> Alternatively, insert unstable_noStore() before the try/catch.

try..catch 就是一个独立的 scope,同理,unstable_cache 也一样
unstable_noStore 不期望在这些 scope 中被调用,否则会错误
因为 markCurrentScopeAsDynamic 期望 mark 到一个 suspense 边界,如果 unstable_noStore 在页面顶层/或者一个没有被 suspense 包裹的组件内,(本质上一样)被调用,外面没有 suspense,那会发生什么?
答案很简单,整个页面都会被 suspense,这一点参考
> https://nextjs.org/docs/app/api-reference/file-conventions/loading

整个世界就是一个大大的佩拉(误)
整个页面就是一个大大的 suspense

--
先回顾一下上上部分最后一行.

但是,noStore 函数调用的时候,会 store.isUnstableNoStore = true;这个 store 是路线共用的
所以在第二次渲染 RevenueChart 的时候,isUnstableNoStore 会变为 true,其中我记得有个简单的逻辑
if(revalidate === undefined){
if(isUsingNoStore/重命名了){
{revalidate = 0}
cache reason = 'noStore call'
}else{
cache reason = 'auto cache'
}

正是这一步,导致我们以同样的操作第二次渲染 RevenueChart 的时候,revalite 为 undefined 变成了 0
导致后面没有调用 trackFetchMetric 而是 trackDynamicFetch 并最终把 revalite0 送到了 postpone 手里,产生了报错

---

所以我上次给的临时解放方案,在 markCurrentScopeAsDynamic 之后 isUnstableNoStore = false
才会奏效

但是,其实这并没有解决根本问题

---
那么,为什么会这样?为什么会有这个逻辑
if(isUsingNoStore/重命名了){
{revalidate = 0}
cache reason = 'noStore call'
}else{
cache reason = 'auto cache'
}

这个逻辑是在这时候被添加的 https://github.com/vercel/next.js/pull/60630

他的解释

```
When you're using noStore() with fetch it's currently saying "auto cache" in cache missed reason, adding "noStore call" here to show it's caused by using with unstable_noStore
当您使用 noStore() with fetch 时,它当前在缓存错过原因中显示“自动缓存”,在此处添加“noStore 调用”以表明它是由使用 with unstable_noStore 引起的

GET /no-store 200 in 4069ms
│ GET https://next-data-api-endpoint.vercel.app/api/random?another-no-cache 200 in 257ms (cache: SKIP)
│ │ Cache missed reason: (noStore call)

```

如果不添加这个逻辑,不在 noStore 里设置 isUnstableNoStore 为 true
会发生什么?
直接走到了 else 的最后一步,revalidate 为 undefined 的 ,cache reason 设置为 auto cache,这适用于
fetch 的时候不写 revalidate 的函数,组件会被默认预渲染
而 noStore 这个不会被预渲染的,也用的是相同的 cache Reason,显然不符合不对的,所以这个 commit 就这么被提交了

---

总结和补充一些内容

1.每个路线上的 fetch 都会被 patch,组件,会被渲染两次,关于这个渲染两次,官方 doc 里面也有提过,你就理解成在服务器上模拟客户端的操作?
2.revalidate 为 undefined 的,没有 noStore 的,会正常渲染
3.有 noStore 的组件,会被标记为动态渲染,从而不调用组件里的 fetch?那什么时候调用?当然是访问页面的时候,build,start 以后访问页面就能看见

那我们思考一下访问会访问什么

会调用 patch 过的 fetch,传入的 revalidate 是 0,走到刚才那个逻辑,如果没有提前设置 isUnstableNoStore 为 true,就会 else{
cache reason = 'auto cache'
}
发现了吗,不正确的 cache reason

---

加上的这一步会影响什么?


if(isUsingNoStore){
revalidate = 0
cache reason = 'noStore call'
}

使用了 noStore 的组件是舒服了
但是没使用 noStore 组件却被 revalidate = 0 给坑了,错误就是这么来的

---
所以我昨天的解决方法是解决了我自己的问题,但没解决那个 commit 想解决的问题

可以说,干脆就把 noStore 里面赋值那一行删掉就可以解决问题

不过我的问题比较关键好吧,他的问题不过是调试才会发现的问题,根本不影响使用...


---





@lazyczx
注意,以下内容包含大量代码,为了良好的阅读体验.建议复制到 vscode 或者其他 markdown 编辑器里查看
---


有的时候,一个 bug,它不会直接让你定位的到,它会表现出别的 bug 的形式来误导你,我想你应该已经感受到了

事实上,在 ppr 里,在 `unstable_cache` 里使用 `unstable_noStore` 没有任何问题,

因为什么都不会发生

```ts
// unstable_noStore.ts
export function unstable_noStore() {
...
else {
store.isUnstableNoStore = true
markCurrentScopeAsDynamic(store, callingExpression)
}
}

```

```ts
// dynamic-rendering.ts
export function markCurrentScopeAsDynamic(
store: StaticGenerationStore,
expression: string,
): void {
...
if (store.isUnstableCacheCallback) {
// inside cache scopes marking a scope as dynamic has no effect because the outer cache scope
// creates a cache boundary. This is subtly different from reading a dynamic data source which is
// forbidden inside a cache scope.
return;
}
}
```

除非是使用 `unstable_cache` 本身带来的其它问题

比如

```ts
// unstable-cache.ts
if (options.revalidate === 0) {
throw new Error(
`Invariant revalidate: 0 can not be passed to unstable_cache(), must be "false" or "> 0" ${cb.toString()}`,
);
}
```

不过应该不是这里的问题

---

> 然后我最后发现,要成功,只能把两个想做静态的组件在原来的基础上用上 cache ,这样页面才会如预期,只有一个是动态渲染的,其他俩静态(最终只有一个地方用了 noStore ),这应该就是你说的 cache 一把梭了。。。

> 然后我试了你的那个改源码的办法,出意外了。。。没办法通过改源码,把我上面的 俩 cache 给省掉啊。不加 cache 还是会报错。

我当时说的 `unstable_cache` 梭哈指的是全部使用 `unstable_noStore` + `unstable_cache` ,

放弃在其中一个组件开启 `unstable_noStore` 的时候,预渲染另一个带有 `db` 操作的组件的幻想

你的意思是,

- 在需要预渲染的组件里用 `unstable_cache` 缓存 db 函数,不使用 unstable_noStore

- 在需要动态渲染的组件里用 `unstable_noStore`

这样就可以了?

说实话我很质疑...不过 `unstable_cache` 的源码部分我没怎么看,因为没测试这一部分,主要精力直接放在不加 `unstable_cache` 就可以预渲染,

因为很明显,没理由在你需要预渲染的组件里加上 `unstable_cache`,不符合逻辑和语义

所以我觉得你可能碰上了我昨天说的第二个 `bug`,你的本地缓存里意外出现了你想缓存的数据,我建议你删掉 `.next` 重新 `build` 试试

---

真正需要搞明白的问题是,报错的 `{revalidate: 0}` 是怎么来的,db 操作是不是 `{revalidate: 0}`,是不是基于 `fetch`,有没有自带 `cache`

**到底发生了什么.**

---

很多问题,我暂时也还是一知半解,毕竟,源码真的很大还都是屎山,一个 `patch-fetch` 写 600 行..........

我只能先讲一下,我解决这个问题的思路

> Error connecting to database: Route /dashboard needs to bail out of prerendering at this point because it used revalidate: 0. React throws this special object to indicate where. It should not be caught by your own try/catch. Learn more: https://nextjs.org/docs/messages/ppr-caught-error

万恶之源是这个报错,应该很熟悉了吧,这个报错来自

```ts
//dynamic-rendering.ts
function postponeWithTracking(
prerenderState: PrerenderState,
expression: string,
pathname: string
): never {
const reason =
`Route ${pathname} needs to bail out of prerendering at this point because it used ${expression}. ` +
`React throws this special object to indicate where. It should not be caught by ` +
`your own try/catch. Learn more: https://nextjs.org/docs/messages/ppr-caught-error`
...
React.unstable_postpone(reason)
}
```

关于 React 的 Postpone API 可以参考

[Add Postpone API by sebmarkbage · Pull Request #27238 · facebook/react · GitHub]( https://github.com/facebook/react/pull/27238)

预渲染的函数调用路线

> entry-base > patchFetch > trackFetchMetric >

动态渲染的函数调用路线

> entry-base > patchFetch > unstable_noStore > > markCurrentScopeAsDynamic > postponeWithTRracking

万恶之源报错时的错误路线

> entry-base > patchFetch > trackDynamicFetch > postponeWithTracking

---

nextjs 在 build 的时候,会用一个 `AsyncLocalStorage` 的实例保存某条路线的元信息

如果组件内部声明了 `unstable_noStore()`,那么,就由这个 `noStore` 函数负责后续操作,~~而且这个组件会先被处理~~

糟糕的地方在于 `unstable_noStore()`函数内部,直接获取 Storage ,然后用点号赋值,`store.isUnstableNoStore = true`,

更糟糕的地方在于这个 Storage 似乎是被单个路线所有组件共用的。

而且还有一个函数 `patchFetch`,每次渲染组件都会调用,根据你的 `store.isUnstableStore`,修改你的 `store.revalidate` 值( db 操作默认为 undefined )

```ts
// patch-fetch.ts/patchFetch
const isUsingNoStore = !!staticGenerationStore.isUnstableNoStore
...

if (typeof revalidate === 'undefined') {
...
if (isUsingNoStore) {
revalidate = 0
cacheReason = 'noStore call'
} else {
cacheReason = 'auto cache'
revalidate =
typeof staticGenerationStore.revalidate === 'boolean' ||
typeof staticGenerationStore.revalidate === 'undefined'
? false
: staticGenerationStore.revalidate
}
} else if (!cacheReason) {
cacheReason = `revalidate: ${revalidate}`
}
```

值得注意的是,使用 `unstable_noStore` 的组件不会走到这一步,只有没有声明 `noStore` 的才会

所以,一开始,单独 build 的时候

- 第一个组件是 `isUnstableStore` 为 `true`,有 `noStore` 函数,直接就走动态渲染

- 第二个组件是 `isUnstableStore` 为 `false`,但是`{cacheReason:auto cache,revalidate:false}`,数据是可以 cache 的,调用 `trackFetchMetric`,所以走预渲染

如果组件一起渲染的话

第二个组件被坑了,`isUnstableStore:true` ,但是`{ revalidate : 0,cacheReason : 'noStore call'}`,

由于 `revalidate === 0`

```ts
//patch-fetch.ts/patchFetch
if (revalidate === 0) {
trackDynamicFetch(staticGenerationStore, 'revalidate: 0');
}
```

会调用 `trackDynamicFetch`,而且 `prerenderState` 为 `true`,会导致我们调用 `postponeWithTracking`,并调用 `React.unstable_postpone(reason)`,最终报错

```ts
// dynamic-rendering.ts/trackDynamicFetch
export function trackDynamicFetch(
store: StaticGenerationStore,
expression: string,
) {
if (store.prerenderState) {
postponeWithTracking(store.prerenderState, expression, store.urlPathname);
}
}
```

注意,`store.sprerenderState` 表示我们在 ppr 模式下,处于构建中,参考

```ts
// dynamic-rendering.ts/markCurrentScopeAsDynamic
if (
// We are in a prerender (PPR enabled, during build)
store.prerenderState
) {
// We track that we had a dynamic scope that postponed.
// This will be used by the renderer to decide whether
// the prerender requires a resume
postponeWithTracking(store.prerenderState, expression, pathname);
}
```

---

为什么动态渲染最后也会调用 `postponeWithTracking`,却没有报错?

```ts
// dynamic-rendering.ts/postponeWithTracking

prerenderState.dynamicAccesses.push({

// When we aren't debugging, we don't need to create another error for the
// stack trace.
stack: prerenderState.isDebugSkeleton ? new Error().stack : undefined,
expression,
});



```

因为被 `unstable_noStore` 函数调用后的 `store,prerenderState` 长这样

```ts
// postponeWithTracking prerenderState
{
isDebugSkeleton: undefined,
dynamicAccesses: [
{ stack: undefined, expression: 'unstable_noStore()' },
{ stack: undefined, expression: 'unstable_noStore()' }
]
}
```

```ts
// unstable-no-store.ts
const callingExpression = 'unstable_noStore()';
markCurrentScopeAsDynamic(store, callingExpression);
```

而调用 `trackDynamicFetch` 的`store,prerenderState` 最终长这样

```ts
// postponeWithTracking prerenderState
{
isDebugSkeleton: undefined,
dynamicAccesses: [
{ stack: undefined, expression: 'unstable_noStore()' },
{ stack: undefined, expression: 'revalidate: 0' },
{ stack: undefined, expression: 'unstable_noStore()' },
{ stack: undefined, expression: 'revalidate: 0' }
]}
```

推测: `React.unstable_postpone(reason)`接收的 `reason` 里不能有`revalidate:0`

具体实现参考 `React/packages/react/src/ReactPostpone.js` 还有上面上面提过的 `github pull`

---

#### 解决方案

临时解决方案:

在 `dynamic-rendering.ts/markCurrentScopeAsDynamic`

调用 `postponeWithTracking(store.prerenderState, expression, pathname);` 之前加上 `store.isUnstableNoStore = false`

最终解决方案

我对 AsyncLocalStorage 还有 nextjs 整体的理解还有待加强,所以上面只能作为临时方案

#### 最后

看不看源码这个东西,主要是兴趣吧,React 的官方文档就提到过很多次,不希望开发者关注底层是如何实现,只要专注 UI 部分就行了,他们负责 DX

不过作为开发者本身,对我这种人,对 BUG 的热情还是挺高的,乐在其中,我觉得有所收获就挺好的,至于成本的问题,不可避免,开心最重要 hhh
好消息是,经过扒了一夜的源码,我成功搞定了这个 bug 可以怎么被修复.
坏消息是我好困

一个简单的修复方法,现在就可以尝试.
首先确定你装的是 canary

打开你的 node_module/next/dist/server/app-render/dynamic-rendering.js
找到 markCurrentScopeAsDynamic 函数

在 调用 postponeWithTracking(store.prerenderState, expression, pathname); 这一行的上面
加上 store.isUnstableNoStore = false;

记得保存

---如何验证

第一个组件设置 noStore
第二个组件什么都不设置
两个组件都有 db 操作

不出意外的话,第二个组件会被部分预渲染
@lazyczx

我发现一些奇怪的事情,我的评价是远离 canary...

以下为一些发现,都指的是 ppr 开启的情况下
---

###第一个 bug.
先不考虑 unstable_cache 。
1. noStore 的位置没有影响。
只要 suspense 边界内有 noStore,这个组件就不会被预渲染。
在没有 unstable_cache,开启 ppr,路由会被部分渲染,从 dashboard 上面那个标题就能看出来

2.db 操作不是 revalidate 0 ,这一点跟原来想得不一样

在没有 unstable_cache,没有 noStore 的情况下,开不开 ppr,路由都会被渲染成静态的

3.非常非常有意思的一点,
假如现在有两个组件,第一个组件满足条件 1(只有 noStore),第二个组件满足条件 2(没有 noStore,没有 unstable_cache)

然后,会报错!!!直接黑人问号???

这时候你先后注释掉一个组件,你会发现,每一个都可以 build,不是 static,就是 partial,合一起就不行了。

我直接 hhhhhhhhhhhhhhh,要疯了
这个 bug 直接误导我们 db 操作是 revalidate0

---

### 第二个 bug
在第一个 bug 的基础上,我们想探索出,如何让两者共存
目标是,两个 db 查询的组件,一个是动态渲染的,一个是构建时渲染的
动态渲染的那个同样先不考虑 unstable_cache,因为那属于优化体验的事情.
直接考虑第二个组件,目前是什么都没有,没有 cache,noStore
这时候我们走进了第二个坑,准确来说就我一个
> 在 unstable_cache 内使用 unstable_noStore 不会选择退出静态生成。相反,它将根据缓存配置来确定是否缓存结果。
接着我们尝试在第二个组件中,在 unstable_cache 缓存的函数内使用 noStore,我说行,你说不行,那到底行不行?
答案是,有时候行,有时候不行

如果你意外让你的本地缓存里出现了你想要缓存的数据,那就行.

首先,删掉你的.next,然后 build,直接 errrrrrrrrrrrrrooor(感觉自己有点精神不稳定了)

第二部,注释掉 ppr,删掉.next(如果有),然后 build 一下,访问一下 dashboard 不出意外,第一个组件是动态渲染的,第二个组件是秒加载的,
值得注意的是,这时候 dashboard 路线是动态的,第二个组件在 build 的时候被执行过一次,得益于 unstable_cache(谢谢你啊我的 cache!!!!),你第二次(你以为你是第一次,实际不是)访问的时候,数据是秒加载的


这一点很容易验证,你在第二个组件首行加上终端输出就会看到,每次刷新都会有终端输出


最后,加上 ppr,然后 build,成功了????恩?恩?



---
一些多余的总结,包括你已经知道了的,
在 ppr 中,noStore 用来声明动态渲染组件,unstable_cache 可以用来缓存函数返回值,降低数据库压力
在 ppr 中,在 unstable_cache 中使用 noStore 目前会报错,应该在 cache 外使用


> 在 unstable_cache 内使用 unstable_noStore 不会选择退出静态生成。相反,它将根据缓存配置来确定是否缓存结果。
这句话好像只对非 ppr 有效

非 ppr 时,在 unstable_cache 中使用 noStore 确实不会退出静态生成
比如说,我们把第一个组件注释掉,在第二个组件中如此使用,生成的是 static 的
不过,为什么要在 unstable_cache 中使用 nostore 呢?明明不使用 unstable_cache,和 unstable_noStore,一样可以生成 static 啊(终端不会再有东西蹦出来了)


梳理一下应对方案和注意事项
假设你正在构造一个 ppr 的页面,除非你想让页面完全 static,那你所有的 db 组件可以什么都不用管,(不过那你还要 ppr 干啥呢)
就算你只有一个想动态渲染组件,这个组件也应该加上 noStore,不然它会 static

现在似乎没有办法在开启 ppr 的时候,只静态渲染某个 db 组件
---

最后的最后

我已经不想思考为什么了,好在我错的够多,聪明如我,已经在之前给出过答案了.

> 我的建议是 unstable_cache 梭哈,不设置过期时间,算是一种半静态吧,除了第一次比较慢,后面其他用户第二次访问就很快了
1  2  3  4  5  6  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2607 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 35ms · UTC 10:51 · PVG 18:51 · LAX 02:51 · JFK 05:51
Developed with CodeLauncher
♥ Do have faith in what you're doing.