Vue的响应式原理的简单介绍

106 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第20天

Vue的响应式原理简单介绍

使用Vue开发的项目能实现在数据发生改变时,界面紧跟着进行更新,这并不是js的原生特性,而是Vue底层替我们实现的。

什么是响应式

<div id="app">
    {{message}}
</div>
<script>
    const app = new Vue({
        el:`#app`,
        data:{
            message: `111`
        }
    })
</script>

这样在我们的页面中就会显示出111

但如果我们在控制台改变message内容:

app.message=222

页面中的内容也会随之立即自动更改

我们接下来就看看,我们是怎么实现这种响应式改变的

实现响应的基本原理

要实现响应式,我们能想到的大致思路:

  1. 有一个监听message的监听器,看它什么时候发生了改变
  2. 记录哪里调用了message
  3. 当数据改变时,通知调用message的地方一同改变
前两个问题:如何监听改变和监听调用?

那它是如何进行监听的呢?

我们知道,创建Vue时传入的参数都是键值对形式的对象,传入后Vue底层就可以通过遍历来获取其中的每个元素

Object.key(obj).forEach(key => {
    let value = obj[key]
​
})

当它取到了每个键值对,就可以单独为他们定义set、get方法

Object.key(obj).forEach(key => {
    let value = obj[key]
    Object.defineProperty(obj, key, {
        set(newValue) {
            value = newValue;
        },
        get(){
            return value;
        }
    })
})

当我们通过obj.key改变值时,底层就会调用其obj[key].set方法来传入新值,那么我们就能利用set方法来进行监听值的改变

同样的,当我们通过obj.key来获取值时,底层就会调用其obj[key].get方法来获取值,那么我们就能利用get方法来监听值的获取

我们通过get和set就能知道值的改变使用值的位置,接下来就需要将其关联起来👇

第三个问题:将值的改变和调用点关联起来——发布者订阅者

在掘金中,我关注(订阅)一个用户,该用户发帖时就会提醒到我 在Vue中,值的调用点关注(订阅)了对应的值,当值发生改变时就会提醒到调用点

那么它是如何订阅的呢?

我们定义一个Watcher类来创建订阅者(相当于关注别人的用户):

class Watcher{
    constructor(name){
        this.name = name;
    }
}

我们定义一个Dep(dependency)类来记录订阅者,创建订阅关系(相当于被关注用户,即发布者):

class Dep{
    constructor(){
        this.subs = [];//记录订阅者的列表
    }
    addSub(watcher){
        this.subs.push(watcher);//将新创建的订阅者加入到订阅者列表中
    }
    notify(){
        this.subs.forEach(item => {
            item.update();//调用每一个订阅者的update方法进行更新
        })
    }
}

接下来我们就可以实现订阅及更新通知了:

//在get中创建watcher类实例,并将其加入到对应属性的订阅者列表中
get(){
    const w1 = new Watcher("第一个订阅点");
    dep.addSub(w1);
    return value;
}
//在set中调用dep的notify方法,完成所有订阅者的更新
set(newValue){
    value = newValue;
    dep.notify();
}