响应式原理
Vue 实现响应式的一个核心 API 是 Object.defineProperty
defineReactive
对数据进行监听的具体实现
js
// 触发更新视图
function updateView() {
console.log('视图更新')
}
// 重新定义属性,监听起来
function defineReactive(target, key, value) {
// 核心 API
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
// 设置新值
value = newValue
// 触发更新视图
updateView()
}
},
})
}observer
调用该函数后,可对目标对象进行监听,将目标对象编程响应式的
js
// 监听对象属性
function observer(target) {
if (typeof target !== 'object' || target === null) {
// 监听的不是对象或数组时,直接返回
return target
}
// 重新定义各个属性(for in 也可以遍历数组)
for (let key in target) {
defineReactive(target, key, target[key])
}
}如何深度监听 data 变化
对于有嵌套属性的数据,深度监听
js
// 重新定义属性,监听起来
function defineReactive(target, key, value) {
// 深度监听
observer(value)
// 核心 API
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
// 深度监听
observer(newValue)
// 设置新值
// 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
value = newValue
// 触发更新视图
updateView()
}
}
})
}如何监听数组变化
对于数组,vue 是通过重写数组方法来实现
js
// 触发更新视图
function updateView() {
console.log('视图更新')
}
// 重新定义数组原型
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty)
;['push', 'pop', 'shift', 'unshift', 'splice'].forEach((methodName) => {
arrProto[methodName] = function () {
updateView() // 触发视图更新
oldArrayProperty[methodName].call(this, ...arguments)
}
})
// 重新定义属性,监听起来
function defineReactive(target, key, value) {
// 深度监听
observer(value)
// Object.defineProperty 部分的代码省略
}
// 监听对象属性
function observer(target) {
if (typeof target !== 'object' || target === null) {
// 监听的不是对象或数组时,直接返回
return target
}
if (Array.isArray(target)) {
target.__proto__ = arrProto
}
// 重新定义各个属性(for in 也可以遍历数组)
for (let key in target) {
defineReactive(target, key, target[key])
}
}