前端开发那些事儿JavaScript

Vue源码解析-响应式原理(一)

2021-01-11  本文已影响0人  Raral

Vue 响应式

概述

数据和视图相互关联,相互依赖,数据变化了,视图也随之更新,视图改变了数据也会随之改变。

Vue2.0 响应式原理

Vue2.0的响应式原理核心是通过es5的Object.defineProperty中的访问属性中的get和set方法,data中声明的属性都被添加了访问器属性,当读取data中的数据时自动调用get方法,当修改data中的数据时,会自动调用set 方法,检测到数据的变化,会通知观察者Wacher,观察者会自动触发重新render当前组件,生成新的虚拟DOM树,Vue框架会遍历并对比新的虚拟DOM树和旧的虚拟DOM树中每个节点的差别,并记录下来,最后加载操作,将所有记录的不同点,局部修改到真实DOM树上。

整体过程分3部分

  1. 数据劫持 / 数据代理
  2. 依赖收集
  3. 发布订阅模式

原理图

vue.png

手写Vue2.0 响应式--数据代理(数据劫持)


function render() {
    console.log("视图更新")
}
//重写数组方法
let methods = ["pop","push","unshift","shift","sort","reverse","splice"];
let arrayProto = Array.prototype;
let proto = Object.create(arrayProto);
methods.forEach(method => {
    proto[method] = function() {
        arrayProto[method].call(this, ...arguments);
    }
})


//观察者
function observe(obj) {
    //判断是否数组,如果是改变数组的原型
    if(Array.isArray(Obj)) {
        obj._proto_ = proto;
        return
    }

    if(!obj || typeof obj !=="object") {
        return
    }
    Object.keys(obj).forEach(key => {
        defineReactive(obj,key, obj[key])
    })
    
}

//使用Object.defineProerty 拦截数据/代理数据
function defineReactive(data, key, value) {
    observe(value);//递归子属性
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get:function getter() {
            return value;
        },
        set: function setter(newValue) {
            if(newValue != value) {
                value = newValue;
                render();//更新视图
            }
        }
    })
}


let data = {
    name: 'zhangsan',
    location: { x: 100, y: 100 }
  }

observe(data);

注意

proxy实现 数据劫持/数据代理

Proxy 是 JavaScript 2015 的一个新特性。Proxy 的代理是针对整个对象的,而不是对象的某个属性,因此不同于 Object.defineProperty 的必须遍历对象每个属性,Proxy 只需要做一层代理就可以监听同级结构下的所有属性变化,当然对于深层结构,递归还是需要进行的。此外Proxy支持代理数组的变化。

function render() {
    console.log("视图更新")
}
function observe(data) {
   let handler = {
       get(target, key) {
           if(typeof target[key] == "object" && target[key] !== null) {
                return new Proxy(target[key], handler);
           }
           return Reflect.get(target, key);
       },
       set(target, key, value) {
           if(key === "length") return true;
           render();
           return Reflect.set(target, key, value);
       }
   }

    let proxy = new Proxy(data, handler);

    return proxy;

}

let data = {
    name: 'zhangsan',
    location: { x: 100, y: 100 },
    arr: [1,3,5]
  }

let proxy = observe(data);
console.log(proxy);
console.log(proxy.name);//zhangsan
proxy.arr[0] = "lisi"// 视图更新
proxy.arr.push(100)// 视图更新
console.log(proxy.arr)//['lisi', 3, 5, 100]
上一篇下一篇

猜你喜欢

热点阅读