资讯专栏INFORMATION COLUMN

【Vue原理】依赖收集 - 源码版之基本数据类型

VincentFF / 1450人阅读

摘要:当东西发售时,就会打你的电话通知你,让你来领取完成更新。其中涉及的几个步骤,按上面的例子来转化一下你买东西,就是你要使用数据你把电话给老板,电话就是你的,用于通知老板记下电话在电话本,就是把保存在中。剩下的步骤属于依赖更新

写文章不容易,点个赞呗兄弟
专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue版本 【2.5.17】

如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧

【Vue原理】依赖收集 - 源码版之基本数据类型

如果对依赖收集完全没有概念的同学,可以先看我这篇

【Vue原理】响应式原理 - 白话版

依赖收集,主要是为了解决一个问题,什么问题呢?

首先,我们都知道,Vue 的数据是响应式更新的,一旦数据改变了,那么相应使用到 数据的地方也会跟着改变。

那么问题来了,数据改变的时候,Vue 怎么知道,去让那些使用到数据的地方也改变呢?

这就是依赖收集要解决的问题!

他是怎么解决的?简单说就是把依赖了数据的地方,给集中收集起来以便变化后通知!我们今天来看源码的流程

首先,响应式更新,分为两步,依赖收集和依赖更新

今天讲的是依赖收集,如何去收集 使用了数据的地方

依赖收集,又分为两个流程

1、数据初始化流程

2、依赖收集流程

当前篇,先以基本数据类型为例讲解,因为 基本数据和 引用数据 在处理上有很大的不同,引用类型需要理解的东西更多更复杂,所以需要循序渐进,分两篇描述

数据初始化流程

首先,在实例初始化的时候,需要对数据进行响应式处理,也就是给每个属性都使用 Object.defineProperty 处理

处理的流程是怎么样的呢?

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() { ....依赖更新,源码下篇文章放出 }
    });
}
依赖收集流程

写一个小例子来解析

new Vue({    
    el: document.getElementsByTagName("div")[0],
    data(){        
        return  {            
            name:11
        }
    }
})

页面模板

页面引用了数据 name,name 需要保存 页面的watcher,以便于 name 变化时,通知 页面watcher 更新

1、页面渲染函数

with(this){  
    return _c("div",{},[name])
}

2、读取 name

渲染函数执行,上下文对象绑定为 实例,于是name读取到实例上的 name

3、保存 watcher

name 被读取,自然走到 Object.defineProperty.get 方法上,从这里开始收集 watcher

先来观察下 defineReactive 中省略的 get 的源码

function defineReactive(obj, key) {    

    var dep = new Dep();    
    var val  = obj[key]    

    Object.defineProperty(obj, key, {

        get() {            
            if (Dep.target) {                
                // 收集依赖
                dep.addSub(Dep.target)
            }            
            return val
        }
    });
}

哈哈哈,这里就有意思了,这段代码就是 依赖收集的核心,主要是三个点

Dep.target
Dep
dep.addSub

1、Dep.target

Dep.target 指向的是各种 watcher,watch的watcher,页面的watcher 等等

Dep.target 是变化的,根据当前解析流程,不停地指向不同的 watcher (指向,其实就是直接赋值 ,如下)

Dep.target = 具体watcher

“当然没有这么简单,就是表示一个意思而已”

简单想,指向哪个watcher,那么就是那个 watcher 正在使用数据,数据就要收集这个watcher

你可以先不用管 Dep.target 到底是怎么指向,你只用记住 在 watcher

比如当前页面开始渲染时,Dep.target 会提前指向当前页面的 watcher。

于是页面渲染函数执行,并引用了数据 name 后,name 直接收集 Dep.target,就会收集到当前页面的 watcher

watcher 有负责实例更新的功能,所以会被收集起来,数据变化时通知 watcher,就可以调用 watcher 去更新了

watcher 在 依赖收集中只起到被收集的作用,所以不会在这里详细解释

2、Dep

Dep 是一个构造函数,用于创建实例,并带有很多方法

实例会包含一个属性 subs 数组,用于存储不同数据 【收集的依赖】

看下dep的构造函数

var Dep = function Dep() {    

    // 保存watcher 的数组
    this.subs = [];
};

3、dep.addSub

原型上的方法,作用是往 dep.subs 存储器中 中直接添加 watcher

Dep.prototype.addSub = function(sub) {    
    this.subs.push(sub);
};

所以,【dep.addSub(Dep.target) 】就会直接添加当前 watcher

于是,收集流程大概是这样

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

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

3、于是,页面的 watcher 就会被收集到 name 专属的闭包dep 的 subs 中

总结

为什么需要依赖收集,之前也已经说过,是为了变化时通知 那些使用过数据的地方

就好比,你去商店买东西,东西还没有发售,于是你把你的电话给老板,老板把你的记在电话本上。当东西发售时,就会打你的电话通知你,让你来领取(完成更新)。

其中涉及的几个步骤,按上面的例子来转化一下

1、你买东西,就是你要使用数据 name

2、你把电话给老板,电话就是你的 watcher,用于通知

3、老板记下电话在电话本,就是把 watcher 保存在 subs 中。

4、剩下的步骤属于依赖更新

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109953.html

相关文章

  • Vue原理依赖收集 - 源码版之引用数据类型

    摘要:写文章不容易,点个赞呗兄弟专注源码分享,文章分为白话版和源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于版本如果你觉得排版难看,请点击下面链接或者拉到下面关注公众号也可以吧原理依赖收集源码版之引用数据类型上 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于...

    vvpvvp 评论0 收藏0
  • Vue原理依赖更新 - 源码

    摘要:写文章不容易,点个赞呗兄弟专注源码分享,文章分为白话版和源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于版本如果你觉得排版难看,请点击下面链接或者拉到下面关注公众号也可以吧原理依赖更新源码版如果对依赖收集完 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于...

    moven_j 评论0 收藏0
  • Vue原理】Props - 源码

    写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Props - 源码版 今天记录 Props 源码流程,哎,这东西,就算是研究过了,也真是会随着时间慢慢忘记的。 幸好我做...

    light 评论0 收藏0
  • Vue原理】VModel - 源码版之input详解

    摘要:因为失去焦点之后被强制更新了一波嗯,这就是的作用,把页面上的显示值也过滤一遍 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】VModel - 源码版之input详...

    leanxi 评论0 收藏0
  • Vue原理】Slot - 源码版之普通插槽

    摘要:我们都知道分为普通和作用域,两个内容都很多,所以分两部分进行讲述。今天讲的是普通其实普通,表示默认和具名,只是他们的处理方式都差不多,就只是是否有自定义名字而已,所以,表示一种类型。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请...

    lansheng228 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<