Vue SSR中serverPrefetch到mounted数据丢失问题求助(数据预取与状态)
Hey there! Let's break down why your Vuex device value isn't sticking from the server to the client, and how to fix it.
Why Your Current Code Isn't Working
When dealing with Vue SSR, the server creates a new Vuex Store instance for every request, and the client spins up its own completely separate Store instance when the page loads.
Your current approach has two critical flaws:
- You're directly assigning
this.$store.deviceinstead of using Vuex's mutation system—this skips state tracking and best practices. - Most importantly, the server's Store state never gets passed to the client. The client starts with a fresh, empty Store, so
deviceends upundefinedin themountedhook.
Step-by-Step Solution
1. Add a Vuex Mutation for State Updates
First, fix how you modify state by adding an official Vuex mutation (this is the only recommended way to change Vuex state):
// store/index.js import { createStore } from 'vuex' export function createStore() { return createStore({ state() { return { device: null // Initialize the device state } }, mutations: { SET_DEVICE(state, device) { state.device = device } } }) }
2. Update serverPrefetch to Use the Mutation
Modify your component's serverPrefetch to commit the mutation instead of direct assignment:
export default { // ... serverPrefetch () { this.$store.commit('SET_DEVICE', this.$vnode.ssrContext.device) console.log(this.$store.state.device) // Still logs "desktop" }, mounted () { console.log(this.$store.state.device) // Will now log "desktop" instead of undefined! } // ... }
3. Pass Server State to the Client (Critical Step)
This is what makes the server's state available to the client-side Store.
Server Entry File
In your server-side entry script, after handling data prefetching, attach the Store's state to the SSR context:
// server-entry.js import { createApp } from './app' export default async context => { const { app, store } = createApp() // Wait for all async serverPrefetch calls to finish (if any) await Promise.all( app._instance.subTree.children.map(child => { if (child.type.serverPrefetch) { return child.type.serverPrefetch.call(child.proxy) } }) ) // Attach store state to the context for injection into HTML context.state = store.state return app }
HTML Template
Update your server-rendered HTML template to inject the serialized state as a global variable:
<!-- index.html (server template) --> <!DOCTYPE html> <html> <head> <title>Vue SSR App</title> </head> <body> <div id="app">{{ html }}</div> <!-- Inject server state into client-side global scope --> <script> window.__INITIAL_STATE__ = ${JSON.stringify(state)} </script> <script src="/client.bundle.js"></script> </body> </html>
Client Entry File
Finally, initialize the client-side Store with the injected server state:
// client-entry.js import { createApp } from './app' const { app, store } = createApp() // Replace client store state with the server's initial state if (window.__INITIAL_STATE__) { store.replaceState(window.__INITIAL_STATE__) } app.mount('#app')
Quick Notes
- Always use mutations to modify Vuex state—it ensures consistency and makes debugging easier.
- If
serverPrefetchdoes async work, make sure it returns a Promise so the server waits for data before rendering. - Avoid injecting sensitive data into
__INITIAL_STATE__—it will be visible in the client's HTML source.
内容的提问来源于stack exchange,提问作者gorevanova

