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

相关推荐
牛奶5 分钟前
前端学AI:LangGraph学习-基础概念
前端·langchain·ai编程
welkin9 分钟前
算法区间合并问题
前端·算法
Mintopia16 分钟前
Three.js高效几何体创建指南:BufferGeometry深度解析
前端·javascript·three.js
ak啊19 分钟前
Webpack Loader 执行机制
前端·webpack·源码
牛马喜喜20 分钟前
如何从零实现一个todo list(1)
前端
牛马喜喜20 分钟前
Vue编写一个自己的树形组件
前端
Mintopia24 分钟前
vue3 element-plus 二次封装Drawer抽屉,关闭时添加二次对话,开箱即用
前端·javascript·vue.js
stanny25 分钟前
Terminal里的ChatGPT:用80行代码实现带记忆的智能对话流
javascript
悟空非空也26 分钟前
超级简单,Trae 开发Chrome浏览器插件,AI编程时代来了
前端
木西27 分钟前
从0到1搭建一个RN应用从开发测试到上架全流程
android·前端·react native