V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  epiloguess  ›  全部回复第 5 页 / 共 5 页
回复总数  100
1  2  3  4  5  
@lazyczx

只是一个假设,方便测试,使用 canary 但是不使用 ppr,表现出的行为应该和正式版的行为是一样,如果每次测非 ppr 都要切换回正式版就很麻烦了。


---

> 后备与其他静态内容一起嵌入到初始静态文件中。在构建时(或重新验证期间),路线的静态部分被预渲染,其余部分被推迟,直到用户请求路线。

主要的问题在与,你要深刻理解 nextjs 中服务端组件的渲染机制

动态组件,在客户端的服务端组件,客户端组件本身
ppr 和静态+cache+dynamic
静态导出/静态渲染
这些概念不要搞混,这些并不相等

> https://nextjs.org/docs/app/building-your-application/rendering/server-components
> 使用静态渲染,路线在构建时渲染,或者在数据重新验证后在后台渲染。结果被缓存并可以推送到内容交付网络 (CDN)。此优化允许您在用户和服务器请求之间共享渲染工作的结果。

在没有 ppr 之前,路线只有静态渲染,动态渲染,流式渲染
这就意味者,一旦一条路线被确认为动态渲染,这条路线中的所有内容都不会在构建或者重新验证后在后台渲染,从而被缓存为 RSCP 和 html ,推送到 CDN,共享渲染结果
而是会在请求的时候进行渲染,在请求的时候才利用 data cache (或者 unstable_cache)加速渲染结果

这也是为什么我们需要 ppr,尽可能提前渲染更多的内容,加速体验


> 在渲染过程中,如果发现动态函数或未缓存的数据请求,Next.js 将切换为动态渲染整个路由。下表总结了动态函数和数据缓存如何影响静态或动态渲染路由:

这里是理解的重点,动态函数和未缓存的数据请求
动态函数中就包括你后面的迷惑点 searchParams ,虽然它是你传的 props,但是只有在请求的时候才能被获取,页面也会因为变成 dynamic
未缓存的数据请求,包括,
> https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#opting-out-of-data-caching
The cache: 'no-store' is added to fetch requests.
The revalidate: 0 option is added to individual fetch requests.
The fetch request is inside a Router Handler that uses the POST method.
The fetch request comes after the usage of headers or cookies.
The const dynamic = 'force-dynamic' route segment option is used.
The fetchCache route segment option is configured to skip cache by default.
The fetch request uses Authorization or Cookie headers and there's an uncached request above it in the component tree.

其中数据库查询的背后用的就是 revalidate0,这一点,我给出那个 github 上的 discussion,那个信息已经过时,unstable_cache 在那之后发生了一次重构,本身也很合理,没理由一个数据库操作自带 cache,改成用 revalidate0 很合理

官方文档这一部分也值得多阅读几次
> https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#fetching-data-on-the-server-with-third-party-libraries

---
> 加上了 unstable cache 之后,如果没有设定 revalidation 的话,不管怎么操作,都不会 rerender 了,就像是一个动态页面里独立出来了一个静态的玩意儿

unstable cache 的缓存是存放在服务端的,在.next/cache 目录下,完全重新部署就会没有的,只是作为组件内 db 操作无法 ppr 的权宜之计,这一点后面再说,实际上,如果你这么使用,本质上和不用 ppr 没有区别,单就这个组件而言,表现出的效果是一样的


---

> 我刚刚去把版本换成 canary without ppr 测试了一下,我用了 noStore 在 API 方法里,然后使用 cache 把 API 包裹起来,生成树还是静态的(注意我没有使用你上面回答里的推荐做法,即把 noStore 放在组件里,放在 cache 方法上方,不知道是不是这里有误解。。)。

这一点我之前已经提到过了,不过就连我自己都忘了,借助这一点我们就可以 ppr 数据查询操作了,太棒了!
> https://nextjs.org/docs/app/api-reference/functions/unstable_noStore
> 在 unstable_cache 内使用 unstable_noStore 不会选择退出静态生成。相反,它将根据缓存配置来确定是否缓存结果。

