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 的内容。通过这种方式,组件之间可以共享同一个数据,保持同步更新。

相关推荐
想吃火锅10056 小时前
【leetcode】405.数字转换为十六进制数js
开发语言·javascript·ecmascript
原则猫8 小时前
HOOKS 背后机制
前端
码语智行8 小时前
首页导航跳转功能深度解析-系统内和系统外
前端
阿猫的故乡9 小时前
Vue过渡动画从入门到装X:淡入淡出、滑动、列表动画、第三方库全搞定
前端·javascript·vue.js
裕波9 小时前
Vue&ViteConf 2026 将于 7 月 18 日在上海举办,尤雨溪将现场发表主题演讲
vue.js·vite
IManiy9 小时前
总结之Vibe Coding前端骨架
前端
小和尚敲木头9 小时前
vue3 vite动态拼接图片路径
javascript
JS菌9 小时前
AI Agent 沙箱双层防护体系:从权限过滤到内核隔离的完整实现
前端·人工智能·后端
Aphasia3119 小时前
从输入URL到页面展示全流程
前端·面试
我叫黑大帅10 小时前
前端如何竖屏固定视口背景
前端·javascript·面试