Vue.set
因为我们一开始可能不知道入口在哪,这时我们可以写个测试用例,用来查找入口
先创建一个测试文件
<!-- test/test4.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../dist/vue.js"></script>
</head>
<body>
<div id="setDemo">
<h1>set/del</h1>
<p>{{obj.foo}}</p>
</div>
<script>
const app = new Vue({
el: "#setDemo",
data: {
obj: {}
},
mounted() {
setTimeout(() => {
Vue.set(this.obj, 'foo', 'fooo')
}, 1000);
}
})
</script>
</body>
</html>
在浏览器中打开这个测试文件,找到源码位置
- 在sources中,ctrl+p输入test4.html查找到文件,然后我们在
Vue.set
处打上断点,开始调试
调试入口 -
因为this.obj是一个对象,所以会先走一遍代理,这步可以忽略
image.png - 第二次进入
Vue.set
时,才是真正进入set
方法,发现set
方法在src\core\observer\index.js
文件中
image.png
打开源码对应位置解读
// src\core\observer\index.js
export function set (target: Array<any> | Object, key: any, val: any): any {
// 判断目标是不是一个数组,且key是一个有效值
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 将数组的长度修改为 target.length 和 key 中的较大者,否则如果当要设置的元素的索引大于数组长度时 splice 无效
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
// 如果不是数组,就是对象
// 这个判断本质上是判断key是否在target自身属性上,即obj.hasOwnProperty(key)
// 即这个key不是新增的,本身就是响应式的,不需要做额外操作,直接返回
if (hasOwn(target, key)) {
target[key] = val
return val
}
// 通过target是否包含__ob__属性,判断它本身是不是一个响应对象
const ob = (target: any).__ob__
// 判断target是不是Vue实例本身,或者是否为根数据对象,如果是发出警告,避免覆盖
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
}
// target是对象,才对新增的key做响应式处理
defineReactive(ob.value, key, val)
// 从而触发响应
ob.dep.notify()
return val
}
Vue.delete
del
的方法src\core\observer\index.js
文件中,且就在set
方法下面,同理,你也可以写个测试用例验证,我这就不再累赘述了
// Vue.delete删除操作
export function del (target: Array<any> | Object, key: any) {
// 判断是不是一个数组,且key有效
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 直接做删除操作
target.splice(key, 1)
return
}
// 判断target是不是Vue实例本身,或者是否为根数据对象,如果是发出警告,避免覆盖
const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
return
}
// 如果为对象,key不在对象属性上,则不需要做删除操作
if (!hasOwn(target, key)) {
return
}
// 否则使用delete关键字作删除操作
delete target[key]
// 如果targett本身不是响应式的,就不需要做响应通知
if (!ob) {
return
}
// 否则,则做响应通知
ob.dep.notify()
}
网友评论