让我们简单回顾一下
1.在正式版/canary without ppr 中,如果我们在页面中任意位置声明了 noStore ,路线就会退出静态渲染,因为这相当于在路线中出现了 未缓存的数据请求,等价与 force-dynamic 或者 no-store
但是,如果不声明 noStore ,尽管我们猜测 db 操作背后是 revalidate0,它不会让路线退出静态渲染,
关于这一点,或许我们就不应该在正式版中使用 noStore,这个 function 如我之前所说,是用来在 ppr 中细粒度的控制组件渲染方式的
2.在 ppr,
如果我们在 suspense 边界内部声明了 noStore,根据约定,suspense 内部应该是一个动态组件,内容不应该被 ppr
如果我们的组件没有声明 noStore,却使用了 db 操作,我们猜测 db 操作背后是 revalidate0 ,nextjs 会表现出和正式版不一样的操作,它会很困惑,不知道我们到底要做什么,我觉得这里就是一切困惑的来源。ppr 无法和 revalidate0 共存,可以提 issue 。
好在官方可能考虑过这一点了,如果你在 unstable_cache 缓存的函数中声明 noStore,那么在 nextjs 看来,组件仍然是可以被 ppr 的,不会受到 nosStore 和 revalidate0 影响了

---


> 我真是惊了个呆 o_o ....,这个不是我自己定义的一个 prop 吗。为什么会把 route 变成 dynamic 的。。。

上面已经提到了,searchParams 属于动态函数

> 另外我发现我之前没 push 数据库配置。。。我打开你 fork 的仓库跑起来直接数据出不来。。现在我 push 上去了)

你需要 clone 我 fork 的版本然后加上.env 啊,不然没有数据啊,这一点我忘了提了


---

> 这部分不懂,动态组件是 PPR 的概念对吧?意思是动态组件也可以调用这个内置的数据缓存吗?如果是这样的话,那 sql 不也是基于 fetch 的吗?意思是通过恰当的配置,可以让一个动态组件每次读的是缓存的数据?

> 动态组件的情况下,并不意味着我们不能用缓存吗?

没错,是这样的,前面说过了,要分清楚动态组件和服务端组件的区别

或许你应该把 ppr 换个角度,看成缓存了更多静态内容的动态页面

动态组件也要经过服务器才能查询 db ,或者 fetch 相关数据,不可能把 db 相关操作放到客户端来进行

这也就意味着他们可以使用
1. fetch 带来的持久缓存
2. 每次 req-res 周期中存在的 rect Request Memoization
3.unstable-cache

但是动态组件并不能使用 全路由缓存
> https://nextjs.org/docs/app/building-your-application/caching#opting-out-2
> 您可以选择退出完整路由缓存,或者换句话说,为每个传入请求动态渲染组件,方法是:


---

不会觉得 bother

我其实也是最近一个月才接触 nextjs,hh ,没有做过这个项目,因为我学东西喜欢从 doc 看起,doc 中其实 canary,ppr 的内容很少,几乎没有。很高兴多学习了一些知识,也加深了自己对 nextjs ,data fetch ,cache,render 的理解。

码字组织语言有助于提高自己的逻辑表达能力,不过 v 站这个排版效果真是让我一言难尽哦
确实,目前函数式组件是主流
参考
https://npmstats.com/package/react
更正:
1. 我看到一个 noStore() 标注的方法执行的时候,别的 API 方法也跟着被调用,但是作为 cache fallback 的方法不被调用。
第一点讨论的有点多余,因为我把 fallback 看成 callback 了

2.但是这并意味着我们就没有 data cache 了
“并”改成“并不”
忘了发 fork 地址了抱歉,https://github.com/epiloguess/nextjs-dashboard
前提:canary 但是 config 里面注释掉 ppr = 正式版

