v-show 和 v-if 的底层实现
之前面试突然被问到,我觉得大家几乎都能答出来个大概,但我还是决定看一下源码吧~ (基于 vue3)
从 render 函数观察
大家应该都知道 vue3 官方文档提供了一个演练场吧?可以直观的看到组件编译后的结果。 我在 sfc.vuejs.org/ 添加两句代码:
<script setup>
import { ref } from 'vue'
const msg = ref('Hello World!')
const isShow = ref(true)
</script>
<template>
<div v-if="isShow">{{ msg }}</div>
<div v-show="isShow">{{ msg + '111111' }}</div>
<h1>{{ msg }}</h1>
<input v-model="msg">
</template>
编译的 render 代码如下,很明显能看到根据 isShow 的值做了什么处理:
const _hoisted_1 = { key: 0 }
return (_ctx, _cache) => {
return (_openBlock(), _createElementBlock(_Fragment, null, [
(isShow.value)
? (_openBlock(), _createElementBlock("div", _hoisted_1, _toDisplayString(msg.value), 1 /* TEXT */))
: _createCommentVNode("v-if", true),
_withDirectives(_createElementVNode("div", null, _toDisplayString(msg.value + '111111'), 513 /* TEXT, NEED_PATCH */), [
[_vShow, isShow.value]
]),
_createElementVNode("h1", null, _toDisplayString(msg.value), 1 /* TEXT */),
_withDirectives(_createElementVNode("input", {
"onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((msg).value = $event))
}, null, 512 /* NEED_PATCH */), [
[_vModelText, msg.value]
])
], 64 /* STABLE_FRAGMENT */))
}
那么就开始看看源码吧~
v-show
基本流程:通过 createElementVNode 创建一个 vNode,然后再通过 withDirectives 将 vue 的本身对 vShow 特殊指令的处理把指令加到 vNode 上,最后返回最终的 vNode
withDirectives
在 runtime-dom/src/directives 文件夹,有 vue 对内置指令 v-model、 v-on、 v-show 的 实现(withDirectives)
withDirectives 最终返回的是一个 vnode
vShow
vShow.ts 主要是 vue 针对 v-show 指令做一些处理:在生命周期 beforeMount、mounted、updated、beforeUnmount 都做了不同的处理(普通情况 和 transition 情况),最终主要实现还是 setDisplay 方法
function setDisplay(el: VShowElement, value: unknown): void {
el.style.display = value ? el._vod : 'none'
}
如此看到,底层实现主要还是通过对该元素的 css 属性 display 的设置来进行显隐的
v-if
值为 false
_createCommentVNode("v-if", true)
可以看到传入的 text 参数为 v-if,asBlock 设置为 true
-
openBlock() :打开一个块。这必须在createBlock之前调用。它不能是createBlock的一部分,因为该块的子级额能自己调用createBlock。当创建 v-for fragment 块时,disableTracking 为 true,因为 v-for fragment 总是对其子级进行 diff。
-
createBlock() :创建块根 vnode。采用与 createVNode 相同的参数。块根跟踪 dynamicChildren 数组中块内的动态节点。
所以这里创建一个块,最终渲染成注释,内容是 v-if
值为 true
(_openBlock(), _createElementBlock("div", _hoisted_1, _toDisplayString(msg.value)) 创建一个块,放入后面创建的块元素 div
_hoisted_1: { key: 0 }:hoist提升的意思,试了一下,应该只是用 key 来标识不同节点的
_toDisplayString(msg.value):将 msg 的值转为 string 类型
综上, v-if 主要是通过值来判断创建节点的。
总结
转载自:https://juejin.cn/post/7238604003599204408