面试官:请你描述一下你对Vue的数据双向绑定原理的理解
我:Vue2.0版本的数据双向绑定是基于Object.defineProperty这个API来实现的,它是底层暴露给我们的一个封装方法.参数分别是被监听的对象obj,被监听的属性a,以及第三个用来设定属性特性值的对象;我们可以利用get和set这两个回调函数来实现双向绑定
面试官:那么这两个回调函数是否还有别的作用?Vue中的响应依赖又是什么?如何进行收集和触发?你又是怎么理解观察者和监听者的?
我:
Vue的数据渲染过程
请看下面这段代码
<template>
<div id="app">
<div>
{{ msg }}
</div>
<span>{{ msg + 1 }}</span>
</div>
</template>
<script>
export default {
data() {
return {
msg: 123,
};
},
computed:{
getMsg(){
return this.msg + 100;
}
},
watch:{
msg:function(val){
console.log(val);
}
}
};
</script>
那么以上代码在Vue的处理中大概走这几步:
- 使用Object.defineProperty对data中的msg进行数据劫持
- 收集msg的数据依赖,并派一个监听者(watcher),来对这些数据依赖进行挨个监听
- 将已经收集好的数据依赖进行保存,当数据发生变化时触发它们,对数据和Dom进行双向通知
- 生成虚拟DOM->真实DOM
- 挂载到页面上
我们来尝试一下实现以上第1,2,3步,第4第5步后面我们出续集说明。
1.实现数据劫持
function defineData(data,key,val){
Object.defineProperty(data,key,{
get:function(){
return val;
},
set:function(newVal){
if(newVal === val){
return;
}
val = newVal;
}
})
};
2.收集依赖
- div标签中的引用
- span标签中的引用
- 计算属性getMsg中的引用
- watch监听
我们将这四个依赖放入一个数组中,以便于管理触发
let watcherList = [依赖1,依赖2,依赖3,依赖4];
我上面说过,哪里使用了,哪里就需要收集,以便于通知.所以我们可以在使用属性值时触发的get回调中来进行收集.所以同理,当我们修改值时,触发通知的行为也应该在set函数中去执行.代码如下:
function defineData(data,key,val){
let watcherList = [];
Object.defineProperty(data,key,{
get:function(){
watcherList.push(依赖);
return val;
},
set:function(newVal){
if(newVal === val){
return;
}
val = newVal;
waterList.forEach(item=>{
// 通知所属依赖的每一项进行相应的修改
item.notice();
})
}
})
};
在真实的Vue源码中,负责承担通知的角色正是watcher.也就是说,其实我们每一次收集使用了数据的依赖,实际上是把watcher进行了实例化,收集了watcher的实例化对象.这样当数据变化时,watcher的实例化对象就可以帮助我们进行通知修改.
Vue对所有Key值的检测
代码如下:
class Observer{
constructor(value){
this.value = value;
if(Array.isArray(value)){
// 这里是对Array类型的单独处理,下篇会详细介绍
}else{
this.walk(value);
}
}
walk(obj){
const keys = Object.keys(obj);
keys.forEach(key=>{
defineData(obj,key,obj[key]);
});
}
}
function defineData(data,key,val){
// 递归判断,如果val仍然是object类型,则继续实例化Observer进行侦测
if(Object.prototype.toString.call(val) === '[object Object]'){
new Observer(val);
};
let watcherList = [];
Object.defineProperty(data,key,{
enumerable:true, // 是否可枚举
configurable:true, // 是否可修改和可删除,
get:function(){
watcherList.push(new watcher);
return val;
},
set:function(newVal){
if(newVal === val){
return;
}
val = newVal;
waterList.forEach(item=>{
// 通知所属依赖的每一项进行相应的修改
item.notice();
})
}
})
}
- 资料来源:《深入浅出Vue.js》
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!