当前位置:   article > 正文

vue 核心原理之数据响应式核心原理及实现_vue响应式原理涉及的设计模式

vue响应式原理涉及的设计模式
一、数据响应式核心原理
  1. MVVM 模式,数据变化,视图会自动变化。model、view、view-model
  2. Vue数据变化是非侵入式,React数据变化和小程序数据变化是侵入式。
  3. Object.defineProperty(), 是数据劫持/数据代理,利用JavaScript引擎赋予的功能,检测对象属性变化。方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。同时,可以设置一些额外隐藏的属性。
  4. getter/setter,用闭包存储getset的值。get,当访问该属性时,会调用此函数。set,当属性值被修改时,会调用此函数。
  5. getter/setter需要变量周转才能工作。使用defineReactive函数不需要设置临时变量了,而是用闭包。
  6. 递归侦测对象全部属性,Observer,将一个正常的object转换为每个层级的属性都是响应式(可以被侦测的)的object,如下所示:
  • observe(obj),看obj身上有没有__ob__
  • new Observer()
    将产生的实例,添加__ob__上。
  • 遍历下一层属性,逐个defineReactive
  • 当设置某个属性值的时候,会触发set,里面有newValue。这个newValue也得被observe()一下。
  1. 数组的响应式处理,改写七个方法,push、pop、shift、unshift、splice、sort、reverse,如下所示:
Object.setPrototypeOf(o, arrayMethods)
o.__proto__ = arrayMethods
  • 1
  • 2
  1. 依赖收集,依赖是需要用到数据的地方,如下所示:
  • Vue1.x,细粒度依赖,用到数据的DOM都是依赖
  • Vue2.x,中等粒度依赖,用到数据的组件是依赖
  • getter中收集依赖,在setter中触发依赖
  1. Dep类和Watcher类,如下所示:
  • 把依赖收集的代码封装成一个Dep类,它专门用来管理依赖,每个Observer的实例,成员中都有一个Dep的实例
  • Watcher是一个中介,数据发生变化时通过Watcher中转,通知组件
  • 依赖就是Watcher。只有Watcher触发的getter才会收集依赖,哪个Watcher触发了getter,就把哪个Watcher收集到Dep
  • Dep使用发布订阅模式,当数据发生变化时,会循环依赖列表,把所
    有的Watcher都通知一遍
  • Watcher把自己设置到全局的一个指定位置,然后读取数据,因为读取了数据,所以会触发这个数据的getter。在getter中就能得到当前正在读取数据的Watcher,并把这个Watcher收集到Dep

二、数据响应式实现

  1. Dep,如下所示:
  • 用数组存储自己的订阅者。subs是英语subscribes订阅者的意思,这个数组里面放的是Watcher的实例。
  • 添加订阅,添加依赖,Dep.target就是一个我们自己指定的全局的位置,你用window.target也行,只要是全剧唯一,没有歧义就行。
  • 通知更新,浅克隆一份,遍历。
  1. Dep,代码如下所示:
