Vue响应式原理
从原理上来讲vue是利用了Object的defineProperty的属性,它把我们放进去的data中每一个属性都设置了getter和setter,这样当data的属性值更新后,我们就会收到通知,并可以通知那些需要更新的地方去进行更新。
getter&&setter简单实现
//检查是否为对象
function isObject (obj){
return typeof obj === 'object'
&& !Array.isArray(obj)
&& obj !==null
&& obj !==undefined
}
function convert (obj){
if(!isObject(obj)){
throw new TypeError()
}
//为每个对象的键值对添加getter和setter
Object.keys(obj).forEach(key=>{
//提供变量存储键对应的值
let internalValue = obj[key]
Object.defineProperty(obj,key,{
get(){
console.log(`getting key "${key}": ${internalValue}`)
return internalValue
},
set(newValue){
console.log(`setting key "${key}" to: ${newValue}`)
internalValue = newValue
}
})
})
}
依赖跟踪
使用上面的getter和setter我们模拟了对象的响应式,那么问题来了,假设我们有一个对象,其中有个属性added:233,我们在一个函数中使用了这个added做一个加法,得到了一个结果,但是我们修改了added的值,并期望函数的结果也会随之改变,这要怎么实现呢?、
// a class representing a dependency
// exposing it on window is necessary for testing
window.Dep = class Dep {
constructor () {
//设置一个变量存储
this.subscribers = new Set()
}
//跟踪函数依赖,类似于在此处插眼
depend () {
if (activeUpdate) {
// register the current active update as a subscriber
//将跟踪的函数加入到set表中
this.subscribers.add(activeUpdate)
}
}
//重新执行函数,在刚刚插眼的函数中重新执行该函数,达到更新的效果
notify () {
// run all subscriber functions
//set列表中的每一个函数都重新执行
this.subscribers.forEach(subscriber => subscriber())
}
}
let activeUpdate
function autorun (update) {
//用一个全局变量储存函数(重点!),这样可以让类实例在外部访问这个函数
function wrappedUpdate () {
activeUpdate = wrappedUpdate
update()
activeUpdate = null
}
wrappedUpdate()
}
简单来讲,就是创建一个类,它提供一种类似“插眼”的功能(depend ()),我们在需要跟踪的函数中插入这个眼,当我们需要它更新时,再调用这个眼(notify ())重新执行一遍函数,达到更新结果的目的。
迷你观察者
我们把依赖追踪和getter&&setter结合起来,就可以完成我们需要的功能了。
function isObject (obj) {
return typeof obj === 'object'
&& !Array.isArray(obj)
&& obj !== null
&& obj !== undefined
}
function observe(obj) {
if(!isObject(obj)){
throw new TypeError()
}
Object.keys(obj).forEach(key=>{
let internalValue = obj[key]
let dep = new Dep()
Object.defineProperty(obj,key,{
get () {
//插眼
dep.depend()
return internalValue
},
set(v) {
let ifValueChange = v !==internalValue
if(ifValueChange){
internalValue = v
//检测到数值变化重新调用该函数
dep.notify()
}
}
})
})
}
window.Dep = class Dep {
constructor () {
this.subscribers = new Set()
}
depend () {
if (activeUpdate) {
// register the current active update as a subscriber
this.subscribers.add(activeUpdate)
}
}
notify () {
// run all subscriber functions
this.subscribers.forEach(subscriber => subscriber())
}
}
let activeUpdate
function autorun (update) {
function wrappedUpdate () {
activeUpdate = wrappedUpdate
update()
activeUpdate = null
}
wrappedUpdate()
}
可以看到我们把插眼这个动作放到了getter里面,把重新调用放到了setter里面,这样我们就可以监听对象的变化,一旦它的属性值发生改变,就会触发重新调用,这样就达到了监听对象,并实时更新与他有关的值,即Vue响应式的简单原理。
Vue通过Object的defineProperty实现响应式,为data中的每个属性设置getter和setter,当属性值改变时,触发setter更新视图。依赖跟踪机制通过Dep类记录依赖关系,autorun函数在属性变化时重新执行相关函数,实现数据变化的实时响应。这一过程结合getter和setter,构成了Vue的迷你观察者模式。
1481

被折叠的 条评论
为什么被折叠?



