vue.js响应式原理解析与实现

作者: a333661d6d6e | 来源:发表于2019-04-16 21:15 被阅读10次

    vue.js响应式原理解析与实现。angularjs是通过脏检查来实现数据监测以及页面更新渲染。之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面。vue.js响应式原理解析与实现

    Object.defineProperty

    es5新增了Object.defineProperty这个api,它可以允许我们为对象的属性来设定getter和setter,从而我们可以劫持用户对对象属性的取值和赋值。比如以下代码:

    const obj = {
    };
    let val = 'cjg';
    Object.defineProperty(obj, 'name', {
     get() {
     console.log('劫持了你的取值操作啦');
     return val;
     },
     set(newVal) {
     console.log('劫持了你的赋值操作啦');
     val = newVal;
     }
    });
    console.log(obj.name);
    obj.name = 'cwc';
    console.log(obj.name);
    //欢迎加入全栈开发交流群一起学习交流:864305860
    

    我们通过Object.defineProperty劫持了obj[name]的取值和赋值操作,我们可以在obj[name]被赋值的时候触发更新页面操作。

    发布订阅模式

    当事件发生的时候,发布者通知所有订阅该事件的订阅者。我们来看一个例子了解下。

    class Dep {
     constructor() {
     this.subs = [];
     }
     // 增加订阅者
     addSub(sub) {
     if (this.subs.indexOf(sub) < 0) {
     this.subs.push(sub);
     }
     }
     // 通知订阅者
     notify() {
     this.subs.forEach((sub) => {
     sub.update();
     })
     }
    }
    const dep = new Dep();
    const sub = {
     update() {
     console.log('sub1 update')
     }
    }
    const sub1 = {
     update() {
     console.log('sub2 update');
     }
    }
    dep.addSub(sub);
    dep.addSub(sub1);
    dep.notify(); // 通知订阅者事件发生,触发他们的更新函数
    
    全栈开发交流群:864305860.png

    vue.js首先通过Object.defineProperty来对要监听的数据进行getter和setter劫持,当数据的属性被赋值/取值的时候,vue.js就可以察觉到并做相应的处理。

    class Observer {
     constructor(data) {
     // 如果不是对象,则返回
     if (!data || typeof data !== 'object') {
     return;
     }
     this.data = data;
     this.walk();
     }
     // 对传入的数据进行数据劫持
     walk() {
     for (let key in this.data) {
     this.defineReactive(this.data, key, this.data[key]);
     }
     }
     // 创建当前属性的一个发布实例,使用Object.defineProperty来对当前属性进行数据劫持。
     defineReactive(obj, key, val) {
     // 创建当前属性的发布者
     const dep = new Dep();
     /*
     * 递归对子属性的值进行数据劫持,比如说对以下数据
     * let data = {
     * name: 'cjg',
     * obj: {
     * name: 'zht',
     * age: 22,
     * obj: {
     * name: 'cjg',
     * age: 22,
     * }
     * },
     * };
     * 我们先对data最外层的name和obj进行数据劫持,之后再对obj对象的子属性obj.name,obj.age, obj.obj进行数据劫持,层层递归下去,直到所有的数据都完成了数据劫持工作。
     */
     new Observer(val);
     Object.defineProperty(obj, key, {
     get() {
     // 若当前有对该属性的依赖项,则将其加入到发布者的订阅者队列里
     if (Dep.target) {
     dep.addSub(Dep.target);
     }
     return val;
     },
     set(newVal) {
     if (val === newVal) {
     return;
     }
     val = newVal;
     new Observer(newVal);
     dep.notify();
     }
     })
     }
    }
    // 发布者,将依赖该属性的watcher都加入subs数组,当该属性改变的时候,则调用所有依赖该属性的watcher的更新函数,触发更新。
    class Dep {
     constructor() {
     this.subs = [];
     }
     addSub(sub) {
     if (this.subs.indexOf(sub) < 0) {
     this.subs.push(sub);
     }
     }
     notify() {
     this.subs.forEach((sub) => {
     sub.update();
     })
     }
    }
    Dep.target = null;
    // 观察者
    class Watcher {
     /**
     *Creates an instance of Watcher.
     * @param {*} vm
     * @param {*} keys
     * @param {*} updateCb
     * @memberof Watcher
     */
     constructor(vm, keys, updateCb) {
     this.vm = vm;
     this.keys = keys;
     this.updateCb = updateCb;
     this.value = null;
     this.get();
     }
     // 根据vm和keys获取到最新的观察值
     get() {
     Dep.target = this;
     const keys = this.keys.split('.');
     let value = this.vm;
     keys.forEach(_key => {
     value = value[_key];
     });
     this.value = value;
     Dep.target = null;
     return this.value;
     }//欢迎加入全栈开发交流群一起学习交流:864305860
     update() {
     const oldValue = this.value;
     const newValue = this.get();
     if (oldValue !== newValue) {
     this.updateCb(oldValue, newValue);
     }
     }
    }
    let data = {
     name: 'cjg',
     obj: {
     name: 'zht',
     },
    };
    new Observer(data);
    // 监听data对象的name属性,当data.name发现变化的时候,触发cb函数
    new Watcher(data, 'name', (oldValue, newValue) => {
     console.log(oldValue, newValue);
    })
    data.name = 'zht';
    // 监听data对象的obj.name属性,当data.obj.name发现变化的时候,触发cb函数
    new Watcher(data, 'obj.name', (oldValue, newValue) => {
     console.log(oldValue, newValue);
    })
    data.obj.name = 'cwc';
    data.obj.name = 'dmh';
    

    这样,一个简单的响应式数据监听就完成了。当然,这个也只是一个简单的demo,来说明vue.js响应式的原理,真实的vue.js源码会更加复杂,因为加了很多其他逻辑。

    了解更多
    本次给大家推荐一个免费的学习群,里面概括移动应用网站开发,css,html,webpack,vue node angular以及面试资源等。
    对web开发技术感兴趣的同学,欢迎加入Q群:864305860,不管你是小白还是大牛我都欢迎,还有大牛整理的一套高效率学习路线和教程与您免费分享,同时每天更新视频资料。
    最后,祝大家早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。

    相关文章

      网友评论

        本文标题:vue.js响应式原理解析与实现

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