v-if和v-show的区别和理解

举报
林太白 发表于 2025/04/10 11:38:09 2025/04/10
【摘要】 v-if和v-show的区别和理解

vue-v-if和v-show的区别和理解

作用

v-if和v-show都控制元素在页面的显示,写法上没什么区别

<div v-show="isShow"></div>
<div v-if="isShow"></div>

区别

控制手段不同:v-show是控制css的display是否为none来隐藏或展示,dom元素一直都在。而v-if显示隐藏是直接将整个dom元素添加或删除。

编译过程不同:v-show只是简单地基于css切换。而v-if的切换有一个局部编译/卸载的过程,切换过程中会销毁和重建内部的事件监听和子组件。

编译条件不同:v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件被销毁和重建,只有渲染条件为真时才渲染。

v-show不会触发组件的生命周期。
v-if由false变为true的时候,触发组件的beforeCreate、create、beforeMount、mounted钩子,由true变为false时触发组件的beforeDestory、destoryed方法。

适用场景

v-if有更高的切换消耗,v-show有更高的初始渲染消耗。
如果需要频繁地切换,则v-show较好。
如果在运行时条件很少改变,则使用v-if较好

v-if 实现

vue2

v-if核心是通过Vue实例的 patch 函数实现的,patch 函数会根据虚拟 DOM 的不同情况,执行不同的操作,比如创建元素、更新元素、删除元素等。

分为以下三个阶段

编译阶段=> 虚拟DOM阶段=> DOM更新阶段

  1. 编译阶段:编译模板时,v-if 会被编译为一个 if 的表达式,判断该元素是否应该渲染
  2. 虚拟DOM阶段:在渲染函数中,根据v-if条件的真假值决定是否创建对应的 DOM 节点。
  3. DOM更新阶段:当 v-if 的条件发生变化时,Vue 会根据 v-if 条件是否为 true 来决定是否渲染该节点。false直接销毁该节点及其子节点;true则重新渲染该节点

👉 源码部分

// Vue 2.x 中的 v-if 实现
Vue.prototype._update = function (vnode, hydrating) {
  const prevVnode = this._vnode
  this._vnode = vnode
  if (!prevVnode) {
    this.$el = this.__patch__(this.$el, vnode, hydrating, false /* removeOnly */)
  } else {
    this.__patch__(prevVnode, vnode)
  }
}

// 虚拟 DOM 创建时,v-if 会在此阶段决定是否需要渲染该节点
const patchVIf = function (oldVnode, vnode) {
  if (vnode.data.directives) {
    for (let i = 0; i < vnode.data.directives.length; i++) {
      const directive = vnode.data.directives[i]
      if (directive.name === 'if') {
        // 根据条件决定是否渲染该节点
        if (directive.value) {
          vnode.elm = createElm(vnode)
        } else {
          vnode.elm = null
        }
      }
    }
  }
}

👉__patch__作用

::: info
Vue中,this.patch 用于Vue 内部用于更新虚拟 DOM 和真实 DOM 的关键方法

作用是将更新后的虚拟DOM当前的虚拟DOM 比较,根据差异对真实DOM进行更新,用于高效地进行 DOM 操作和渲染更新。
:::

👉__patch__过程

  1. 虚拟 DOM 更新
    ::: info
    当组件的状态或数据变化时,Vue创建新的虚拟DOM 树,与旧的虚拟 DOM 树进行比较,找出差异(“虚拟 DOM diff 算法”)

__patch__ 方法会接收新旧虚拟 DOM,计算差异,决定如何最小化DOM更新
:::

  1. DOM 操作

根据虚拟 DOM 的差异,__patch__ 会对真实 DOM 进行相应的增删改操作(比如添加、删除、更新节点),通常是在一个批量更新的过程中进行的,以提升性能。

  1. 条件渲染
    使用 v-if 或类似指令时,__patch__处理节点的条件渲染。虚拟DOM树中的某些节点根据条件进行添加或移除时,__patch__ 会决定这些节点是否需要被渲染到真实 DOM 中

👉this.patch 的使用

在Vue中由 Vue 实例(或组件实例)调用

  1. 初次渲染时
    在组件或应用的初次渲染过程中,Vue 会调用 patch 来渲染虚拟 DOM 树,并将其挂载到真实 DOM 上。
// 初次渲染时
this.__patch__(this.$el, vnode, false);

