美文网首页
浅谈数据响应的Vue实现及横纵对比

浅谈数据响应的Vue实现及横纵对比

作者: CJ_景元 | 来源:发表于2019-09-26 15:36 被阅读0次

把数据渲染到页面,并且能够追踪数据的改变并自动更新页面的显示,是现代前端框架做的重要工作之一,下面是一段常见的Vue代码片段。

<template>
    <div>
        <p>{{msg}}</p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            msg:""
        }
    },
    mounted() {
        setTimeout(() => {
            this.msg='Hello'
        }, 3000);
    },
}
</script>

运行起来可以看到页面由空白变为展示相应字符,中间到底发生了什么?

为了便于理解需要了解的前置知识
  1. 实现于ES5Object.definePropertyMDN
/*
 *  obj: 目标对象
 *  prop: 需要操作的目标对象的属性名
 *  descriptor: 描述符  
 *  return value 传入对象
 */
Object.defineProperty(obj, prop, descriptor)

重点关注的描述符对象

  • enumerable,属性是否可枚举,默认 false。
  • configurable,属性是否可以被修改或者删除,默认 false。
  • get,获取属性的方法,值被读取时调用。
  • set,设置属性的方法,值被更改时调用。
  1. 发布订阅模式
    Vue的数据响应系统设计里,采用了发布-订阅设计模式,这是一种经常与观察者模式比较的模式。
观察者模式

观察者模式中有两个角色,首先 观察者(Observers) 告诉 被观察者(Subject) 关注的事件,然后当事件发生后,被观察者(Subject)观察者(Observers) 进行通知。
这种模式对于复杂高的系统可能不太合适,因为两个角色的关系耦合性很高,对于跨组件跨系统的事件的处理增加了复杂度,使代码变得难以维护。

发布-订阅模式
发布-订阅模式模式其实是观察者模式的应用,有的资料里干脆说是别名,在这个模式里,多了一个中间人的角色,中间人充当了 消息中介
  • 对于 发布者(Publisher),它不需关心是谁在关注哪些事件,只需要在事件发生时通知 消息中介
  • 对于 订阅者(Subscriber) ,同样不需要关注事件在哪里发生,只需要告诉 消息中介 关注哪些事件然后坐等通知即可。

接下来是Vue响应系统的简单示例:

function observer (value) {
    if (!value || (typeof value !== 'object')) {
        return;
    }
    Object.keys(value).forEach((key) => {
        // 将对象的每个属性进行响应式处理
        defineReactive(value, key, value[key]);
    });
}
function cb (val) {
    console.log("视图更新啦~", val);
}

function defineReactive (obj, key, val) {
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
            return val;         
        },
        set: function reactiveSetter (newVal) {
            if (newVal === val) return;
            val = newVal;
            // 在对响应式对象的属性赋值时,触发视图更新
            cb(newVal);
        }
    });
}

class Vue {
    constructor(options) {
        this.$data = options.data;
        observer(this.$data);
    }
}

let o = new Vue({
    data: {
        test: "I am test."
    }
});
o.$data.test = "hello,test.";

以上简单实现了在数据更新后,对视图进行更新的功能。

下面是Vue观察者 Watcher 的简单实现,对应上图叫做 订阅者(Subscriber)

class Watcher {
    constructor () {
        /* 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到 */
        Dep.target = this;
    }

    /* 更新视图的方法 */
    update () {
        console.log("视图更新啦~");
    }
}

Dep.target = null;

接下来实现Vue订阅者 Dep,我的理解是相当于上图的 消息中介 的角色,一方面收集 订阅Vue称依赖),一方面通知 订阅者 更新视图。

class Dep {
    constructor () {
        /* 用来存放Watcher对象的数组 */
        this.subs = [];
    }

    /* 在subs中添加一个Watcher对象 */
    addSub (sub) {
        this.subs.push(sub);
    }

    /* 通知所有Watcher对象更新视图 */
    notify () {
        this.subs.forEach((sub) => {
            sub.update();
        })
    }
}

function defineReactive (obj, key, val) {
    /* 一个Dep类对象 */
    const dep = new Dep();
    
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
            /* 将Dep.target(即当前的Watcher对象存入dep的subs中) */
            dep.addSub(Dep.target);
            return val;         
        },
        set: function reactiveSetter (newVal) {
            if (newVal === val) return;
            /* 在set的时候触发dep的notify来通知所有的Watcher对象更新视图 */
            dep.notify();
        }
    });
}

