目前前端社区比较推崇的框架有Vue 和 React,公司内部许多端都自发的将原有的老技术方案(widget + jQuery)迁移到 Vue / React上了。
我以为Vue / React 有如下几点优点javascript
首先它们都有完整的组件化方案前端
virtual Dom (前端性能提高利器)vue
成熟的社区生态java
上面的例子咱们初始化了一个vue组件,当咱们改变这个组件的状态时,页面的内容也会随之改变,这中间并不须要咱们手动的去操做页面上的dom元素。编程
同时咱们注意到Vue提供了一个语法糖 ——watch,这个就是咱们今天要讲的 Vue响应式数据流
的主角!代码很简单,就是组件的状态 name 改变的时候咱们输出一句话 “name change”。
下面咱们会向你们解释清楚为何这个 watch 这么重要,以及它和 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则须要老师告诉它长和宽变了,须要从新画了。
JavaScript 提供一个很是强大的方法 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的initState 方法。
prop和data都是组件的属性,prop一般上是父组件传递下来的,data是组件自身定义的,Vue不推荐你去改组件传递下来的prop,由于那样会带来没必要要的复杂度。
Prop和data 的最终归宿都是递归执行 defineReactive方法。
那defineReactive方法作了什么呢?
defineReactive会用 Object.defineProperty将组件的每一个属性都包装一下,这样谁访问了这些属性,谁从新赋值了这些属性咱们都能追踪到了。
Vue里面有一个 Observe类,全部的prop子属性和data自己都会带有一个Observer对象,Observer的构造函数
在控制台咱们能够看到每一个属性下都有__ob__,这说明这个属性已经被包装成 Observer对象了,因此的访问和赋值都能给追踪到,这里面也保存着全部订阅该Observer的订阅者Watcher。
咱们看一下Watcher的构造函数
Watcher支持 watch 一个表达式或者是一个方法。Watcher在构造的时候会先获取一次expOrFn的值,下面咱们把expOrFn称为Watcher的Getter。
还有一个关键的类是Dep,这个类会帮助咱们的属性记录下全部的Watcher,每一个属性都有本身的Dep实例,同时Vue的Watcher访问的属性的时候 Dep会做为一个全局变量将自身的target属性指向访问的Wathcer。会执行下面的方法
同时咱们再回来看 defineReactive这个重要的方法
当Watcher访问组件的属性时,经过Dep.target,Vue能够知道是Watcher访问的, 这样当Vue本身的Watcher访问属性的时候会被记录成订阅者,而咱们访问的时候Vue不会执行多余的代码。这是一个很精妙的设计,将Object.defineProperty 与 订阅发布设计模式结合起来了。
看一下整个的流程图
理解了以上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响应式数据流的基础上可以更加自信、灵活和稳健的使用这个优秀的框架。