【vue篇】Vue 核心指令原理解析:v-if、v-show、v-html 的底层奥秘

在 Vue 开发中,v-ifv-showv-html 是我们每天都在使用的指令。

但你是否思考过:

"为什么 v-if 切换时组件会重新渲染,而 v-show 不会?"
"v-html 真的安全吗?它底层是怎么工作的?"

本文将带你深入 Vue 源码,从编译到渲染 ,彻底解析这三大指令的工作原理与性能差异


一、v-if:条件渲染的"销毁与重建"大师

✅ 行为表现

vue 复制代码
<div v-if="isVisible">我是条件内容</div>
  • isVisible = true:元素存在 DOM 中;
  • isVisible = false元素从 DOM 中完全移除

📌 编译阶段:生成条件 AST 节点

当 Vue 编译器解析模板时:

  1. 遇到 v-if,调用 addIfCondition 方法;
  2. 将该节点标记为"条件节点";
  3. 生成抽象语法树(AST)时,为该节点添加 ififConditions 属性。
js 复制代码
// 编译后的 AST 片段
{
  tag: 'div',
  if: 'isVisible',
  ifConditions: [
    {
      exp: 'isVisible',
      block: { /* 节点本身 */ }
    }
  ]
}

📌 渲染阶段:动态生成 VNode

render 函数执行时:

  • 如果条件为 false不生成该节点的 VNode
  • 如果为 true,生成 VNode 并参与后续 diff。
js 复制代码
// render 函数类似
render(h) {
  return this.isVisible 
    ? h('div', '我是条件内容') 
    : undefined; // 不渲染
}

✅ 生命周期影响

  • 销毁时 :触发 beforeDestroydestroyed
  • 重建时 :重新执行 createdmounted
  • 子组件状态丢失

📌 适用场景

  • 条件很少改变;
  • 切换开销大,但初始渲染性能敏感;
  • 需要完全隔离组件实例。

二、v-show:CSS 控制的"显示开关"

✅ 行为表现

vue 复制代码
<div v-show="isVisible">我是显示控制内容</div>
  • isVisible = truedisplay: block(或其他);
  • isVisible = falsedisplay: none

📌 编译阶段:生成普通 VNode

v-if 不同,v-show

  1. 不会被标记为条件节点;
  2. 正常生成 VNode,无论条件真假;
  3. 编译器为其添加一个 directives 属性:
js 复制代码
{
  tag: 'div',
  directives: [
    {
      name: 'show',
      value: 'isVisible',
      rawName: 'v-show'
    }
  ],
  children: [ /* 文本节点 */ ]
}

📌 渲染阶段:通过 CSS 控制显示

patch 阶段,Vue 会执行 v-show 的钩子函数:

js 复制代码
// 源码简化
function updateShow (oldVnode, vnode) {
  const show = vnode.data.directives.find(d => d.name === 'show').value;
  vnode.elm.style.display = show ? '' : 'none';
}
  • 始终渲染:VNode 始终存在;
  • 仅修改 display:DOM 节点不销毁;
  • 保留组件状态data、事件监听器、子组件均保留。

✅ 生命周期影响

  • ❌ 不触发 destroyedcreated
  • 组件始终处于"活跃"状态。

📌 适用场景

  • 频繁切换显示状态;
  • 需要保留组件内部状态;
  • 初始渲染性能不敏感。

三、v-html:HTML 内容注入的"双刃剑"

✅ 行为表现

vue 复制代码
<div v-html="htmlContent"></div>
js 复制代码
data() {
  return {
    htmlContent: '<span style="color:red">红色文本</span>'
  };
}
  • 结果:<div><span style="color:red">红色文本</span></div>

📌 编译阶段:标记为 HTML 指令

  1. 编译器识别 v-html
  2. 生成 AST 时,添加 directives 属性:
js 复制代码
{
  tag: 'div',
  directives: [
    {
      name: 'html',
      value: 'htmlContent',
      rawName: 'v-html'
    }
  ]
}
  1. 移除原有子节点(如文本、其他元素)。

