Client-side hydration of server-side rendering
Client-Side Hydration in Server-Side Rendering
Client-side hydration in Server-Side Rendering (SSR) refers to the process of converting statically rendered HTML from the server into a dynamic, interactive application. Vue 3 implements this mechanism through the hydrate
function, ensuring seamless integration between the DOM structure generated by the server and the client-side application.
Relationship Between SSR and Client-Side Hydration
Server-Side Rendering produces static HTML strings without any interactive logic. When this HTML is sent to the browser, the client-side Vue application needs to "take over" these DOM nodes:
<!-- Server-rendered result -->
<div id="app">
<button>Click count: 0</button>
</div>
Corresponding client-side code:
import { createSSRApp } from 'vue'
const app = createSSRApp({
data() {
return { count: 0 }
},
template: `
<button @click="count++">Click count: {{ count }}</button>
`
})
app.mount('#app', true) // Second parameter enables hydration
Core Logic of the Hydration Process
The hydration process in Vue 3 primarily occurs during the mount
phase, with key steps including:
- DOM Comparison: Checks whether the server-rendered DOM structure matches the client-side virtual DOM.
- Event Binding: Adds event listeners to existing DOM elements.
- State Synchronization: Synchronizes the client-side application state with the server-rendered snapshot.
Example of the comparison algorithm:
function hydrate(node, vnode) {
if (node.nodeType === Node.TEXT_NODE) {
// Compare text content
if (node.textContent !== vnode.children) {
// Handle mismatch
}
return
}
// Compare element type and attributes
if (node.tagName.toLowerCase() !== vnode.type) {
// Handle mismatch
}
// Recursively compare child nodes
for (let i = 0; i < vnode.children.length; i++) {
hydrate(node.childNodes[i], vnode.children[i])
}
}
Common Issues and Solutions
Hydration Failures
When the server and client rendering results do not match, Vue performs the following actions:
- Development Warnings: Outputs mismatch details in the console.
- Recovery Strategy: By default, discards the server-rendered DOM and re-renders.
Custom handling can be implemented via the hydrateMismatch
hook:
app.config.globalProperties.$hydrate = {
onMismatch(node, vnode) {
console.warn('Hydration mismatch at:', node)
// Custom recovery logic
}
}
Handling Dynamic Content
For fully dynamic content (e.g., timestamps), use the v-once
directive or <ClientOnly>
component:
<template>
<div>
<!-- Renders the same time on both server and client -->
<span v-once>{{ serverTime }}</span>
<!-- Renders only on the client -->
<ClientOnly>
{{ clientTime }}
</ClientOnly>
</div>
</template>
Performance Optimization Strategies
Partial Hydration
For large applications, partial hydration can be adopted:
// Hydrate only specific components
import { hydrateComponent } from 'vue'
hydrateComponent(
document.getElementById('interactive-component'),
MyComponent
)
Lazy Hydration
Non-critical components can be hydrated lazily:
import { onMounted } from 'vue'
onMounted(() => {
import('./HeavyComponent.vue').then(module => {
hydrateComponent(document.getElementById('heavy'), module.default)
})
})
Debugging Tips
During development, hydration issues can be debugged using:
- Checking the
__VUE_HYDRATION_MISMATCH__
global variable in the browser console. - Using the "Hydration" panel in Vue Devtools.
- Adding custom hydration logs:
app.config.performance = true
app.config.globalProperties.$hydrate = {
onNodeMismatch(node, vnode) {
console.group('Hydration mismatch')
console.log('DOM node:', node)
console.log('Virtual DOM node:', vnode)
console.groupEnd()
}
}
Integration with the Composition API
When using the Composition API with SSR, special attention is needed for reactive state synchronization:
import { ref, onServerPrefetch } from 'vue'
export default {
setup() {
const data = ref(null)
onServerPrefetch(async () => {
data.value = await fetchData()
})
return { data }
}
}
During client-side hydration, the data fetched on the server is automatically reused to avoid duplicate requests.
Handling Custom Directives
Custom directives require special handling in SSR environments:
const myDirective = {
mounted(el, binding) {
// Client-side logic
},
ssrRender(props, directives, context) {
// Server-side rendering logic
return {
class: directives.myDirective.value ? 'active' : ''
}
}
}
Third-Party Library Compatibility
Many UI libraries require special configuration for SSR hydration:
// Example with Element Plus
import ElementPlus from 'element-plus'
const app = createSSRApp(App)
app.use(ElementPlus, {
ssr: true // Enable SSR mode
})
Incompatible libraries can be wrapped in <ClientOnly>
or dynamically imported.
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:自定义渲染器的扩展点
下一篇:测试工具的内部支持