【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 安全漏洞。

相关推荐
C_心欲无痕19 分钟前
nginx - 核心概念
运维·前端·nginx
开开心心_Every24 分钟前
安卓做菜APP:家常菜谱详细步骤无广简洁
服务器·前端·python·学习·edge·django·powerpoint
前端_Danny25 分钟前
用 ECharts markLine 实现节假日标注
前端·信息可视化·echarts
古城小栈27 分钟前
Rust 丰富&好用的 格式化语法
前端·算法·rust
丢,捞仔38 分钟前
uni-app上架应用添加权限提示框
前端·javascript·uni-app
Glink43 分钟前
从零开始编写自己的AI账单Agent
前端·agent·ai编程
Hilaku43 分钟前
我是如何用一行 JS 代码,让你的浏览器内存瞬间崩溃的?
前端·javascript·node.js
努力犯错玩AI43 分钟前
如何在ComfyUI中使用Qwen-Image-Layered GGUF:完整安装和使用指南
前端·人工智能
Lefan1 小时前
在浏览器中运行大模型:基于 WebGPU 的本地 LLM 应用深度解析
前端
五仁火烧1 小时前
npm run build命令详解
前端·vue.js·npm·node.js