1.
> 我看到一个 noStore() 标注的方法执行的时候,别的 API 方法也跟着被调用,但是作为 cache fallback 的方法不被调用。我觉得这个就是 ppr 了对吗? canary 的支持只不过让这个实现更加简单了一点而已:不需要 cache 方法包裹,而是没有使用 noStore 就默认为 cache 。

ppr 指的是部分预渲染,相当于默认一切都是静态的,只要 suspense 里面没有 no store,但是这里有坑,最后说。

在正式版/canary without ppr 中,
这个 noStore 是不是 cache 的 callback,都没有关系,只要你这个路线中,任意一个地方出现了 noStore,next 在 build 的时候,就等价于路由段配置中的 force-dynamic 或者 fetch 中的 no-store,相当于退出静态渲染,改用动态。

当你刷新的时候,为什么另外两个组件都去获取数据了,有 cache 的却没反应呢,因为 unstable_cache 它 cache 的函数的返回值。

所以这跟 ppr 有什么关系?重要的是要把 ppr 和 cache 分开。

你加 cache,只是为了优化,降低它查询数据库的频率。

2.
有一点非常值得注意的是,在 nextjs 中的 fetch,和 unstale_cache 表现是不一样的,

fetch 缓存的是 fetch 请求的返回值,也就是说,为什么在没有 unstable_cache 的时候,你会看到你写的测试时间的方法被调用,因为整个请求数据+计算时间的函数都会被执行,只不过 await sql`` 的时候,直接从缓存中给你值了(这里也有坑)

而 unstable_cache 缓存的是给定函数的返回值,(请求数据+计算时间)这个函数的返回值被缓存了,里面的计算时间自然不会被执行了。

3.
> 但是我发现,如果页面是 static 的,肯定就不会有问题。但是我猜大概从表现的危害上来说也可以接受吧,毕竟页面都是 dynamic 的,让它每点一下就调一次又怎么样了呢,但是原因还是不清楚。

static 的时候,直接就是 html 了,肯定不会调用你写的计时方法。
你需要确保的是,如果函数都加上 unstable_cache 了,你这个问题应该就不会出现了吧。


4.
> 关于第 5 点,我不知道你是怎么测试的,但是我测试的情况下,如果 API 方法里使用了 noStore ,然后把这个方法作为 cache 的 fallback 的话,确实不会退出静态生成。build 的时候显示的 tree 也显示页面仍然是 static ,

你这个是在什么条件下测试的?

> 如果不用 cache 的话,tree 马上变为动态 λ 的了。
在正式版中,任意组件任意位置存在 noStore ,是的,路线就会退出静态渲染,跟 cache 没什么关系。

> 我感觉正式版里只要用 cache 了,noStore 就跟没有调用一样,好像 cache 这个缓存,会直接接管这整个方法调用的结果,然后选择是否缓存。

正如我前面所说,这事儿跟 cache 没什么关系,cache 只是为了降低 动态组件 查询数据库的频率,在静态路线里写不写 cache 没什么意义,是同一个结果

5.一些坑以及一些猜测
深入理解 nextjs 的缓存,
我 fork 了你的项目,RevenueChart 没有 cache,另外两个有,build,start
当你刷新页面的时候,你会发现 RevenueChart 在获取数据,你的终端上出现 fetching data,另外两个数据立刻就有了
这中间发生了什么?

> unstable_noStore can be used to declaratively opt out of static rendering and indicate a particular component should not be cached.
> unstable_noStore 优于 export const dynamic = 'force-dynamic' ,因为它更细粒度并且可以在每个组件的基础上使用

unstable_noStore 是用来配合部分预渲染,实现细粒度的控制组件的渲染方式,当你在另外两个组件中声明 noStore 的时候,就成了一个动态组件
> unstable_noStore is equivalent to cache: 'no-store' on a fetch
> no-store - Next.js 在每次请求时从远程服务器获取资源,而不查看缓存,并且不会使用下载的资源更新缓存。