var uid = 0;
export default class Dep {
    constructor() {
        console.log('我是DEP类的构造器');
        this.id = uid++;
        this.subs = [];
    }
    addSub(sub) {
        this.subs.push(sub);
    }
    depend() {
        if (Dep.target) {
            this.addSub(Dep.target);
        }
    }
    notify() {
        console.log('我是notify');
        const subs = this.subs.slice();
        for (let i = 0, l = subs.length; i < l; i++) {
            subs[i].update();
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  1. observe,如下所示:
  • 如果value不是对象,什么都不做。
  • 定义ob,判断返回。
  1. observe,代码如下所示:
import Observer from './Observer.js';
export default function (value) {
    if (typeof value != 'object') return;
    var ob;
    if (typeof value.__ob__ !== 'undefined') {
        ob = value.__ob__;
    } else {
        ob = new Observer(value);
    }
    return ob;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  1. defineReactive,如下所示:
  • 子元素要进行observe,至此形成了递归。这个递归不是函数自己调用自己,而是多个函数、类循环调用。
  • 可枚举,可以被配置,比如可以被delete
  • getter,如果现在处于依赖收集阶段。
  • setter,当设置了新值,这个新值也要被observe,发布订阅模式,通知dep
  1. defineReactive,代码如下所示:
import observe from './observe.js';
import Dep from './Dep.js';

export default function defineReactive(data, key, val) {
    const dep = new Dep();
    if (arguments.length == 2) {
        val = data[key];
    }

    let childOb = observe(val);

    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get() {
            console.log('你试图访问' + key + '属性');
            if (Dep.target) {
                dep.depend();
                if (childOb) {
                    childOb.dep.depend();
                }
            }
            return val;
        },
        set(newValue) {
            console.log('你试图改变' + key + '属性', newValue);
            if (val === newValue) {
                return;
            }
            val = newValue;
            childOb = observe(newValue);
            dep.notify();
        }
    });
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  1. utils,代码如下所示:
export const def = function (obj, key, value, enumerable) {
    Object.defineProperty(obj, key, {
        value,
        enumerable,
        writable: true,
        configurable: true
    });
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. array,如下所示:
  • 得到Array.prototype``Array.prototype为原型创建arrayMethods对象,并暴露。
  • 被改写的7个数组方法,备份原来的方法,因为push、pop等7个函数的功能不能被剥夺。
  • 定义新的方法,恢复原来的功能,把类数组对象变为数组,把这个数组身上的__ob__取出来,__ob__已经被添加了,为什么已经被添加了?因为数组肯定不是最高层,比如obj.g属性是数组,obj不能是数组,第一次遍历obj这个对象的第一层的时候,已经给g属性(就是这个数组)添加了__ob__属性。
  • 有三种方法push\unshift\splice能够插入新项,现在要把插入的新项也要变为observe的,splice格式是splice(下标, 数量, 插入的新项)
  • 判断有没有要插入的新项,让新项也变为响应的。
  1. array,代码如下所示:
import { def } from './utils.js';

const arrayPrototype = Array.prototype;

export const arrayMethods = Object.create(arrayPrototype);

const methodsNeedChange = [
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
];

methodsNeedChange.forEach(methodName => {
    const original = arrayPrototype[methodName];
    def(arrayMethods, methodName, function () {
        const result = original.apply(this, arguments);
        const args = [...arguments];
        const ob = this.__ob__;

        let inserted = [];

        switch (methodName) {
            case 'push':
            case 'unshift':
                inserted = args;
                break;
            case 'splice':
                inserted = args.slice(2);
                break;
        }

        if (inserted) {
            ob.observeArray(inserted);
        }

        console.log('啦啦啦');

        ob.dep.notify();

        return result;
    }, false);
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  1. Watcher,代码如下所示:
import Dep from "./Dep";

var uid = 0;
export default class Watcher {
    constructor(target, expression, callback) {
        console.log('我是Watcher类的构造器');
        this.id = uid++;
        this.target = target;
        this.getter = parsePath(expression);
        this.callback = callback;
        this.value = this.get();
    }
    update() {
        this.run();
    }
    get() {
        Dep.target = this;
        const obj = this.target;
        var value;

        try {
            value = this.getter(obj);
        } finally {
            Dep.target = null;
        }

        return value;
    }
    run() {
        this.getAndInvoke(this.callback);
    }
    getAndInvoke(cb) {
        const value = this.get();

        if (value !== this.value || typeof value == 'object') {
            const oldValue = this.value;
            this.value = value;
            cb.call(this.target, value, oldValue);
        }
    }
};

function parsePath(str) {
    var segments = str.split('.');

    return (obj) => {
        for (let i = 0; i < segments.length; i++) {
            if (!obj) return;
            obj = obj[segments[i]]
        }
        return obj;
    };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  1. Observer,如下所示:
  • 每一个Observer的实例身上,都有一个dep
  • 给实例(this,一定要注意,构造函数中的this不是表示类本身,而是表示实例)添加了__ob__属性,值是这次new的实例。
  • Observer类的目的是:将一个正常的object转换为每个层级的属性都是响应式(可以被侦测的)的object,检查它是数组还是对象。
  • 如果是数组,要非常强行的蛮干:将这个数组的原型,指向arrayMethods,让这个数组变的observe
  • 遍历,数组的特殊遍历,逐项进行observe
  1. Observer,代码如下所示:
import { def } from './utils.js';
import defineReactive from './defineReactive.js';
import { arrayMethods } from './array.js';
import observe from './observe.js';
import Dep from './Dep.js';

export default class Observer {
    constructor(value) {
        this.dep = new Dep();
        def(value, '__ob__', this, false);
        if (Array.isArray(value)) {
            Object.setPrototypeOf(value, arrayMethods);
            this.observeArray(value);
        } else {
            this.walk(value);
        }
    }
    walk(value) {
        for (let k in value) {
            defineReactive(value, k);
        }
    }
    observeArray(arr) {
        for (let i = 0, l = arr.length; i < l; i++) {
            observe(arr[i]);
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  1. index,代码如下所示:
import observe from './observe.js';
import Watcher from './Watcher.js';

var obj = {
    a: {
        m: {
            n: 5
        }
    },
    b: 10,
    c: {
        d: {
            e: {
                f: 6666
            }
        }
    },
    g: [22, 33, 44, 55]
};


observe(obj);
new Watcher(obj, 'a.m.n', (val) => {
    console.log('★我是watcher,我在监控a.m.n', val);
});
obj.a.m.n = 88;
console.log(obj);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/259191
推荐阅读
相关标签
  

闽ICP备14008679号