Vue中组件传值的六种方式

在 Vue 中,组件之间的通信有很多种方式。通过以下七组代码示例,我们可以清晰地看到不同的组件通信方式。

第一组:父组件通过 props 传值,子组件通过 watch 监听变化(父传子)

父组件:

vue 复制代码
<template>
  <div class="head">
    <input type="text" v-model="msg">
    <button @click="add">添加</button>
  </div>

  <Child :data="data"/>  //通过:绑定属性传值给子组件
</template>

<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const msg = ref('')
const data = ref('')
const add = () => {
  data.value = msg.value
}
</script>

子组件:

vue 复制代码
<template>
  <div class="body">
    <ul>
      <li v-for="(item, index) in list" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const props = defineProps({  //子组件通过defineProps接受父组件传过来的值
  data: {
    type: String,
    default: ''
  }
})

const list = ref(['html', 'css', 'js'])

watch(() => props.data, (newVal) => {
  if (newVal) {
    list.value.push(newVal)
  }
})
</script>

解释:

在这组代码中,父组件通过 v-model 获取用户输入,并将其传递给子组件。在父组件中,当点击"添加"按钮时,data 的值会更新为输入框的内容,这个更新的值通过 props 传递给子组件。子组件通过 watch 监听 props.data 的变化,一旦 data 发生变化,它就会将新的值推入 list 中。

第一组改版:父组件通过 props 传递数组,子组件显示数组内容(这并不算新增方式,只是逻辑换一下可以不写watch监听)

父组件:

vue 复制代码
<template>
  <div class="head">
    <input type="text" v-model="msg">
    <button @click="add">添加</button>
  </div>

  <Child :list="list"/>
</template>

<script setup>
import Child from './child.vue'
import { ref } from 'vue'

const list = ref(['html', 'css', 'js'])
const msg = ref('')
const add = () => {
  list.value.push(msg.value)
}
</script>

子组件:

vue 复制代码
<template>
  <div class="body">
    <ul>
      <li v-for="(item, index) in list" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
defineProps({
  list: {
    type: Array,
    default: () => [],
  },
});
</script>

解释:

在这个例子中,父组件持有一个名为 list 的数组,并通过 v-model 获取用户输入。在点击"添加"按钮后,父组件会将输入框的内容添加到 list 中,并通过 props 将这个数组传递给子组件。子组件通过 props.list 接收这个数组并在模板中展示出来。

第二组:父组件通过 provide 传递数据,子组件通过 inject 获取数据(父传子)

父组件:

vue 复制代码
<template>
  <div class="head">
    <input type="text" v-model="msg">
    <button @click="add">添加</button>
  </div>

  <Child/>
</template>

<script setup>
import Child from './child.vue'
import { ref, provide, readonly } from 'vue'

const msg = ref('')
const list = ref(['html', 'css', 'js'])
const add = () => {
  // list.value.push(msg.value)
  console.log(list.value);
}

provide('list', readonly(list.value))  // 向下提供只读数据
</script>

子组件:

vue 复制代码
<template>
  <div class="body">
    <ul>
      <li v-for="(item, index) in list" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
import { inject } from 'vue'

const list = inject('list')  // 注入只读数据
list.push('vue')  //无法新增
</script>

解释:

在这组代码中,父组件使用 provide 向下传递了一个 list 数据。这个 list 被标记为只读 (readonly) 以防止子组件修改它。子组件通过 inject 获取到父组件提供的 list 数据。在这个例子中,子组件将一个新值 "vue" 添加到 list 中,但实际上这个修改会失败,因为父组件提供的数据是只读的。

第三组:子组件通过发布订阅向父组件传值(子传父)

父组件:

vue 复制代码
<template>
  <Child @add="handle"/>  //订阅add事件

  <div class="body">
    <ul>
      <li v-for="(item, index) in list" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
import Child from './child.vue'
import { ref } from 'vue'

const list = ref(['html', 'css', 'js'])
const handle = (val) => {
  list.value.push(val)
}
</script>

子组件:

vue 复制代码
<template>
  <div class="head">
    <input type="text" v-model="msg">
    <button @click="add">添加</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const msg = ref('')

const emit = defineEmits(['add'])  // 发布订阅
const add = () => {
  emit('add', msg.value)
}
</script>

解释:

在这组代码中,父组件通过 @add 监听子组件 add 事件,并在事件发生时调用 handle 方法,将子组件传递过来的数据 (msg.value) 添加到 list 数组中。子组件通过 defineEmits 定义了一个名为 add 的事件,当用户点击"添加"按钮时,子组件会触发该事件,并将输入框的内容传递给父组件。

第四组:通过 defineExpose 暴露数据给父组件(子传父)

子组件:

vue 复制代码
<template>
  <div class="head">
    <input type="text" v-model="msg">
    <button @click="add">添加</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const msg = ref('')
const list = ref(['html', 'css', 'js'])