但是这并意味着我们就没有 data cache 了

> Next.js 有一个内置的数据缓存(data cache),可以在传入的服务器请求和部署中保留数据获取的结果。这是可能的,因为 Next.js 扩展了本机 fetch API 以允许服务器上的每个请求设置自己的持久缓存语义。


---以下为猜测---
假如我们的组件声明了 noStore,组件内部 fetch 了一个资源,没有理由这个 fetch 会退出 nextjs 的 data cache.

我们声明了 noStore,只表达了这是个动态组件,它不是网页静态的一部分,当你访问/刷新网页的时候,你应该永远从服务器获取组件的内容(这里先不提客户端缓存)


因为 @vervel/postgre 的 sql 也是基于 fetch 的
这一点很奇怪,完全想不通,但是参考
https://github.com/orgs/vercel/discussions/4696
https://nextjs.org/docs/messages/ppr-caught-error
> Database Error: NeonDbError: 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
> As a convenience, it is not necessary to set the cache option if revalidate is set to a number since 0 implies cache: 'no-store' and a positive value implies cache: 'force-cache'.
> 为方便起见,如果 revalidate 设置为数字,则无需设置 cache 选项,因为 0 隐含 cache: 'no-store' 且正值意味着 cache: 'force-cache'

sql 的内部实现可能用的是 fetch(``, { next: { revalidate: 0 } })

一个悲伤的事情是,正式版的静态渲染和 revalidate 0 配合工作良好,ppr 却不行,这也就意味着,对于带有数据库查询的组件,你并不能 ppr 它们变成完全静态的

当然,这个问题是可以被解决的,配合 router handle,你其实可以用 fetch 获取某些数据库的信息(安全吗?)

---接下来让我们回到刷新后的渲染过程---

为了解决 sql 的 revalidate0,我们引入了 unstable_cache 。

当我们刷新页面的时候,服务器上已经有了那两个组件 cache 的 RSCP,直接就发送过来了,内容是立即出现的
而 RevenueChart 没有 cache,并且由于 sql 的 revalidate0 ,需要重新查询数据库,终端上出现 fetching data

为什么第一次慢后面快?所以给你一种查询没有发出去的错觉?
猜测 1:可能确实没发出去,毕竟 revalidate0 是我的猜测
猜测 2:发出去了,第一次慢是因为网络问题,vercel 在国外,tcp 慢启动,第二次第三次就快了

这一点其实也很好验证,你一边刷新(可以用插件自动刷新),一边在 vercel 数据库中新建一个数据就可以判断了,交给你了,等你反馈

---客户端缓存/路由器缓存---

> 当用户在路线之间导航时,Next.js 会缓存访问过的路线段并预取用户可能导航到的路线(基于视口中的 <Link> 组件)。
> 导航之间不会重新加载整页,并且会保留 React 状态和浏览器状态。
> 会话:缓存在整个导航过程中持续存在。但是,它会在页面刷新时被清除

这一点也很好验证,当你刷新的时候,RevenueChart 会重新获取数据,当你点击左边导航随便一个再点回来,不会触发重新获取,终端上也不会有 fetching data


---最后---

目前你这个例子有一点小,还都是获取 db ,可能无法明显看出 ppr 的优势

对于 ppr 和 sql revalidate 0 的问题,如果真的有想要完全静态的组件还带有 db 查询,
我的建议是 unstable_cache 梭哈,不设置过期时间,算是一种半静态吧,除了第一次比较慢,后面其他用户第二次访问就很快了
232 天前
回复了 hypnosj 创建的主题 程序员 请教有后端基础如何学习前端开发
可以先看一些高屋建瓴的文章
https://frontendmastery.com/posts/the-new-wave-of-javascript-web-frameworks/

https://frontendmastery.com/posts/navigating-the-future-of-frontend/

https://frontendmastery.com/posts/building-future-facing-frontend-architectures/