此时,vnode 是新的虚拟 DOM 树,this.$el 是组件或应用的根 DOM 元素。false 表示不进行节点移除操作(removeOnly)。

  1. 更新时
    当组件的状态或数据发生变化时,Vue 会重新生成新的虚拟 DOM 树,并与旧的虚拟 DOM 树进行比较。此时,patch 会被调用,以根据差异更新真实 DOM。
// 在数据变化后更新 DOM
this.__patch__(prevVnode, vnode);

这里的 prevVnode 是更新前的虚拟 DOM,vnode 是更新后的虚拟 DOM。patch 会根据两者的差异来决定是否需要更新真实 DOM。

  1. 条件渲染(例如 v-if)
    在使用 v-if 这样的条件渲染指令时,patch 会帮助 Vue 在虚拟 DOM 更新过程中决定哪些节点需要被插入或删除。例如,如果 v-if 的条件变为 false,patch 会移除对应的 DOM 节点。
// 条件渲染时
if (condition) {
  this.__patch__(prevVnode, vnode);  // 插入节点
} else {
  this.__patch__(prevVnode, null);  // 移除节点
}

👉__patch__ 原理

Vue 2.x的源码中,__patch__位于 vue/src/core/vdom/patch.js 中,实现流程如下:

::: info

Diff 算法:patch 比较旧的虚拟 DOM (prevVnode) 和新的虚拟 DOM (vnode)。如果两者不同,通过 diff 算法找出它们的差异

更新真实DOM:patch 根据差异来更新真实 DOM。包括添加、删除、重排、更新节点的属性和事件等

条件判断:在某些情况下(例如 v-if 或 v-for 等条件渲染),patch 决定是否插入新的节点、销毁旧的节点或重用现有的节点。

生命周期钩子调用:patch 在渲染过程中会触发 Vue 组件的生命周期钩子函数(例如 beforeUpdate、updated 等)

:::

Vue 3.x

在 Vue 3.x 中,v-if 使用 createVNode 和 patch 的机制来实现动态的虚拟 DOM 更新。

// Vue 3.x 中 v-if 的实现
function patchVIf(oldVNode, vnode) {
  if (vnode.shapeFlag & ShapeFlags.IF) {
    if (vnode.props['v-if']) {
      // 判断是否满足渲染条件
      if (vnode.props['v-if']) {
        if (!oldVNode) {
          vnode.el = createElm(vnode)
        }
      } else {
        vnode.el = null
      }
    }
  }
}

::: info
这里的vnode.shapeFlag 是用于标识虚拟节点(VNode)类型的一个标志位。通过一组位掩码来表示不同类型的节点,帮助 Vue 识别一个虚拟节点的类型,从而在渲染过程中做出相应的优化

这部分后面详细研究
:::

createElm方法

function createElm(vnode) {
  let elm;
  
  // 如果是文本节点
  if (vnode.tag === undefined) {
    elm = document.createTextNode(vnode.text);
  } else {
    // 如果是元素节点
    elm = document.createElement(vnode.tag);
    
    // 处理元素的属性
    if (vnode.data) {
      // 设置属性、事件、样式等
      for (let key in vnode.data.attrs) {
        elm.setAttribute(key, vnode.data.attrs[key]);
      }
      
      // 如果有事件监听器,绑定事件
      if (vnode.data.on) {
        for (let event in vnode.data.on) {
          elm.addEventListener(event, vnode.data.on[event]);
        }
      }
    }

    // 递归创建子元素
    if (vnode.children) {
      for (let child of vnode.children) {
        elm.appendChild(createElm(child));
      }
    }
  }

  return elm;
}

v-show实现

👉 Vue 2.x

// 在 Vue 2.x 中,`v-show` 的实现会根据指令的表达式动态修改 `display` 样式
function show(el, binding) {
  if (binding.value) {
    el.style.display = ''; // 恢复原始 display 样式
  } else {
    el.style.display = 'none'; // 隐藏元素
  }
}

👉Vue 3.x

Vue 3.x 中v-show 指令与Vue 2.x类似,但是是通过 vnode 的 props 来管理显示状态的。

// Vue 3 的 `v-show` 绑定更新通常在更新函数中,通过响应式 `Proxy` 处理
function showElement(el, binding) {
  const { value } = binding;
  // 在响应式值变化时,直接通过样式控制元素显示或隐藏
  el.style.display = value ? '' : 'none';
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。