遇到一个问题:使用useFetch获取页面数据出现水合过程DOM不匹配,代码如下:
const { data, error } = await useFetch('/api/request', {
method: 'POST',
baseURL: process.client ? '' : runtimeConfig.public.apiBase,
})
问题表现是,在浏览器端,刚开始能看到数据,当浏览器加载完成之后,页面数据消失,控制台出现警告:
Hydration children mismatch in <div>: server rendered element contains more child nodes than client vdom.
观察到在浏览器有再次获取接口数据,接口数据和服务端的一致,并无区别. 在Nuxt3的useAsyncData文档中有这句话:
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.
意思是服务端获取到的数据会通过 payload 的形式传递给客户端,客户端不需要再次获取.出现问题的原因可能是,服务端获取的数据,客户端认为并没有命中缓存,重新获取一次,因为重新获取,导致渲染的时候无数据,出现水合DOM不匹配问题.
useFetch是useAsyncData的封装,useAsyncData的第一个参数就是用于缓存的key(见 在 Data fetching · Get Started with Nuxt 的 caching-and-refetching 一节).那么问题应该是useFetch传递给useAsyncData的key参数,在服务端和客户端并不一致.
那么就只能看看useFetch是如何生成缓存key的了.
在nuxt/packages/nuxt/src/app/composables/fetch.ts at main · nuxt/nuxt (github.com)中:
const _key = opts.key || hash([autoKey, typeof _request.value === 'string' ? _request.value : '', ...generateOptionSegments(opts)])
可以看到,在自动生成key值的情况下,key值跟_request.value(在本例中是'/api/request')以及opts(也就是本例中的method和baseUrl组成的对象)有关.
因为 baseUrl 在服务端和客户端并不相同,导致hash的结果也不相同,所以缓存不命中.
那么解决办法就是手动指定一个 key 值,修改如下:
const { data, error } = await useFetch('/api/request', {
method: 'POST',
baseURL: process.client ? '' : runtimeConfig.public.apiBase,
key: 'a-unique-key'
})
修改后问题解决.在看文档的时候,以为useFetch仅仅以传给它的接口字符串作为key,但实际上key还包括了请求的具体参数.