Object.defineProperty劫持数据时存在两大缺陷:
均不会触发组件的重新渲染,因为Object.defineProperty不能拦截到这些操作,更精确的来说,对于数组而言,大部分操作都是拦截不到的.vue内部通过重写函数的方式解决了这个问题。
Object.defineProperty()<= vue的响应式原理
1.Object.defineProperty(obj,prop,descriptor)
v-model双向绑定的实现原理
通过Object.defineProperty劫持数据发生变化,如果数据发生改变了(在set中进行赋值的),触发update方法进行更新节点内容,从而实现了数据双向绑定原理
Object.defineProperty劫持数据发生变化=>在set中赋值=>触发update方法进行节点更新=>实现v-model双向数据绑定
input触发:
set数据改变执行那个update
update更新DOM节点中的内容
Object.defineProperty劫持数据时存在两大缺陷:
1.下标方式修改数组数据
2.给对象新增属性
均不会触发组件的重新渲染,因为Object.defineProperty不能拦截到这些操作,更精确的来说,对于数组而言,大部分操作都是拦截不到的.vue内部通过重写函数的方式解决了这个问题。
缺陷的解决:
1.vue提供了一个API进行解决
export function set (target: Array<any> | Object, key: any, val: any): any {
// 判断是否为数组且下标是否有效
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 调用 splice 函数触发派发更新
// 该函数已被重写
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
// 判断 key 是否已经存在
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
// 如果对象不是响应式对象,就赋值返回
if (!ob) {
target[key] = val
return val
}
// 进行双向绑定
defineReactive(ob.value, key, val)
// 手动派发更新
ob.dep.notify()
return val
}
2.对于数组而言,vue内部重写了以下函数实现派发更新
// 获得数组原型
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
// 重写以下函数
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method) {
// 缓存原生函数
const original = arrayProto[method]
// 重写函数
def(arrayMethods, method, function mutator (...args) {
// 先调用原生函数获得结果
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
// 调用以下几个函数时,监听新数据
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// 手动派发更新
ob.dep.notify()
return result
})
})
Object.defineProperty()<= vue的响应式原理
1.Object.defineProperty(obj,prop,descriptor)
静态方法 Object.defineProperty(obj,prop,descriptor) 可以通过定义属性的元数据信息精确地控制属性的行为
2. 参数:
obj : (必须)要在其定义属性的对象,object
prop : (必须)要定义或修改的属性名称,stringSymbol
descriptor :(必须)将被定义或修改的属性描述符,object
3. 返回值:
object,返回被传递给函数的对象obj
4.实例
let data = {
name:" 张三 " , age:20 }
let value = data.name
Object.defineProperty(data,"name",{
configurable:true //默认可配置 ; 为false时不可修改name值
enumerable:true //默认可迭代,输出age name ; 为false时不可迭代,输出age
get(){
console.log("get"); return value // 只能return value 不能return data.name
原因: 在内部使用会导致重复陷入死循环},
set(newVal){
console.log("set"); value = newVal},
//value: 1 // get set 和value 不能同时使用
//value(){ return 2 } //value可以是函数
})