JavaScript进阶讲解十五—>实现响应式

响应式

可以自动响应数据变量的代码机制,我们就称之为是响应式。如:当有一个值发生了变化,引用了这个值的地方会自动重新执行

响应式的实现

我们已经知道了其原理,那么我们如何实现对象的响应式呢?

我们看看我们需要做什么?对象的属性发生变化时,我们需要将要做响应式的代码,重新执行。

我们先来分析步骤。

  1. 我们我们需要实现一个响应式函数(他接收需要响应式的函数)
  2. 响应式依赖收集(第一反应就是用数组管理,但数组又太难管理,所以可以用类)
  3. 监听对象的变化(Object.defineProperty或者用proxy)
  4. 当变化后,重新执行我们刚刚保存的依赖。

我们先来一个最简单的(就是什么都不考虑,只要达到效果就行)

let obj = {
    name: 'xt'
}


// 依赖收集
const dependArr = []

// 响应式函数
function watchFn(fn) {
    dependArr.push(fn)
    fn()
}

watchFn(function() {
    console.log('我是要响应的内容');
})

Object.keys(obj).forEach(key => {
    let val = obj[key]

    Object.defineProperty(obj, key, {
        get: function() {
            return val
        },
        set: function(newVal) {
            dependArr.forEach(fn => fn())
            val = newVal
        }
    })
})

obj.name = 'tx'

如上,当我们obj.name发生了变化 我们传入进在watchFn中的函数就会重新执行,接下来,我们就只是需要在这上面做拓展(接下来将会使用proxy的方式)

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

// 响应式的函数
function watchFn(fn) {
    activeReactiveFn = fn
    fn()
    activeReactiveFn = null
}

// 依赖类
class Depend {
    constructor() {
        // 依赖容器
        this.reactiveFns = new Set()
    }
    // 添加依赖的方法
    depend() {
        activeReactiveFn && this.reactiveFns.add(activeReactiveFn)
    }
    // 执行依赖的方法
    notify() {
        this.reactiveFns.forEach(fn => {
            fn()
        })
    }
}

// 获取当前依赖  数据结构:WeakMap中的属性名是对象,值为map  map中的属性名是对象的属性名,值为new Depend()
const targetMap = new WeakMap()
function getDepend(target, key) {
    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)
    }

    return depend
}

// 将对象变为响应式
function reactive(obj) {
    return new Proxy(obj, {
        get(target, key, receiver) {
            // 收集依赖
            const depend = getDepend(target, key)
            depend.depend()
            return Reflect.get(target, key, receiver)
        },
        set(target, key, newValue, receiver) {
            Reflect.set(target, key, newValue, receiver)
            // 获取当前依赖,并重新执行
            const depend = getDepend(target, key)
            depend.notify()
        }
    })
}

const infoProxy = reactive({
    name: "xt", // depend对象
    age: 18 // depend对象
})

// 当infoProxy.address变化时需要执行的代码 当其首次执行行 我们需要收集依赖
watchFn(() => {
    console.log(infoProxy.name)
})
watchFn(() => {
    console.log(infoProxy.age)
})

infoProxy.name = "tx"
infoProxy.name = "20"

总结步骤

  1. 我们需要将一个普通对象转为一个响应式对象,即通过reactive函数,他接收一个普通对象,返回的是一个proxy对象。
  2. 在reactive函数中,我们需要用到porxy的两个捕获器get与set,get是用来收集依赖的,当我们对象的某个属性值变化时,我们可以在set捕获器中捕获到,让他重新执行需要响应的函数
  3. 不管我们set还是get,我们都需要找到,对应的依赖来执行,而不是乱执行,所以我们需要用一个合适的数据结构来存取我们对应的依赖(数据结构:WeakMap中的属性名是对象,值为map map中的属性名是对象的属性名,值为new Depend())

你可能感兴趣的:(javaScript,javascript,前端)