一、概念理解:v-show 在 SSR 环境中的本质问题
在 Vue 的客户端渲染(CSR)中,v-show 是通过动态修改元素的 display 样式属性来控制显隐的。
但在 SSR(Server-Side Rendering) 环境下,没有真实的 DOM 操作,因此必须在 编译阶段 将其转换为合适的"样式表达式",以便在渲染 HTML 时即决定元素的显示与否。
这段源码正是 Vue SSR 编译阶段对 v-show 指令的专用转换函数:ssrTransformShow。
二、原理剖析:从 AST 转换到可执行的 SSR 表达式
源码如下:
typescript
import {
DOMErrorCodes,
type DirectiveTransform,
createConditionalExpression,
createDOMCompilerError,
createObjectExpression,
createObjectProperty,
createSimpleExpression,
} from '@vue/compiler-dom'
export const ssrTransformShow: DirectiveTransform = (dir, node, context) => {
if (!dir.exp) {
context.onError(
createDOMCompilerError(DOMErrorCodes.X_V_SHOW_NO_EXPRESSION),
)
}
return {
props: [
createObjectProperty(
`style`,
createConditionalExpression(
dir.exp!,
createSimpleExpression(`null`, false),
createObjectExpression([
createObjectProperty(
`display`,
createSimpleExpression(`none`, true),
),
]),
false /* no newline */,
),
),
],
}
}
🔍 逐行注释讲解
1. 导入编译器工具函数
typescript
import {
DOMErrorCodes, // DOM 编译器错误码枚举
type DirectiveTransform, // 指令转换器类型定义
createConditionalExpression, // 创建条件(三元)表达式节点
createDOMCompilerError, // 创建编译错误对象
createObjectExpression, // 创建对象字面量表达式节点
createObjectProperty, // 创建对象属性节点
createSimpleExpression, // 创建简单表达式节点
} from '@vue/compiler-dom'
👉 这些函数用于生成 Vue 编译器 AST(抽象语法树)节点,使模板语法转换为 JavaScript 渲染函数表达式。
2. 定义 ssrTransformShow
javascript
export const ssrTransformShow: DirectiveTransform = (dir, node, context) => {
dir:指令对象,包含name、exp、modifiers等信息。node:当前处理的 AST 节点(如<div v-show="visible">)。context:编译上下文,包含错误处理、代码生成状态等。
3. 错误处理:未传入表达式时报错
scss
if (!dir.exp) {
context.onError(
createDOMCompilerError(DOMErrorCodes.X_V_SHOW_NO_EXPRESSION),
)
}
🧩 解释:
- 若
v-show未绑定表达式(如<div v-show>),在编译阶段抛出X_V_SHOW_NO_EXPRESSION错误。 - 这是语义层检查,确保 SSR 输出逻辑正确。
4. 返回转换结果:生成 SSR 样式表达式
go
return {
props: [
createObjectProperty(
`style`,
createConditionalExpression(
dir.exp!, // 条件:若表达式为真
createSimpleExpression(`null`, false), // 则样式为 null(保持默认)
createObjectExpression([ // 否则强制设置 display:none
createObjectProperty(
`display`,
createSimpleExpression(`none`, true),
),
]),
false /* no newline */,
),
),
],
}
🧠 逻辑转换效果如下:
模板:
ini
<div v-show="visible"></div>
编译成 SSR 渲染表达式:
css
{
style: visible ? null : { display: "none" }
}
此结构在服务端渲染时即能决定元素是否隐藏,而不依赖浏览器执行逻辑。
三、对比分析:v-show vs v-if 在 SSR 中的差异
| 指令 | 渲染机制 | SSR 表现 | 性能特征 |
|---|---|---|---|
v-if |
条件性渲染(创建/销毁 DOM) | 服务端直接生成或省略对应 HTML | 轻量但有重绘代价 |
v-show |
样式控制(display: none) |
服务端生成元素但隐藏 | 保持结构完整,适用于频繁切换显示 |
📌 结论:ssrTransformShow 的存在,使得 v-show 也能在 SSR 输出阶段保留 DOM 结构一致性,有利于 hydration(客户端激活) 一致性。
四、实践部分:自定义 SSR 指令转换示例
假设我们要实现一个类似 v-visible 的 SSR 指令,其逻辑与 v-show 类似但控制 visibility:
javascript
export const ssrTransformVisible: DirectiveTransform = (dir, node, context) => {
if (!dir.exp) {
context.onError(createDOMCompilerError(DOMErrorCodes.X_V_SHOW_NO_EXPRESSION))
}
return {
props: [
createObjectProperty(
`style`,
createConditionalExpression(
dir.exp!,
createSimpleExpression(`null`, false),
createObjectExpression([
createObjectProperty(
`visibility`,
createSimpleExpression(`hidden`, true),
),
]),
false,
),
),
],
}
}
🔧 编译结果:
ini
<div v-visible="isVisible"></div>
➡ SSR 渲染后:
css
{
style: isVisible ? null : { visibility: "hidden" }
}
五、拓展思考:SSR 编译器的可扩展性机制
Vue 编译器的设计高度模块化。
所有指令都通过类似的 DirectiveTransform 接口实现:
kotlin
interface DirectiveTransform {
(dir: DirectiveNode, node: ElementNode, context: TransformContext): TransformResult
}
因此开发者可自由扩展:
- 新增自定义指令;
- 为 SSR 定义独立转换逻辑;
- 拓展 AST 节点结构;
- 实现插件式编译中间层。
这也是 Vue 编译器能支持多平台(Web、SSR、Custom Renderer)的关键机制。
六、潜在问题与注意事项
v-show的动态性缺失
SSR 仅处理初始渲染,后续仍需在客户端由 runtime 更新样式。
若表达式依赖异步数据,则 SSR 阶段无法精确控制。- 样式覆盖冲突
若模板或 CSS 已设置display样式,SSR 输出的内联样式可能被覆盖。 - hydration 不一致风险
若 SSR 阶段与客户端初始数据不一致,会造成 hydration mismatch。
总结
ssrTransformShow 是 Vue SSR 编译管线中的一个小而精的组件,其核心使命是:
将"显示控制指令"转译为"样式表达式",确保 SSR 输出结构完整且可预测。
通过 createConditionalExpression、createObjectExpression 等函数的组合,Vue 实现了"模板 → AST → SSR 表达式"的全链条自动化编译。
本文部分内容借助 AI 辅助生成,并由作者整理审核。