回忆 Vue3 中自定义指令:
<template> <input v-focus /></template>
<script setup>const vFocus = { mounted: (el) => el.focus(),}</script>在 SSR 模式下,由于没有真实的 DOM,Vue 在渲染阶段不会执行大部分直接操作 DOM 的自定义指令逻辑,比如 mounted 钩子中对 DOM 元素的修改(例如设置元素的 id、class 或样式等)。
为了在 SSR 中也能“呈现出”自定义指令想要产生的效果,Vue3 引入了一个特殊的钩子函数:getSSRProps。该钩子函数的作用是在服务器端渲染期间,将自定义指令需要作用于渲染结果上的属性提前返回,然后 Vue 将这些属性合并到最终生成的 HTML 元素中。
例如:
// 自定义指令const myDirective = { mounted(el, binding) { // 这是属于客户端的实现 // 设置元素的 id,相当于直接更新 DOM el.id = binding.value }, getSSRProps(binding) { // 会在服务端执行 // 可以返回一个对象,该对象就是需要渲染的属性的键值对 return { id: binding.value, } },}- 客户端行为:真实 DOM 挂载后,执行 mounted 钩子,将元素的 id 设置为 binding.value
- 服务端行为:在 SSR 期间,不会执行 mounted 钩子,取而代之是执行 getSSRProps。该方返回一个对象,对象里面是要挂载到 html 元素上的属性的键值对
有些时候我们希望指令根据绑定值做一些条件渲染,在 SSR 中也保持一致。举个例子:如果绑定值为真,则添加某个属性,否则不添加。
此时要让 SSR 渲染和客户端渲染保持一致,可以这样做:
const vConditionalDirective = { mounted(el, binding) { // 客户端:根据绑定值添加或移除 class if (binding.value) { el.classList.add('active') } else { el.classList.remove('active') } }, getSSRProps(binding) { // 服务端:如果绑定值为 true,则返回 class 属性 if (binding.value) { return { class: 'active' } } // 返回空对象表示不需要添加任何属性 return {} },}在模板中使用:
<template> <!-- 当传入 true 时,元素会拥有 active 样式类 --> <div v-conditional-directive="true">Conditional SSR</div></template>课堂练习
在很多项目中,我们希望在元素上显示一个自定义的提示信息,客户端可以使用 Popper.js、Tippy.js 或自定义代码来实现丰富的交互效果;但在服务端渲染时,由于没有 DOM 操作,无法创建复杂的 tooltip,通常我们希望输出一个标准的 title 属性来提供基本的提示。
Nuxt注册自定义指令
首先拿到 Vue 实例,然后在此基础上进行注册:
export default defineNuxtPlugin((nuxtApp) => { // 通过 nuxtApp.vueApp 拿到 Vue 的实例,之后的注册方式和原生 Vue 是一样的 nuxtApp.vueApp.directive('focus', { mounted(el) { el.focus() }, getSSRProps(binding, vnode) { // 你可以在这里提供SSR特定的props return {} }, })})-EOF-