vue响应式原理

什么是响应式?

在数据变化的时候,视图也会随着改变,这就是响应式

如何实现响应式?

VUE使用Object.defineProperty 中getter和setter方法中的观察者模式来实现响应式

Object.defineProperty

这个方法是对象方法,在一个对象上定义一些新的属性及方法,或改变对象的现有方法,并返回这个对象。
举个示例看一下:

var mVal = 0;
var o = {};
Object.defineProperty(o, 'm'){
	get: function(){
		console.log('这里监听获取m值");
		return mVal;
	},
	set: function(newVal){
		console.log('这里监听修改m值");
		mVal = newVal;
	},
	enumerable : true,
  	configurable : true
}
o.m = 88;
console.log(o.m);  // 88

分析:当调用o.m给对象o中的m属性赋值的时候,会调用set方法,将m的值赋给mVal,此时就会调用get方法,获取这个值。
通过这种方式我们就可以实现一个简单的vue双向绑定了,给data中的所有属性加上get和set方法。

观察者模式

什么是观察者模式?

观察者模式分为注册环节与发布环节。
将需要修改的属性集中注册一下,当处理完后一起发布出去。

function Observer(){
	this.dep = [];
	register(fn){
		this.dep.push(fn)
	}
	notify(){
		this.dep.forEach(item => item())
	}
}
依次注册多个想要执行的函数
const wantCake = new Observer();
wantCake.register(console.log('call dish'));
wantCake.register(console.log('call jaks'));
wantCake.register(console.log('call mejdh'));

在完成后通知所有客户并执行函数
wantCake.notify()
原理解析

首先参考一张原理图
在这里插入图片描述
我们分3个步骤来解释
1、init阶段,VUE实例的data属性reactive化,加上get、set方法

function defineReactive(obj: Object, key: String, ...){
	const dep = new Dep();
	Object.defineProperty(o, key){
		get: function reactiveGetter(){
			...
			dep.depend();
			return value;
			...
		},
		set: function reactiveSetter(newVal){
			...
			val = newVal;
			dep.notify();
			...
		},
		enumerable : true,
	  	configurable : true
	}
}
const Dep{
	static target: ?Watcher;
    subs: Array<Watcher>;

    depend () {
      if (Dep.target) {
        Dep.target.addDep(this)
      }
    }

    notify () {
      const subs = this.subs.slice()
      for (let i = 0, l = subs.length; i < l; i++) {
        subs[i].update()
      }
    }
}

这里的dep是一个观察者类,每一个属性都有一个dep,调用getter时去dep里注册函数,在调用setter时,执行注册的函数。

2、mount阶段

mountComponent(vm: Component, el: ?Element, ...) {
    vm.$el = el

    ...

    updateComponent = () => {
      vm._update(vm._render(), ...)
    }

    new Watcher(vm, updateComponent, ...)
    ...
}

class Watcher {
  getter: Function;

  // 代码经过简化
  constructor(vm: Component, expOrFn: string | Function, ...) {
    ...
    this.getter = expOrFn
    Dep.target = this                      // 注意这里将当前的Watcher赋值给了Dep.target
    this.value = this.getter.call(vm, vm)  // 调用组件的更新函数
    ...
  }
}

在mount时会创建一个Watcher类,这个类是链接dep与vue组件的桥梁。每一个watcher对应一个vue component。
在new Watcher()时会调用组件的getter方法,此时会调用render重新渲染函数。
render函数会访问data属性,这时就去调用这个属性的getter函数

// getter函数
get: function reactiveGetter () {
    ....
    dep.depend()
    return value
    ....
 },

// dep的depend函数
depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
}

在depend函数中,dep就是watcher对象本身。这样每次渲染这个组件时,如果用到了这个属性,组件对应的watcher都会注册到这个属性的dep中。这个过程被称为依赖收集。
在收集完所有的依赖后,如果这个属性变化,就会通知watcher去更新相关的组件。

3、更新阶段
属性改变时,回去调用dep里面的notify函数,然后通知所有的watcher去调用update函数进行更新。

notify () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
}

4、流程展示

reactive属性
setter
Dep
notify
watcher
re-render
VueComponent

5、总结一下
第一步:组件初始化时给data中所有属性添加get、set,reactive化;然后注册一个Watcher对象,此时watcher会立即调用组件里的render去生成虚拟DOM,此时会用到data,所以会触发getter函数,将当前的watcher注册到sub里。
第二步:在data属性变化时,会遍历sub中所有watcher对象,通知它们去渲染组件。

借鉴文章:https://zhuanlan.zhihu.com/p/88648401

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值