美文网首页
响应式数据

响应式数据

作者: 静小弟 | 来源:发表于2020-02-24 16:17 被阅读0次

响应式系统

什么是响应式,也即是说,数据发生改变的时候,视图会重新渲染,匹配更新为最新的值。

使用 Object.defineProperty 可以为对象中的每一个属性,设置 get 和 set 方法。

  • Vue 是怎么知道数据改变?
    • Vue 在 属性的 set 方法中做了手脚,因而当数据改变时,触发 属性的 set 方法,Vue 就能知道数据有改变
  • Vue 在数据改变时,怎么知道通知哪些视图更新?
    • 通过Object.defineProperty-get,通知那些存在依赖收集器中的视图
  • Vue 在数据改变时,视图怎么知道什么时候更新?
    • 触发Object.defineProperty-set,在数据变化触发 set 函数时,通知视图,视图开始更新

依赖收集

  • data 中的声明的每个属性,都拥有一个数组(依赖收集器subs),保存着 谁依赖(使用)了 它。
  • 当页面使用到 某个属性时,页面的 watcher 就会被 放到 依赖收集器 subs 中
https://pic3.zhimg.com/80/v2-8d6ac9507c5ac9f738b2ef5fc7b1482e_hd.jpg

name 属性,使用了 一个 dep 保存了 页面A 这个依赖,而保存的实际上是 页面A的 Watcher。

数据初始化流程

1、实例初始化中,调用 initState 处理部分选项数据,initData 用于处理选项 data

Vue.prototype._init=function(){
    ...
    initState(this)
    ...

}

function initState(vm) {    

    var opts = vm.$options;

    ... props,computed,watch 等选项处理

    if (opts.data) {
        initData(vm);
    }
};

2、initData 遍历 data,definedReactive 处理每个属性

function initData(vm) {  

    var data = vm.$options.data;

    data = typeof data === 'function' ? 
           data.call(vm, vm) : data || {};

    // ... 遍历 data 数据对象的key,重名检测,合规检测等代码
    new Observer(data);
}

function Observer(value) {    

    var keys = Object.keys(value);

    // ...被省略的代码
    for (var i = 0; i < keys.length; i++) {
        defineReactive(obj, keys[i]);
    }
};

3、definedReactive 给对象的属性 通过 Object.defineProperty 设置响应式

function defineReactive(obj, key) {    

    // dep 用于中收集所有 依赖我的 东西
    var dep = new Dep();    
    var val  = obj[key]    

    Object.defineProperty(obj, key, {        
        enumerable: true,        
        configurable: true,

        get() { ...依赖收集},
        set() { ....依赖更新}
    });
}

依赖收集-基本数据类型

1、页面的渲染函数执行, name 被读取

2、触发 name的 Object.defineProperty.get 方法

3、页面的 watcher 就会被收集到 name 专属的闭包dep 的 subs 中。dep.addSub(Dep.target)

依赖收集-引用数据类型

引用数据类型,使用 【闭包dep】 和 【 __ ob __.dep】 两种来存储依赖

1、引用类型会多添加一个 __ ob __属性,其中包含 dep,用于存储 收集到的依赖

2、对象使用 __ ob __.dep,作用在 Vue 自定义的方法 set 和 del 中

3、数组使用 __ ob __.dep,作用在 Vue 重写的数组方法 push 等中 。用到Vue封装方法 set 和 del,set 和 del 会通知依赖更新,所以子项对象也要保存。

依赖更新

  • 当 name 改变的时候,name 会遍历自己的 依赖收集器 subs,逐个通知 watcher,让 watcher 完成更新

  • 这里 name 会通知页面A,页面A 重新读取新的 name ,然后完成渲染

通知更新做了这些工作

1、直接调用 watcher.update,也就是重新调用给 watcher 保存的更新函数

2、更新更新函数就是执行渲染函数,然后读取实例最新的值(已被修改过的值),最后重新生成DOM 节点

3、DOM 节点 插入或替换页面,完成更新

代理data

初始化数据

function initData(vm) {    
    var data = vm.$options.data; 
    var keys = Object.keys(data); 
    var i = keys.length;

    data = vm._data = 
        ( typeof data === 'function' ? 
            data.call(vm) : data ) || {};    

    while (i--) {        
        var key = keys[i];       
        if (只要不是_和$开头的属性) {
            proxy(vm, "_data", key);
        }
    }
}
  • 拿到data数据,如果data是函数,执行就拿到返回值,否则就拿设置的对象data
  • 初始化数据,是为了拿到数据,然后放到存到实例上,作为代理总部
return data.call(vm)
  • data 函数执行的时候 用 call 方法,让 vm 继承了 data 的属性和方法,将data的属性和方法指向vm。 所以我们可以使用 this.xxx

  • call 知识点:call 是 Function 对象自带的一个方法,可以改变函数体内部的 this 指向,第一个参数就是 this要指向的对象,也就是想指定的上下文,后面的参数它会按顺序传递进去。它的函数会被立即调用。

  • 官网解释:调用一个对象的一个方法,以另一个对象替换当前对象。也就是继承模式:挟持另一个对象的方法,继承另外一个对象的属性

data 为什么是个函数而不是个对象?

如果 data 是个对象,那么整个vue实例将共享一份数据,也就是各个组件实例间可以随意修改其他组件的任意值,但是 data 定义成一个函数,将会 return 出一个唯一的对象,不会和其他组件共享一个对象。

代理开花

proxy(vm,"_data",key)
// 定义了一个构造函数
function Vue(options) {
    this.$data = options.data || {};
    this.initState(options);
}
Vue.prototype.initState = function (opts) {
    if(opts.data)
        this.initData(opts.data);
    if(opts.methods)
        this.initMethods(opts.methods);
    if(opts.created)
        opts.created.call(this);
}
Vue.prototype.initData = function (data) {
    var keys = Object.keys(data);
    var i = keys.length;
    while (i--){
        const key = keys[i];
        this.proxy("$data",key);
    }
}
Vue.prototype.initMethods = function (methods) {
    for (var key in methods) {
        this[key] = methods[key];
    }
}
Vue.prototype.proxy =function(sourceKey, key) {
    Object.defineProperty(this, key, {
        get() {
            return this[sourceKey][key]
        },
        set(val){
            this[sourceKey][key] = val;
        }
    });
};

在Vue.prototype.proxy中的Object.defineProperty中,绑定了this和key的值,使得this.key 可以访问data中的值

相关文章

  • 前端面试题【Day02】

    本篇绪论 1,Vue响应式原理 1,Vue响应式原理 在vue实例中声明的数据就是响应式的。响应式:数据发生改变,...

  • 2020-12-25

    Vue数据响应式 响应式:当一个物体对外界刺激做出反应,就是响应式。例如:我打你一拳你知道躲。 Vue 数据响应式...

  • 学会这个技能,还能在涨5k。