Vue--双向绑定

忘是亡心i 2022-12-21 06:24 402阅读 0赞

原文网址:Vue—双向绑定_IT利刃出鞘的博客-CSDN博客

其他网址

父子组件prop值的双向绑定

简介

说明

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。

众所周知,vue组件间的传值都是单向数据流。按官网的 单向数据流 解释:所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。

最常见的就是在公共组件中(如计数器、单选按钮)中,子组件(计数器)把父组件传来的数字展示出来。在点击子组件的添加按钮时,因为单向数据流的原因,不能直接在子组件中给数字+1。

本文以计数器为例,说明子组件给父组件传递数据的方案。

公共代码

  1. import Vue from 'vue'
  2. import Router from 'vue-router'
  3. import CompA from "@/components/CompA";
  4. export default new Router({
  5. routes: [
  6. {
  7. path: '/compA',
  8. name: 'compA',
  9. component: CompA
  10. }
  11. ]
  12. })

方案1:prop + $emit

components/CompA(父组件)

  1. <template>
  2. <div class="compA">
  3. <p>CompA</p>
  4. <p>count: {
  5. { count }}</p>
  6. <hr>
  7. <comp-b v-bind:count="count" v-on:update:count="count = $event"></comp-b>
  8. </div>
  9. </template>
  10. <script>
  11. import CompB from "@/components/CompB";
  12. export default {
  13. components: {CompB},
  14. data() {
  15. return {
  16. count: 0,
  17. }
  18. },
  19. }
  20. </script>
  21. <style scoped>
  22. </style>

components/CompB(子组件)

  1. <template>
  2. <div class="compB">
  3. <p>CompB</p>
  4. <button @click="updateCount">You click me {
  5. {count}} times</button>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. props: ['count'],
  11. methods: {
  12. updateCount() {
  13. this.$emit('update:count', +this.count + 1);
  14. },
  15. }
  16. }
  17. </script>
  18. <style scoped>
  19. </style>

测试

访问:http://localhost:8080/#/compA

未点击按钮前

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70

每点击按钮一次,数目加1(父组件和子组件会同步显示)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 1

方案2:v-model

其他网址

官方网址:自定义组件的-v-model

简介

自定义组件的v-model其实就是父子组件通信的语法糖而已,它等价于:value=”value(固定格式)” @input=”emitEvent(组件emit带的第一个参数)”。即:

  1. <comp-b v-model="value"></comp-b>
  2. <!-- 等价于下边这样写 -->
  3. <!-- <comp-b :value="value" v-on:input="value = $event"></comp-b>-->

也就是说,此时。子组件中emit的自定义事件名需要为input。父子组件中传的值键名需要是value。

实例

components/CompA(父组件)

  1. <template>
  2. <div class="compA">
  3. <p>CompA</p>
  4. <p>count: {
  5. { value }}</p>
  6. <hr>
  7. <comp-b v-model="value"></comp-b>
  8. <!-- 等价于下边这样写 -->
  9. <!-- <comp-b :value="value" v-on:input="value = $event"></comp-b>-->
  10. </div>
  11. </template>
  12. <script>
  13. import CompB from "@/components/CompB";
  14. export default {
  15. components: {CompB},
  16. data() {
  17. return {
  18. value: 0,
  19. }
  20. },
  21. }
  22. </script>
  23. <style scoped>
  24. </style>

components/CompB(子组件)

  1. <template>
  2. <div class="compB">
  3. <p>CompB</p>
  4. <button @click="changeCount">You click me {
  5. {value}} times</button>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. props: ['value'],
  11. methods: {
  12. changeCount() {
  13. this.$emit('input', +this.value + 1);
  14. },
  15. }
  16. }
  17. </script>
  18. <style scoped>
  19. </style>

测试

访问:http://localhost:8080/#/compA

未点击按钮时:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 2

每点击按钮一次,数目加1(父组件和子组件会同步显示)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 3

注意

components/CompA中,必须用,它生成的html如下:如果将CompA和CompB的value都替换为count,则是不成功的。如下

未点击按钮时:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 4

点击按钮之后

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 5

方案3:v-model + model

其他网址

官方网址:model

简介

允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。

实例

components/CompA(父组件)

  1. <template>
  2. <div class="compA">
  3. <p>CompA</p>
  4. <p>count: {
  5. { count }}</p>
  6. <hr>
  7. <comp-b v-model="count"></comp-b>
  8. </div>
  9. </template>
  10. <script>
  11. import CompB from "@/components/CompB";
  12. export default {
  13. components: {CompB},
  14. data() {
  15. return {
  16. count: 0,
  17. }
  18. },
  19. }
  20. </script>
  21. <style scoped>
  22. </style>

