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

相关推荐
爱编程的王小美1 分钟前
前端代理问题
前端
pink大呲花9 分钟前
Vue 跨域解决方案及其原理剖析
前端·javascript·vue.js
亦世凡华、11 分钟前
前端npm包发布流程:从准备到上线的完整指南
前端·经验分享·npm·node.js·npm发包
Python私教18 分钟前
全栈开发实战:FastAPI + React + MongoDB 构建现代Web应用
前端·react.js·fastapi
110546540129 分钟前
7、三维机械设计、装配与运动仿真组件 - /设计与仿真组件/3d-mechanical-designer
前端·javascript·3d
東雪木1 小时前
Web开发—Vue工程化
前端·javascript·vue.js·前端框架·web
全栈凯哥1 小时前
ES6 (ECMAScript 2015) 详解
前端·ecmascript·es6
二狗子的翠花1 小时前
vue-grid-layout实现拖拽修改工作台布局
前端·javascript·vue.js
想不明白的过度思考者1 小时前
为了结合后端而学习前端的学习日志(1)——纯CSS静态卡片案例
前端·css·学习
未来之窗软件服务2 小时前
打开所在文件路径,鸿蒙系统,苹果macos,windows,android,linux —智能编程—仙盟创梦IDE
前端·ide·资源管理器·仙盟创梦ide