总结一下,以便忘了回头看看
响应式数据
主要用到Object.defineProperty
这个API,改写对象属性的get属性和set属性。
function defineReactive(obj, key, value) { Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: () => { console.log(`${key}属性被读`) return value }, set: (newValue) => { if (value === newValue) { return } value = newValue console.log('数据被改变了,我要渲染新的值') } }) } let obj = { name: '小明' } defineReactive(obj, 'name', obj.name) console.log(obj.name)// name属性被读 小明 obj.name = '小豪' //数据被改变了,我要渲染新的值
至于监听对象里的所有属性,就是循环一下就可以了
function observe(data) { const keys = Object.keys(data) keys.forEach(key => { defineReactive(data, key, data[key]) }) }
Dep--依赖管理
我们通过defineReactive
方法将data中的数据进行响应式后,虽然可以监听到数据的变化了,那我们怎么处理通知视图就更新呢?Dep就是帮我们收集【究竟要通知到哪里的】。比如下面的代码案例,我们发现,虽然data中有text和message属性,但是只有message被渲染到页面上,至于text无论怎么变化都影响不到视图的展示,因此我们仅仅对message进行收集即可,可以避免一些无用的工作。那这个时候message的Dep就收集到了一个依赖,这个依赖就是用来管理data中message变化的。
<div> <p>{{message}}</p> </div> data: { text: 'hello world', message: 'hello vue', }
Dep主要用于收集三个地方的依赖,一个是data
(就是上面的例子),一个是computed
,一个是watch
,比如下面的代码
computed: { messageT() { return this.message + ‘!’; } } watch: { message: function (val, oldVal) { console.log('new: %s, old: %s', val, oldVal) }, }
图中的data、computed、watch被我们称之为Watcher
收集依赖
所谓的依赖是什么呢,就是Watcher。Watcher扮演的是一个类似于中介的角色,比如message就有三个中介,当message变化时,就通知这三个中介,让他们去执行各自需要做的变化。所以Watcher里应该主要有两个方法,一个是通知变化,一个是被收集到Dep中去。
function Watcher() { this.addDep = function () { } this.update = function () { } }
总结
回顾一下,Vue响应式原理的核心就是Observer、Dep、Watcher。Observer中进行响应式的绑定,在数据被读的时候,触发get方法,执行Dep来收集依赖,也就是收集Watcher。在数据被改的时候,触发set方法,通过对应的所有依赖(Watcher),去执行更新。比如watch和computed就执行开发者自定义的回调方法。