v-show 与 v-if 的区别
一、共同点
v-show
和 v-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>
性能建议
- 切换频率考虑:
- 高频切换用
v-show
- 低频切换用
v-if
- 初始渲染考虑:
- 初始渲染需要显示用
v-show
- 初始渲染不需要显示用
v-if
- 条件判断复杂度:
- 简单条件用
v-show
- 复杂条件用
v-if