资讯专栏INFORMATION COLUMN

VUE - MVVM - part3 - Watcher

wums / 1706人阅读

摘要:解决第一个问题很简单,我们把某个属性的值对应值变化时需要执行的函数抽象成一个对象,然后把这个对象当成是依赖,推入依赖管理中。的实现有了以上的考虑,那个依赖对象在中就是。新值作为添加的第一个函数,很自豪。

看这篇之前,如果没有看过之前的文章,可拉到文章末尾查看之前的文章。

前言

step2 中,我们实现了一个管理依赖的 Dep ,但是仅仅使用这个类并不能完成我们想实现的功能,而且代码的解耦上也有点小问题。以下是在 step2 中最后说的几个问题:

解耦不完全,需要传递参数

没有地方可以移除依赖

考虑问题

第一个问题显示出来一个问题,由于我们的依赖是函数,为了函数的执行我们只能讲参数传进去,这个问题的根源在于我们的依赖是一个函数;

第二个问题其实反映出当前的 dep 实例只有在 defineReactive 中使用,而没有暴露出来,只要在外部有这个实例的引用,那么我们就能顺利的调用移除依赖了(removeSub)。

解决第一个问题很简单,我们把某个属性的值、对应值变化时需要执行的函数抽象成一个对象,然后把这个对象当成是依赖,推入依赖管理中。

在第一个问题的基础上第二个问题就能解决了,我们只需要把 dep 的引用保存在依赖对象中就可以了。

当然我也是在看了 Vue 源码的基础上才有了上面的解决办法,这里不得不给尤大大赞一个。

Watcher 的实现

有了以上的考虑,那个依赖对象在 Vue 中就是 Watcher

let Watcher = function(object, key, callback){
    this.obj = object
    this.getter = key
    this.cb = callback
    this.dep = null
    this.value = undefined

    this.get = function(){
        Dep.target = this
        let value = this.obj[this.getter]
        Dep.target = null
        return value
    }

    this.update = function(){
        const value = this.obj[this.getter]
        const oldValue = this.value
        this.value = value
        this.cb.call(this.obj, value, oldValue)
    }

    this.addDep = function(dep) {
        this.dep = dep
    }

    this.value = this.get()
}

上述代码实现了一个 Watcher ,为了方便起见我这里叫它监听。

该类的实例保存了需要监听的对象(object),取值方法(key),对应的回调(callback),需要监听的值(value),以及取值函数(get)和触发函数(update),这样我们就把依赖相关的所有内容保存在这个 Watcher 的实例中。

为了保存对 Dep 的引用,在 Watcher 中设置了 dep ,用于存放该监听被那个 Dep 给引用了。

由于在 Watcher 实例化的时候,我们已经对相应的值取了一次值,就是将以下代码放在在 Watcher

Dep.target = function(newValue, oldValue){
    console.log("我被添加进去了,新的值是:" + newValue)
}  
object.test
Dep.target = null  

对应的代码为

this.get = function(){
    Dep.target = this
    let vaule = this.obj[this.getter]
    Dep.target = null
    return value
}

this.value = this.get()

所以在编写代码的时候,不用特地的去触发 get 添加依赖。

那么针对 Watcher 我们需要改造一下之前实现的 DepdefineReactive 函数。

由于依赖变成了 Watcher 所以在 Depnotify 应该改成 Watcher 下的触发函数:update

由于 watcher 中存放了变量的状态,所以不需要在 defineReactive 函数中传入参数

let Dep = function(){

    this.subs = []

    this.addSub = function(sub){
        this.subs.push(sub)
    }

    this.removeSub = function(sub){
        const index = this.subs.indexOf(sub)
        if (index > -1) {
            this.subs.splice(index, 1)
        }
    }

    this.notify = function(){
        // 修改触发方法
        this.subs.forEach(watcher=>watcher.update())
    }
}

Dep.target = null

let defineReactive = function(object, key, value){
    let dep = new Dep()
    Object.defineProperty(object, key, {
        configurable: true,
        enumerable: true,
        get: function(){
            if(Dep.target){
                dep.addSub(Dep.target)
                // 添加 watcher 对 dep 的引用
                Dep.target.addDep(dep)
            }
            return value
        },
        set: function(newValue){
            if(newValue != value){
                value = newValue
                // 不需要特地传入参数
                dep.notify()
            }
        }
    })
}

接下来我们来测试一下

let object = {}
defineReactive(object, "test", "test") 

