
大前端成长进阶课程:进入学习
在日常开发中,
$set的也是一个非常实用的api,因为vue2实现响应式的核心是利用了es5的object.defineproperty,当我们通过直接修改数组下标更改数组或者给对象添加新的属性,这个时候object.defineproperty是监听不到数据的变化的,这时候大家就会用上$set,让修改的操作也实现响应,我们知其然更要知其所以然,接下来看一下vue中的$set是如何实现的。【相关推荐:vuejs视频教程、web前端开发】
应用场景
let dataarr = ["item1"];
let dataobject = {
name: "ccs"
};
dataarr[2] = "item2";
dataobject.age = 22;
响应失败,页面没有显示更新新增的数据
this.$set(this.dataarr,2,'item2')
this.$set(this.dataobject,'age',22)
响应成功,页面显示更新新增的数据set实现
接下来我们看一下$set在vue中的定义
function set(target: array| object, key: any, val: any): any { if ( process.env.node_env !== "production" && (isundef(target) || isprimitive(target)) ) { warn( `cannot set reactive property on undefined, null, or primitive value: ${(target: any)}` ); } if (array.isarray(target) && isvalidarrayindex(key)) { target.length = math.max(target.length, key); target.splice(key, 1, val); return val; } if (key in target && !(key in object.prototype)) { target[key] = val; return val; } const ob = (target: any).__ob__; if (target._isvue || (ob && ob.vmcount)) { process.env.node_env !== "production" && warn( "avoid adding reactive properties to a vue instance or its root $data " "at runtime - declare it upfront in the data option." ); return val; } if (!ob) { target[key] = val; return val; } definereactive(ob.value, key, val); ob.dep.notify(); return val; }
在源码中首先判断set的目标是否是undefined和基本类型如果是undefined或基本类型就报错,
因为用户不应该往undefined和基本类型中set东西,
然后又判断了目标是否是数组与key是不是合法的index,合法的index是指值为大于等于0的整数,
如果两个条件都成立就对目标数组调用splice方法插入或者修改数组,
这里的splice不是普通的splice,是王维诗里的splice,是被vue代理重写过的splice
数组实现响应
$set实现数组修改响应的方式是代理重写的数组的一部分方法,接下来我们看一下具体实现
const arrayproto = array.prototype
export const arraymethods = object.create(arrayproto)
const methodstopatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
function def(obj, key, val, enumerable) {
object.defineproperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
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
})
})vue中代理重写的不只是splice,有push、pop、shift、unshift、splice、sort、reverse这七个方法,
首先执行了const result = original.apply(this, args)执行原本数组的方法并获取它的值,接下来判断如果是往数组中添加值就将新添加的值也实现响应式,
最后一步拿到这个数组的_ob_对象对_ob_里的dep进行派发更新。
想深入了解vue的响应式可以查阅往期文章
面试官问你vue2的响应式原理,你怎么答? - 掘金 (juejin.cn)
对象实现响应
$set中下半部分的逻辑就是用来处理对象响应的,我们接着往下看
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;首先判断了属性如果在目标对象中直接return结束逻辑,
因为vue只有添加目标对象中原本没有的属性时才会失去响应,例如 let obj={} obj.name='ccs',
vue在初始化的时候会将data里的所有属性都变成响应式,如果的值是对象或者数组则会new一个observer实例储存在__ob__,想深入了解vue的响应式可以查阅往期文章
面试官问你vue2的响应式原理,你怎么答? - 掘金 (juejin.cn)
拿到这个对象的_ob_进行判断,如果不存在就说明是未经过vue初始化的普通对象而不是响应式对象否则就手动通过definereactive为属性添加get方法与set方法实现响应,
然后手动调用dep里的notify()发布更新。
总结
vue中$set方法对数组和对象的处理本质上的一样的,对新增的值添加响应然后手动触发派发更新。
(学习视频分享:vuejs入门教程、编程基础视频)
以上就是聊聊vue中$set是如何实现的?的详细内容,更多请关注其它相关文章!
我放的下