Vue数据响应式

Vue数据相应式

Vue数据相应式:就是当修改普通JavaScript对象时,视图(view)会进行更新

响应式系统

1625895230359

当把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。这个过程称为数据劫持

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。

具体来说,

  • 当执行get()响应时,会调用dep.depend(),此函数将target对象push进监听队列(subscris),然后读取property。
  • 当执行set()响应时,会先拿到新值,然后调用dep.notify(),此函数执行监听队列中的每个target,重新更新。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触(Touch)”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
let target, total
let data = {price:5, quantity:2}

// 定义依赖(dependency-tracking)。
class Dep{
constructor(){
this.subscribers= []
}
depend(){
// 存储被监听对象target
if(target && !this.subscribers.includes(target)){
this.subscribers.push(target)
}
}
notify(){
// 更新target
this.subscribers.forEach(sub => sub())
}
}

// 建立依赖(dependency-tracking)。在 property 被访问和修改时通知变更。
Object.keys(data).forEach(key =>{
let internalValue = data[key]

const dep = new Dep()

// 异步函数
Object.defineProperty(data, key, {
get(){
dep.depend()
return internalValue
},
set(newValue){
internalValue = newValue
dep.notify()
}
})
})

// 观察者函数(极简化版)
function watcher(myFunc){
// 监听和执行target
target = myFunc
target()
target = null

// ...记录依赖
// ...Trigger re-render重新渲染
}

watcher(()=>{
total = data.price * data.quantity
})

测试:

1
2
3
4
5
6
7
8
9
// 测试
console.log(data.price) //5
console.log(data.quantity) //2
console.log(total) //10

data.price=10

console.log(total) //20
// 修改data.price之后,total自动更新了

异步更新队列

上面例子中,watcher被极度简化。实际上,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并buffer在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次(数据去重),避免不必要的计算和 DOM 操作。然后,在下一个的事件循环“tick”中,Vue 刷新队列。

具体见一下例子:

https://jsbin.com/mocotic/edit?html,js,output

html:

1
2
3
4
5
6
7
8
<div id="app">
<span class=span-a>
{{obj.a}}
</span>
<span class=span-b>
{{obj.b}}
</span>
</div>

JavaScript:

1
2
3
4
5
6
7
8
9
10
11
12
var app = new Vue({
el: '#app',
data: {
obj: {
a: 'a',
}
},
})

app.obj.b = 'addB'

// oupput:a

因为 b 一开始不是 obj 的 key,所以 Vue 并没有监听 b。即使 b 的值变了,视图也不会更新。

JavaScript:

1
2
3
4
5
6
7
8
9
10
11
12
13
var app = new Vue({
el: '#app',
data: {
obj: {
a: 'a',
}
},
})

app.obj.a = 'newA'
app.obj.b = 'addB'

// oupput:newA addB

由于更新DOM过程是异步的。当更新a属性时,Vue监听到变化,先是开启了一个新的监听队列,将更新任务push到新的更新队列里。等到代码运行完毕,再更新DOM。此时页面展示的就是所有的跟新的数据。


版权声明:本文作者为「Andy8421」.本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!