vue 源码分析-数据驱动视图
vue 是数据驱动视图的框架,简单来说就是数据发生变化,调用vue 的 render 方法,返回视图,显示在浏览器上;
如何知道数据发生变化?
做到数据每次的读和写的操作都可以监测,vue 2.0 是用了 Object.defineProperty 方法,方法接收3个参数,
1. 原始对象
2. 原始对象上,需要监听的属性(字符串)
3. 该属性的一些配置,能否删除,是否可以枚举,是否可写,还有get、set方法 等等(对象)
其中 get set 就是vue实现数据监测的关键:
get 每次 获取值,都会调用该方法,可以对返回值进行处理;
set 每次改变值,都进入该方法,可以拦截修改;
数据变化,如何通知视图更新?
数据变化,不可能把这个视图都遍历,vue的做法是给某一个监测对象新建一个依赖数据,那个谁用到这个数据,就将谁放到数组中,当数据发生变化,就把每一个依赖通知一遍;
何时收集依赖?谁用到某一个监测数据,那么就会调用get方法,在这个方法收集依赖;
何时通知更新?监测数据改变,会调用set方法,在该方法可以通知依赖数据发生变化;
把依赖收集到哪里?
vue 有一个依赖管理器Dep
类,dep类有一个subs的数组,用于存放依赖;还包含一些基本的操作,添加依赖depend、删除依赖removeSub、通知所有依赖更新notify;
依赖到底是谁?
Vue
中还实现了一个叫做Watcher
的类,而Watcher
类的实例就是我们上面所说的那个"谁"。
谁用到了数据,谁就是依赖,我们就为谁创建一个Watcher
实例。
数据变化时,我们不直接去通知依赖更新,而是通知依赖对应的Watch
实例,由Watcher
实例去通知真正的视图。
不足之处
向object
数据里添加一对新的key/value
或删除一对已有的key/value
时,它是无法观测到的,导致当我们对object
数据添加或删除值时,无法通知依赖,无法驱动视图进行响应式更新。
Vue
也注意到了这一点,为了解决这一问题,Vue
增加了两个全局API:Vue.set
和Vue.delete
总结:
通过Object.defineProperty
方法实现了对object
数据的可观测,并且封装了Observer
类,让我们能够方便的把object
数据中的所有属性(包括子属性)都转换成getter/seter
的形式来侦测变化。
接着,我们学习了什么是依赖收集?并且知道了在getter
中收集依赖,在setter
中通知依赖更新,以及封装了依赖管理器Dep
,用于存储收集到的依赖。
最后,我们为每一个依赖都创建了一个Watcher
实例,当数据发生变化时,通知Watcher
实例,由Watcher
实例去做真实的更新操作。
其整个流程大致如下:
Data
通过observer
转换成了getter/setter
的形式来追踪变化。- 当外界通过
Watcher
读取数据时,会触发getter
从而将Watcher
添加到依赖中。 - 当数据发生了变化时,会触发
setter
,从而向Dep
中的依赖(即Watcher)发送通知。 Watcher
接收到通知后,会向外界发送通知,变化通知到外界后可能会触发视图更新,也有可能触发用户的某个回调函数等。