大家好,我是前端小喵
一、vModelRadio做了什么
created:
1、初始化设置每个radio的value是否等于v-model当前值
2、保存更新v-model值的函数
3、监听每个radio的change事件去更新v-model的值为当前选中的radio的值(优先el._value再el.value)
beforeUpdate:
1、刷新 更新v-model值的函数
2、v-model绑定值发生变化,更新每个radio的选中状态 为 v-model当前值和radio设置的value是否相等
二、带着问题去看源码
1、input的type为radio的时候,初始化v-model绑定的值怎么和每个radio的value匹配上的
2、radio切换的时候是怎么更新的
三、demo
<div id="demo">
<template v-for="branch in branches">
<input
type="radio"
:id="branch"
:value="branch"
name="branch"
v-model="currentBranch"
/>
<label :for="branch">{{ branch }}</label>
</template>
</div>
<script>
const { createApp, ref, watchEffect } = Vue
createApp({
setup() {
const currentBranch = ref('main')
watchEffect(() => {
console.log(currentBranch.value)
})
return {
branches: ['main', 'v2-compat'],
currentBranch
}
}
}).mount('#demo')
</script>
四、源码理解
// radio的源码不多
// el: 每个radio dom
// value: v-model当前值
// vnode radio的虚拟dom vnode.props.value: 每个radio设置的value(例如如上demo的:‘main’, ‘v2-compat’)
// looseEqual:比较两个值是否相等的工具函数(想了解可以看最后部分)
// getModelAssigner(vnode) 获取更新v-model绑定值函数(保证传入更新值,无论是一个v-model绑定还是多个v-model绑定,都会更新所有v-model绑定的值)可以看上一篇文章
// getValue 获取dom值的函数(想了解可以看最后部分)
var vModelRadio = {
created(el, { value }, vnode) {
console.log(value, vnode.props.value,looseEqual(value, vnode.props.value) );
// 初始化的时候 设置radio的checked 为v-model值是否和radio绑定的value相等
el.checked = looseEqual(value, vnode.props.value);
// 获取更新v-model绑定值函数
el._assign = getModelAssigner(vnode);
// 给每个radio添加change事件监听处理
addEventListener(el, "change", () => {
console.log(getValue(el),'change');
// 获取当前选中radio的值 并更新给v-model绑定值
el._assign(getValue(el));
});
},
beforeUpdate(el, { value, oldValue }, vnode) {
// 刷新 更新v-model绑定值函数
el._assign = getModelAssigner(vnode);
// 当v-model绑定值发生变化时 再次更新各个radio是否选中
if (value !== oldValue) {
console.log(value, vnode.props.value, 'beforeUpdate');
el.checked = looseEqual(value, vnode.props.value);
}
}
};
// 比较两个值是否相等的工具函数,对各种数据类型做了比较
function looseEqual(a, b) {
if (a === b)
return true;
let aValidType = isDate(a);
let bValidType = isDate(b);
if (aValidType || bValidType) {
return aValidType && bValidType ? a.getTime() === b.getTime() : false;
}
aValidType = isSymbol(a);
bValidType = isSymbol(b);
if (aValidType || bValidType) {
return a === b;
}
aValidType = isArray(a);
bValidType = isArray(b);
if (aValidType || bValidType) {
return aValidType && bValidType ? looseCompareArrays(a, b) : false;
}
aValidType = isObject(a);
bValidType = isObject(b);
if (aValidType || bValidType) {
if (!aValidType || !bValidType) {
return false;
}
const aKeysCount = Object.keys(a).length;
const bKeysCount = Object.keys(b).length;
if (aKeysCount !== bKeysCount) {
return false;
}
for (const key in a) {
const aHasKey = a.hasOwnProperty(key);
const bHasKey = b.hasOwnProperty(key);
if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key])) {
return false;
}
}
}
return String(a) === String(b);
}
// 获取dom值的函数
function getValue(el) {
return "_value" in el ? el._value : el.value;
}
五、问题答案
1、input的type为radio的时候,初始化v-model绑定的值怎么和每个radio的value匹配上的
console.log(value, vnode.props.value,looseEqual(value, vnode.props.value) );
// 初始化的时候 设置radio的checked 为v-model值是否和radio绑定的value相等
el.checked = looseEqual(value, vnode.props.value);
2、radio切换的时候是怎么更新的
2.1:先触发change事件,获取选中radio的值,然后更新v-model绑定值
2.2:然后在beforeUpdate阶段,更新每个radio的选中状态 为 v-model当前值和radio设置的value是否相等
created(el, { value }, vnode) {
console.log(value, vnode.props.value,looseEqual(value, vnode.props.value) );
// 初始化的时候 设置radio的checked 为v-model值是否和radio绑定的value相等
el.checked = looseEqual(value, vnode.props.value);
// 获取更新v-model绑定值函数
el._assign = getModelAssigner(vnode);
// 给每个radio添加change事件监听处理
addEventListener(el, "change", () => {
console.log(getValue(el),'change');
// 获取当前选中radio的值 并更新给v-model绑定值
el._assign(getValue(el));
});
},
beforeUpdate(el, { value, oldValue }, vnode) {
// 刷新 更新v-model绑定值函数
el._assign = getModelAssigner(vnode);
// 当v-model绑定值发生变化时 再次更新各个radio是否选中
if (value !== oldValue) {
console.log(value, vnode.props.value, 'beforeUpdate');
el.checked = looseEqual(value, vnode.props.value);
}
}