美文网首页
Vue的响应式原理

Vue的响应式原理

作者: 史蒂夫sdf | 来源:发表于2021-07-17 18:45 被阅读0次

    vue数据双向绑定是通过数据劫持(Object.defineProperty)+发布者-订阅者模式来实现的

        function defineReactive(obj, key, val){
            Object.defineProperty(obj, key, {
                enumerable:true,//可枚举;改为false,for in时不会被遍历,但使用 "."依然可访问。
                configurable:true,//可配置;改为false之后,不能删除修改(不可逆)。
                writable:true,//可写入;改为false,当前属性变为只读。
                get(){//读取key触发,方法内不能读取key,否则会无限循环调用自身。
                    console.log('get=>'+val)
                    return val;//val是闭包值
                },
                set(newVal){
                    console.log('set=>'+newVal);
                    val = newVal;//改变闭包值
                }
            })
        }
    

    发布者-订阅者模式

    原始思路,一个商家,一个客户

    let saler = {
        production: '地瓜',
        customerLists: [],
        regist(callback){
            this.customerLists.push(callback)
        },
        notify(){
            this.customerLists.forEach(v=>{
                v(this.production)
            })
        }
    };
    let customer = {
        name: 'xiaoming',
        readProduction(production){
            console.log(this.name+' like '+production);
        }
    };
    saler.regist(customer.readProduction);
    saler.notify();
    

    多个商家,多个客户

    //Saler方法被生产者继承,每个生产者都是独立的
    function Saler(prod){
        this.production = prod;
        this.customerLists = [];
    }
    Saler.prototype.regist = function regist(callback,context){
        this.customerLists.push(callback.bind(context))
    }
    Saler.prototype.notify = function notify(){
        this.customerLists.forEach(v=>{
            v(this.production)
        })
    }
    function Customer(name){
        this.name = name;
    }
    Customer.prototype.readProduction = function readProduction(production){
        console.log(this.name+' like '+production);
    }
    let saler0 = new Saler('红薯');
    let customer0 = new Customer('xiaoqiang');
    saler0.regist(customer0.readProduction,customer0);
    saler0.notify();
    let saler1 = new Saler('米粉');
    let customer1 = new Customer('xiaoli');
    saler1.regist(customer1.readProduction,customer1);
    saler1.notify();
    

    一个售卖中介,多个生产者,多个客户

    // saler作为公共方法被每个生产者使用
    let Saler = {
        customerLists: [],
        regist(callback,context){
            this.customerLists.push(callback.bind(context))
        },
        notify(){
            this.customerLists.forEach(v=>{
                v(this.production)
            })
        }
    }
    function productor(prod){
        this.production = prod;
        this.__proto__ = Saler;
    }
    
    
    function Customer(name){
        this.name = name;
    }
    Customer.prototype.readProduction = function readProduction(production){
        console.log(this.name+' like '+production);
    }
    
    let saler0 = new productor('红薯');
    let customer0 = new Customer('xiaoqiang');
    saler0.regist(customer0.readProduction,customer0);
    saler0.notify();
    let saler1 = new productor('米粉');
    let customer1 = new Customer('xiaoli');
    saler1.regist(customer1.readProduction,customer1);
    saler1.notify();
    

    发布者-订阅者模式+数据劫持:数据劫持是数据修改时的动作,整个发布者和订阅者都要在这个动作内实现。

    //一个订阅者应该对应一个发布者,没有所谓的中介
    // saler销售中介作为公共方法被每个生产者使用
    function Saler(){
        this.customerLists = [];
    }
    Saler.prototype.regist = function regist(callback,context){
        this.customerLists.push(callback.bind(context))
    }
    Saler.prototype.notify = function notify(){
        this.customerLists.forEach(v=>{
            v(this)
        })
    }
    function productor(obj){
        Object.assign(this,obj);
        Saler.call(this);
        this.__proto__ = new Saler();//这种继承方式使得每个生产者共用一个销售中介
    }
    
    
    function Customer(name){
        this.name = document.getElementById(name);
    }
    Customer.prototype.readProduction = function readProduction(production){
        console.log(this.name,production);
    }
    
    function defineItem(key, value, item, productor){
        Object.defineProperty(item, key, {
            enumerable: true,
            configurable: true,
            get(){
                return value;
            },
            set(val){
                value = val;
                productor.notify();
            }
        })
    }
    function reactInit(obj,productor){
        for(let i in obj){
            if(typeof obj[i] === 'object'){
                reactInit(obj[i],productor);
            }else if(typeof obj[i] !== 'function'){
                defineItem(i, obj[i], obj, productor);
            }
        }
    }
    function dataInit(obj){
        let productor0 = new productor(obj);//初始化发布者对象
        let customer0 = new Customer('app');//初始化订阅者对象view
        productor0.regist(customer0.readProduction,customer0);//将订阅者的回调函数注册到发布者对象
        reactInit(productor0,productor0);//初始化数据
        customer0.readProduction(productor0);//初始化view
        return productor0;//返回数据对象
    }
    let vm = dataInit({
        production:'红薯',
        p2:'番茄',
        p3:{
            p31:'里脊',
            p32:'牛排'
        }
    })
    v2 = dataInit({q:123456})
    

    《JavaScript设计模式与开发实践》书中详细介绍了发布-订阅模式。
    vue 的双向绑定原理及实现

    相关文章

      网友评论

          本文标题:Vue的响应式原理

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