什么是变化侦测?
vue.js会自动通过状态生成DOM,并将其输出到页面上显示出来,这个过程叫渲染。vue的渲染时声明式的,通过模板来描述状态与DOM之间的映射关系。变化侦测用来解决状态中发生什么变化的问题。 变化侦测的作用是侦测数据的变化。当数据变化时,会通知视图进行相应的更新。
Object的变化侦测
- 通过Object.defineProperty和Proxy追踪变化
- 观察数据的目的是当数据的属性发生变化时,可以通知那些曾经使用了该数据的地方
- 把依赖(Wather:属性变化后通知的目标)收集到dep中。
Watcher类
原理
把自己设置到全局唯一指定位置。读取数据就会触发数据的getter,在getter中就会从全局唯一的位置读取当前正在读取数据的watcher,并把watcher收集到Dep中去。
/**
* 数据变化通知Watcher,它再通知其他地方
*/
// 解析简单的路径
const bailRE = /[^\w.$]/
function parsePath(path) {
if (bailRE.test(path)) {
return 0
}
const segments = path.split('.')
return (obj) => {
for (let i = 0; i < segments.length; i++) {
if (!obj) return
obj = obj[segments[i]]
}
return obj
}
}
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm
// 执行getter读取内容
this.getter = parsePath(expOrFn)
this.cb = cb
this.value = this.get()
}
get() {
window.target = this
const value = this.getter.call(this.vm, this.vm)
window.target = undefined
return value
}
update() {
const oldVal = this.value
this.value = this.get()
this.cb.call(this.vm, this.value, oldVal)
}
}
Dep类
/**
* 管理依赖:收集依赖、删除依赖、向依赖发送通知
*/
// 移除元素函数
function remove(arr, item) {
if (arr.length) {
const index = arr.indexOf(item)
if (index > -1) {
return arr.splice(index, 1)
}
}
}
class Dep {
constructor() {
this.subs = []
}
// 收集依赖
addSub(sub) {
console.log('正在收集依赖...')
this.subs.push(sub)
}
// 删除依赖
removeSub(sub) {
console.log('正在删除依赖...')
remove(this.subs, sub)
}
depend() {
if (window.target) {
this.addSub(window.target)
}
}
// 向依赖发送通知
notify() {
console.log('正在向依赖发送通知...')
const subs = this.subs.slice()
for (let i = 0, l = this.subs.length; i < l; i++) {
subs[i].update()
}
}
}
Observer类
/**
* Observer类会附加到每一个被侦测的object上
* 侦听所有的属性,将数据内的所有属性都转换成getter/setter的形式,然后去追踪变化
*/
class Observer {
constructor(value) {
this.value = value
if (!Array.isArray(value)) {
this.walk(value)
}
}
// 数据类型为Object时,将每一个属性都转换成getter/setter的形式
walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
}
defineReactive函数
function defineReactive(data, key, val) {
// 递归子属性
if (typeof val === 'object') {
const observer = new Observer(val)
}
const dep = new Dep()
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get() {
dep.depend()
return val
},
set(newVal) {
if (val === newVal) {
return
}
val = newVal
dep.notify()
},
})
}
参考文献
《深入浅出Vue.js》刘博文