上一篇文章我们知道了指令的实现原理,接下来我们来研究下Vue提供的一些默认指令的实现原理。
v-text
使用案例
<div v-text="'value'"
实现逻辑
- 先来看下
render
函数
const _hoisted_1 = ["textContent"]
function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return (_openBlock(), _createElementBlock("div", {
textContent: _toDisplayString('value')
}, null, 8 /* PROPS */, _hoisted_1))
}
}
})
在创建VNode的时候传递了一个
textContent
的pro
export function patchDOMProp(
el: any,
key: string,
value: any,
prevChildren: any,
parentComponent: any,
parentSuspense: any,
unmountChildren: any
) {
if (key === 'innerHTML' || key === 'textContent') {
// 如果`textContent`直接更新元素的textContent
el[key] = value == null ? '' : value
return
}
}
这个
pro
直接被用来作为元素的textContent。
总结
v-text
设置元素的textContent
。
v-html
我们通过上面的代码,估计你看到innerHTML
应该就理解了v-html
的实现逻辑。v-html
是渲染函数生成VNode的时候传了一个innerHTML
的pro
, 这个pro
直接被用来作为元素的 innerHTML
- 验证下确实如此
const _hoisted_1 = ["innerHTML"]
function render(_ctx, _cache) {
with (_ctx) {
const { openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return (_openBlock(), _createElementBlock("div", { innerHTML: 'value' }, null, 8 /* PROPS */, _hoisted_1))
}
}
总结
v-html
设置元素的innerHTML
。
v-show
使用案例
<div v-show="true">div元素</div>
实现逻辑
- 先来看下
render
函数
function render(_ctx, _cache) {
with (_ctx) {
const { vShow: _vShow, withDirectives: _withDirectives, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return _withDirectives((_openBlock(), _createElementBlock("div", null, "div元素", 512 /* NEED_PATCH */)), [
[_vShow, false]
])
}
}
v-show
实现逻辑:绑定了vShow
指令在元素上
- 我们来看下
v-show
这个内部指令
interface VShowElement extends HTMLElement {
// _vod = vue original display
_vod: string
}
export const vShow: ObjectDirective<VShowElement> = {
beforeMount(el, { value }, { transition }) {
el._vod = el.style.display === 'none' ? '' : el.style.display
setDisplay(el, value)
},
updated(el, { value, oldValue }, { transition }) {
setDisplay(el, value)
},
beforeUnmount(el, { value }) {
setDisplay(el, value)
}
}
function setDisplay(el: VShowElement, value: unknown): void {
el.style.display = value ? el._vod : 'none'
}
v-show
在内部就是切换元素的style.display
。如果传入的值为false
就将style.display
设置为none
不显示,如果传入的值为true
,则是元素原本设置style.display
值。
总结
v-show
控制元素的style.display
来切换显示和隐藏。
v-if
&& v-else-if
&& v-else
使用案例
<!-- 数据 -->
let condition = ref(1);
<!-- 模板 -->
<div v-if="condition == 1">状态1</div>
<div v-else-if="condition == 2">状态2</div>
<div v-else>其他状态</div>
实现逻辑
- 来看下
render
函数
const _hoisted_1 = { key: 0 }
const _hoisted_2 = { key: 1 }
const _hoisted_3 = { key: 2 }
function render(_ctx, _cache) {
with (_ctx) {
const { openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode } = _Vue
return (condition == 1)
? (_openBlock(), _createElementBlock("div", _hoisted_1, "状态1"))
: (condition == 2)
? (_openBlock(), _createElementBlock("div", _hoisted_2, "状态2"))
: (_openBlock(), _createElementBlock("div", _hoisted_3, "其他状态"))
}
}
v-if
&&v-else-if
&&v-else
实现逻辑:直接进行表达式的判断,然后不同的表达式渲染不同的DOM元素。
重要知识点
问题:
v-if
&&v-else-if
&&v-else
切换时会进行元素复用吗?
答案:不会。因为不同的元素赋予了不同的
key
,const _hoisted_1 = { key: 0 } const _hoisted_2 = { key: 1 } const _hoisted_3 = { key: 2 }
这样切换条件,会直接卸载旧的元素节点,挂载新的元素节点,不会进行复用。
v-for
使用案例
let items = [{
id: 1,
name: "张三"
},
{ id:2,
name: "李四"
}];
<div v-for="(item, index) in items" :key="item.id">{{ item.name }}</div>
实现逻辑
- 来看下
render
函数
function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, toDisplayString: _toDisplayString } = _Vue
return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item, index) => {
return (_openBlock(), _createElementBlock("div", { key: item.id }, _toDisplayString(item.name), 1 /* TEXT */))
}), 128 /* KEYED_FRAGMENT */))
}
}
渲染结果是
Fragment
中包含了每个Item
对应的Element
。
重要知识点
问题:
v-for
的遍历的数据只能是数组吗?
答案:不是
- 如果是数组,则遍历的是数组的每个元素;
- 如果是字符串,则遍历的是字符串的每个字符;
- 如果是数字,则遍历的是从 0 到 数据对应的那个值;
- 如果是实现了可迭代协议的数据,则是迭代遍历到的所有值;
- 如果是对象,则遍历的所有的key锁对应的值;
问题:
v-if
和v-for
同时使用,会有优先使用哪个指令?
<div v-if="items.length > 0" v-for="(item, index) in items" :key="item.id">{{ item.name }}</div>
function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, toDisplayString: _toDisplayString, createCommentVNode: _createCommentVNode } = _Vue
return (items.length > 0)
? (_openBlock(true), _createElementBlock(_Fragment, { key: 0 }, _renderList(items, (item, index) => {
return (_openBlock(), _createElementBlock("div", { key: item.id }, _toDisplayString(item.name), 1 /* TEXT */))
}), 128 /* KEYED_FRAGMENT */))
: _createCommentVNode("v-if", true)
}
}
答案:优先判断
v-if
指令,如果条件成立,才会进行v-for
遍历生成数组元素节点。
其他
还有一些其他常用的指令,例如v-model
进行双向绑定,v-on
进行事件绑定,v-slot
进行插槽的设置。这几个指令由于相对复杂,我们将每个使用一个章节来介绍。本章节就到此为止。
网友评论