资讯专栏INFORMATION COLUMN

VUE - MVVM - part4 - 优化Watcher

CoffeX / 2361人阅读

摘要:关于中的的实现,差不多也就这样了,当然这仅仅是基础的实现,而且视图层层渲染抽象成一个函数。不同于中的实现,这里少了很多各种标记和应用标记的过程。

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

回顾

首先我们思考一下截止当前,我们都做了什么

通过 defineReactive 这个函数,实现了对于数据取值和设置的监听

通过 Dep 类,实现了依赖的管理

通过 Watcher 类,抽象出了对象下某个属性的依赖,以及属性变换的 callBack

发现问题

对比 VueMVVM(先把视图层的渲染抽象成一个函数),我们仅仅是实现了一些基础性的东西。还有很大的区别,比如

我们的 Watcher 仅仅是抽象了对象下的单一属性,而一般视图层的渲染是涉及多个属性的,而这些属性的变化是同一个渲染函数(也就是 Vue 中编译模板字符串最终生成的函数)。

通过第一点,我们可以得知,对象下的某几个属性是拥有同一个 Watcher 的,换句话说就是,多个 Dep 依赖与同一个 Watcher,那么 Watcher 中该如何保存这些 Dep ,因为按照我们的实现,都一个 Watcher 中仅仅保持一个 Dep

解决问题 问题1

先让我们想想,我们是如何把依赖注入到 Dep 中的

通过取值触发 defineProperty 中的 get,然后添加依赖

换句话说就是,我只要取过对应属性的值,那么就可以添加依赖。
看到之前 Watcher 的实现:

this.get = function () {
    Dep.target = this
    let value = this.getter.call(object)
    Dep.target = null
    return value
}

这段代码就实现了添加相应属性的依赖,归根到底是这段起了作用

let value = this.obj[this.getter]

这里触发了对应属性的 get ,那好针对第一个问题,我们只要在这里触发多个属性的 get 即可,至于要触发那些属性,我们交由调用者来控制,顺理成章的这里应该是一个函数。考虑之后便有了以下代码

let Watcher = function (object, getter, callback) {
    this.obj = object
    // 这里的 getter 应该是一个函数
    this.getter = getter
    this.cb = callback
    this.dep = null
    this.value = undefined

    this.get = function () {
        Dep.target = this
        // 将取值方式改成函数调用
        let value = this.getter.call(object)
        Dep.target = null
        return value
    }

    this.update = function () {
        const value = this.getter.call(object)
        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()
}
问题二

问题二其实很简单,既然要保存多个 dep 我们把保存的值声明成一个数组即可

let Watcher = function (object, getter, callback) {
    this.obj = object
    this.getter = getter
    this.cb = callback
    // 声明成数组
    this.deps = []
    this.value = undefined

    this.get = function () {
        Dep.target = this
        let value = this.getter.call(object)
        Dep.target = null
        return value
    }

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

    this.addDep = function (dep) {
        // 将 dep 推入数组中
        this.deps.push(dep)
    }

    this.value = this.get()
}

为了方便取消这个 Watcher ,我们在添加一个函数,用于取消所有 DepWatcher 的依赖,所以最终 Watcher 的代码如下:

let Watcher = function (object, getter, callback) {
    this.obj = object
    this.getter = getter
    this.cb = callback
    this.deps = []
    this.value = undefined

    this.get = function () {
        Dep.target = this
        let value = this.getter.call(object)
        Dep.target = null
        return value
    }

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

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

    // 新添加的取消依赖的方法
    this.teardown = function () {
        let i = this.deps.length
        while (i--) {
            this.deps[i].removeSub(this)
        }
        this.deps = []
    }

    this.value = this.get()
}
测试

我们仅仅优化了 Watcher 的实现,其他的代码并没有发生变化

let object = {}
defineReactive(object, "num1", 2)
defineReactive(object, "num2", 4)

let watcher = new Watcher(object, function () {
    return this.num1 + this.num2
}, function (newValue, oldValue) {
    console.log(`这是一个监听函数,${object.num1} + ${object.num2} = ${newValue}`)
})

object.num1 = 3
// 这是一个监听函数,3 + 4 = 7
object.num2 = 10
// 这是一个监听函数,3 + 10 = 13

let watcher2 = new Watcher(object, function () {
    return this.num1 * this.num2
}, function (newValue, oldValue) {
    console.log(`这是一个监听函数,${object.num1} * ${object.num2} = ${newValue}`)
})

object.num1 = 4
// 这是一个监听函数,4 + 10 = 14
// 这是一个监听函数,4 * 10 = 40
object.num2 = 11
// 这是一个监听函数,4 + 11 = 15
// 这是一个监听函数,4 * 11 = 44

// 测试取消
watcher2.teardown()

object.num1 = 5
// 这是一个监听函数,5 + 11 = 16
object.num2 = 12
// 这是一个监听函数,5 + 12 = 17

这就实现了对于多个属性设置同一个监听,当监听函数中的依赖属性发生变化时,自动执行了相应的函数。

关于 Vue 中的 MVVM 的实现 ,差不多也就这样了,当然这仅仅是基础的实现,而且视图层层渲染抽象成一个函数。

不同于 Vue 中的实现,这里少了很多各种标记和应用标记的过程。

这些会增加理解难度,之后有用到再说,实现完整的 MVVM 还需要对数组进行特殊的处理,因为数组是不能用 Object.defineProperty 来处理索引值的,这个也之后再说。

点击查看相关代码

系列文章地址

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/94210.html

相关文章

  • VUE - MVVM - part3 - Watcher

    摘要:解决第一个问题很简单,我们把某个属性的值对应值变化时需要执行的函数抽象成一个对象,然后把这个对象当成是依赖,推入依赖管理中。的实现有了以上的考虑,那个依赖对象在中就是。新值作为添加的第一个函数,很自豪。 看这篇之前,如果没有看过之前的文章,可拉到文章末尾查看之前的文章。 前言 在 step2 中,我们实现了一个管理依赖的 Dep ,但是仅仅使用这个类并不能完成我们想实现的功能,而且代码...

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

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

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

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

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

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

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

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

    hover_lew 评论0 收藏0

发表评论

0条评论

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