@
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/sqlinit {
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/sqlinit {
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