手写Vue2响应式面向对象版

127 阅读1分钟

1、先创建一个类实现初始化功能

class Vue{
    constructor(options){
        // 将配置项保存起来
        this.$options = options;
        // 调用初始化方法完成Vue实例对象初始化,Vue这里将这个方法抽离单独的是因为实例化组件对象也需要调用初始化方法
        this._init();
    }
  	//判断参数是否是一个对象
    isObject(obj) {
        return obj !== null && typeof obj === 'object';
   }
}

2、创建初始化方法

_init(){
    // 除去前面的一些其他的数据验证之外 正式开始进行数据代理
    this.initProxy()
}

3、实现数据数据代理初始化方法

initProxy(){
    // 需要将数据提取出去 并且还需要在_data属性中保存一份
    let data = this.$options.data;
    // 验证data是函数就调用(在Vue2中) 否则就直接赋值
    data = this._data = typeof data == 'function'?data():data;
    // 开启数据代理
    this.observe(data);
}

4、实现代理的方法

observe(data){
    // 不是对象 直接终止 对于对象嵌套对象的这种格式 后期需要进行递归的进行代理
    if(!this.isObject(data)){
        return ;
    }
  	//这里实例化开始代理的类的时候传递的this就是当前的Vue实例对象 目的是为了后面可以再次调用到observe方法
    return new Observer(data,this);
}

5、实现Observer类

class Observer{
            constructor(data,vm){
                this.vm = vm;
                Object.keys(data).forEach(key=>{
                    // key 是属性名称  第三个参数传递值
                    this.defineReactive(data,key,data[key])
                })
            }
            defineReactive(obj,key,val){
                // 可能val 还是对象 继续进行代理
                this.vm.observe(val);
                Object.defineProperty(obj,key,{
                    get(){
                        console.log(`读取了${key}属性`)
                        return val;
                    },
                    set(value){
                        console.log(`有人修改了${key}属性! 我需要更新界面`);
                        val = value;
                    }
                })
            }
        }

6、实现二次代理的方法

//因为在Vue2中为了方便使用 将数据转移到了实例对象下 所以再次进行代理 在Vue类中增加
proxy(data){
    // 二次代理
    for(let key in data){
        Object.defineProperty(this,key,{
            get(){
                return this._data[key];
            },
            set(value){
                this._data[key] = value;
            }
        })
    }
}

7、initProxy增加再次代理的调用

initProxy(){
    // 需要将数据提取出去 并且还需要在_data属性中保存一份
    let data = this.$options.data;
    // 验证data是函数就调用 否则就直接赋值
    data = this._data = typeof data == 'function'?data():data;
    // 开启数据代理
    this.observe(data);
    // 将数据设置到当前对象下一份
    this.proxy(data);
}

8、完整代码

class Vue{
            constructor(options){
                // 将配置项保存起来
                this.$options = options;
                // 开始初始化
                this._init();
            }
            _init(){
                // 很多的前置的操作
                this.initProxy()
            }
            initProxy(){
                // 需要将数据提取出去 并且还需要在_data属性中保存一份
                let data = this.$options.data;
                // 验证data是函数就调用 否则就直接赋值
                data = this._data = typeof data == 'function'?data():data;
                // 开启数据代理
                this.observe(data);
                // 将数据设置到当前对象下一份
                this.proxy(data);
            }
            proxy(data){
                // 二次代理
                for(let key in data){
                    Object.defineProperty(this,key,{
                        get(){
                            return this._data[key];
                        },
                        set(value){
                            this._data[key] = value;
                        }
                    })
                }
            }
            observe(data){
                // 不是对象
                if(!this.isObject(data)){
                    return ;
                }
                return new Observer(data,this);
            }

            isObject(obj) {
                return obj !== null && typeof obj === 'object';
            }
        }
        class Observer{
            constructor(data,vm){
                this.vm = vm;
                Object.keys(data).forEach(key=>{
                    // key 是属性名称  第三个参数传递值
                    this.defineReactive(data,key,data[key])
                })
            }
            defineReactive(obj,key,val){
                // 可能val 还是对象 继续进行代理
                this.vm.observe(val);
                Object.defineProperty(obj,key,{
                    get(){
                        console.log(`读取了${key}属性`)
                        return val;
                    },
                    set(value){
                        console.log(`有人修改了${key}属性! 我需要更新界面`);
                        val = value;
                    }
                })
            }
        }

        const vm = new Vue({
            el:'#app',
            data(){
                return {
                    name:'lisi',
                    age:28,
                    person:{
                        height:180
                    }
                }
            }
        })
        console.log(vm)