看了挺多关于这几个概念的文章,发现有很多作者对于双向数据绑定的理解不大一样
在这里聊一聊我自己的理解(自信即巅峰)
单向数据流
简单点说就是父子组件间的值传递,只能是由父组件单向传递给子组件,子组件不能直接改变props中的值返回给父组件,这是一个单向的过程,所以叫做单向数据流。
// parent.vue
<template>
<children :msg="pMsg"></children>
</template>
<script>
import Children from './children'
export default {
data() {
return {
pMsg: 'hello'
}
},
components: {
Children
}
}
</script>
// children.vue
<template>
<div>
<input :value="msg" type="text" />
</div>
</template>
<script>
export default {
props: {
msg: ''
}
}
</script>
双向数据绑定
如果想在子组件中改变数据值怎么办呢,这时候就引入了一个概念叫'双向数据绑定'。有两种方式可以实现,.sync修饰符和v-model
// parent.vue
<children :msg.sync="pMsg"></children>
// .sync就是下方的一种缩写方式(普通自定义事件的简写方式)
<children :msg="pMsg" @update:msg="v => (pMsg = v)"></children>
// children.vue
<input :value="msg" type="text" @input="onInput" />
methods: {
onInput(e) {
// 告知父组件我要更新数据值啦
this.$emit('update:msg', e.target.value)
}
}
看官方文档一眼就能懂系列
.sync修饰符
组件中实现v-model
表单标签实现v-model
以input标签为例,在触发了监听的input事件后,去onInput方法里改变了msg的值
<template>
<input :value="msg" type="text" @input="onInput" />
</template>
<script>
export default {
data(){
return{
msg:'hello'
}
},
methods: {
onInput(e) {
this.msg = e.target.value
}
}
}
</script>
说是实现双向数据绑定,无论是.sync修饰符还是v-model,都是通过一个中间事件去改变对应的数据值
vue的官方文档里也指出了双向数据绑定的概念,其实就是通过单向数据流去实现的。
响应式原理
Object.defineProperty!
Object.defineProperty!
Object.defineProperty!
上面说的是在vue2的版本实现方式,在vue3是通过proxy去实现的。
官方文档以及一些大佬们都讲的很好了,我这里就不(偷)写(懒)了。
极力推荐看下这篇图解 Vue2 响应式原理,总结很到位,看完后任督二脉瞬间都通了,塞住的鼻子也瞬间通气儿了😂。
小结
我在这里用这位大佬的总结再总结下
1.在created生命周期中,完成了数据观测, get 和 set 方法已经准备就绪,此时只创建了对应属性中的dep实例对象。
2.在beforeMount生命周期中,找到templte节点开始模板编译并生成render函数,这个过程中通过new Watcher()将Dep.target指向自身,然后在获取data中的数据时会触发 get 方法,通过dep.depend()开始收集依赖。
3.在更新data中的数据时会触发 set 方法,通过dep.notify()通知Watcher类更新视图。
// Dep类
class Dep {
subs: Array<Watcher>;
depend() {
if (Dep.target) {
Dep.target.addDep(this)
}
}
addSub(sub: Watcher) {
this.subs.push(sub)
}
notify() {
const subs = this.subs.slice()
// ... 省略不想看的代码
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
// Watcher类
class Watcher {
addDep(dep: Dep) {
// ... 省略不想看的代码
dep.addSub(this)
}
update() {
// ... 省略不想看的代码
queueWatcher(this)
}
}
总结
1.单向数据流,只允许父组件传递值给子组件,不能反向传递值。
2.双向数据绑定,在单向数据流的基础上通过自定义事件改变父组件中的数据值。
3.响应式原理,通过数据劫持(Object.defineProperty)和发布订阅者模式实现。