vue2 vue3中父子组件数据双向绑定更新方式

前言

这里说的数据双向绑定,指的是 vue 父子组件的数据双向绑定,而不是 vue 的数据双向绑定原理(数据与视图的双向绑定更新)

关于子组件不能修改父组件穿入的props数据,官方这样解释:

“注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果在子组件中直接修改父组件传递的对象或数组,会影响到父组件的状态,违背了单向数据流的规则。”

这意味着如果子组件直接修改了 props 数据,父组件中的相应数据也会被修改,可能会导致非预期的结果和调试困难。

但是很多封装组件都是有这方面的需求,不然来回的回调事件会让代码更加臃肿繁琐,那么如何实现父子组件数组的双向绑定,有如下几种方式:

vue2

v-model

Vue 2 中 提供了 一个v-model 的指令,实际上是一个语法糖,

是对 :value@input 两个指令的结合使用,简化了常见的表单元素绑定代码

相当于以下的缩写

<input :value="message" @input="message = $event.target.value" type="text">

那在子组件中该如何使用最为方便呢?

可以使用vue的计算属性,当计算属性发生改变时更新value值,这样 子组件只需要直接操作这个计算属性值就可以了,具体代码如下

<template>
  <div>
    <div>父组件传过来的值:{{ value }}</div>
    <div>子组件内的计算属性:{{ childrenValue }}</div>
    <button @click="btn('add')">子组件按钮+1</button>
    <button @click="btn('delete')">子组件按钮-1</button>
  </div>
</template>
<script>
export default {
  props: {
    value: {
      type: Number,
      default: 0
    }
  },
  computed: {
    // 通过get/set函数 当计算属性改变时触发$emit更新父组件的值
    childrenValue: {
      get() {
        return this.value
      },
      set(val) {
        this.$emit('input', val)
      }
    }
  },
  methods: {
    btn(type) {
      switch (type) {
        case 'add':
          this.childrenValue++
          break;
        case 'delete':
          this.childrenValue--
          break
      }
    }
  }
}
</script>

GIF1.gif

v-model 指令默认使用的键名为 value 和 input,意思就是相当于传入props value,和发出 $emit('input', '修改的值')

如有需要,这个使用的键名是可以更改的

在子组件中,可以通过model属性来进行更改,例如

export default {
    model: {
		prop: "visible", // 默认接收键名更改
		event: "visibleChange", // 默认发起事件键名更改
	},
    props: {
        visible: { // v-mode传入的值
            type: Boolean,
            default: false
        }
    },
    methods: {
        setValue() {
            this.$emit("visibleChange", false); // 修改 visible 值
        }
    }
}

sync 修饰符

sync 是一个修饰器,用于简化父子组件之间的双向数据绑定。它通过自动生成一个名为 update:propertyName 的自定义事件,实现父组件能够直接修改子组件的属性值。

<myComponent :value.sync="valueText" />

相当于如下的简写

<myComponent v-bind:title="valueText" v-on:update:title="valueText=$event" />

所以在子组件直接调用 $emit('update:title', '更新的值') 就可以更新父组件的值

与v-model不同的是,sync修饰符可以用于多个props传值,并不局限于一个

逻辑与之前类似

<myComponent :value1.sync="valueText1" :value2.sync="valueText2" />
<template>
  <div>
    <div>父组件传过来的value1:{{ value1 }}</div>
    <div>父组件传过来的value2:{{ value2 }}</div>
    <button @click="btn('childrenValue1')">子组件修改value1</button>
    <button @click="btn('childrenValue2')">子组件修改value2</button>
  </div>
</template>
<script>
export default {
  props: {
    value1: {
      type: Number,
      default: 0
    },
    value2: {
      type: Number,
      default: 0
    }
  },
  computed: {
    // 通过get/set函数 当计算属性改变时触发$emit更新父组件的值
    childrenValue1: {
      get() {
        return this.value1
      },
      set(val) {
        this.$emit('update:value1', val)
      }
    },
    childrenValue2: {
      get() {
        return this.value2
      },
      set(val) {
        this.$emit('update:value2', val)
      }
    },
  },
  methods: {
    btn(target) {
      this[target]++
    }
  }
}
</script>

GIF2-1691727500544-4.gif

vue3

v-model

在vue3中,剔除了 sync 修饰符,v-model 指令不再是一个特殊的指令,而被视为一种语法糖,默认传入props键名为 modelValue

并支持使用多个v-model,但是要自定义键名,如

<myComponent v-model="valueText" v-model:value2="valueText2" />

在组件内同样通过 emit('update:modelValue', '修改的值') 来进行更新父组件的值

示例如下,案例使用了ts和script setup模式

<template>
  <div>value1值:{{ value1 }}<button @click="btn('value1')">按钮1</button></div>
  <div>value2值:{{ value2 }}<button @click="btn('value2')">按钮2</button></div>
</template>
<script setup lang="ts">
import { toRefs } from 'vue'
interface Props {
  value1: number
  value2: number
}
const props = withDefaults(defineProps<Props>(), { // props
  value1: 0,
  value2: 0
})
const emit = defineEmits(['update:value1', 'update:value2']) // 定义事件

const { value1, value2 } = toRefs(props) // 引用解构出value1、value2

// 按钮操作
function btn(type: string) {
  console.log(value1, value2)
  switch (type) {
    case 'value1':
      emit('update:value1', value1.value + 1) // 更新父组件值
      break;
    case 'value2':
      emit('update:value2', value2.value + 1) // 更新父组件值
      break
  }
}
</script>

使用组件

<template>
  <myComponent v-model:value1="value1Text" v-model:value2="value2Text"  />
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import myComponent from './components/VMdel.vue'

const value1Text = ref<number>(0)
const value2Text = ref<number>(0)
</script>
<style scoped>
.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: filter 300ms;
}
.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
  filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

效果如下,在子组件内可以更新父组件的值

GIF3.gif

本文完!如有不足或缺漏之处请及时指出,感谢!!

全部评论

相关推荐

点赞 评论 收藏
分享
03-19 21:31
已编辑
上海大学 Web前端
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;今天早上又焦虑的投了些简历,&amp;amp;简单修改了下,then开始看昨天剩下的内容,&amp;amp;练习了跟Ajax的很多操作(还有非原生的jQuery&amp;amp;axios&amp;amp;fetch)。but感觉不熟悉的时候感觉真的写的慢哇。then写到下午吃饭的时候,知道npy同样工作难找的困境,更加想要努力学习了,尽早找到工作,不然都找不到怎么养她&nbsp;去吃下午饭的路上,收到pdd的笔试通知,感觉自己算法还是一坨,so吃完就回来在看算法了。听了之前评论区的建议,看了下labuladong(拉布拉多),不对,东哥的算法笔记,看了前面的(算导论吧)还是挺有感触的,之后就开始跟着上面的做了起来。不过有些时候感觉就喜欢自己给自己下定义,觉得自己不行就迟迟不开始真正的行动,思考,今天突然感觉算法没有完全无法下手的感觉,只不过是自己刷的太少,经验不足,没有自己的体系。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;然后不知不觉时间又到图书馆要关门的时候了,就记录今天的所想所为吧。晚上回到寝室先健身(感觉运动的多巴胺确实可以让自己乐观,少焦虑一点),然后洗个澡在突击一下算法,加油!每一个努力的自己!&amp;amp;:有没有大佬知道假if某家公司春招的笔试挂了第一次之后,还能接着投这家公司的其他职位吗?&amp;amp;&amp;amp;它一次只能投一个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务