const add = () => {
  list.value.push(msg.value) // 把输入框中的值添加到 list 数组中
}

// 使用 defineExpose 暴露数据给父组件
defineExpose({
  list
})
</script>

<style lang="css" scoped></style>

父组件:

vue 复制代码
<template>
  <Child ref="childRef"/>  //接收到子组件暴露的数据

  <div class="body">
    <ul>
      <li v-for="(item, index) in childRef?.list" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
import Child from './child.vue'
import { ref } from 'vue'

const childRef = ref(null)

setTimeout(() => {
  console.log(childRef.value.list)  // 访问子组件暴露的 list 数据
}, 1000)
</script>

<style lang="css" scoped></style>

解释:

在这组代码中,子组件通过 defineExposelist 数组暴露给父组件。父组件通过 ref="childRef" 获取对子组件的引用,进而访问子组件暴露的 list 数据。这种方式可以在父组件中直接访问子组件的内部状态或者方法。

第五组:通过 v-model 绑定和 emits 更新父组件的数据

子组件:

vue 复制代码
<template>
  <div class="head">
    <input type="text" v-model="msg">
    <button @click="add">添加</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const msg = ref('')
const props = defineProps(['list'])  // 接收父组件传来的 list 数据

const emits = defineEmits(['update:list'])  // 发送 update:list 事件
const add = () => {
  const arr = props.list
  arr.push(msg.value)  // 将输入框的值添加到 list 中

  emits('update:list', arr)  // 触发事件并更新父组件的 list
}
</script>

<style lang="css" scoped></style>

父组件:

vue 复制代码
<template>
  <Child v-model:list="list"/>  <!-- 使用 v-model 双向绑定 -->
  
  <div class="body">
    <ul>
      <li v-for="(item, index) in list" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
import Child from './child.vue'
import { ref } from 'vue'

const list = ref(['html', 'css', 'js'])  // 父组件的 list 数据
</script>

<style lang="css" scoped></style>

解释:

这组代码展示了如何使用 v-model 实现双向绑定和更新父组件的数据。父组件通过 v-model:listlist 数据与子组件进行双向绑定。当用户在子组件中点击"添加"按钮时,子组件会通过 emits 发出一个 update:list 事件,并将更新后的 list 传递给父组件,父组件的数据也随之更新。

第六组:使用一个全局的共享数据管理组件间通信(兄弟组件通信)

body.vue

vue 复制代码
<template>
  <div class="body">
    <ul>
      <li v-for="(item, index) in list" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
import { list } from './bus.js'  // 从 bus.js 引入共享数据
</script>

<style lang="css" scoped></style>

bus.js

javascript 复制代码
import { ref } from 'vue'

export const list = ref(['html', 'css', 'js'])  // 这里定义了一个全局共享的 list

head.vue

vue 复制代码
<template>
  <div class="head">
    <input type="text" v-model="msg">
    <button @click="add">添加</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { list } from './bus.js'  // 引入共享的 list 数据

const msg = ref('')
const add = () => {
  list.value.push(msg.value)  // 向 list 中添加输入框的内容
}
</script>

<style lang="css" scoped></style>

index.vue

vue 复制代码
<template>
  <Head />
  <Body />
</template>

<script setup>
import Head from './head.vue'
import Body from './body.vue'
</script>

<style lang="css" scoped></style>

解释:

这里展示了一种常见的跨组件共享数据的方式------通过一个共享的 bus.js 文件来存储数据。在 bus.js 中,定义了一个 list,它是一个响应式数据。head.vuebody.vue 组件都从这个 bus.js 中引入了 list 数据。head.vue 中的输入框内容会被添加到 list 中,而 body.vue 会实时展示这个 list 的内容。通过这种方式,组件之间可以共享同一个数据,保持同步更新。

相关推荐
人工智能训练师2 小时前
Ubuntu22.04如何安装新版本的Node.js和npm
linux·运维·前端·人工智能·ubuntu·npm·node.js
Seveny072 小时前
pnpm相对于npm,yarn的优势
前端·npm·node.js
yddddddy3 小时前
css的基本知识
前端·css
昔人'3 小时前
css `lh`单位
前端·css
前端君4 小时前
实现最大异步并发执行队列
javascript
Nan_Shu_6145 小时前
Web前端面试题(2)
前端
知识分享小能手5 小时前
React学习教程,从入门到精通,React 组件核心语法知识点详解(类组件体系)(19)
前端·javascript·vue.js·学习·react.js·react·anti-design-vue
蚂蚁RichLab前端团队6 小时前
🚀🚀🚀 RichLab - 花呗前端团队招贤纳士 - 【转岗/内推/社招】
前端·javascript·人工智能
孩子 你要相信光6 小时前
css之一个元素可以同时应用多个动画效果
前端·css
萌萌哒草头将军6 小时前
Oxc 和 Rolldown Q4 更新计划速览!🚀🚀🚀
javascript·vue.js·vite