一、问题描述
在页面setup中调用$http.get(封装了useFetch),发现不仅在服务端发送了接口请求,而且在客户端也重新发送了一遍接口请求,造成资源浪费及页面加载缓慢。
二、问题原因
首先看一下Nuxt 的useFetch文档:
This composable provides a convenient wrapper around useAsyncData and $fetch. It automatically generates a key based on URL and fetch options, provides type hints for request url based on server routes, and infers API response type.
这个可组合函数提供了一个方便的封装,包装了useAsyncData和$fetch。它根据URL和fetch选项自动生成一个键,根据服务器路由提供请求URL的类型提示,并推断API响应类型。
useFetch is a composable meant to be called directly in a setup function, plugin, or route middleware. It returns reactive composables and handles adding responses to the Nuxt payload so they can be passed from server to client without re-fetching the data on client side when the page hydrates.
useFetch是一个在直接在setup函数、插件或路由中间件中调用的组合函数。它返回响应式的组合函数(响应式数据和逻辑抽象为可重用的 composable 函数),并处理将响应添加到Nuxt的负载中,以便在页面水合(Hydration)
时可以从服务器传递给客户端,而无需在客户端重新获取数据。
这里提到一个概念Hydration
,这里涉及到有关Nuxt的渲染模式的介绍:
Making a static page interactive in the browser is called “Hydration”.
为了保证前端程序员能够使用熟悉的方式编写页面,即“同构开发”,服务端渲染时,Nuxt 实际上是在服务器上执行 Vue。在服务器端渲染时将页面的静态内容和交互逻辑一起发送给浏览器,使页面在首次加载时能够更快地呈现给用户,同时也能够保持页面的交互性能。这种方式可以有效减少页面加载时间和提升用户体验。客户端激活时执行的 JS 实际上也是 Vue,它会重新接管文档,恢复数据和状态,使静态页面变得可交互,这一过程称为“水合(Hydration)”。
所以,使用useFetch不应该会导致客户端二次获取数据,因为页面已经在服务器端获取过一次数据,并一起返回给客户端,所以客户端不需要再额外请求:
我们看一下服务端获取的数据是怎样的存在,在network中找到html内容,可以发现数据应该是存在于这样的一个标签节点内:
<script type="application/json" id="__NUXT_DATA__" data-ssr="true">
:
再调试入断点:
可以发现缓存数据的提取代码:
const getDefaultCachedData = ()=>nuxtApp.isHydrating ? nuxtApp.payload.data[key] : nuxtApp.static.data[key];
其中关键就在于nuxtApp.payload.data[key]
,里面的key
是useAsyncData
的入参,看一下useAsyncData的文档关于key
参数说明:
key: a unique key to ensure that data fetching can be properly de-duplicated across requests. If you do not provide a key, then a key that is unique to the file name and line number of the instance of useAsyncData will be generated for you.
key:一个唯一的键,用于确保数据获取可以在请求之间正确去重。如果未提供,将根据使用useAsyncData的静态代码位置生成。
所以key
就是用来防止服务端和客户端重复请求获取数据的标识。之前提到,服务端在获取数据后,会将数据附加到Nuxt payload
(也就是nuxtApp.payload),它和前面提到的__NUXT_DATA__
之间应该是有关联的,具体是如何关联上的,我们下一篇文章再展开。
三、解决办法
在使用useFetch时,加上key参数,来唯一标识每个请求: