JS响应式对象 - Vue3响应式原理 | 青训营

54 阅读3分钟

1. 响应式对象是什么

  • JavaScript中的响应式对象指的是一种设计模式:(观察者模式)(发布-订阅模式),它允许对象在其属性发生变化时自动更新相关内容或通知其他部分进行相应的操作

2. 响应式对象有什么用

  1. 响应式对象可以更好地开发MVVM模式应用,当数据发生变化时,相关的视图会自动更新。这对于构建实时数据展示、表单验证和交互式应用非常有用。
  2. 通过使用响应式对象,我们可以轻松地管理应用程序的状态。当状态发生改变时,相关组件或模块可以自动更新以保持一致性和同步性
  3. 响应式对象有助于开发者跟踪和调试应用程序的状态变化。通过观察响应式对象的变化,我们可以更容易地追踪和定位问题,提高调试效率

3. 认识Proxy

  • 在JavaScript中,Proxy是一种内置对象,它允许你创建一个代理对象,用于拦截并自定义目标对象的操作。Proxy可以用于修改或增强目标对象的行为,提供了一种对对象进行元编程的能力。
  • 通过使用Proxy对象,你可以拦截目标对象的操作,包括属性访问、属性赋值、属性删除等。每个操作都可以被拦截,并可以自定义处理逻辑来改变默认行为或添加额外的逻辑。

Proxy对象由两个主要部分组成:

  1. 目标对象:也称为被代理对象,是你想要拦截和自定义行为的对象
  2. 处理器对象:它是一个包含各种代理方法的对象。当对目标对象进行操作时,这些代理方法会被调用,从而允许你拦截并自定义操作的行为
  3. Proxy对象的语法如下:target是目标对象,handler是处理器对象。
const proxy = new Proxy(target, handler);

一些Proxy可以做的事情:

  • 拦截并修改属性访问:可以拦截并自定义对目标对象属性的读取、写入和删除操作。
  • 阻止对不存在属性的访问:可以检测并限制对目标对象不存在属性的访问。
  • 函数调用的拦截:可以拦截并自定义对目标对象方法的调用。
  • 构造函数的拦截:可以拦截并自定义对目标对象作为构造函数时的行为。
  • 验证和过滤数据:可以在赋值操作前对数据进行验证和过滤。
  • 监听属性变化:可以监视目标对象属性的变化,并触发相应的操作。

通过利用Proxy对象,你可以灵活地拦截和自定义对象的操作,实现更高级的元编程和改变默认行为。它在一些场景下非常有用,比如数据验证、访问控制、日志记录等。

4. Vue3响应式对象原理

//保存当前需要收集的响应式函数
let activeReactiveFn = null

class Depend {
    constructor() {
        this.reactiveFns = new Set()
    }

    //收集方法
    depend() {
        if (activeReactiveFn) {
            this.reactiveFns.add(activeReactiveFn)
        }
    }

    //调用方法
    notify() {
        this.reactiveFns.forEach(fn => {
            fn()
        })
    }
}

//封装一个响应式函数
function watchFn(fn) {
    activeReactiveFn = fn
    fn()
    activeReactiveFn = null
}

//封装一个获取depend的函数
const targetMap = new WeakMap()

function getDepend(target, key) {
    //根据target对象获取map的过程
    let map = targetMap.get(target)
    if (!map) {
        map = new Map()
        targetMap.set(target, map)
    }

    let depend = map.get(key)
    if (!depend) {
        depend = new Depend()
        map.set(key, depend)
    }

    //根据key获取depend对象
    return depend
}

function reactive(e) {
    return new Proxy(e, {
        get(target, p, receiver) {
            //根据target获取对应的depend
            const depend = getDepend(target, p)

            //给depend添加对应的函数
            depend.depend()
            return Reflect.get(target, p, receiver)
        },
        set(target, p, newValue, receiver) {
            Reflect.set(target, p, newValue, receiver)
            //执行需要响应的函数
            const depend = getDepend(target, p)
            depend.notify()
        }
    })
}

//需要响应式的对象,一个属性对应一个depend对象
//监听对象属性变化:proxy(vue3)/ Object.defineProperty(vue2)
const obj = reactive({
    name: 'aka',
    age: 18
})

watchFn(() => {
    console.log(obj.name, 'obj---------')
})

const info = reactive({
    address: '广州市',
    height: 1.88
})

watchFn(() => {
    console.log(info.address, 'info---------')
})

console.log('\n\n')
info.address = '北京市'