vue2.x defineProperty Api

vue2.X 中的 defineProperty API

Vue 初始化过程

Vue 的初始化过程,分别有Observer、Compiler和Watcher,当我们 new Vue 的时候,会调用Observer,通过 Object.defineProperty 遍历 vue 对象的 data、computed 或者 props(如果是组件的话)的所有属性进行监听。同时通过Compiler解析模板指令,解析到属性后就 new 一个Watcher并绑定更新函数到 watcher 当中,Observer 和 Compiler 就通过属性来进行关联。

vue2.X的响应式:

基于Object.defineproperty 中的getter和setter函数进行数据劫持完成数据响应式的。

Object.defineproperty()方法会直接在一个对象上定义一个新属性或修改对象现有属性,并返回此对象。

  • Object.defineProperty(obj, prop, descriptor) 的参数

obj         要定义属性的对象(目标对象)

prop        要定义或修改的属性的名称

descriptor  目标对象属性的一些特征(是一个对象)

    descriptor  下有6个参数

    参数1:
        value: 属性值
    参数2:
      writable:对象属性值是否可以被修改  true允许  false不允许
    参数3:
      configurable: 对象属性是否可以被删除 true允许  false不允许
    参数4:
      enumerable: 对象属性是否可被枚举
    参数5:
        get(): 给一个属性提供getter方法,当访问这个对象的属性值得时候触发该方法
    参数6:
        set(): 给一个属性提供setter方法,当设置属性值得时候触发该方法 
  • get

当访问对象中的属性时,会调用getter函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值

  • set

当属性值被修改时,会调setter函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。默认为 undefined。

  • Object.defineProperty 监听简单的对象
// 触发更新视图
function updateView() {
    console.log('更新视图')
}
const obj = {}

function defineReactive (obj,key,value){

    Object.defineProperty(obj,key,{ 

        get(){
            console.log(key,value,'get')
            return value
        },
        set(newVal){
            if (newVal !== value) {
                value= newVal
                updateView() 
                console.log(key,value,'set')
            }
        }
    })
}

defineReactive(obj, 'name','zhu') // 添加属性 name

obj.name='li'  // 修改name属性,调用set 方法

console.log(obj.name,'obj') // 读取name属性,调用get

  • Object.defineProperty 监听复杂的对象
const obj= {
    name: 'zhu',
    age: 20,
    info: {
        address: '北京'  // 需要深度监听
    },
}

function updateView() {
    console.log('更新视图')
}

function defineReactive (obj,key,value){

    Object.defineProperty(obj,key,{ 

        get(){
            console.log(key,value,'get')
            return value
        },
        set(newVal){
            if (newVal !== value) {
                value= newVal
                updateView() 
                console.log(key,value,'set')
            }
        }
    })
}

// 监听对象属性
function observer(obj) {

    if (typeof obj !== 'object' || obj=== null) {
        // 不是对象或数组
        return obj
    }
    // 循环定义各个属性
    for (let key in obj) {
        defineReactive(obj, key, obj[key])
    }
}

// 监听数据
observer(obj)

obj.name='li' // 调用set,改变name为li
console.log(obj.name,'name') // 调用get

obj.info.address='上海'  //没有调用set
console.log(obj.info.address,'address') // 结果输出北京,并没有调用set,也就是说该属性没有被监听到

obj.age= {num:22}  // 此时的data.age = {num:21}可以监听到
console.log(obj.age.num) // 没有调用get,也就是说该属性没有被监听到
  • Object.defineProperty 监听复杂的多层嵌套的对象
const obj= {
    name: 'zhu',
    age: 20,
    info: {
        address: '北京'  // 需要深度监听
    },
}

function updateView() {
    console.log('更新视图')
}

function defineReactive (obj,key,value){
    // 深度监听
    observer(value)

    Object.defineProperty(obj,key,{ 

        get(){
            console.log(key,value,'get')
            return value
        },
        set(newVal){
            if (newVal !== value) {
                // 深度监听
                observer(value)

                value= newVal
                updateView() 
                console.log(key,value,'set')
            }
        }
    })
}

// 监听对象属性
function observer(obj) {

    if (typeof obj !== 'object' || obj=== null) {
        // 不是对象或数组
        return obj
    }
    // 循环定义各个属性
    for (let key in obj) {
        defineReactive(obj, key, obj[key])
    }
}

// 监听数据
observer(obj)

obj.info.address='上海'  // 调用了set,改变了属性的value
console.log(obj.info.address,'address') // 结果输出上海,也就是说该属性被监听到

obj.age={num:28} // // 调用了set,改变了属性的value
console.log(obj.age.num,'0') // 调用了get方法,也就是说该属性被监听到
  • Object.defineProperty 监听的缺点
  1. 深度监听,需要递归到底,一次性计算量大
  2. 新增属性,监听不到 —— 使用Vue.set
// 上面例子
obj.sex='男' // 并没有出发set
console.log(obj.sex) // 没有出发get
// 解决方法 Vue.set
Vue.set(obj.'sex','男')
  1. 删除属性,监听不到 —— 使用Vue.set
// 上面例子
delete  obj.age

// 解决方法 Vue.set
Vue.delete(obj.'age')
  1. 数组,监听不到 —— 使用push()、pop()、shift()、unshift()、splice()、sort()、reverse() 7种方法
    Object.defineProperty本身是可以监控到数组下标的变化的,但是在 Vue 中,从性能/体验的性价比考虑,尤大大就弃用了这个特性。

你可能感兴趣的:(vue2.x defineProperty Api)