zoukankan      html  css  js  c++  java
  • Vue.set 解析

    一、前言

    Vue 的一个特点就是数据的双向绑定。

    在 Vue2.x 中使用的是 defineProperty 对数据进行的拦截监听,由于这种方式对 Object 和 Array 的拦截有缺陷。

    所以在 Vue2.x 中有全局的 Vue.set API 来对这个缺陷进行修正(对于 Array 来说也是部分)。

    二、什么时候使用

    1、Object 使用

    Object 在 data 初始化的时候,就已经有的属性,在后面修改是可以直接修改,并被监听到的。

    对于后面新添加的属性,这时需要用 Vue.set 来新增添加,set 会对这个新增的属性进行拦截。

    2、Array 使用

    由于 Array 的操作都是监听不到的,所以 Vue 里面是做了两个操作的。

    • 对几个常用的操作单独处理
    • 下标操作转换成 splice (回到上面)

    对 Array 几个操作的特殊处理是下面这样:

    // 路径:srccoreobserverarray.js
    
    const arrayProto = Array.prototype
    export const arrayMethods = Object.create(arrayProto)
    
    // 特殊处理的方法列表
    const methodsToPatch = [
      'push',
      'pop',
      'shift',
      'unshift',
      'splice',
      'sort',
      'reverse'
    ]
    
    /**
     * 拦截方法并抛出事件
     */
    methodsToPatch.forEach(function (method) {
      // 缓存原始方法
      const original = arrayProto[method]
      def(arrayMethods, method, function mutator (...args) {
        const result = original.apply(this, args)
        const ob = this.__ob__
        let inserted
        switch (method) {
          case 'push':
          case 'unshift':
            inserted = args
            break
          case 'splice':
            inserted = args.slice(2)
            break
        }
        if (inserted) ob.observeArray(inserted)
        // 通知更改
        ob.dep.notify()
        return result
      })
    })

    三、源码分析

    在 Vue 源码中,是把 set 放在 observer 中定义的。

    set 是对这些进行监听处理。

    // 路径:srccoreobserverindex.js
    
    /**
     * 给 Object set 属性,当不存在时新增并做拦截代理,已经存在的不做处理
     * Array 的下标处理转换成 splice
     */
    export function set (target: Array<any> | Object, key: any, val: any): any {
      if (process.env.NODE_ENV !== 'production' &&
        (isUndef(target) || isPrimitive(target))
      ) {
        warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
      }
      // 是数组,并且是已有的下标处理,转换成 splice
      if (Array.isArray(target) && isValidArrayIndex(key)) {
        target.length = Math.max(target.length, key)
        target.splice(key, 1, val)
        return val
      }
      // 是对象,并且是已有属性,不做处理
      if (key in target && !(key in Object.prototype)) {
        target[key] = val
        return val
      }
      const ob = (target: any).__ob__
      if (target._isVue || (ob && ob.vmCount)) {
        process.env.NODE_ENV !== 'production' && warn(
          'Avoid adding reactive properties to a Vue instance or its root $data ' +
          'at runtime - declare it upfront in the data option.'
        )
        return val
      }
      if (!ob) {
        target[key] = val
        return val
      }
      defineReactive(ob.value, key, val)
      ob.dep.notify()
      return val
    }

    这里定义后,在 global-api 中赋值给全局 Vue

    // 路径:srccoreglobal-apiindex.js
    
    // 下面是截取部分代码
    
    import { set} from '../observer/index'
    
    export function initGlobalAPI (Vue: GlobalAPI) {
        Vue.set = set
    }
  • 相关阅读:
    C++ 中复杂的声明
    指向成员的指针
    指针与引用的操作符
    char指针
    软件测试
    网络应用层协议
    BOOL,int,float,指针变量与零值比较的if语句
    有关单向链表的题目
    main方法执行之前,做什么事
    C++复制控制
  • 原文地址:https://www.cnblogs.com/zhurong/p/14927358.html
Copyright © 2011-2022 走看看