Vue.set与this.$set源码

HaoOuBa
2021-07-28 / 2 评论 / 650 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2021年07月28日,已超过1232天没有更新,若内容或图片失效,请留言反馈。

Vue.set()和this.$set()应用的场景

在 Vue 2.X 项目开发中,有时候需要对数组进行修改,或是对对象新增一个属性,但是发现页面并不会同步更新。例如:

const vm = new Vue({
 data: {
  arr: [1, 2],
  obj: {
    a: 3
  }
 }
});
vm.$data.arr[0] = 3;  // 页面不会发生改变
vm.$data.obj.b = 3;  // 页面不会发生改变

此时就需要使用到 Vue.set() 或 this.$set()。这个2个的使用方法一样,不过一个是挂载在Vue身上,一个挂载在Vue.prototype上

// Vue.set
import { set } from '../observer/index'
Vue.set = set
// this.$set
import { set } from '../observer/index'
Vue.prototype.$set = se

使用Vue.set或this.$set来实现页面不更新的问题:

const vm = new Vue({
 data: {
  arr: [1, 2],
  obj: {
    a: 3
  }
 }
});
// 修改数组
Vue.set(vm.$data.arr, 0, 3);
vm.$set(vm.$data.arr, 0, 3);
// 对象新增属性
Vue.set(vm.$data.obj, 'b', 3);
vm.$set(vm.$data.obj, 'b', 3);

set函数的源代码

../observer/index

function set (target: Array<any> | Object, key: any, val: any): any {
  // isUndef(target):用于判断传入的target是否是undefined或null
  // isPrimitive(target):用于判断传入的target是否是原始数据类型
  // 如果是 null 或者是 undefined 时,抛出异常
  if (process.env.NODE_ENV !== 'production' &&(isUndef(target) || isPrimitive(target))) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }

  // 如果传入了一个数组,并且传入的key是数组的有效值
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 用于设置数组的最大length
    // Math.max(target.length, key):取数组的length和key二者中的最大值
    target.length = Math.max(target.length, key)
    // 直接调用数组身上的splice方法,因为Vue里变异了数组方法,调用会触发dep.notify()
    target.splice(key, 1, val)
    return val
  }

  // 接下来就是对象的判断了
  // 如果传入的 key 在原对象中,说明已经是响应式了,直接修改即可
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }

  // 这里到最后的代码,都是对象不在原对象上,是新增的属性值
  const ob = (target: any).__ob__
  // 如果是Vue实例对象,或根数据对象,则抛出警告
  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
  }

  // 如果没有 __ob__ 表示当前 target 不是响应式的,那么直接赋值
  if (!ob) {
    target[key] = val
    return val
  }

  // 剩下就是给响应式对象增加一个 key 新属性,并通知更新
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}
12

评论 (2)

取消
  1. 头像
    wzx
    Windows 10 · Google Chrome

    代码块如何加入行号显示?

    回复
    1. 头像
      假欢
      Windows 10 · Google Chrome
      @ wzx

      https://www.gold404.cn/index.php/info/53

      回复