0
点赞
收藏
分享

微信扫一扫

Vue 数据响应式原理

汤姆torn 2022-05-03 阅读 39

文章目录

Vue 数据响应式原理

Vue2.0 对象

完整流程图
在这里插入图片描述

Observe类:将正常的object转换为被检测的object

  1. Observer类的作用将正常的object转换为被侦测的object,通过getter和setter的形式来追踪。
  2. 在每个属性的getter中会生成对应的Dep对象。属性1 --> Dep对象1 属性2–> Dep对象2 …
  3. 在每个属性的setter中会通知Dep对象里的watcher对象数据发生变化了

Watcher类

  1. 在解析el模板中的指令的时候,通过get获取到数据,创建对应的watcher对象,将watcher对象存入该数据对应的Dep对象中
  2. 当数据发生变化时,在每个属性的setter中会通知Dep对象,调用notify()遍历通知watcher调用update()更新函数更新视图

过程详述

在这里插入图片描述

问题1:数据被修改后,Vue内部如何监听message数据的改变的? --> Object.defineProperty -> 监听对象属性的改变
问题2:当数据发生改变,Vue如何知道需要通知哪些地方更新界面? -->发布订阅者模式

  1. Object.defineProperty -> 监听对象属性的改变
    defineReactive函数定义一个响应式数据
function defineReactive(data,key,val){
	Objcet.defineProperty(data,key,{//代码①
		enumerable:true,
		configurable:true,
		get:function(){
			return val;
		},
		set:function(){
			if(val===newVal)return;
			val = newVal;
		}
	})
}
  1. 定义Observer类将一个正常的object转换成被侦测的object,循环给一个数据内的所有属性(包括子属性)都转换成getter/setter形式,
//{a:"x",b:{c:"y",d:"z"}}
//Observer类的作用将正常的object转换为被侦测的object
class Observer{
	constructor(data){
		this.data = data;
		if(!Array.isArray(data)){ //这里数组和对象都会进来,只处理对象
			Object.keys(data).forEach(key=>{
			defineReactive(this.data,key,data[key]);
			})
		}	
	}
}
function defineReactive(data,key,val){
	//如果val是对象,比如b:{c:"y",d:"z"},说明不是最里层,还需要对{c:"y",d:"z"}进行转换getter/setter
	if(typeof val === 'object')new Observer(val);//递归的目的是每一层的属性都应该被绑定响应式
	//..代码①
}
  1. 现在对数据绑定了监听,但是当数据发生变化时,我们通知谁?我们怎么知道那些地方使用了这个数据?
    对于模板来说使用了name数据,也就是调用了name.get()方法,所以我们可以在get方法中存储用到了name属性的地方,我们可以先把使用了name属性的地方,叫做name属性的依赖。
<template>
	{{name}}
</template>

定义Dep类,Dep类的目的是存储使用了某数据的依赖,同时可以删除依赖、给依赖发更新通知等等,那么Dep类与某数据的关系应该是一一对应,所以我们在绑定响应式时,可以为每个属性绑定一个dep对象。在get的时候,将使用了name的地方(依赖)存进dep中,在set数据改变的时候,通知dep中的依赖数据发生改变了,在getter中收集依赖,在stter中触发依赖

class Dep {//Dep类存储依赖,添加依赖, 删除依赖,通知依赖等
	constructor(){
		this.subs = [];//subs存储依赖
	}
	addSub(sub){
		this.subs.push(sub);
	}
	depend(){
		//if(依赖){
		//	this.addSub(依赖);
		//}
		if(window.target){
			this.addSub(window.target);
		}
	}
	notify(){
		this.subs.forEach(sub =>{
			sub.update(); //通知这个属性的所有依赖数据更新
		})
	}
}
function defineReactive(data,key,val){
	if(typeof val === 'object')new Observer(val);
	let dep = new Dep(); //绑定dep对象
	Objcet.defineProperty(data,key,{
		enumerable:true,
		configurable:true,
		get:function(){
			dep.depend();//修改dep对象,这里应该添加依赖
			return val;
		},
		set:function(){
			if(val===newVal)return;
			val = newVal;
			dep.notify();//通知依赖,数据发生了修改
		}
	})
}
  1. 依赖是什么?之前我们把使用了数据的地方,叫做依赖,那么依赖描述成数据结构应该是什么样子的?
    定义一个Watcher类,一个Watcher对象就是一个依赖
    当属性变化时,就会调用dep的notify属性循环通知watcher调用update()方法,修改模板中的数据。
class Watcher{
	constructor(vm,name,node){
		this.node = node;
		this.vm = vm;
		this.name = name;
		window.target = this, //我们给依赖命名为window.target, = watcher对象,所以依赖就是watcher对象
		this.update();
		window.target = null; //并没有绑定在实例上,全局仅有一个,数据更新后会重新调用get,防止一个watcher对象被多次加入dep.sub数组中
	}
	update(){//将{{name}}的name更新为vm里面的值,这里是在vm上代理了_data的值
		this.node.nodeValue = this.vm[this.name]; 
		//从vm中取某个属性,相当于调用该属性的getter方法,此时window.target是有值的,值为watcher对象,所以这个watcher对象会被存储进dep.sub数组里面
	}
}
//watcher对象在解析模板中的指令的时候会被创建new Watcher

不足以及解决办法

新增属性、删除属性、界面不会更新
解决:通过vm.$set(obj,key.val)/vm.$delete(obj,key)新增属性/删除属性

Vue2.0 数组

Array是通过改写数组的pushpopshiftunshiftsplicesortreverse

不足以及解决办法

直接通过下标修改数组,界面不会自动更新

解决:vm.$set(arr,index.value),调用数组的splice方法,或者采用splice用新值替换该下标值

举报

相关推荐

0 条评论