本文能帮你做什么?好奇vue双向绑定的同学,可以部分缓解好奇心,还可以帮你了解如何实现$watch。
前情回顾
我之前写了一篇没什么干货的文章,并且刨了一个大坑。
今天,打算来填一天,并再刨一个。
不过话说说回来了,看本文之前,如果不知道Object.defineProperty,还必须看看解析神奇的Object.defineProperty
不得不感慨vue的作者,人长得帅,码写的也好,本文是根据作者源码,摘取出来的
本文将实现什么
正如上一篇许下的承诺一样,本文要实现一个$wacth
const v = new Vue({ data:{ a:1, b:2 }})v.$watch("a",()=>console.log("哈哈,$watch成功"))setTimeout(()=>{ v.a = 5},2000) //打印 哈哈,$watch成功
为了帮助大家理清思路。。我们就做最简单的实现。。只考虑对象不考虑数组
1. 实现 observer
思路:我们知道Object.defineProperty的特性了,我们就利用它的set和get。我们将要observe的对象,通过递归,将它所有的属性,包括子属性的属性,都给加上set和get。这样的话,给这个对象的某个属性赋值,就会触发set。开始吧
export default class Observer{ constructor(value) { this.value = value this.walk(value) } //递归。。让每个字属性可以observe walk(value){ Object.keys(value).forEach(key=>this.convert(key,value[key])) } convert(key, val){ defineReactive(this.value, key, val) }}export function defineReactive (obj, key, val) { var childOb = observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: ()=>val, set:newVal=> { childOb = observe(newVal)//如果新赋值的值是个复杂类型。再递归它,加上set/get。。 } })}export function observe (value, vm) { if (!value || typeof value !== 'object') { return } return new Observer(value)}
代码很简单,就给每个属性(包括子属性)都加上get/set,这样的话,这个对象的,有任何赋值,就会触发set方法。。
所以,我们是不是应该写一个消息-订阅器呢?
这样的话,一触发set方法,我们就发一个通知出来,然后,订阅这个消息的,就会怎样?对咯。、收到消息、触发回调。
2. 消息-订阅器
很简单,我们维护一个数组,,这个数组,就放订阅着,一旦触发notify,订阅者就调用自己的update方法
export default class Dep { constructor() { this.subs = [] } addSub(sub){ this.subs.push(sub) } notify(){ this.subs.forEach(sub=>sub.update()) }}
所以,每次set函数,调用的时候,我们是不是应该,触发notify,对吧。所以我们把代码补充完整
export function defineReactive (obj, key, val) { var dep = new Dep() var childOb = observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: ()=>val, set:newVal=> { var value = val if (newVal === value) { return } val = newVal childOb = observe(newVal) dep.notify() } }) }
新闻热点
疑难解答
图片精选