vue的响应式原理代码实现

最近新学习了vue的响应式原理的源码的逻辑与实现。在此做一个记录与分享。
**我们发现vue的data里return的必须是一个对象,并且只有先定义了对象的属性才可能会有响应式变化。**主要是因为响应式原理最基础是基于 Object.defineProperty()这个方法,这个方法主要用于在一个对象上修改一个属性,或者定义一个新属性。不太了解这个方法的可以参照https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty。其中它有命名访问器属性,可以通过getter和setter进行读取和赋值的属性。

 Object.defineProperty(obj, key, {
                get() {
                    return value;
                },
                set(newvalue) {
                    if (value !== newvalue) {
                        observer(newvalue)//2.如果newvalue也是一个对象,要给它增加响应式
                        value = newvalue;
                        console.log('更新视图')
                    }
                }
            })

Get:指读取属性时调用的函数。
Set:指写入属性时调用的函数。

        //处理数组
        function arrayObserver(obj){
            for(let i=0;i<obj.length;i++){
                    let item = obj[i];
                    observer(item)//只有对象才是响应式的
                }
        }
        //入口
        function observer(obj) {
            if (typeof obj !== 'object' || typeof obj == null) {
                return obj;//普通值停止
            }
            if (Array.isArray(obj)) {
                //obj._proto_=proto;
                Object.setPrototypeOf(obj,proto);//实现对数组的方法进行重写,将obj的原型指向proto
                arrayObserver(obj);
            } else {
                //处理对象
                //默认只循环第一层
                for (let key in obj) {
                    defineReactive(obj, key, obj[key]);
                }
            }
        }
        //对象的数据劫持
        function defineReactive(obj, key, value) {
            observer(value);//1.递归处理,如果value还需要循环
            Object.defineProperty(obj, key, {
                get() {
                    return value;
                },
                set(newvalue) {
                    if (value !== newvalue) {
                        observer(newvalue)//2.如果newvalue也是一个对象,要给它增加响应式
                        value = newvalue;
                        console.log('更新视图')
                    }
                }
            })
        }

        //但是这里不能直接篡改 Array.prototype 对象,这样会影响所有的数组实例,为了避免这种情况,需要采用原型继承得到一个新的原型对象,拿到新的原型对象之后,再重写这些常用的操作方法
        let arrayProto = Array.prototype;//数组原型对象的拷贝,将对象里的特定方法进行变异后替换正常的数组原型对象
        let proto = Object.create(arrayProto);//创建原型指向arrayProto的对象
        ['push','unshift','splice'].forEach(methods=>{//methods是item
            proto[methods]=function(...args){//对象【key】=value,...args是不定个参数,在函数内部的args是个数组
                console.log('更新视图');
                let inserted;
                switch (methods) {
                    case 'push':
                    case 'unshift':
                        inserted = args;
                        break;
                    case 'splice':
                        inserted = args.splice(2);
                        break;
                    default:
                        break;
                }
                arrayObserver(inserted);//对于新增元素继续劫持
                arrayProto[methods].call(this,...args);//按照原来的方法进行数组处理
            }
        })
	 //1
    let data = { name: 'Anita' };
    let data2 = { name: { a: 'me' } };//多层处理
    //2
    data.name = { n: 'abc' };//需要给这个对象添加响应式
    data.name.n = 'sss';
    //3
    let data={name:[{n:2},3]};
    data.name[0].n=4;//可以修改,数组里的对象是响应式;
    data.name[1]=10;//不可以修改,数组里的常量不能修改;

    //1.使用对象的时候必须先声明属性,这个属性才是响应式的
    //2.增加不存在的属性不能更新视图
    //3.默认递归数据,增加getter和setter,性能不好
    //4.如果是数组里套对象,则支持响应式变化,如果是常量不支持

你可能感兴趣的:(vue的响应式原理代码实现)