components/CompB(子组件)

  1. <template>
  2. <div class="compB">
  3. <p>CompB</p>
  4. <button @click="changeCount">You click me {
  5. {count}} times</button>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. props: ['count'],
  11. model:{
  12. prop: "count",
  13. event: 'change-count'
  14. },
  15. methods: {
  16. changeCount() {
  17. this.$emit('change-count', +this.count + 1);
  18. },
  19. }
  20. }
  21. </script>
  22. <style scoped>
  23. </style>

测试

访问:http://localhost:8080/#/compA

未点击按钮时:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 6

每点击按钮一次,数目加1(父组件和子组件会同步显示)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 7

方案4:.sync 修饰符

其他网址

官方网址:.sync 修饰符

简介

同样是语法糖,具体用法直接戳进官网查看。

  1. <comp-b v-bind:count.sync="myCount"></comp-b>
  2. <!-- 等价于-->
  3. <!--<comp-b
  4. v-bind:count="myCount"
  5. v-on:update:count="myCount=$event">
  6. </comp-b>-->

实例

components/CompA(父组件)

  1. <template>
  2. <div class="compA">
  3. <p>CompA</p>
  4. <p>count: {
  5. { myCount }}</p>
  6. <hr>
  7. <comp-b v-bind:count.sync="myCount"></comp-b>
  8. <!-- 等价于-->
  9. <!--<comp-b
  10. v-bind:count="myCount"
  11. v-on:update:count="myCount=$event">
  12. </comp-b>-->
  13. </div>
  14. </template>
  15. <script>
  16. import CompB from "@/components/CompB";
  17. export default {
  18. components: {CompB},
  19. data() {
  20. return {
  21. myCount: 0,
  22. }
  23. },
  24. }
  25. </script>
  26. <style scoped>
  27. </style>

可以看到:此时变量名不对应子组件的props的prop名。

components/CompB(子组件)

  1. <template>
  2. <div class="compB">
  3. <p>CompB</p>
  4. <button @click="changeCount">You click me {
  5. {count}} times</button>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. props: ['count'],
  11. methods: {
  12. changeCount() {
  13. this.$emit('update:count', +this.count + 1);
  14. },
  15. }
  16. }
  17. </script>
  18. <style scoped>
  19. </style>

测试

访问:http://localhost:8080/#/compA

未点击按钮时:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 8

每点击按钮一次,数目加1(父组件和子组件会同步显示)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 9

方案5:多个 prop的双向绑定

其他网址

官方网址:.sync 修饰符

简介

跟方案4相同,依然使用.sync。把 countGroup 对象中的每一个 property (如 count) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器。

实例

components/CompA.vue(父组件)

  1. <template>
  2. <div class="compA">
  3. <p>CompA</p>
  4. <p>count1: {
  5. { countGroup.count1 }}</p>
  6. <p>count2: {
  7. { countGroup.count2 }}</p>
  8. <hr>
  9. <comp-b v-bind.sync="countGroup"></comp-b>
  10. </div>
  11. </template>
  12. <script>
  13. import CompB from "@/components/CompB";
  14. export default {
  15. components: {CompB},
  16. data() {
  17. return {
  18. countGroup:{
  19. count1: 0,
  20. count2: 0
  21. }
  22. }
  23. },
  24. }
  25. </script>
  26. <style scoped>
  27. </style>

components/CompB.vue(子组件)

  1. <template>
  2. <div class="compB">
  3. <p>CompB</p>
  4. <button @click="changeCount1">You click count1 {
  5. {count1}} times</button>
  6. <button @click="changeCount2">You click count2 {
  7. {count2}} times</button>
  8. </div>
  9. </template>
  10. <script>
  11. export default {
  12. props: ['count1', 'count2'],
  13. methods: {
  14. changeCount1() {
  15. this.$emit('update:count1', +this.count1 + 1);
  16. },
  17. changeCount2() {
  18. this.$emit('update:count2', +this.count2 + 1);
  19. },
  20. }
  21. }
  22. </script>
  23. <style scoped>
  24. </style>

测试

访问:http://localhost:8080/#/compA

未点击按钮时:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 10

每点击按钮一次,数目加1(父组件和子组件会同步显示)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70 11

发表评论

表情:
评论列表 (有 0 条评论,402人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Vue双向

    实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发

    相关 VUE双向

    1、什么是setter、getter ?   答:首先,别误以为他们就是一会要说的get、set,我们先看一句定义: >        对象有两种属性:(1)数据属性,