vue3 的组件通信以及ref的使用v-model
一. 组件通信
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 好在哪里呢?
- 首先它足够小,仅有200bytes。
- 其次支持全部事件的监听和批量移除。
- 它还不依赖 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相关推荐
- vue3之组件通信 (props父传子,子传孙)(ts定义数组类型)
目录 vue3之组件通信 1 props父传子,子传孙 1-1 父组件 1-2 子组件 1-3 孙组件 2:父子传值 2:-1 父组件向子组件传值 Props 2-2 子组件向父组件传值 emit 3 ...
- vue3 父子组件通信
前提:封装组件是前端开发必备的,所以父子组件的通信相当重要,接下来总结下vue3父子组件通信的知识点,使用<script setup>语法糖 一.defineProps 和 defineE ...
- Vue 非父子组件通信 (ref)
Vue 非父子组件通信 (ref) 流程:1. 先在son子组件中定义一个数据和事件处理程序data( ) { return { flag: false }} , methods: { cry( ) ...
- vue3父子组件通信
应用场景 子组件中的值发生改变时,向父组件传递一个标识符代表子组件的值已被改变,通过这个标识符对业务进行不同的处理. 基本思路 在子组件被监听的组件上绑定@change事件,@change调用传参方法 ...
- vue3异步组件怎么获取ref,获取组件实例?曲线救国的方式。
翻看文档实在没找到异步组件加载成功后的回调,获取ref写个setTimeout延迟个一两秒可以,万一网速慢就GG,所以还是得明确的知道这个组件什么时候下载引入成功的. 目前的思路是把defineAsy ...
- vue3兄弟组件通信-mitt
首先安装mitt cnpm install --save mitt 在本地新建一个js文件mitt.js import mitt from 'mitt'const emitter = mitt() e ...
- Vue3 - 组件通信(父传子)
前言 在 Vue3 中,父组件向子组件传参的方法. 与 Vue2 相比,还是有一些区别的. 基础示例 现在我们的需求是,要通过父组件,传递一个标题来让子组件显示. 子组件 Com.vue: <t ...
- Vue3 组件通信学习笔记
一.父子组件之间通信 父子组件之间如何进行通信呢? 父组件传递给子组件:通过props属性: 子组件传递给父组件:通过$emit触发事件: 1.1 父组件传递给子组件 在开发中很常见的就是父子组件之间 ...
- com组件的ref有时需要有时不需要?_vue 组件通信看这篇就够了(12种通信方式)
点击上方"前端小苑",选择"置顶公众号" 精品技术文章,热门资讯第一时间送达 vue 组件间的通信是 vue 开发中很基础也十分重要的部分,作为使用 vue 的 ...
最新文章
- 算法实现太难了?机器学习也需要开源软件
- OpenAI首次推出数学定理推理模型GPT-f,23个推导结果被专业数据库收录
- 如何借助 svn update 自动更新线上的web
- 我发现一个新的软件,用自然语言编程!非常酷!
- 【动态规划】01背包问题
- 信号与线性系统分析_线性系统与采样定理
- 扎心!七夕刚过“玫瑰花是什么垃圾”就成热搜 支付宝都看不下去了...
- vba 添加outlook 签名_利用VBA发送附件电子邮件
- mybatisplus activerecord之mybatisplus entity XXX Not Found TableInfoCache.错误
- 《数字图像处理 第三版》(冈萨雷斯)——第七章 小波和多分辨率处理
- bus hound usb 调试
- 使用echarts来显示世界地图和全国地图,并且可以下钻层级
- c++ 两个栈实现一个队列
- Ubuntu WPS字体缺失
- usnews 计算机专业排名,2020USNEWS计算机科学专业排名
- 一步步教您搞定讯飞语音识别 | 寻找C站宝藏
- The Oregon Trail 俄勒冈之旅
- java熟人_英语中对各种不同程度关系的朋友是怎么表示的(就像汉语里有泛泛之交、熟人、朋友、死党之类的)?...
- [渝粤教育] 同济大学 外科手术技能教学 参考 资料
- js中文汉字转拼音详细教程