vue2响应式原理

核心部件

Observer-观察者

作用: 将一个普通对象转换为一个响应式对象

响应式对象:简单来说就是每个属性都具有getter与setter方法的对象

实现原理: 遍历普通对象,通过Object.defineProperty方法重新定义普通对象中的每个属性,将其转化为响应式对象.数组对象,嵌套对象使用递归实现深层次遍历

该事件发生在beforeCreate与created之间

由于遍历时仅能遍历到对象的当前属性,因此无法检测到将来动态增加或者删除的属性,因此vue提供$set$delete两个实例方法对已有的响应式对象进行添加或删除属性,但是这两个方法很有可能会引发没有必要的重新渲染,所以vue并不怎么推荐使用,而是期望我们在定义对象时給属性一个默认值null或者别的值

对于数组: vue会更改其隐式原型__proto__,增加vue自己写的pop,push,shift,unshift,sort,reverse,splice方法,之所以这样做是因为vue需要监听那些可能改变数组内容的方法

数组原型链: 数组 — vue自定义对象 — Array.prototype

Observer终极目标: 让对象的任何操作都能受到Vue监控

如上Observer解决了属性变化会通知vue,让Vue做操作的问题
但是 vue用通知谁去做呢?又要去做什么呢?

Dep – Dependency – 依赖

作用: 解决两个问题
1 读取属性时去做什么
2 属性改变时去做什么

vue会为响应式对象中的每个属性,对象本身,数组本身创建一个Dep实例,每个实例都有能力做到两件事

  1. 记录依赖: 当响应式对象的属性被读取时,它会进行依赖收集,既是谁用到了我
  2. 派发更新: 当改变某个属性时,它会通知那些用到我的人,我改变了
    上述两点中的我指的是该属性,对象,或数组

Dep解决了Observer未能解决的做什么的问题,现在vue具有了能动态做事的能力,但是现阶段Dep实例只是知道去做事,还是不知道要谁去做事.

Watcher

作用: 完成闭环,解决是谁去做事的问题

拿render函数举例
ps: 不知道render函数的话去补补吧
render函数不会直接执行,而是交给Watcher去执行,在watcher要执行render之前,watcher将会在全局设置一个变量,并使其等于render,例如window.currentHandler = watcher.render(只是瞎举例),然后去执行函数render,当render执行过程中用到响应式数据,则会触发Dep实例的记录依赖功能,在这时,Dep会记录当前全局变量windowcurrentHandler的值,当Dep触发派发功能时,通知之前记录的watcher,响应式原理完成闭环
但是此时产生一个效率问题

//假设页面使用了a b c d四个属性
//一个函数foo修改四个属性
foo() {
	this.a = 1;
	this.b = 2;
	this.c = 3;
	this.d = 4;
}
//由于a b c d均为响应式数据, 此函数运行时会触发四次Dep的派发功能,
//也就是会触发四次watcher, 也就是会触发四次render函数,渲染四次页面,
//由此产生效率问题,那么如何解决呢?

Scheduler–调度器

作用: 解决效率问题
其实Watcher在收到派发通知的时候并不会立即去执行对应的函数,而是把自己交给调度器Scheduler

Scheduler维护一个执行队列,该队列同一个Watcher仅会存在一次该队列的Watcher不会立即执行,他会通过一个nextTick的工具方法,把需要执行的watcher放入到事件循环的微队列中,nextTick的具体做法师通过Promise完成的

nextTick工具会通过$nextTick暴露給开发者

也就是说,当响应式数据变化时,render函数的执行时异步的,并且在微队列中

不算太好理解,来搞个事情

<div id="app">
    <h1 id="test">{{ a }}, {{ b }}</h1>
    <button @click="changeSomething">改变</button>
</div>
<script>
const vm = new Vue({
    el: "#app",
    data() {
        return {
            a: 1,
            b: 2
        }
    },
    methods: {
        changeSomething() {
        	//例1
            this.$nextTick(()=> {
                let h1 = document.getElementById("test");
                console.log(h1.innerText); //打印 1, 2
                /*
					打印 1, 2 的原因是 scheduler维护一个微队列,nextTick中的函数
					已被放入此微队列
					this.a的修改触发的watcher放入微队列时被排在了后面
					所以执行时this.a this.b并没有被更改
				*/
            });
            this.a = 99;
            this.b = 100;
			// ---------------------------
			//例2
			// this.a = 99;
            // this.$nextTick(()=> {
            //     let h1 = document.getElementById("test");
            //     console.log(h1.innerText); //打印 99, 100
            		/*
            			this.a先执行,所以watcher已被放入微队列
            			nextTick中的函数放在后面
            			this.b执行时触发的是同一个watcher所以
            			没有新的watcher加在后面
            		*/
            // });
            // this.b = 100;
        }
    },
})
</script>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

合法的咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值