源码解析 —— Vue的响应式数据流

Vue、React介绍

目前前端社区比较推崇的框架有Vue 和 React,公司内部许多端都自发的将原有的老技术方案(widget + jQuery)迁移到 Vue / React上了。
我以为Vue / React 有如下几点优点javascript

  • 首先它们都有完整的组件化方案前端

  • virtual Dom (前端性能提高利器)vue

  • 成熟的社区生态java

介绍一个Vue例子

图片描述

上面的例子咱们初始化了一个vue组件,当咱们改变这个组件的状态时,页面的内容也会随之改变,这中间并不须要咱们手动的去操做页面上的dom元素。
图片描述编程

同时咱们注意到Vue提供了一个语法糖 ——watch,这个就是咱们今天要讲的 Vue响应式数据流的主角!代码很简单,就是组件的状态 name 改变的时候咱们输出一句话 “name change”。
下面咱们会向你们解释清楚为何这个 watch 这么重要,以及它和 Vue的响应式数据流有什么关系。设计模式

Vue的响应式数据流的优点在哪?

Vue 和 React 都是前端的组件化框架,功能上大同小异,本质上就是借助virtual Dom帮助开发者管理混乱的Dom,并提供给开发者像操做状态机同样操做页面的能力。框架

可是Vue的virtual Dom 不是普通的 virtual Domdom

Vue 2.0 的实现有不同凡响的地方。和 Vue 的响应式系统结合在一块儿以后,它可让你没必要作任何事就得到彻底优化的重渲染。因为每一个组件都会在渲染时追踪其响应依赖,因此系统精确地知道应该什么时候重渲染、应该重渲染哪些组件。不须要 shouldComponentUpdate,也不须要 immutable 数据 - it just works . —— 尤雨溪前端性能

咱们看一下第三方的性能分析:
图片描述函数

除了性能,最大的优点是减轻了开发者的负担,开发者大多数状况下不须要依赖 shouldComponentUpdate,也不须要依赖 immutable 数据去判断组件是否须要从新渲染,Vue会帮你作好这件事。

举个例子来讲明这两个的virtual dom的不一样之处:

开发者就像一个老师,Vue和React这两个学生要作的事就是根据老师给出的长宽画出对应的长方形。每当老师改变给出的长和宽时,Vue可以本身发现长和宽变没变,需不须要从新画;React则须要老师告诉它长和宽变了,须要从新画了。

预备知识:Object.defineProperty 与 订阅发布设计模式

Object.defineProperty

JavaScript 提供一个很是强大的方法 Object.defineProperty,它能够定义当某一个值访问和赋值时会先执行自定义的钩子方法。

一个简单的Object.defineProperty例子

var obj = {};
var initValue = 'hello';
Object.defineProperty(obj,"newKey",{
    get:function (){
        //当获取值的时候触发的函数
        return initValue;    
    },
    set:function (value){
        //当设置值的时候触发的函数,设置的新值经过参数value拿到
        console.log(value)
        initValue = value;
    }
});
//获取值
console.log( obj.newKey );  //hello
//设置值
obj.newKey = 'change value'; //change value

这个方法给予JavaScript开发一种面向切面编程的能力,使用该方法咱们可以隐式、天然的控制属性的访问和赋值。

订阅发布设计模式

图片描述

订阅发布是一个很是常见的设计模式,原理也很是简单就是订阅者订阅信息,而后发布者发布信息通知订阅者更新。

Vue 源码

前面铺垫这么多就是但愿你们能理解接下来要讲的响应式数据流。

Vue的初始化

图片描述
如上图,Vue的初始化会执行一系列的方法,这里咱们主要介绍Vue的initState 方法。
prop和data都是组件的属性,prop一般上是父组件传递下来的,data是组件自身定义的,Vue不推荐你去改组件传递下来的prop,由于那样会带来没必要要的复杂度。

Observer

Prop和data 的最终归宿都是递归执行 defineReactive方法。
图片描述

那defineReactive方法作了什么呢?
图片描述

defineReactive会用 Object.defineProperty将组件的每一个属性都包装一下,这样谁访问了这些属性,谁从新赋值了这些属性咱们都能追踪到了。
Vue里面有一个 Observe类,全部的prop子属性和data自己都会带有一个Observer对象,Observer的构造函数
图片描述
在控制台咱们能够看到每一个属性下都有__ob__,这说明这个属性已经被包装成 Observer对象了,因此的访问和赋值都能给追踪到,这里面也保存着全部订阅该Observer的订阅者Watcher。

Watcher

咱们看一下Watcher的构造函数
图片描述

Watcher支持 watch 一个表达式或者是一个方法。Watcher在构造的时候会先获取一次expOrFn的值,下面咱们把expOrFn称为Watcher的Getter。

Dep

还有一个关键的类是Dep,这个类会帮助咱们的属性记录下全部的Watcher,每一个属性都有本身的Dep实例,同时Vue的Watcher访问的属性的时候 Dep会做为一个全局变量将自身的target属性指向访问的Wathcer。会执行下面的方法
图片描述

同时咱们再回来看 defineReactive这个重要的方法
图片描述

当Watcher访问组件的属性时,经过Dep.target,Vue能够知道是Watcher访问的, 这样当Vue本身的Watcher访问属性的时候会被记录成订阅者,而咱们访问的时候Vue不会执行多余的代码。这是一个很精妙的设计,将Object.defineProperty 与 订阅发布设计模式结合起来了。
看一下整个的流程图
图片描述

Vue的render函数就是 Watcher 的 expOrFn

理解了以上Vue是如何将Object.defineProperty 与 订阅发布设计模式结合起来的,而后咱们再触类旁通:Vue的render函数若是就是 Watcher 的 expOrFn会怎么样?
回到Vue的源码里:
图片描述

这里的 vm._render就是 render函数的一个封装,咱们能够看到本质上:Vue的render函数就是 Watcher 的 expOrFn。那初始化的时候咱们会先执行一边 render函数,在执行render函数的过程当中访问了哪些 组件的属性,Vue都会用上面的提到的方法帮咱们把依赖记录下来。因此当这个属性变化的时候,天然而然,就像文章开头的watch同样,咱们会从新render一次,(开头的例子是输出“name change”)。

总结

讲到这里你们应该都可以明白Vue的响应式数据流是如何实现的。同时咱们可以发现Vue提供给咱们的许多语法糖都是一样的道理,好比Vue的computer就是将computer函数做为Watcher的expOrFn。但愿你们在理解Vue响应式数据流的基础上可以更加自信、灵活和稳健的使用这个优秀的框架。

相关文章
相关标签/搜索