We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在前两篇文章中主要了解了模板编译机制和响应式的基本原理,实现了简单的模板编译和数据响应,但是数据的改变还不能触发视图的更新
本文将主要探索vue中的依赖收集与追踪的原理,实现数据与视图的绑定关系;
首先来实现一个订阅者Dep,它的主要作用是存放Watcher的实例
Dep
Watcher
class Dep { constructor() { // 存放watcher this.subs = [] } // 新增watcher addSub(watcher) { this.subs.push(watcher) } // 通知所有的watcher更新 notify() { this.subs.forEach(watcher => { watcher.update() }) } }
在上面实现的Dep类中,主要做了两件事情:
addSub
watcher
notify
update
接下来实现一个Watcher类,它的主要作用是监听属性的更改并通知视图更新
class Watcher { constructor(vm, expr, cb) { this.vm = vm this.expr = expr this.cb = cb // 获取一个旧的值 this.oldVal = this.getOldValue() } // 获取旧的值 getOldValue() { // 将当前的watcher实例作为Dep.target属性的值 Dep.target = this let oldValue = directiveUtil.getVal(this.expr, this.vm) Dep.target = null return oldValue } // 通知视图更新 update() { let newVal = directiveUtil.getVal(this.expr, this.vm) if (newVal != this.oldVal) { this.cb(newVal) } } }
在上面实现Watcher中,主要做了这几件时间
getOldValue
oldVal
directiveUtil.getVal
cb
Dep.target
到这里,已经实现了依赖收集与追踪的两个非常重要的类;
那么接下来就是收集数据的依赖;因为依赖收集和追踪的主要目的是通知视图更新,所以只需要对模板中绑定的数据进行依赖收集
模板中绑定的数据都会通过指令集中的指令方法去改变视图,所以下面对指令集directiveUtil做一些改造
directiveUtil
directiveUtil = { // 获取属性的value值,eg:data.key getVal(expr, vm) { let exprs = expr.split('.') // console.log('exprs', exprs, vm.$data) return exprs.reduce((current, item) => { return current[item] }, vm.$data) }, // 获取内容,eg:{{a}}{{b}}{{c}} getContent(expr, vm) { return expr.replace(/\{\{(.+?)\}\}/g, (...args) => { return this.getVal(args[1], vm) }) }, // 设置属性的value值 setVal(expr, vm, value) { let exprs = expr.split('.') exprs.reduce((current, item, index, arr) => { if (index == arr.length - 1) { current[item] = value } return current[item] }, vm.$data) }, // v-model指令的处理方法 model(node, expr, vm) { let fn = this.uploadVlue.uploadModel new Watcher(vm, expr, (newVal) => { fn(node, newVal) }) // 绑定事件 addEventListener('change', (e) => { this.setVal(expr, vm, e.target.value) }, false) fn(node, this.getVal(expr,vm)) }, // v-html指令的处理方法 html(node, expr, vm) { let fn = this.uploadVlue.uploadHtml new Watcher(vm, expr, (newVal) => { fn(node, newVal) }) fn(node, this.getVal(expr,vm)) }, // 小胡子语法的处理方法 text(node, expr, vm) { let fn = this.uploadVlue.uploadText let content = expr.replace(/\{\{(.+?)\}\}/g, (...args) => { new Watcher(vm, args[1], () => { fn(node, this.getContent(expr, vm)) }) return this.getVal(args[1], vm) }) fn(node, content) } }
在上面的代码中,主要做了几个改造
getContent
model
setVal
到这里已经找到了需要进行收集的数据,那么接下来就需要对defineReactive方法进行改造,完成依赖的收集
defineReactive
defineReactive(obj, key, value) { let dep = new Dep() Object.defineProperty(obj, key, { configurable: true, // 属性可以修改和删除 enumerable: true, // 属性可以通过for...in和Object.keys()遍历 get() { // 获取属性时触发 Dep.target && dep.addSub(Dep.target) return value }, set(newVal) { // 设置属性时触发 if (newVal != value) { // 如果新的值和旧的值一致,则没有设置的必要 value = new Val dep.notify() } } }) }
在上面的代码改造中,每一个属性都会创建一个独立的Dep实例,然后通过这个实例来收集watcher;在读取属性的时候,get方法会将当前watcher收集到闭包中Dep实例的subs中,在写属性值的时候,set方法则会通过闭包中的Dep实例调用notify方法来触发实例中所有watcher对象的update方法更新相应的视图
get
subs
set
为了便于理解,下面将按照代码的执行顺序来总结一下属性依赖收集与追踪的整个过程
到这里,就已经完成了依赖收集与追踪的整个过程;配合之前的两篇文章,就已经完成了整个响应式系统的原理分析;其主要就是通过Observer类实现数据的可响应,通过Compiler类进行模板编译,通过Watcher和Dep进行依赖收集;
Observer
Compiler
本文的源代码我已经提交到我的GitHub,欢迎大佬们拍砖
end
The text was updated successfully, but these errors were encountered:
No branches or pull requests
依赖收集追踪的基本原理
在前两篇文章中主要了解了模板编译机制和响应式的基本原理,实现了简单的模板编译和数据响应,但是数据的改变还不能触发视图的更新
本文将主要探索vue中的依赖收集与追踪的原理,实现数据与视图的绑定关系;
Dep 订阅者
首先来实现一个订阅者
Dep
,它的主要作用是存放Watcher
的实例在上面实现的Dep类中,主要做了两件事情:
addSub
方法用于向Dep类中添加watcher
notify
方法用于遍历所有的watcher
,并调用watcher
的update
方法,通知视图更新Watcher 观察者
接下来实现一个
Watcher
类,它的主要作用是监听属性的更改并通知视图更新在上面实现Watcher中,主要做了这几件时间
getOldValue
方法用于获取当前的属性值oldVal
,这个值的主要作用是在update
中判断更新的值与当前的值是否相等,如果相等则不更新视图directiveUtil.getVal
方法是在之前的代码中实现的一个通过绑定字符串获取属性值的方法update
方法用于在数据更新的时候通知视图更新cb
是数据更新后的回调函数,并且将新的值作为参数返回Dep.target
设置为当前实例,并在取值完成后将它清空到这里,已经实现了依赖收集与追踪的两个非常重要的类;
那么接下来就是收集数据的依赖;因为依赖收集和追踪的主要目的是通知视图更新,所以只需要对模板中绑定的数据进行依赖收集
模板中绑定的数据都会通过指令集中的指令方法去改变视图,所以下面对指令集
directiveUtil
做一些改造在上面的代码中,主要做了几个改造
Watcher
实例进行依赖追踪,当属性值发生改变时就会触发Watcher
实例创建时的的回调函数,通过回调函数去改变视图getContent
方法用于重新解析小胡子语法中的内容model
方法添加了事件绑定setVal
方法的功能是通过表达式设置属性值到这里已经找到了需要进行收集的数据,那么接下来就需要对
defineReactive
方法进行改造,完成依赖的收集在上面的代码改造中,每一个属性都会创建一个独立的
Dep
实例,然后通过这个实例来收集watcher
;在读取属性的时候,get
方法会将当前watcher
收集到闭包中Dep
实例的subs
中,在写属性值的时候,set
方法则会通过闭包中的Dep
实例调用notify
方法来触发实例中所有watcher
对象的update
方法更新相应的视图小结
为了便于理解,下面将按照代码的执行顺序来总结一下属性依赖收集与追踪的整个过程
directiveUtil
中对应的方法进行视图更新Watcher
实例Watcher
内部,调用getOldValue
方法获取到当前表达式的值,同时会将Dep.target
的值设置为当前Watcher
实例getOldValue
的时候会触发属性的get
方法,在get方法中,会将第3步中创建的Watcher
实例push到闭包中Dep实例的subs
中set
方法,set
方法会调用闭包中Dep实例的notify
方法,notify
方法会遍历当前实例所有的watcher
,并调用watcher
的update
方法update
方法调用的时候会判断新的属性值与旧的属性值(oldVal
)是否相同,如果不相同,则会将新的属性值作为第3步中实例创建的时候传入的回调函数的参数,并且调用该函数directiveUtil
中对应的指令方法中,在这里将调用视图更新的方法进行视图更新到这里,就已经完成了依赖收集与追踪的整个过程;配合之前的两篇文章,就已经完成了整个响应式系统的原理分析;其主要就是通过
Observer
类实现数据的可响应,通过Compiler
类进行模板编译,通过Watcher
和Dep
进行依赖收集;本文的源代码我已经提交到我的GitHub,欢迎大佬们拍砖
end
The text was updated successfully, but these errors were encountered: