【vue面试必备系列】之vue 数据响应式原理

857 阅读2分钟

今天让我们实现一下vue响应式的原理

  1. 响应式数据通过内部的defineReactive函数实现响应式数据更新,defineReactive核心是defineProperty对每个属性进行监听
  2. 每个属性都定义一个dep 空数组在第一次执行更新函数时候调用get方法时候收集依赖(不能每次都进行收集,所以更新函数需要第一次放入watch进行订阅,要设置了active,在watch的时候,active设置为true即可,执行更新函数fn,把active设置为false)
  3. 在set的时候触发依赖执行更新函数
  4. 把更新函数放入watch 函数内进行触发更新。

1 核心defineReactive

  • 响应式数据通过内部的defineReactive函数实现响应式数据更新,
  • defineReactive核心是defineProperty对每个属性进行监听
function defineReactive(obj) {
    for (let key in obj) {
        let value = obj[key]
        Object.defineProperty(obj, key, {
            get() {
                return value
            },
            set(newValue) {
                value = newValue
            }
        })
    }
}
defineReactive(state) // 执行一下 变成响应式的

2 在get中对dep进行收集依赖

  • 循环对象的每个属性都定义一个dep 空数组
  • 第一次执行更新函数fn时候调用get方法时候收集依赖
  • 不能每次都进行收集,所以更新函数需要第一次放入watch进行订阅,要设置了active,在watch的时候,active设置为true即可,执行更新函数fn,把active设置为false(第4步说到)
    let active
    for (let key in obj) {
        let dep = []
        Object.defineProperty(obj, key, {
            get() {
                if (active) {
                    dep.push(active)
                }
            },
        })
    }

3 set的时候发布

  • 在set的时候触发依赖执行更新函数
set(newValue) {
  value = newValue
  dep.forEach(fn => fn())
}

4 watch 观察者

  • 定义一个watch函数
  • 把更新函数fn都放入watch 函数内进行触发更新。
let watch = (fn) => {
    active = fn
    fn()
    active = null
}
watch(() => {
    app.innerHTML = state.count
})
watch(() => {
    console.log(state.count)
})

5 代码合集

let state = { count: 1 }

let active
// 把数据变成响应式
function defineReactive(obj) {
    for (let key in obj) {
        let value = obj[key]
        let dep = []
        Object.defineProperty(obj, key, {
            get() {
                if (active) {
                    dep.push(active)
                }
                return value
            },
            set(newValue) {
                value = newValue
                dep.forEach(fn => fn())
            }
        })
    }
}
defineReactive(state) // 执行一下 变成响应式的
let watch = (fn) => {
    active = fn
    fn()
    active = null
}
watch(() => {
    app.innerHTML = state.count
})
watch(() => {
    console.log(state.count)
})
setTimeout(() => {
    state.count = 2 //测试两秒后是否触发响应
}, 3000)

本文使用 mdnice 排版