你贴的帖子里面也有一个 roadmap ,基本上按照那个学没问题。

不过里面的技术都是国外的主流,国内其实还是有一些差别的。

可以去这里比较,比如说 vue ,
https://npmstats.com/package/vue
这里补充一下,为什么 unstable_noStore 在正式版和 canary 里表现不一致,可能是因为这个 function 就是为了后续的功能开发的,当前版本可以通过配置路由段,或者 fetch 一个空数据加上 no-store 来退出静态渲染
这个问题比较复杂,我一点一点回答你。
先讨论正式版,再讨论部分预预渲染
1.next dev 和 build 的渲染逻辑是不一样的,如果你把 data.ts 中 dashboard 相关的那三个组件的 noStore 注释掉,然后 build 一下,你会发现 /dashboard 生成的是静态页面,因为页面没有动态函数而且数据都 cached 了,但 dev 的时候情况可能不一样,数据可能没有全部 cache 完,或者说每次刷新的时候都会重新请求相关数据,这样其实更符合逻辑。注意,你加不加 suspense 都不会影响静态渲染
2.在正式版中,如果你给那三个组件中任意一个加上了 noStore ,整个 dashboard 页面,包括那三个组件都会退出静态渲染,这一点也并不难理解,回顾官方定义,再考虑一下目前渲染路线其实就两种,组件又在页面之中,next 在 build 的时候遇到 noStore 就知道下一步该选什么渲染路线。
> unstable_noStore 可用于以声明方式选择退出静态渲染并指示不应缓存特定组件。

3.你希望达成什么?我想应该是不能够每次刷新都去查询数据,最好可以手动 revalidateTag 。
有两个函数,可能可以帮到你,react cache 和 unstable_cache
https://nextjs.org/docs/app/building-your-application/caching#react-cache-function
https://nextjs.org/docs/app/api-reference/functions/unstable_cache

你可以现在就试试,记得随便一个组件上加上 noStore,然后在 RevenueChart 组件上创建一个函数,
import { unstable_cache} from 'next/cache';

const getCachedRevenue = unstable_cache(
async () => fetchRevenue(),
['Revenue']
);
组件内部开头删掉
const revenue = await fetchRevenue()
然后这样写,
const revenue = await getCachedRevenue()

先 build,start,然后进 dashboard ,在刷新的时候,你会发现,Revenue 组件会保持不变,另外两个出现了骨架屏,可以换个浏览器或者进隐私窗口,一样是秒开。
4.关于你的第二点,没看太明白,这个 30s,5 分钟之类的,都是客户端缓存,你在开发者工具的网络选项卡把选一下禁用缓存,第三点的效果不会变,多看看文档这一节,https://nextjs.org/docs/app/building-your-application/caching#overview
5.最后再来说一下部分预渲染,

根据,https://nextjs.org/docs/app/api-reference/functions/unstable_noStore
在 unstable_cache 内使用 unstable_noStore 不会选择退出静态生成。相反,它将根据缓存配置来确定是否缓存结果。

这句话不太好懂,不过我实测的结果就是,如果你是正式版,那么无论你任何地方使用了 noStore ,在 build 的时候,路线都会变成动态,而如果你是 canary,想要使用部分预渲染,最好还是不要在 unstable_cache 缓存的函数内使用 noStore,参考 https://nextjs.org/docs/messages/ppr-caught-error
> 确保您没有将选择动态渲染的 Next.js API 包装在 try/catch 块中。
尽管官方建议,可以在 try...catch 前插入 noStore, 但后面实现缓存函数不太方便,所以我个人建议,可以在组件的第一行,也就是 const revenue = await getCachedRevenue()的上面一行,使用 noStore ,第二行用 cache,逻辑也比较清晰。


同时,根据,https://nextjs.org/docs/app/api-reference/next-config-js/partial-prerendering
你需要 npm install next@canary ,然后改 config (这个我看有注释),记得每个组件都要加上 noStore ,不然参考第一条,就会被当成静态内容一次生成哦(关于哪些内容被生成静态的了,你可以去开发者工具的网络选项卡,预览第一个送过来的 Document ),然后可以 build 了哦。

