前言:
这篇文章来自我们团队的田鑫雨同学,强劲的“后浪”。不论使用已有框架,还是自实现框架,数据绑定都是一个热点话题,来看看他对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,props && props.is,props); // ... // props if (props) { for (const key in props) { if (key !== 'value' && !isReservedProp(key)) { hostPatchProp(el,key,null,props[key],vnode.children,unmountChildren); } } // ... } // ... } // ... hostInsert(el,anchor); // ... };
Vue使用createElement
创建了te-tag
元素对象保存在el
中,然后使用for (const key in props) {...}
遍历需要绑定的属性,属性名为key
,属性值为props[key]
,然后执行hostPatchProp()
将该属性添加到el
上。
hostPatchProp()
即patchProp()
方法代码如下
const patchProp = (el,prevValue,nextValue,isSVG = false,prevChildren,unmountChildren) => { if (key === 'class') { patchClass(el,isSVG); } else if (key === 'style') { patchStyle(el,nextValue); } else if (isOn(key)) { // ignore v-model listeners if (!isModelListener(key)) { patchEvent(el,parentComponent); } } else if (key[0] === '.' ? ((key = key.slice(1)),true) : key[0] === '^' ? ((key = key.slice(1)),false) : shouldSetAsProp(el,isSVG)) { patchDOMProp(el,unmountChildren); } else { if (key === 'true-value') { el._trueValue = nextValue; } else if (key === 'false-value') { el._falseValue = nextValue; } patchAttr(el,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,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
上。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。