最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue3疑问系列(6) — v-model(vModelRadio)指令是如何工作的?

    正文概述 掘金(徐志伟酱)   2021-03-02   698

    前言

    尝试编写 vModelRadio 指令对象

    这次就不编写 vModelRadio 指令对象了,因为比较简单.

    小栗子

    小栗子就不贴代码了,点我去看看

    看完使用姿势接下来看看内部是如何使用的.

    vModelRadio内部实现

    vModelRadio源码 runtime-dom/src/directives/vModel.ts

    export const vModelRadio: ModelDirective<HTMLInputElement> = {
      created(el, { value }, vnode) {
        el.checked = looseEqual(value, vnode.props!.value) // eg: `<input type="radio" v-model="str" value="foo" />` value就是str变量的值, vnode.props!.value就是foo这个值
        el._assign = getModelAssigner(vnode) // 拿到 onUpdate:modelValue 函数
        addEventListener(el, 'change', () => { // 给el 监听 change事件
          el._assign(getValue(el)) // eg: `<input type="radio" v-model="str" value="foo" />` 给str赋值
        })
      },
      beforeUpdate(el, { value, oldValue }, vnode) {
        el._assign = getModelAssigner(vnode) // 每次更新时都会获取最新的 onUpdate:modelValue 函数
        if (value !== oldValue) { // 新老值不相等
          el.checked = looseEqual(value, vnode.props!.value) // 根据looseEqual的结果进行回显
        }
      }
    }
    
    // retrieve raw value set via :value bindings
    function getValue(el: HTMLOptionElement | HTMLInputElement) {
      return '_value' in el ? (el as any)._value : el.value
    }
    
    1. vModelRadio实现原理:
    • eg: <input type="radio" value="foo" v-model="str">
      a. 绑定值: str
      b. vnode.props!.value : 'foo'
      c. el: input 这个dom元素
      d. onUpdate:modelValue: $event => (str = $event) 这是模板编译生成的

    • created钩子中:
      a. 根据绑定值vnode.props!.value值的比较结果给el的checked赋值(初始化)
      b. 注册change事件,事件回调触发后调用onUpdate:modelValue(el.value) 就是el.value的值 赋值给 绑定值

    • beforeUpdate钩子中:
      a. 当新老值不相等时,根据 looseEqual(value, vnode.props!.value)的结果 赋值给 el.checked

    • 这样双向绑定就完成了

    looseEqual.ts源码

    import { isArray, isDate, isObject } from './'
    
    function looseCompareArrays(a: any[], b: any[]) { // 数组a 和 数组b会否相等
      if (a.length !== b.length) return false // 长度不一样则不相等
      let equal = true
      for (let i = 0; equal && i < a.length; i++) {
        equal = looseEqual(a[i], b[i]) // 发现有a[i] 和 b[i]不相等,则跳出循环
      }
      return equal // 返回对比结果
    }
    
    export function looseEqual(a: any, b: any): boolean { // a 和 b 是否相等
      if (a === b) return true // 如果强等则返回true
      let aValidType = isDate(a) // a 是否 日期类型 根据 a instanceof Date来判断
      let bValidType = isDate(b) // b 是否 日期类型
      if (aValidType || bValidType) {
        return aValidType && bValidType ? a.getTime() === b.getTime() : false // 如果都是日期类型根据时间戳判断
      }
      aValidType = isArray(a) // a 是否 数组 根据 Array.isArray 来判断
      bValidType = isArray(b) // b 是否 数组
      if (aValidType || bValidType) {
        return aValidType && bValidType ? looseCompareArrays(a, b) : false // 调用looseCompareArrays比较
      }
      aValidType = isObject(a) // a 是否 对象 根据  !== null && typeof a === 'object' 来判断
      bValidType = isObject(b) // b 是否 对象
      if (aValidType || bValidType) {
        /* istanbul ignore if: this if will probably never be called */
        if (!aValidType || !bValidType) { // eg: a是null,b是非null的对象
          return false
        }
        const aKeysCount = Object.keys(a).length // a对象key的个数
        const bKeysCount = Object.keys(b).length // b对象key的个数
        if (aKeysCount !== bKeysCount) { // 个数是否相等
          return false // 不相等肯定不一样
        }
        for (const key in a) { // 遍历
          const aHasKey = a.hasOwnProperty(key) // a是否含有 key
          const bHasKey = b.hasOwnProperty(key) // b是否含有 key
          if (
            (aHasKey && !bHasKey) || // a有且b没有
            (!aHasKey && bHasKey) || // a没有且b有
            !looseEqual(a[key], b[key]) // a[key] 和 b[key] 不相等
          ) { // 上面3中情况任意一种,都表示 a 和 b 不相等
            return false
          }
        }
      }
      return String(a) === String(b) // 转成字符串比较
    }
    
    export function looseIndexOf(arr: any[], val: any): number {
      return arr.findIndex(item => looseEqual(item, val)) // 根据looseEqual来对比
    }
    
    1. 这是使用宽松的比较方式,感觉和鸭式辩型(像鸭子一样走路并且嘎嘎叫的就叫鸭子)类似.
      a. looseEqual(1, '1') // true
      b. looseEqual(new Set(), new Set(['1'])) // true
      c. looseEqual(new Map([['name', 'xzw']]), new Map()) // true

    2. 上面这些都不符合我们预期的结果,所以使用v-model绑定时,进行回显的时候要特别注意.
      可以查看小栗子中的第二个例子,那样使用就是有问题的.

    总结

    vModelRadio 的实现比较简单.
    关于回显时可能需要注意下,当发现回显出问题时,可以查看looseEqual.ts代码.

    下篇: Vue3疑问系列(7) — v-model(vModelSelect)指令是如何工作的?


    起源地下载网 » Vue3疑问系列(6) — v-model(vModelRadio)指令是如何工作的?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元