let watcher = new Watcher(object, "test", function(newValue, oldValue){
    console.log("作为 watcher 添加的第一个函数,很自豪。新值:" + newValue)
})
object.test = "test2"
// 作为 watcher 添加的第一个函数,很自豪。新值:test2

let watcher2 = new Watcher(object, "test", function(newValue, oldValue){
    console.log("作为 watcher 添加的第二个函数,也很自豪。新值:" + newValue)
})
object.test = "test3"
// 作为 watcher 添加的第一个函数,很自豪。新值:test3
// 作为 watcher 添加的第二个函数,也很自豪。新值:test3

// 接着我们来试一下删除依赖,把 watcher2 给删除
watcher2.dep.removeSub(watcher2)
object.test = "test4"
// 作为 watcher 添加的第一个函数,很自豪。新值:test4

通过上面代码,我们成功解耦,用一个监听来处理某个属性的内容(oldValue, newValue, callback),而且我们也能够去除 dep 中没用的依赖。

当然这个 Watcher 还是需要优化的,比如被多个 Dep 引用,这个就得存一个数组,之后继续优化。

点击查看相关代码

系列文章地址

VUE - MVVM - part1 - defineProperty

VUE - MVVM - part2 - Dep

VUE - MVVM - part3 - Watcher

VUE - MVVM - part4 - 优化Watcher

VUE - MVVM - part5 - Observe

VUE - MVVM - part6 - Array

VUE - MVVM - part7 - Event

VUE - MVVM - part8 - 优化Event

VUE - MVVM - part9 - Vue

VUE - MVVM - part10 - Computed

VUE - MVVM - part11 - Extend

VUE - MVVM - part12 - props

VUE - MVVM - part13 - inject & 总结

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94182.html

相关文章

  • VUE - MVVM - part4 - 优化Watcher

    摘要:关于中的的实现,差不多也就这样了,当然这仅仅是基础的实现,而且视图层层渲染抽象成一个函数。不同于中的实现,这里少了很多各种标记和应用标记的过程。 看这篇之前,如果没有看过之前的文章,可拉到文章末尾查看之前的文章。 回顾 首先我们思考一下截止当前,我们都做了什么 通过 defineReactive 这个函数,实现了对于数据取值和设置的监听 通过 Dep 类,实现了依赖的管理 通过 Wa...

    CoffeX 评论0 收藏0
  • VUE - MVVM - part10 - Computed

    摘要:了解之后我们来实现它,同样的为了方便理解我写成了一个类这里的一般是的实例将属性代理到实例下的构造函数我们实现了代理属性和更新计算属性的值,同时依赖没变化时,也是不会触发的更新,解决了以上的个问题。 看这篇之前,如果没有看过之前的文章,移步拉到文章末尾查看之前的文章。 回顾 先捋一下,之前我们实现的 Vue 类,主要有一下的功能: 属性和方法的代理 proxy 监听属性 watche...

    callmewhy 评论0 收藏0
  • VUE - MVVM - part9 - Vue

    摘要:调用父类的方法类在我们上一步已经实现。我们先实现的绑定,因为是要被监听,所以要进行进一步的处理。调用父类的方法方法绑定完事,其实就这么简单。 看这篇之前,如果没有看过之前的文章,可拉到文章末尾查看之前的文章。 前言 激动人心的时候即将来临,之前我们做的 8 步,其实都在为这一步打基础,这一步,我们来简单实现一个 Vue 对象,还没有看过之前代码的同学,请确认看过之前的文章。 主要实现内...

    yzd 评论0 收藏0
  • VUE - MVVM - part5 - Observe

    摘要:具体代码执行方式进入到的目录下,命令行运行即可。确保为一个对象如果对象下有则不需要再次生成函数返回该对象的实例,这里判断了如果该对象下已经有实例,则直接返回,不再去生产实例。这就确保了一个对象下的实例仅被实例化一次。 看这篇之前,如果没有看过之前的文章,可拉到文章末尾查看之前的文章。 回顾 在 step4 中,我们大致实现了一个 MVVM 的框架,由3个部分组成: defineRe...

    xi4oh4o 评论0 收藏0
  • VUE - MVVM - part2 - Dep

    摘要:看这篇之前,如果没看过先移步看实现中。同样的,在取值时收集依赖,在设置值当值发生变化时触发依赖。中实现了一个的类来处理以上两个问题,之后再说。以下语法下的,源码中差不多就这样点击查看相关代码系列文章地址优化优化总结 看这篇之前,如果没看过 step1 先移步看 实现 VUE 中 MVVM - step1 - defineProperty。 在上一篇我们大概实现了,Vue 中的依赖收集和...

    hover_lew 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<