Inertia.js + Vue 3 SSR:服务端预取数据客户端重置问题咨询
Inertia.js + Vue 3 SSR:服务端预取数据后客户端被重置的问题分析与解决
场景与代码
我用Inertia.js + Vue 3实现SSR,希望在服务端渲染阶段预取并设置数据,编写的代码如下:
<template> data: {{myData}} </template> <script setup> import {ref} from "vue"; const isServer = typeof window === 'undefined' const myData = ref(null) if (isServer) { console.log('on server') myData.value = 'data from server' } else { console.log('on client') if (myData.value === null) { myData.value = 'data is null' } } </script>
当前现象
- 服务端与客户端的日志均能正常打印
- 浏览器中
myData初始显示为data from server,但很快变为data is null - 页面源码里
myData显示的是data from server
疑问与预期
- 客户端渲染时
myData是不是被重新定义成null了?这属于正常行为吗? - 是不是SSR无法实现预取数据?
- 预期效果:客户端渲染时显示骨架屏并执行局部重载;SSR时完成完整渲染,无局部重载
问题分析
客户端数据重置的原因
这是SSR水合(Hydration)机制的正常表现:服务端仅生成静态HTML字符串,客户端加载页面后会重新创建Vue组件实例——你的myData在客户端会被重新初始化为ref(null),随后进入else分支,因为此时myData.value为null,所以被替换成data is null。服务端设置的myData值没有被序列化传递到客户端,客户端完全是重新初始化的状态,因此会覆盖服务端渲染的HTML内容。是否属于正常行为
你的代码逻辑导致的结果是SSR机制下的“正常现象”,但这不是正确的SSR数据传递方式。SSR的核心是服务端将预取数据序列化后传递给客户端,让客户端在水合时复用这些数据,而非两端各自初始化。SSR可以预取数据,只是方式错误
SSR完全支持预取数据,只是你没有利用Inertia.js提供的SSR数据传递机制。
解决方案
1. 利用Inertia服务端Props传递预取数据(推荐)
Inertia.js在SSR时会自动将服务端返回的props序列化并传递给客户端,客户端通过usePage()即可获取这些数据,无需重新初始化。
服务端示例(以Laravel为例):
在控制器中预取数据,通过Inertia返回:
public function show() { // 服务端预取数据 $myData = 'data from server'; return Inertia::render('YourPage', [ 'myData' => $myData, ]); }
客户端Vue组件:
<template> <div v-if="!pageProps.myData"> <!-- 骨架屏 --> <div class="skeleton">Loading...</div> </div> <div v-else> data: {{ pageProps.myData }} </div> </template> <script setup> import { usePage } from '@inertiajs/vue3'; import { onMounted } from 'vue'; const page = usePage(); const pageProps = page.props; // 客户端局部重载逻辑(可选) onMounted(() => { // 可在此发起请求更新数据,示例: // fetch('/api/data').then(res => res.json()).then(data => pageProps.myData = data); }); </script>
2. 手动序列化服务端数据(特殊场景使用)
若不想通过Inertia props传递,可在服务端将数据注入全局window对象,客户端读取:
服务端模板示例(如Blade):
<script> window.__INITIAL_DATA__ = { myData: '{{ $myData }}' }; </script>
客户端组件:
<template> <div v-if="!myData"> <!-- 骨架屏 --> <div class="skeleton">Loading...</div> </div> <div v-else> data: {{ myData }} </div> </template> <script setup> import { ref, onMounted } from 'vue'; const isServer = typeof window === 'undefined'; const myData = ref(null); if (isServer) { // 服务端预取数据 myData.value = 'data from server'; } else { // 客户端读取全局初始数据 myData.value = window.__INITIAL_DATA__.myData; // 客户端局部重载逻辑 onMounted(() => { // 可在此发起请求更新数据 }); } </script>
3. 骨架屏与无局部重载的实现
- SSR场景:服务端传递
myData后,客户端水合时直接复用数据,会直接渲染内容,不会显示骨架屏,也无局部重载。 - 客户端路由跳转场景:Inertia会发起AJAX请求获取props,此时可在组件中判断
pageProps.myData是否存在,显示骨架屏直到数据返回后渲染内容。
内容的提问来源于stack exchange,提问作者kresek
相关产品推荐
相关产品推荐

