Vue3属性绑定方法解析
作者:Web能力中心团队
前言:
这篇文章来自我们团队的田鑫雨同学,强劲的“后浪”。不论使用已有框架,还是自实现框架,数据绑定都是一个热点话题,来看看他对Vue3数据绑定方式的分析
Vue3 通常使用 v-bind 绑定数据到子元素上,对于一个元素接收数据的方式有两种:通过property或通过attribute,本文通过分析源码得到结论:Vue会通过判断元素实例el上是否有需要绑定的property,如果有就把数据传给子元素的property,否则传给attribute。当然还有会一些特殊处理,我们这里只讨论一般情况。
首先说结论,对于一般的属性,Vue会判断元素el
上是否有对应的property
属性,如果有就赋值给对应的property
,否则添加到attribute
上。然后Vue会对一些属性做特殊处理直接绑定到attribute
,此外对input/textarea/select
元素也会有特殊处理。
Vue3.2版本还提供了
:xxx.prop
和:xxx.attr
写法指定绑定数据到property
或attribute
上
直接从源码入手。
在Vue初始化过程中,Vue会将<template>
解析并构造成vdom
,收集其中的数据绑定放在props
对象中。到了mount
阶段Vue会根据vdom
创建为真实DOM,然后放到页面中。
创建过程大致为遍历vdom
中的vnode
节点,执行mountElement()
,关键代码如下,根据vnode
创建真实el
元素,进行数据绑定和事件绑定,然后把el
元素插入到父容器中,最后完成页面的加载。
const mountElement = (vnode, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => { let el; // ... const { type, props, shapeFlag, transition, patchFlag, dirs } = vnode; { el = vnode.el = hostCreateElement(vnode.type, isSVG, props && props.is, props); // ... // props if (props) { for (const key in props) { if (key !== 'value' && !isReservedProp(key)) { hostPatchProp(el, key, null, props[key], isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren); } } // ... } // ... } // ... hostInsert(el, container, anchor); // ... };
这里以一个自定义的WebComponent元素
<te-tag>
为例
Vue使用createElement
创建了te-tag
元素对象保存在el
中,然后使用for (const key in props) {...}
遍历需要绑定的属性,属性名为key
,属性值为props[key]
,然后执行hostPatchProp()
将该属性添加到el
上。
hostPatchProp()
即patchProp()
方法代码如下
const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren) => { if (key === 'class') { patchClass(el, nextValue, isSVG); } else if (key === 'style') { patchStyle(el, prevValue, nextValue); } else if (isOn(key)) { // ignore v-model listeners if (!isModelListener(key)) { patchEvent(el, key, prevValue, nextValue, parentComponent); } } else if (key[0] === '.' ? ((key = key.slice(1)), true) : key[0] === '^' ? ((key = key.slice(1)), false) : shouldSetAsProp(el, key, nextValue, isSVG)) { patchDOMProp(el, key, nextValue, prevChildren, parentComponent, parentSuspense, unmountChildren); } else { if (key === 'true-value') { el._trueValue = nextValue; } else if (key === 'false-value') { el._falseValue = nextValue; } patchAttr(el, key, nextValue, isSVG); } };
通过源码可以看到,除了class/style
的属性,Vue会对我们自定义的属性会进行一个判断, 对于key的值:
.xxx
表示通过:xxx.prop
绑定的数据,直接往property上设置^xxx
表示通过:xxx.attr
绑定的值,应该往attribute上设置- 不是以上两种情况的key值如
xxx
,需要调用shouldSetAsProp()判断是否应该设置到property上
判断为真绑定property
会执行patchDOMProp()
,否则绑定attribute
会执行pathAttr()
我们这里最关心的第三种情况执行shouldSetAsProp()
来判断是否应该把xxx
设置到property上,其代码如下
function shouldSetAsProp(el, key, value, isSVG) { if (isSVG) { // most keys must be set as attribute on svg elements to work // ...except innerHTML & textContent if (key === 'innerHTML' || key === 'textContent') { return true; } // or native onclick with function values if (key in el && nativeOnRE.test(key) && isFunction(value)) { return true; } return false; } // these are enumerated attrs, however their corresponding DOM properties // are actually booleans - this leads to setting it with a string "false" // value leading it to be coerced to `true`, so we need to always treat // them as attributes. // Note that `contentEditable` doesn't have this problem: its DOM // property is also enumerated string values. if (key === 'spellcheck' || key === 'draggable' || key === 'translate') { return false; } // #1787, #2840 form property on form elements is readonly and must be set as // attribute. if (key === 'form') { return false; } // #1526 <input list> must be set as attribute if (key === 'list' && el.tagName === 'INPUT') { return false; } // #2766 <textarea type> must be set as attribute if (key === 'type' && el.tagName === 'TEXTAREA') { return false; } // native onclick with string value, must be set as attribute if (nativeOnRE.test(key) && isString(value)) { return false; } return key in el; }
这里可以看到Vue对SVG/spellcheck/draggale/translate/form/input[list]/textarea[type]/onclick
等做了特殊处理,要求返回false
绑定数据到attribute
上。
而我们自定义的属性只通过一行代码来判断,
return key in el;
如果el
的property
上有key
,则返回true
,然后绑定数据到property
上。
到此这篇关于Vue3属性绑定方法解析的文章就介绍到这了,更多相关Vue属性绑定内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!