【学习笔记】vue中单向数据流、双向数据绑定、响应式原理的概念

2,204 阅读2分钟

看了挺多关于这几个概念的文章,发现有很多作者对于双向数据绑定的理解不大一样
在这里聊一聊我自己的理解(自信即巅峰)

单向数据流

简单点说就是父子组件间的值传递,只能是由父组件单向传递给子组件,子组件不能直接改变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 响应式原理

极力推荐看下这篇图解 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)和发布订阅者模式实现。