Vue属性绑定
Web能力中心团队 人气:1前言:
这篇文章来自我们团队的田鑫雨同学,强劲的“后浪”。不论使用已有框架,还是自实现框架,数据绑定都是一个热点话题,来看看他对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
上。
加载全部内容