Skip to content

v-show 与 v-if 的区别

一、共同点

v-showv-if 都是 Vue 中用于控制元素显示与隐藏的指令: 都能够条件性地渲染元素 都支持 boolean 类型的条件表达式 表达式为 true 时显示元素,为 false 时隐藏元素

二、区别

1. 渲染机制

v-show

  • 不管条件是否为真,元素始终会被渲染
  • 只是简单地基于 CSS 的 display 属性进行切换
  • 首次渲染开销较大,切换开销很小
  • v-if
  • 条件为假时,不会渲染元素
  • 会真实地销毁和重建 DOM 节点
  • 首次渲染开销小,切换开销较大

2. 编译过程

javascript
// v-show 编译后
render() {
   return h('div', {
       style: {
           display: this.condition ? 'block' : 'none'
       }
   })
}

// v-if 编译后
render() {
   return this.condition
       ? h('div')
       : null
}

三、原理分析与源码解读

v-show 原理

. 在编译阶段,v-show 会被编译成一个指令 . 指令的更新函数会根据表达式的值设置元素的 display 样式 . 元素始终存在于 DOM 树中,只是改变其显示状态

javascript
// v-show 示例
<template>
  <div v-show="isVisible">这是一个使用 v-show 的元素</div>
</template>
<script>
export default {
   data() {
       return {
           isVisible: true
       }
   }
}
</script>

v-show 源码分析

v-show 的实现主要在 packages/runtime-dom/src/directives/vShow.ts

typescript
// Vue 3.x  v-show 源码实现
export const vShow: ObjectDirective<VShowElement> = {
  beforeMount(el, { value }, { transition }) {
    el._vod = el.style.display === 'none' ? '' : el.style.display
    if (transition && value) {
      transition.beforeEnter(el)
    } else {
      setDisplay(el, value)
    }
  },
  mounted(el, { value }, { transition }) {
    if(transition && value) {
      transition.enter(el)
    } else {
      setDisplay(el, value)
    }
  },
  updated(el, { value,oldValue }, { transition }) {
    // 处理过渡动画
    if(!transition || !oldValue) return;
    if(transition) {
      if(value) {
        transition.beforeEnter(el)
        setDisplay(el, true)
        transition.enter(el)
      } else {
        transition.leave(el, () => {
          setDisplay(el, 'none')
        })
      }
    } else {
      setDisplay(el, value)
    }
  },
  beforeUnmount(el, { value }) {
    setDisplay(el, value)
  }
}

解析:

  • v-show 是一个指令,通过指令的生命周期钩子来控制元素显示
  • 支持过渡动画效果
  • 通过修改 style.display 属性来显示控制和隐藏
  • 会缓存原始的 display 值 (_vod)

v-if 原理

. 在编译阶段,v-if 会被编译成三元表达式 . 当条件变化时,会触发虚拟 DOM 的重新渲染 . 会真实地创建或销毁 DOM 节点

javascript
// v-if 示例
<template>
   <div v-if="isVisible">这是一个使用 v-if 的元素</div>
</template>
<script>
export default {
   data() {
       return {
           isVisible: true
       }
   }
}
</script>

v-if 源码分析

v-if 的实现主要在 packages/runtime-core/src/directives/vIf.ts

typescript
// Vue 3.x  v-if 源码实现
export const transformIf = createStructuralDirectiveTransform( 
  /^(if|else|else-if)$/,
  (node, dir, context) => {
    return processIf(node, dir, context, (ifNode, branch, isRoot) => {
      // 处理 if 节点
      const siblings = context.parent!.children
      let i = siblings.indexOf(ifNode)
      let key = 0

      // 生成条件分支
      while (i-- >= 0) {
        const sibling = siblings[i]
        if(sibling && sibling.type === NodeTypes.IF /* Comment */) {
          continue
        }
      }

      return () => {
        if(isRoot) {
          ifNode.codegenNode = createCodegenNodeForBranch(branch,key,context)
        } else {
          // 非根节点处理
          const parentCondition = getParent Condition(ifNode.codegenNode!)
          parentCondition.alternate = createCodegenNodeForBranch(
            branch,
            key + ifNode.branches.length -1,
            context
          )
        }
      }
    })
  }
)

// 生成条件渲染代码
function createCodegenNodeForBranch(branch, key, context): IfConditionalExpression {
  return createConditionalExpression(
    branch.condition!,
    createChildrenCodegenNode(branch, keyIndex)
    // 空节点
    createSimpleExpression('false',false)
  )
}

四、使用场景

v-show 适用场景:

. 频繁切换的场景

  • 如:切换页签、弹窗 . 初始渲染开销可接受 . 元素内容较为复杂
javascript
/ 适合使用 v-show 的场景
<div v-show="activeTab === 'home'">
   <HomeComponent />
</div>

v-if 适用场景:

. 运行时很少改变的场景

  • 如:用户权限判断 . 条件为假时,不需要渲染元素 . 需要配合 v-else 使用的场景
javascript
// 适合使用 v-if 的场景
<div v-if="userRole === 'admin'">
   <AdminPanel />
</div>
<div v-else>
   <UserPanel />
</div>

性能建议

  1. 切换频率考虑
  • 高频切换用 v-show
  • 低频切换用 v-if
  1. 初始渲染考虑
  • 初始渲染需要显示用 v-show
  • 初始渲染不需要显示用 v-if
  1. 条件判断复杂度
  • 简单条件用 v-show
  • 复杂条件用 v-if