class Vue {
    constructor(options) {
        this._data = options.data;
        observer(this._data);
        /* 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 */
        new Watcher();
        /* 在这里模拟render的过程,为了触发test属性的get函数 */
        console.log('render~', this._data.test);
    }
}

之前说到发布-订阅模式有三种角色,现在就剩下 发布者 了。

实际上在一个Vue实例化时,除了会初始化一些属性和方法以外,还会进行模板的渲染,也就是render()的过程,此时会调用getter(),将data的每个属性与相关的 依赖 建立关系,所以我认为data的属性就扮演了 发布者 的角色,每当通过任何方式修改data属性,就会触发对 消息中介 进行通知,再由 消息中介 通知到相应的 订阅者


横向对比 ReactAngular

  • Angular数据响应
    不像Vue的机制,Angular没有进行精准的观察数据的变动,而是在某些用户的操作或者某些关键时间点时,对所有的数据递归检查,对于有变动的数据进行视图更新,检查可能会重复多次,因为单次的变动是可能引发连锁反应的。有时候还需要手动触发检查。
  • React数据响应
    区分于Vuemutable模式,data属性值的修改只会作用域使用该属性的地方,React采用的是immutable设计,state不能直接修改,而是需要通过setState()来实现修改,并且React会重新渲染当前组件,当然,经过一系列优化,不会对未变动的值这么做。

即将到来的 Vue 3.0 数据响应

Vue3.0即将到来,除了到来很多功能上的改进以外,对于项目结构也将降低开源参与者的门槛,会将许多功能按照模块化的思想进行解耦,可能到时源码内的数据响应这块的代码也将更清晰易懂。

此外将采用ES6引入的元编程语言特性Proxy,来重写数据响应,由于目前是不能很好的检测数组和对象的变动,所以很多地方要使用$set来实现数据响应,这个问题将在3.0版彻底解决,但也因此将不支持IE11,因为这也是个不能polyfill的特性。

本文代码参考和来源自剖析 Vue.js 内部运行机制

相关文章

  • 浅谈数据响应的Vue实现及横纵对比

    把数据渲染到页面,并且能够追踪数据的改变并自动更新页面的显示,是现代前端框架做的重要工作之一,下面是一段常见的Vu...

  • Vue的响应式浅析

    1 Vue如何实现响应式? Vue的响应式是建立在监听data中的数据. 2 在Vue2中响应式的实现 Vue通过...

  • 1.23三峡广场雅思全程班 2.1第10次课笔记大纲

    静态图 强调:数据的对比 分段思路 数据大小对比 数据差异对比 内在逻辑:等级一二三 横纵栏分段:表格图 数据表达...

  • Vue源码03-响应式原理

    这节将专门讲解vue MVVM响应式处理 Vue采用的是 实现数据的响应式 数据劫持 Observer类 Obse...

  • 浅谈Vue数据响应

    Vue 中可以用 $watch 实例方法观察一个字段,当该字段的值发生变化时,会执行指定的回调函数(即观察者),实...

  • Vue 进阶系列(一)之响应式原理及实现

    Vue 进阶系列(二)之插件原理及实现Vue 进阶系列(三)之Render函数原理及实现 什么是响应式Reacti...

  • vue双向数据绑定的实现原理

    实现数据响应式 在Javascript里实现数据响应式一般有俩种方案,分别对应着vue2.x和 vue3.x使用的...

  • Vue实例构造-1

    data Vue 实例的数据对象,Vue会将这些数据对象的属性转换为getter/setter,从而实现数据响应式...

  • Vue

    vue双向绑定原理及实现从零带你手把手实现Vue3响应式原理-上从零带你手把手实现Vue3响应式原理-下为什么说 ...

  • Vue 进阶系列(二)之插件原理及实现

    Vue 进阶系列(一)之响应式原理及实现Vue 进阶系列(三)之Render函数原理及实现 使用方法 插件的详细使...

网友评论

      本文标题:浅谈数据响应的Vue实现及横纵对比

      本文链接:https://www.haomeiwen.com/subject/oyeyuctx.html