vue中各种通信方式总结,不再为数据传输感到烦恼

啥也不多说,直接进入主题

注意:后面给的例子都为实现以下效果(将输入框中的值添加到下面的列表中):

输入一个值并按下确认:

基础实现

先只用一个组件,不进行传值:

js 复制代码
<template>
    <div class="header">
      <input type="text" v-model="newMsg">
      <button @click="add">确定</button>
    </div>
  
    <div class="body">
      <ul>
        <li v-for="item in list">{{item}}</li>
      </ul>
    </div>
  </template>
  
  <script setup>
  import { ref } from 'vue';
  
  const newMsg = ref('')
  const list = ref(['html', 'css'])
  
  const add = () => {
    list.value.push(newMsg.value)
  }
  </script>

再简单不过,v-model绑定一下再加个ref就完成了。

父组件给子组件传

1. 父组件传值,子组件通过defineProps接受:

我们创建两个组件parent.vue与child.vue,将body部分放到child.vue,header部分则放在parent.vue。实现代码:

parent.vue:

js 复制代码
<template>
    <div class="header">
      <input type="text" v-model="newMsg">
      <button @click="add">确定</button>
    </div>
    <child :list="list"/>
  </template>
  
  <script setup>
  import { ref } from 'vue';
  import child from './fan.vue'
  
  const newMsg = ref('')
  const list = ref(['html', 'css'])
  
  const add = () => {
    list.value.push(newMsg.value)
  }
  </script>

child.vue:

js 复制代码
<template>
    <div class="body">
      <ul>
        <li v-for="item in list">{{item}}</li>
      </ul>
    </div>
</template>

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

简单来说就是将父组件的list传给子组件,通过<child :list="list"/>传出,子组件通过defineProps接收,这里尤雨溪封装的已经很完美了,也没必要多说什么,是vue中数据传输最常用的手段。但要注意的是当我们在script中使用list时我们需要用props.list,不然响应式将失效。

2. 使用 provide inject 跨组件通信:

与上面一样,创建两个组件parent.vue与child.vue,将body部分放到child.vue,header部分则放在parent.vue。实现代码:

parent.vue:

js 复制代码
<template>
    <div class="header">
      <input type="text" v-model="newMsg">
      <button @click="add">确定</button>
    </div>
    <child />
  </template>
  
  <script setup>
  import { ref, provide } from 'vue';
  import child from './fan.vue'
  
  const newMsg = ref('')
  const list = ref(['html', 'css'])
  
  const add = () => {
    list.value.push(newMsg.value)
  }

  provide('listSend',list.value)
  </script>

child.vue:

js 复制代码
<template>
    <div class="body">
        <ul>
            <li v-for="item in list">{{ item }}</li>
        </ul>
    </div>
</template>

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

const list = inject('listSend')

</script>

父组件把新加的值push进list后直接将 list provide出去,然后在子组件inject接收。是不是非常方便,且这里provide出去的值所有它的子组件都可以使用,但其实项目中我们很少使用这种方法。当组件树比较大时,这种方法会导致数据来源和依赖关系变得模糊,我们在任何地方都可以修改它的值,很不稳定。另外我们有更好的选择:vuex,或是其他状态管理模式。

子组件给父组件传

1. 发布订阅机制:

父组件订阅一个事件,子组件发布该事件并且将要传递的值一起发布出来,父组件在定义函数中获取该值,实现代码:

parent.vue:

js 复制代码
<template>
  <child @addMsg="handle"/>  // 给接收的事件定义一个订阅名,前面必须是发布的事件名
    <div class="body">
      <ul>
        <li v-for="item in list">{{item}}</li>
      </ul>
    </div>
  </template>
  
  <script setup>
  import { ref } from 'vue';
  import child from './fan.vue'

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

  const handle = (aa) => {  // 父组件订阅一个事件
    list.value.push(aa)
  }
  </script>

child.vue:

js 复制代码
<template>
     <div class="header">
      <input type="text" v-model="newMsg">
      <button @click="add" type="submit">确定</button>
    </div>
</template>

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

const newMsg = ref('')
const emits = defineEmits(['addMsg'])
const add = () => {
    emits('addMsg',newMsg.value)  // 发布事件,并且将输入的值与事件一并发出去
}
</script>

在点击按钮时,子组件将addMsg事件带上输入的值发布出去,然后父组件订阅这个事件并接收到输入的值,开始执行,我们可以随意定义这个事件,这里便是将输入的值push到了list中完成了我们的需求。

2. 通过v-model绑定:

父组件v-model绑定属性传给子组件,子组件发布 update:xxx 事件通知父组件数据更新,其实就是在发布订阅上做了些优化,实现代码:

parent.vue:

js 复制代码
<template>
  <child v-model:list="list" />
    <div class="body">
      <ul>
        <li v-for="item in list">{{item}}</li>
      </ul>
    </div>
  </template>
  
  <script setup>
  import { ref } from 'vue';
  import child from './fan.vue'

  const list = ref(['html', 'css'])
  
  </script>

child.vue:

js 复制代码
<template>
    <div class="header">
        <input type="text" v-model="newMsg">
        <button @click="add">确定</button>
    </div>

</template>

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

const props = defineProps({
    list:[]
})
const newMsg = ref('')

const emits = defineEmits(['update:list'])
const add = () => {
    props.list.push(newMsg.value)
    emits('update:list',props.list)
}

</script>

这种方法带来的便是极简的父组件,我们都知道v-model是双向绑定,这里就相当于把父组件与子组件的list双向绑定了,然后在子组件往list中push值,再将其发布,通知父组件list的值更改了。注意:这里的update:前缀可不能漏了。

3. 通过ref获取DOM结构:

在 Vue 3 中,<script setup> 语法糖提供了一种新特性,即 defineExpose() 函数,允许组件暴露其公共方法或数据,父组件通过ref获取到子组件的dom结构,从而获取到子组件defineExpose()暴露出来的数据,实现代码:

parent.vue:

js 复制代码
<template>
  <child ref="childRef" />
  <div class="body">
    <ul>
      <li v-for="item in childRef?.list">{{ item }}</li>
    </ul>
  </div>
</template>

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

const childRef = ref(null)

</script>

child.vue:

js 复制代码
<template>
  <div class="header">
    <input type="text" v-model="newMsg">
    <button @click="add">确定</button>
  </div>
</template>

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

const add = () => {
  list.value.push(newMsg.value)
}
defineExpose({ list })  // 子组件心甘情愿暴露list
</script>

细心的朋友可能会发现在循环childRef.list时childRef后面有一个问号,这是因为当你使用 $refs 来引用一个组件实例时,它会出现还未挂载的情况,那么childRef的值就会为空,这毕竟是一个需要时间的进程,此时访问childRef.list毫无意义,所有我们需要加上一个问号,等挂载完毕后才会去访问。


我的总结到此为止,希望对你有帮助。

相关推荐
Devil枫5 分钟前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦39 分钟前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子1 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山2 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享2 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
程序媛小果2 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
从兄3 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
凉辰3 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式
清灵xmf4 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
大佩梨4 小时前
VUE+Vite之环境文件配置及使用环境变量
前端