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毫无意义,所有我们需要加上一个问号,等挂载完毕后才会去访问。


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

相关推荐
Mr_Xuhhh21 分钟前
重生之我在学环境变量
linux·运维·服务器·前端·chrome·算法
永乐春秋1 小时前
WEB攻防-通用漏洞&文件上传&js验证&mime&user.ini&语言特性
前端
鸽鸽程序猿1 小时前
【前端】CSS
前端·css
ggdpzhk1 小时前
VUE:基于MVVN的前端js框架
前端·javascript·vue.js
学不会•3 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
活宝小娜6 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点6 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow6 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o6 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic7 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端