一. 组件通信

1. props=>父向子传值

props主要用于父组件向子组件通信。在父组件中通过用 :msg=“msg” 绑定需要传给子组件的属性值,然后再在子组件中用 props 接收该属性值。

方法一 普通方式:

// 父组件 传值
<child :msg1="msg1" :list="list"></child>
<script>
import child from "./child.vue";
import { ref, reactive } from "vue";
export default {setup() {//基础类型传值const msg1 = ref("父组件传给子组件的msg1");// 复杂类型(数组或对象)传值const list = reactive(['苹果', '梨', '香蕉'])return {msg1,list}}
}
</script>// 子组件 接收
<template><ul ><li  v-for="i in list" :key="i">{{ i }}</li></ul>
</template>
<script>
export default {// props接受父组件传过来的值props: ["msg1", "list"],setup(props) {console.log(props);// { msg1:"父组件传给子组件的msg1", list:['苹果', '梨', '香蕉'] }},
}
</script>

方法二:使用 setup 语法糖

// 父组件 传值
<child :msg="msg" :list="list">
</child>
<script setup>import child from "./child.vue";const list = reactive(['苹果', '梨', '香蕉'])const msg = ref("父组件传给子组件的值");
</script>// 子组件 接收
<template><ul ><li  v-for="i in list" :key="i">{{ i }}</li></ul>
</template>
<script setup>// 这里不需要在从vue中引入defineProps,直接用const props = defineProps({// 第一种写法msg: String,// 第二种写法list: {type: Array,default: () => [],}})console.log(props);
</script>

2.emit方式=>子向父传值

$emit 也就是通过自定义事件传值,主要用于子组件向父组件通信
在子组件的点击事件中,通过触发父组件中的自定义事件,把想传给父组件的信息以参数的形式带过去,父组件便可以拿到子组件传过来的参数值。

// 子组件 派发
<template><button @click="handleClick">按钮</button>
</template>
<script setup>let infos = ref('还好');const handleClick = () => {// 触发父组件中的方法,并把值以参数的形式传过去emit('myClick', infos);};const emit = defineEmits(['myClick']);
</script>// 父组件 接收
<template><child @myClick="onMyClick"></child>
</template>
<script setup>import child from "./child.vue";// 父组件接受到子组件传过来的值const onMyClick = (msg) => {console.log(msg);}
</script>

3.expose / ref=》父获取子得属性或方法

expose与ref 主要用于父组件获取子组件的属性或方法。在子组件中,向外暴露出属性或方法,父组件便可以使用 ref 获取到子组件身上暴露的属性或方法。

<template><div>父组件:拿到子组件的message数据:{{ msg }}</div><button @click="callChildFn">调用子组件的方法</button><hr /><Child ref="com" />
</template><script setup>import Child from './child.vue';const com = ref(null); // 通过 模板ref 绑定子组件const msg = ref('');onMounted(() => {// 在加载完成后,将子组件的 message 赋值给 msgmsg.value = com.value.message;});function callChildFn() {console.log(com.value, '====');// 调用子组件的 changeMessage 方法com.value.show();//  重新将 子组件的message 赋值给 msgmsg.value = com.value.message;}
</script>子组件:
<template><div> 子组件:</div>
</template>
<script setup>const message = ref('子组件传递得信息');const show = () => {console.log('子组件得方法');};defineExpose({message,show,});
</script>

4.attrs

attrs 主要用于子组件获取父组件中没有通过 props 接收的属性

<template><Child :msg1="msg1" :msg2="msg2" title="子组件" />
</template><script setup>import Child from './child.vue';const msg1 = ref('信息1');const msg2 = ref('信息2');
</script>子组件
<template><div> 子组件:{{ msg1 }}-{{ attrs.msg2 }}-{{ attrs.title }}</div>
</template>
<script setup>// 子组件接收msg1defineProps({msg1: String,});const attrs = useAttrs();// 因为子组件接收了msg1,所以打印的结果中不会包含msg1, { msg2:"信息1", title: "子组件" }// 如果子组件没有接收msg1,打印的结果就是 { msg1: "信息1", msg2:"信息12", title: "子组件" }console.log(attrs);
</script>

5. provide / inject

遇到多层传值时,使用 props 和 emit 的方式会显得比较笨拙。这时就可以用 provide 和 inject 了。
provide与inject 主要为父组件向子组件或多级嵌套的子组件通信
provide:在父组件中可以通过 provide 提供需要向后代组件传送的信息。
inject:从父组件到该组件无论嵌套多少层都可以直接用 inject 拿到父组件传送的信息。

<template><div>------祖父组件---------</div><button @click="fn">改变location的值</button><br /><div>双向数据绑定:</div>姓名 {{ userInfos.username }}:<input v-model="userInfos.username" /><Child />
</template><script setup>import Child from './child.vue';let location = ref('传递祖父的参数');var userInfos = reactive({username: '张三',age: 20,});let fn = () => {location.value = '改变值';};provide('location', location);provide('userInfos', readonly(userInfos));
</script>子组件:
<template><div><Sun /></div>
</template><script>
import Sun from "./sun.vue";
</script>
孙组件:
<template><div><h5>-------------孙组件接受参数-------------</h5><div>1.祖父组件定义provide,孙组件inject接受:{{ location }}</div><p>用户信息: {{ userInfos.username }}</p><br /><br /><div>2.provide inject实现父子组件传值的时候,子组件改变数据也会影响父组件</div><br />姓名:<input v-model="userInfos.username" /></div>
</template>
<script setup>let location = inject('location');let userInfos = inject('userInfos');
</script>

注意:增加readonly后,子组件修改后,不会影响到父组件

类型安全的provide/inject
使用vue 提供的injectionKey 类型工具来再不同的上下文中共享类型

context。tsimport { InjectionKey, Ref } from 'vue'export interface SetUser{name: stringage: number
}// 函数的的InjectionKey
export const setUserKey: InjectionKey<SetUser> = Symbol()父组件
<script setup>
import {setUserKey } from './context'
provide(setUserKey , {
name: 'Karen', //如果输入1,那么类型就会报错
age: 20})
</script>子组件
<script setup>
import {setUserKey } from './context'
const user =inject(setUserKey)// 输出SetUser | undefined
if(user){console.log(user.name)//Karen
}
</script>

6.readonly

获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理,不能给属性重新赋值。只读代理是递归的:访问的任何嵌套 property 也是只读的。
简单得理解:要确保父组件传递得数据不会被子孙组件更改时,增加readonly

7.v-model

v-model 是 Vue 的一个语法糖。在 Vue3 中的玩法就更多了

单个 v-model 绑定

<template><Child v-model="message" />
</template><script setup>import Child from './child.vue';const message = ref('父传给子');
</script>
子组件:
<template><div><button @click="handleClick">修改model</button>{{ modelValue }}</div>
</template>
<script setup>// 接收defineProps(['modelValue', // 接收父组件使用 v-model 传进来的值,必须用 modelValue 这个名字来接收]);const emit = defineEmits(['update:modelValue']); // 必须用 update:modelValue 这个名字来通知父组件修改值function handleClick() {// 参数1:通知父组件修改值的方法名// 参数2:要修改的值emit('update:modelValue', '子改变值');}
</script>

多个 v-model 绑定

<template><Child v-model:msg1="message1" v-model:msg2="message2" />
</template><script setup>import Child from './child.vue';const message1 = ref('水果1');const message2 = ref('水果2');
</script>
子组件:
<template><div><div><button @click="changeMsg1">修改msg1</button> {{ msg1 }}</div><div><button @click="changeMsg2">修改msg2</button> {{ msg2 }}</div></div>
</template>
<script setup>// 接收defineProps({msg1: String,msg2: String,});const emit = defineEmits(['update:msg1', 'update:msg2']);function changeMsg1() {emit('update:msg1', '蔬菜1');}function changeMsg2() {emit('update:msg2', '蔬菜2');}
</script>

v-model 修饰符

v-model 还能通过 . 的方式传入修饰。v-model 有内置修饰符——.trim、.number 和 .lazy。但是,在某些情况下,你可能还需要添加自己的自定义修饰符。

<template><Child v-model.uppercasefn="message" />
</template><script setup>import Child from './child.vue';const message = ref('水果');
</script>
子组件:
<template><div><div>{{ modelValue }}</div></div>
</template>
<script setup>const props = defineProps(['modelValue', 'modelModifiers']);const emit = defineEmits(['update:modelValue']);onMounted(() => {console.log(props.modelModifiers, '自定义v-model 修饰符');// 判断有没有uppercasefn修饰符,有的话就执行 下面得方法 方法if (props.modelModifiers.uppercasefn) {emit('update:modelValue', '蔬菜');}});
</script>

8.插槽 slot

插槽可以理解为传一段 HTML 片段给子组件。子组件将 元素作为承载分发内容的出口。

默认插槽

插槽的基础用法非常简单,只需在 子组件 中使用 标签,就会将父组件传进来的 HTML 内容渲染出来。

<template><Child><div>渲染</div></Child>
</template>
子组件:
// Child.vue<template><div><slot></slot></div>
</template>

具名插槽

具名插槽 就是在 默认插槽 的基础上进行分类,可以理解为对号入座。

<template><Child><template v-slot:monkey><div>渲染</div></template><button>按钮</button></Child>
</template>
子组件:
<template><div><!-- 默认插槽 --><slot></slot><!-- 具名插槽 --><slot name="monkey"></slot></div>
</template>

父组件需要使用 标签,并在标签上使用 v-solt: + 名称 。子组件需要在 标签里用 name= 名称 对应接收。

作用域插槽

<template><!-- v-slot="{scope}" 获取子组件传上来的数据 --><!-- :list="list" 把list传给子组件 --><Child v-slot="{ scope }" :list="list"><div><div>{{ scope.name }}--职业:{{ scope.occupation }}</div><hr /></div></Child>
</template>
<script setup>import Child from './child.vue';const list = reactive([{ name: '鲁班', occupation: '辅助' },{ name: '貂蝉', occupation: '刺客和法师' },{ name: '虞姬', occupation: '射手' },]);
</script>
子组件:
<template><div><!-- 用 :scope="item" 返回每一项 --><slot v-for="item in list" :scope="item"></slot></div>
</template>
<script setup>defineProps({list: {type: Array,default: () => [],},});
</script>

9.事件总线(mitt&tiny-emitter)

Vue3中移除了事件总线,但是可以借助于第三方工具来完成,Vue官方推荐mitt或tiny-emitter;
在大多数情况下不推荐使用全局事件总线的方式来实现组件通信,虽然比较简单粗暴,但是长久来说维护事件总线是一个大难题。

mitt

mitt.js 不是专门给 Vue 服务的,但 Vue 可以利用 mitt.js 做跨组件通信。(vue3去掉了on、on、on、off后,使用mitt第三方库替代eventBus的原理。)

比起 Vue 实例上的 EventBus,mitt.js 好在哪里呢?

  1. 首先它足够小,仅有200bytes。
  2. 其次支持全部事件的监听和批量移除。
  3. 它还不依赖 Vue 实例,可以跨框架使用,React 或者Vue,甚至 jQuery 项目都能使用同一套库。

npm install --save mitt

父组件:
<template><div><Home /><Mitt/><div>
</template>
<script >
import Home from "@/views/home.vue";
import Mitt from "@/views/mitt.vue";
</script >
emiter.js
// mitt库默认导出的是一个函数,我们需要执行它从而得到事件总线的对象
import mitt from 'mitt'
const emiter = mitt()
export default emiter子组件Home:
<template><div><p>这里是home组件</p><button @click="sendHomeContent">$mitt发送数据</button>  </div>
</template><script>
import { ref}from 'vue'
import emitter from "./../model/emitter.js";
export default {setup(props,ctx) {const money = ref(98);var sendHomeContent=()=>{// emit发送信息emitter.emit("moneyEvent",money.value += 2);// 触发自定义总线moneyEvent,并传入一个对象 。}return{sendHomeContent}}
};
</script>
子组件 Mitt:
<template><div><p>这里是Mitt组件</p><p>接收到的数据:{{ amount }}</p></div>
</template>import { ref,   onUnmounted, onMounted } from 'vue';
import emitter from "../model/event";
export default {setup(props,ctx) {const amount = ref(0);const callback = (res) => {if (res) {amount.value = res;}}onMounted(() => {//DOM挂载完毕emitter.on('moneyEvent', callback );})onUnmounted(() => {//销毁完毕emitter.off('moneyEvent',callback );});return {amount}}
};


总结:
emit 发送信息
on 接收信息
off 取消监听
清除所有的事件写法
emitter.all.clear()

10.状态管理工具

Vuex和Pinia是Vue3中的状态管理工具,使用这两个工具可以轻松实现组件通信。
Pinia 是最近比较火热的一个工具,也是用来处理 跨组件通信 的,极大可能成为 Vuex 5

二. 细节

1.Vue3中的ref如何使用?v-for有如何使用ref?

Vue2 中的 ref

获取节点:这是 ref 的基本功能之一,目的就是获取元素节点,在 Vue 中使用方式也很简单

<template><div id="app"><div ref="test">你好!</div></div>
</template>
<script>
export default {mounted() {console.log(this.$refs.test); // <div>你好!</div>},
};
</script>

Vue3 中 ref 访问元素

Vue3 中通过 ref 访问元素节点与 Vue2 不太一样,在 Vue3 中我们是没有 this 的,也没有 this.$refs。想要获取 ref,我们只能通过声明变量的方式。

<template><div ref="test">你好!</div>
</template>
<script setup >
import { onMounted, ref } from "vue";
const test = ref(null);
onMounted(() => {console.log(test.value); // <div>你好!</div>
});
</script>

注意点:
• 变量名称必须要与 ref 命名的属性名称一致。
• 通过 test.value 的形式获取 DOM 元素。
• 必须要在 DOM 渲染完成后才可以获取 test.value,否则就是 null。

v-for 中使用 ref

使用 ref 的场景有多种,一种是单独绑定在某一个元素节点上,另一种便是绑定在 v-for 循环出来的元素上了。这是一种非常常见的需求,在 Vue2 中我们通常使用:ref="…"的形式,只要能够标识出每个 ref 不一样即可。
但是在 Vue3 中又不太一样,不过还是可以通过变量的形式接收。

<template><div><p ref="test">1. v-for 中的 Ref 数组</p><div v-for="item in 5" :key="item"  :ref="setItemRef">{{ item }} -水果</div><button @click="printFN">点击2</button></div>
</template><script>
import { ref,onMounted}from 'vue'
export default {setup(props,ctx) {const test = ref(null);let itemRefs = [];const setItemRef = el => {if (el) {itemRefs.push(el)}}onMounted(() => {console.log(test.value,'在 vue3 中'); // <div>小猪课堂</div>});const printFN=()=>{console.log(itemRefs,'在嵌套 vue3 中')}return {test,setItemRef,printFN}}
};
</script>


这里我们需要注意一下:我们似乎没办法区分哪个 li 标签哪个 ref,初次之外,我们的 itemRefs 数组不能够保证与原数组顺序相同,即与 list 原数组中的元素一一对应。

ref 绑定函数

前面我们在组件上定义 ref 时,都是以一个字符串的形式作为 ref 的名字,其实我们的 ref 属性还可以接收一个函数作为属性值,这个时候我们需要在 ref 前面加上:。

<template><div :ref="setHelloRef">水果</div>
</template><script>
import { ref}from 'vue'
export default {setup(props,ctx) {const setHelloRef = (el) => {console.log(el); // <div>水果</div>};return {setHelloRef}}
};
</script>

上段代码中 ref 属性接收的是一个 setHelloRef 函数,该函数会默认接收一个 el 参数,这个参数就是我们需要获取的 div 元素。假如需求中我们采用这种方式的话,那么完全可以把 el 保存到一个变量中去,供后面使用。

v-for 中使用ref 函数

<template><div v-for="item in 10" :key="item" :ref="(el) => setItemRefs(el, item)">{{ item }} -水果</div>
</template><script>
import { ref,onMounted}from 'vue'
export default {setup(props,ctx) { let itemRefs = [];const setItemRefs = (el,item) => {if(el) {itemRefs.push({id: item,el,});}}onMounted(() => {console.log(itemRefs,'在 vue3 中'); // <div>小猪课堂</div>});return {setItemRefs}}
};
</script>

在 v-for 中使用函数的形式传入 ref 与不使用 v-for 时的形式差不多,不过这里我们做了一点变通,为了区别出哪个 ref 是哪一个 li 标签,我们决定将 item 传入函数,也就是(el) => setItemRefs(el, item)的写法。
这种形式的好处既让我们的操作性变得更大,还解决了 v-for 循环是 ref 数组与原数组顺序不对应的问题。

组件上使用 ref

之前所使用 ref 时,都是在一个具体的 dom 元素上绑定,但是我们也可以将 ref 绑定在组件上,比如在 Vue2 中,我们将 ref 绑定在组件上时,便可以获取到该组件里面的所有数据和方法.在 Vue3 中,使用 ref 获取子组件时,如果想要获取子组件的数据或者方法,子组件可以通过defineExpose 方法暴露数据
参考上面“expose / ref=》父获取子得属性或方法”

vue3 的组件通信以及ref的使用v-model相关推荐

  1. vue3之组件通信 (props父传子,子传孙)(ts定义数组类型)

    目录 vue3之组件通信 1 props父传子,子传孙 1-1 父组件 1-2 子组件 1-3 孙组件 2:父子传值 2:-1 父组件向子组件传值 Props 2-2 子组件向父组件传值 emit 3 ...

  2. vue3 父子组件通信

    前提:封装组件是前端开发必备的,所以父子组件的通信相当重要,接下来总结下vue3父子组件通信的知识点,使用<script setup>语法糖 一.defineProps 和 defineE ...

  3. Vue 非父子组件通信 (ref)

    Vue 非父子组件通信 (ref) 流程:1. 先在son子组件中定义一个数据和事件处理程序data( ) { return { flag: false }} , methods: { cry( ) ...

  4. vue3父子组件通信

    应用场景 子组件中的值发生改变时,向父组件传递一个标识符代表子组件的值已被改变,通过这个标识符对业务进行不同的处理. 基本思路 在子组件被监听的组件上绑定@change事件,@change调用传参方法 ...

  5. vue3异步组件怎么获取ref,获取组件实例?曲线救国的方式。

    翻看文档实在没找到异步组件加载成功后的回调,获取ref写个setTimeout延迟个一两秒可以,万一网速慢就GG,所以还是得明确的知道这个组件什么时候下载引入成功的. 目前的思路是把defineAsy ...

  6. vue3兄弟组件通信-mitt

    首先安装mitt cnpm install --save mitt 在本地新建一个js文件mitt.js import mitt from 'mitt'const emitter = mitt() e ...

  7. Vue3 - 组件通信(父传子)

    前言 在 Vue3 中,父组件向子组件传参的方法. 与 Vue2 相比,还是有一些区别的. 基础示例 现在我们的需求是,要通过父组件,传递一个标题来让子组件显示. 子组件 Com.vue: <t ...

  8. Vue3 组件通信学习笔记

    一.父子组件之间通信 父子组件之间如何进行通信呢? 父组件传递给子组件:通过props属性: 子组件传递给父组件:通过$emit触发事件: 1.1 父组件传递给子组件 在开发中很常见的就是父子组件之间 ...

  9. com组件的ref有时需要有时不需要?_vue 组件通信看这篇就够了(12种通信方式)

    点击上方"前端小苑",选择"置顶公众号" 精品技术文章,热门资讯第一时间送达 vue 组件间的通信是 vue 开发中很基础也十分重要的部分,作为使用 vue 的 ...

最新文章

  1. 算法实现太难了?机器学习也需要开源软件
  2. OpenAI首次推出数学定理推理模型GPT-f,23个推导结果被专业数据库收录
  3. 如何借助 svn update 自动更新线上的web
  4. 我发现一个新的软件,用自然语言编程!非常酷!
  5. 【动态规划】01背包问题
  6. 信号与线性系统分析_线性系统与采样定理
  7. 扎心!七夕刚过“玫瑰花是什么垃圾”就成热搜 支付宝都看不下去了...
  8. vba 添加outlook 签名_利用VBA发送附件电子邮件
  9. mybatisplus activerecord之mybatisplus entity XXX Not Found TableInfoCache.错误
  10. 《数字图像处理 第三版》(冈萨雷斯)——第七章 小波和多分辨率处理
  11. bus hound usb 调试
  12. 使用echarts来显示世界地图和全国地图,并且可以下钻层级
  13. c++ 两个栈实现一个队列
  14. Ubuntu WPS字体缺失
  15. usnews 计算机专业排名,2020USNEWS计算机科学专业排名
  16. 一步步教您搞定讯飞语音识别 | 寻找C站宝藏
  17. The Oregon Trail 俄勒冈之旅
  18. java熟人_英语中对各种不同程度关系的朋友是怎么表示的(就像汉语里有泛泛之交、熟人、朋友、死党之类的)?...
  19. [渝粤教育] 同济大学 外科手术技能教学 参考 资料
  20. js中文汉字转拼音详细教程

热门文章

  1. 基于NE555的施密特触发器用于整形变换电路
  2. 程序员接到项目之前和之后的表现,网友:太搞笑了!
  3. C++ STL 体系结构与内核分析 P8-P15(list源码,迭代器设计原则)
  4. kali Linux Vega工具安装与解决报错
  5. 情系玉树, 大爱无疆----抗震救灾
  6. 微信公众号自动回复聊天机器人实现(PHP)
  7. 移动端元素跟随移动-防苹果悬浮球效果(转载)
  8. 新华三服务器CPU型号,产品技术-H3C UniServer R4950 G3 服务器-新华三集团-H3C
  9. Position属性之relative用法
  10. 使用adb安装遇到的一些坑