【Vue源码】变化侦测篇-Objcet的变化侦测

变化侦测

前言

众所周知vue最大的特点之一是数据驱动视图,举例一个公式:

 UI = render(state)

上方公式state(状态)是输入,UI(视图)是输出,render(渲染)是驱动state来改变UI的方法,当state发生变化时,输出也会跟着变化,我们把这种特性称之为数据驱动视图

我们知道state和UI是用户规定的,而不变的是render(),所以VUE扮演的就是render()这个角色,VUE发现了state的变化,经过一系列的加工,最终反映在UI上,那么VUE是如何知道state的变化的呢,由此引出了变化侦测

结论: 数据驱动视图就是状态变化引起了视图变化

变化侦测

变化侦测就是追踪状态的变化,一旦发生了改变就去更新视图,js为我们提供了Object.defineProperty方法,通过该方法,我们可以知道数据在什么时候发生了变化

Object的变化侦测

使Object的数据变得可观测

数据的每次读和写都能被我们看到,即我们知道数据什么时候读取或者数据什么时候被改写,就是数据变得可观测。

要将数据变得可观测,我们就要借助Object.defineproperty()方法。

首先我们定义一个数据对象car

let car = {
    'brand': 'BMW',
    'price': 3000
}

要如何做才能让car变得可观测呢,我们使用Object.defineproperty()来改写car

let car = {}
let val = 3000

Object.defineproperty(car,'price',{
    enumerable: true, //是否可以被枚举
    configurable: true, //是否可以被配置
    get(){
        console.log("price属性被读取了")
        return val
    },
    set(newVal){
        cosole.log("price属性被改写了")
        return newVal
    }
})

通过Object.defineproperty()方法给car定义一个price属性,并把这个属性的get()和set()进行拦截,每当该属性进行读或者写操作的时候就会触发get()和set(),这就已经把car的属性变成可观测的了

为了把car的所有属性都变成可观测的,我们可以编写如下代码

//源码位置: src/core/observer/index.js

/**
 * observer类会通过递归的方法把一个对象的所有属性都转化成可观测对象
 */

export class Observer {
    constructor(value){ //Observer的构造函数, this指向实例
        this.value = value

        //给value新增一个__ob__属性,值为该value的Observer实例
        //相当于为value打上标记,表示它已经被转化成响应式了,避免重复操作
        def(value,'__ob__',this)

        if(Array.isArray(value)){
            //当value为数组时的逻辑
            //...
        }else{ 
            this.walk(value)
        }
    } 

    walk(obj: Object){ 
        //如果时对象就将对象的key枚举出来进行循环
        //让每一个对象的属性都执行一遍可观测的方法
        const keys = Object.keys(obj)
        fot(let i = 0; i < keys.length; i++){
            defineReactive(obj,keys[i])
        }
    }
}

/**
 * 是一个对象转化成可观测的对象
 * @param { Object } obj 对象
 * @param { String } key 对象的key
 * @param { Any } val 对象的某个key的值
 */

function defineReactive(obj,key,val){
    //如果只传了obj和key,那么val = obj[key]
    if(arguments.length === 2){
        val = obj[key]
    }
    if(typeof val === 'Object'){ 
        //如果值是对象,那么就要进行递归,来将下一层的数据变成可观测的对象,直到最后一层
        new Observer(val)
    }

    Object.defineproperty(obj,key,{
        enumerable: true, //是否可以被枚举
        configurable: true, //是否可以被配置
        get(){
            console.log(`${key}属性被读取了`)
            return val
        },
        set(newVal){
            //如果新的值与旧的值一样那么就不执行操作
            if(val === newVal){
                return
            }
            console.log(`${key}属性被修改了`)
            val = newVal
        }
    })
}

在上面的代码中我们定义了一个observer类,它用来将一个object转换成可观测的object

并且给value新增一个__ob__的属性,值为该value的observer实例,这个操作相当于为value添加一个标记,表示它已经被转换成响应式的了,避免重复操作

然后判断该值的数据类型,只有object类型的数据才会调用walk将每一个属性转换成getter/setter的形式来侦测变化,最后在defineReactiver中传入的属性值还是一个object时使用new observer(val)来递归子属性,这样我们就可以把obj中的所有属性(包括子属性)都转换成getter/setter的形式来侦测变化,只要我们传一个object到observer中,那么这个object就会变成可侦测的,响应式的obj

现在我们可以这样定义car:

let car = new Observer({
    'brand''BMW',
    'price': 3000
})
  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值