📌 渲染阶段:直接设置 innerHTML

patch 过程中,Vue 调用 addProp 为元素添加 innerHTML 属性:

js 复制代码
// 源码简化
function updateHtml (oldVnode, vnode) {
  const html = vnode.data.directives.find(d => d.name === 'html').value;
  if (vnode.elm.innerHTML !== html) {
    vnode.elm.innerHTML = html;
  }
}
  • 性能高 :浏览器原生 innerHTML 操作;
  • 不安全 :可能引发 XSS 攻击
  • 不响应式 :如果 htmlContent 变化,会完全替换内部内容,而非 diff。

⚠️ 安全警告

js 复制代码
// 危险!用户输入可能包含恶意脚本
this.htmlContent = '<img src=x onerror="alert(\'XSS\')">';

// ✅ 正确做法:使用插值或文本指令
<div>{{ userContent }}</div>

📌 适用场景

  • 渲染可信的富文本(如 CMS 内容);
  • 性能要求极高,且内容简单;
  • 配合 DOMPurify 等库进行内容净化。

四、三大指令对比表

特性 v-if v-show v-html
是否生成 VNode 条件生成 始终生成 始终生成
DOM 操作 添加/移除节点 修改 display 设置 innerHTML
初始渲染性能 ✅ 更好(条件为假) ❌ 较差 ✅ 快(但风险高)
切换性能 ❌ 差(重建组件) ✅ 好(仅 CSS) ✅ 快(但全替换)
保留状态 ❌ 否 ✅ 是 ❌ 否(内容被替换)
安全性 ✅ 安全 ✅ 安全 ❌ 可能 XSS
适用场景 条件少变 频繁切换 可信富文本

五、性能优化建议

v-if vs v-show 如何选?

条件 推荐
切换频率 < 3次/分钟 v-if
切换频率 > 3次/分钟 v-show
组件包含大量子节点 v-show(避免重复 diff)
初始加载需隐藏 v-if(减少首次渲染)

v-html 安全使用指南

  1. 永远不要 将用户输入直接用于 v-html
  2. 使用 DOMPurify 净化 HTML:
js 复制代码
import DOMPurify from 'dompurify';

const clean = DOMPurify.sanitize(dirtyHTML);
this.htmlContent = clean;
  1. 考虑使用 v-dom(第三方指令)或 dangerouslySetInnerHTML(Vue 3 Composition API)替代。

💡 结语

"理解原理,才能写出高性能、安全的代码。"

  • v-if条件渲染,走的是"销毁-重建"路线;
  • v-show显示控制 ,靠 display: none 隐藏;
  • v-htmlHTML 注入 ,直接设置 innerHTML,快但危险。

掌握它们的底层机制,你就能:

✅ 避免不必要的组件重建;

✅ 提升频繁切换的性能;

✅ 防止 XSS 安全漏洞。

相关推荐
阿成学长_Cain30 分钟前
Linux telinit 命令详解:运行级别切换|关机重启|系统维护一站式掌握
linux·运维·前端·网络
Patrick_Wilson43 分钟前
最佳实践是有保质期的:从一次 CDN external 白屏事故说起
前端·性能优化·前端工程化
YHHLAI1 小时前
[特殊字符] Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活
前端·人工智能
nuIl1 小时前
我把 5 个编码 Agent 塞进了一个 npm 包
前端·agent·claude
L-影1 小时前
FastAPI 静态文件:Web 页面的“固定展柜”与“加速引擎”
前端·fastapi
陪我去看海1 小时前
受够了 AI 写完代码留下一堆端口?我做了一个端口管理App!
前端·macos·vibecoding
Xuepoo1 小时前
我抛弃了 DOM,但保留了无障碍访问
前端
李明卫杭州1 小时前
Vue2 vs Vue3 的 h 函数:渲染函数完整迁移指南
前端
星栈2 小时前
LiveView 的认证系统:从登录到权限,我一开始以为有 `current_user` 就算完事了
前端·前端框架·elixir
在因斯坦2 小时前
摸摸鱼之前端自己研究 Jenkins 自动化部署
前端