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

前言

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

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

"注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果在子组件中直接修改父组件传递的对象或数组,会影响到父组件的状态,违背了单向数据流的规则。"
这意味着如果子组件直接修改了 props 数据,父组件中的相应数据也会被修改,可能会导致非预期的结果和调试困难。

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

vue2

v-model

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

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

相当于以下的缩写

html 复制代码
<input :value="message" @input="message = $event.target.value" type="text">

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

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

html 复制代码
<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>

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

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

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

js 复制代码
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 的自定义事件,实现父组件能够直接修改子组件的属性值。

html 复制代码
<myComponent :value.sync="valueText" />

相当于如下的简写

html 复制代码
<myComponent v-bind:title="valueText" v-on:update:title="valueText=$event" />

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

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

逻辑与之前类似

html 复制代码
<myComponent :value1.sync="valueText1" :value2.sync="valueText2" />
html 复制代码
<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>

vue3

v-model

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

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

html 复制代码
<myComponent v-model="valueText" v-model:value2="valueText2" />

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

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

html 复制代码
<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>

使用组件

html 复制代码
<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>

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

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

相关推荐
计算机学姐8 分钟前
基于微信小程序的调查问卷管理系统
java·vue.js·spring boot·mysql·微信小程序·小程序·mybatis
学习使我快乐013 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19953 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
黄尚圈圈4 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水5 小时前
简洁之道 - React Hook Form
前端
正小安7 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
小飞猪Jay9 小时前
C++面试速通宝典——13
jvm·c++·面试
_.Switch9 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光9 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js