祝好!

其他参考(不分先后,我也没看(完),不过很值得看):
- https://github.com/vercel-labs/next-partial-prerendering
- https://github.com/orgs/vercel/discussions/4696
- https://codedrivendevelopment.com/posts/rarely-known-nextjs-features
- https://github.com/vercel/next.js/pull/56930
- https://stackoverflow.com/questions/76829076/in-next-js-13-app-router-how-can-i-use-data-caching-when-not-using-fetch-but
- https://github.com/vercel/next.js/discussions/54075
233 天前
回复了 Licsber 创建的主题 程序员 人月神话的困境
今天读过一篇类似的文章,项目的压力往往不仅项目本身,管理能力也是必不可少的

https://frontendmastery.com/posts/the-three-ds-of-frontend-feature-leading/
233 天前
回复了 fen 创建的主题 程序员 大家写静态博客是否会搭配 Headless CMS 使用?
我用的 astro+obsidian,没有用 cms,通过 frontmatter 的字段控制元信息,obsidian 自带 git 插件可以在修改之后推送的远程 git,通过模板自动插入 frontmatter,包括标题日期标签,默认 featured 为 false,draft 为 true,感觉挺自动化的,好像用不上 cms
hackernews ?
https://wunhao.com
有是有,就是不怎么更新,主要是自己水平不够 hhh
至于怎么帮助到人,大概别人搜 bug 的时候能在你这里搜到解决思路,就算有所帮助了吧。
关于推广,说实话,如果为了流量,个人运营,选择一些自带信息流的平台会更好,坚守博客的大多还是有个人的想法吧。

附上我比较喜欢的博客,https://www.taniarascia.com/me

> This site has no ads, no affiliate links, no tracking or analytics, no sponsored posts, and no paywall. My motivation for this site is to have a space for self-expression and to share what I've learned with the world. I hope I will inspire others to make their own creative corner on the web as well.
> 该网站没有广告,没有会员链接,没有跟踪或分析,没有赞助帖子,也没有付费墙。我创建这个网站的动机是有一个自我表达的空间,并与世界分享我所学到的东西。我希望我能激励其他人在网络上也创造自己的创意角落。
239 天前
回复了 LawlietZ 创建的主题 程序员 4202 年了,前端开发一定必须要用 mac 吗
我用的 debian testing, 日常开发预览用 firefox,最后才用 chromium 跑一下 lighthouse,顺便看看哪里有什么布局 bug
基数大,升级成本高,基本上大部分软件版本都和主流版本分布有差异
确实,gulp 没什么人用了
https://npmstats.com/tags/bundler
241 天前
回复了 zxy23 创建的主题 前端开发 VUE3 的前端开源框架推荐一下?
可以看看我最近在做的网站,https://npmstats.com/tags/vue ,不过可能帮不到你什么,更适合新手,你已经有很多经验了。
@rowink 是的,这个数据来自国内的 npmmirror ,淘宝镜像,后期再添加 npm 的。
不做动态获取,因为这个 api 返回的数据太大了,Next.js 缓存不了,不想从客户端发请求滥用 api ,考虑成本,使用场景,还是选择构建纯静态的,维护一个{"pkg":[tags]} json 就可以搞定,每个月定期更新。
类似的网站有,npm trends ,best of js ,说不定能满足你的需求。

我有考虑过引入时间成本,用 下载量/包创建后的时间 ,这样可以选择更新更好用的轮子,慢慢搞
https://npmstats.com/ 最近在做一个类似的网站,不过还在早期,而且可能和你的需求不完全一致
1  2  3  4  5  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2879 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 26ms · UTC 14:42 · PVG 22:42 · LAX 06:42 · JFK 09:42
Developed with CodeLauncher
♥ Do have faith in what you're doing.