Vue的双向数据绑定

Vue的双向数据绑定

一、什么是双向绑定

Vue的双向绑定就是数据变化引起视图渲染(即数据响应式)视图变化引起数据更新

二、Vue如何实现双向绑定

1. 数据变化引起视图渲染

当把一个普通的 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,从而使它关联的组件重新渲染(这个过程DOM更新是异步的)

具体介绍参见上一篇博客:Vue数据响应式

2. 视图变化引起数据更新

这个相对数据响应式就非常简单了,我们只要利用事件的监听即可。

例如监听 input 事件,一般使用:v-on:input。有时也会使用v-on:change, onChange 事件是在移出焦点时才会触发。因此这样就不会实时地更新数据了,具体选择监听哪个事件看实际需求而定。

三、Vue创建双向绑定

首先看一段初始化代码,之后基于此 demo 创建双向绑定:

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
import Vue from 'vue'

const component = {
template: `
<label>
<input type="text" @input="handleInput">
</label>
`,
methods: {
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}

new Vue({
conponents: {
CompA: component
},
el: '#root',
template: `
<div>
<comp-a></comp-a>
</div>
`
})

绑定props和事件

①数据变化引起视图渲染

首先子组件要接受一个props,同时监听这个props

1
2
3
4
5
6
7
8
const component = {
props: ['value'],
template: `
<label>
<input type="text" @input="handleInput" :value="value">
</label>
`
}

这样父组件就可以从外部传入props:

1
2
3
4
5
6
7
8
9
10
template: `
<div>
<comp-a :value="value"></comp-a>
</div>
`,
data () {
return {
value: 'newValue'
}
}

每当外部传入value时,就会触发 setter,进而引起视图的渲染。

②视图变化引起数据更新

父组件监听子组件暴露出的 input 事件(即Event Bus),当其触发时,会引起 value 的变化

1
2
3
4
5
template: `
<div>
<comp-a :value="value" @input="value = arguments[0]"></comp-a>
</div>
`,

其中,arguments[0] 即子组件传出的 e.target.value

注意,此处并不是对子组件的 props 进行修改,而是监听子组件暴露的事件,然后修改自己的value

因此,完整代码如下:

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
import Vue from 'vue'

const component = {
props: ['value'],
template: `
<label>
<input type="text" @input="handleInput" :value="value">
</label>
`,
methods: {
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}

new Vue({
data () {
return {
value: 'newValue'
}
conponents: {
CompA: component
},
el: '#root',
template: `
<div>
<comp-a :value="value" @input="value = arguments[0]"></comp-a>
</div>
`
})

v-model

官方文档这样解释 v-model 的用法:

由于以上 demo 中,父组件 :value 和 @input 比较冗余,所以 Vue 封装了 v-model 指令直接替换这两个指令:

1
2
3
4
5
template: `
<div>
<comp-a v-model="value"></comp-a>
</div>
`,

总结

在数据渲染时使用 props 渲染数据,将 props 绑定到子组件自身的数据上。

修改数据时更新自身数据来替代 propswatch 子组件自身数据的改变,触发事件通知父组件更改绑定到 props 的数据。


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