vue3源码--v-model之vModelRadio

170 阅读3分钟

大家好,我是前端小喵

WechatIMG18166.jpg

一、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是否相等

二、带着问题去看源码

1inputtype为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);

截屏2023-08-12 上午1.00.00.png

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);
      }
    }

截屏2023-08-12 上午1.04.11.png