首页 > 其他分享 >vue3组件间通信

vue3组件间通信

时间:2023-05-25 20:56:33浏览次数:44  
标签:money props 间通信 let vue3 组件 import ref

props

  • 父子组件之间通信最好的方式
// 父组件
<template>
  <div class="box">
    <h1>props:这里是父组件</h1>
    <hr />
    <Child :money="money"></Child>
  </div>
</template>

<script setup lang="ts">
//props:可以实现父子组件通信,props数据还是只读的!!!
import Child from "./Child.vue";
import { ref } from "vue";
let money = ref(10000);
</script>

// 子组件
<template>
  <div class="son">
       <h1>我是子组件</h1>
       <p>{{props.money}}</p>
      <!--props可以省略前面的名字--->
       <p>{{money}}</p>
       <button @click="updateProps">修改props数据</button>
  </div>
</template>

<script setup lang="ts">
//需要使用到defineProps方法去接受父组件传递过来的数据
//defineProps是Vue3提供方法,不需要引入直接使用
let props = defineProps(['money']); //数组|对象写法都可以
//按钮点击的回调
const updateProps = ()=>{
  // props.money+=10;  props:只读的
  console.log(props)
}
</script>

自定义事件

  • 父子组件通信
// 父组件
<template>
  <div>
    <button @click="handler1(1,2,3,$event)">点击我传递多个参数</button>
    <hr>
    <!--
        vue2框架当中:这种写法自定义事件,可以通过.native修饰符变为原生DOM事件
        vue3框架下面写法其实即为原生DOM事件
        vue3:原生的DOM事件不管是放在标签身上、组件标签身上都是原生DOM事件
      -->
    <Event1 @click="handler2"></Event1>
    <hr>
    <!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
    <Event2 @xxx="handler3" @click="handler4"></Event2>
  </div>
</template>
<script setup lang="ts">
//引入子组件
import Event1 from './Event1.vue';
//引入子组件
import Event2 from './Event2.vue';
//事件回调--1
const handler = (event)=>{
    //event即为事件对象
    console.log(event);
}
//事件回调--2
const handler1 = (a,b,c,$event)=>{
   console.log(a,b,c,$event)
}
//事件回调---3
const handler2 = ()=>{
    console.log(123);
}
//事件回调---4
const handler3 = (param1,param2)=>{
    console.log(param1,param2);
}
</script>

// 子组件2
<template>
  <div class="child">
    <p>我是子组件2</p>
    <button @click="handler">点击我触发自定义事件xxx</button>
    <button @click="$emit('click','111','222')">点击我触发自定义事件click</button>
  </div>
</template>

<script setup lang="ts">
//利用defineEmits方法返回函数触发自定义事件
//defineEmits方法不需要引入直接使用
let $emit = defineEmits(['xxx','click']);
//按钮点击回调
const handler = () => {
  //第一个参数:事件类型 第二个|三个|N参数即为注入数据
    $emit('xxx','333','444');
};
</script>

全局事件总线

  • 任意组件间通信
// 使用mitt,先封装一个$bus
// bus
//引入mitt插件:mitt一个方法,方法执行会返回bus对象
import mitt from 'mitt';
const $bus = mitt();
export default $bus;

// 组件1
<template>
  <div class="child1">
    <h3>我是子组件child1</h3>
  </div>
</template>

<script setup lang="ts">
import $bus from "../../bus";
//组合式API函数
import { onMounted } from "vue";
//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(() => {
  //第一个参数:即为事件类型  第二个参数:即为事件回调
  $bus.on("child1", (str) => {
    console.log(str);
  });
});
</script>

// 组件2
<template>
  <div class="child2">
     <h2>我是子组件child2</h2>
     <button @click="handler">点击给child1传值</button>
  </div>
</template>

<script setup lang="ts">
//引入$bus对象
import $bus from '../../bus';
//点击按钮回调
const handler = ()=>{
  $bus.emit('child1',"你好");
}
</script>

v-model

  • 本质上还是使用了自定义事件
// v-model原理
// 在原生元素上
<input v-model="text" />

// 等价展开
<input
  :value="text"
  @input="text = $event.target.value"
/>

// 在组件上展开
<Child1
  :modelValue="text"
  @update:modelValue="newValue => text = newValue"
/>

// Child1
<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>

<script setup>
    // 这里只能使用modleValue和upate:modleValue
    defineProps(['modelValue'])
    defineEmits(['update:modelValue'])
</script>

// 指定参数名称
<Child2 v-model:text="text" />

// Child2
<script setup>
    defineProps(['text'])
    defineEmits(['update:text'])
</script>

// 实际上指定参数名称就是在子组件的defineEmits(['update:指定参数'])、defineProps([指定参数])

useAttrs

  • 接收父组件的属性和方法,多用于组件二次封装
// 父组件
<template>
  <div>
    <h1>useAttrs</h1>
    <!-- 自定义组件 -->
    <HintButton type="primary" size="small" :icon="Edit" title="编辑按钮" @click="handler" ></HintButton>
  </div>
</template>

// 子组件
<template>
  <div :title="title">
    <el-button :="$attrs"></el-button>
  </div>
</template>
<script setup lang="ts">
//引入useAttrs方法:获取组件标签身上属性与事件
import { useAttrs } from "vue";
//此方法执行会返回一个对象,里面是传过来的一堆属性与方法
let $attrs = useAttrs();

//万一用props接受title
let props = defineProps(["title"]);
//props与useAttrs方法都可以获取父组件传递过来的属性与属性值
//但是props接受了useAttrs方法就获取不到了
console.log($attrs);
</script>

ref和$parent

  • ref 可以获取真实的DOM节点,可以获取到子组件实例VC
  • $parent 可以在子组件内部获取到父组件的实例
  • 只能获取通过 defineExpose 暴露出来的对象
// 父组件
<template>
  <div class="box">
    <h1>我是父亲曹操:{{money}}</h1>
    <button @click="handler">找我的儿子曹植借10元</button>
    <hr>
    <Son ref="son"></Son>
    <hr>
    <Dau></Dau>
  </div>
</template>

<script setup lang="ts">
//ref:可以获取真实的DOM节点,可以获取到子组件实例VC
//$parent:可以在子组件内部获取到父组件的实例
//引入子组件
import Son from './Son.vue'
import Dau from './Daughter.vue'
import {ref} from 'vue';
//父组件钱数
let money = ref(100000000);
//获取子组件的实例
let son = ref();
//父组件内部按钮点击回调
const handler = ()=>{
   money.value+=10;
   //儿子钱数减去10
   son.value.money-=10;
   son.value.fly();
}
//对外暴露
defineExpose({
   money
})
</script>

// 子组件1
<template>
  <div class="son">
    <h3>我是子组件:曹植{{money}}</h3>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue';
//儿子钱数
let money = ref(666);
const fly = ()=>{
  console.log('我可以飞');
}
//组件内部数据对外关闭的,别人不能访问
//如果想让外部访问需要通过defineExpose方法对外暴露
defineExpose({
  money,
  fly
})
</script>

// 子组件2
<template>
  <div class="dau">
     <h1>我是闺女曹杰{{money}}</h1>
     <button @click="handler($parent)">点击我爸爸给我10000元</button>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue';
//闺女钱数
let money = ref(999999);
//闺女按钮点击回调
const handler = ($parent)=>{
   money.value+=10000;
   $parent.money-=10000;
}
</script>

provide 和 inject

  • 与后代组件通信,provide 注入数据,inject 接收数据
// 祖先组件
<script setup lang="ts">
import Child from "./Child.vue";
//vue3提供provide(提供)与inject(注入),可以实现隔辈组件传递数据
import { ref, provide } from "vue";
let car = ref("法拉利");
//祖先组件给后代组件提供数据
//两个参数:第一个参数就是提供的数据key
//第二个参数:祖先组件提供数据
provide("TOKEN", car);
</script>

// 孙子组件
<script setup lang="ts">
import {inject} from 'vue';
//注入祖先组件提供数据
//需要参数:即为祖先提供数据的key
let car = inject('TOKEN');
const updateCar = ()=>{
   car.value  = '自行车';
}
</script>

pinia

  • vuex 升级版,任意组件间通信

pinia 只有三个核心概念

  • state,存放数据和状态
  • actions,存放数据和状态处理的方法
  • getters,类似于计算属性

store/index.ts

// 创建 pinia 仓库,在 src 根目录下创建 store 文件夹,在 store 中创建 modules 文件夹存放小仓库的数据,在 store 中创建 index.ts 文件进行大仓库的创建
// index.ts
//创建大仓库
import { createPinia } from 'pinia';
//createPinia方法可以用于创建大仓库
let store = createPinia();
//对外暴露,安装仓库
export default store;

store/modules/info.ts

//定义info小仓库
import { defineStore } from "pinia";
//第一个仓库:小仓库名字  第二个参数:小仓库配置对象
//defineStore方法执行会返回一个函数,函数作用就是让组件可以获取到仓库数据
let useInfoStore = defineStore("info", {
    //存储数据:state,使用箭头函数声明会自动推断属性的类型
    state: () => {
        return {
            count: 99,
            arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        }
    },
    actions: {
        //注意:函数没有context上下文对象
        //没有commit、没有mutations去修改数据
        updateNum(a: number, b: number) {
            this.count += a;
        }
    },
    getters: {
        // 数组求和
        total() {
            let result:any = this.arr.reduce((prev: number, next: number) => {
                return prev + next;
            }, 0);
            return result;
        }
    }
});
//对外暴露方法
export default useInfoStore;

store/modules/todo.ts

//定义组合式API仓库,有点类似于自定义 hook 的感觉了
import { defineStore } from "pinia";
import { ref, computed,watch} from 'vue';
//创建小仓库
let useTodoStore = defineStore('todo', () => {
    // 声明响应式变量
    let todos = ref([{ id: 1, title: '吃饭' }, { id: 2, title: '睡觉' }, { id: 3, title: '打豆豆' }]);
    let arr = ref([1,2,3,4,5]);
    
    // 使用计算属性
    const total = computed(() => {
        return arr.value.reduce((prev, next) => {
            return prev + next;
        }, 0)
    })
    //务必要返回一个对象:属性与方法可以提供给组件使用
    return {
        todos,
        arr,
        total,
        updateTodo() {
            todos.value.push({ id: 4, title: '组合式API方法' });
        }
    }
});

export default useTodoStore;

组件1

<template>
  <div class="child">
    // 函数.属性获取store的属性值
    <h1>{{ infoStore.count }}---{{infoStore.total}}</h1>
    <button @click="updateCount">点击我修改仓库数据</button>
  </div>
</template>

<script setup lang="ts">
import useInfoStore from "../../store/modules/info";
//获取小仓库对象,defineStore 返回的是一个函数,所以需要调用函数的方式来调用 store
let infoStore = useInfoStore();
console.log(infoStore);
//修改数据方法
const updateCount = () => {
  //仓库调用自身的方法去修改仓库的数据,函数.方法调用方法
  infoStore.updateNum(66,77);
};
</script>

插槽

  • 也是父子组件通信的方式
// 组件1
    <Test>
      <div>
        <pre>默认</pre>
      </div>
      <!-- 具名插槽填充a -->
      <template #a>
        <div>我是填充具名插槽a位置结构</div>
      </template>
      <!-- 具名插槽填充b v-slot指令可以简化为# -->
      <template #b>
        <div>我是填充具名插槽b位置结构</div>
      </template>
    </Test>
// 默认插槽和具名插槽
<template>
  <div class="box">
    <h1>我是子组件默认插槽</h1>
    <!-- 默认插槽 -->
    <slot></slot>
    <h1>我是子组件默认插槽</h1>
    <h1>具名插槽填充数据</h1>
    <slot name="a"></slot>
    <h1>具名插槽填充数据</h1>
    <h1>具名插槽填充数据</h1>
    <slot name="b"></slot>
    <h1>具名插槽填充数据</h1>
  </div>
</template>
// 组件2
// 根据子组件传过来的值 done 来决定展示的颜色
    <Test1 :todos="todos">
      <template v-slot="{ $row, $index }">
        <p :style="{ color: $row.done ? 'green' : 'red' }">
          {{ $row.title }}--{{ $index }}
        </p>
      </template>
    </Test1>
// 作用域插槽
<template>
  <div class="box">
    <h1>作用域插槽</h1>
    <ul>
      <li v-for="(item, index) in todos" :key="item.id">
        <!--作用域插槽:可以讲数据回传给父组件-->
        <slot :$row="item" :$index="index"></slot>
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
//通过props接受父组件传递数据
defineProps(["todos"]);
</script>

标签:money,props,间通信,let,vue3,组件,import,ref
From: https://www.cnblogs.com/wj-goodgoodstudy/p/17430387.html

相关文章

  • 使用form.ModelForm组件注意点
    定义类时是使用model=table中表名如下:classTplModelForm(BootStrapModelForm):classMeta:model=models.Templatefields="__all__"而在函数中定义时是使用form如下deftpl(request):ifrequest.method=="GET":form=TplModelForm() ......
  • 云原生第四周--kubernetes组件详解(下)
    ConfigmapConfigMap是一种API对象,用来将非机密性的数据保存到键值对中。使用时,Pods可以将其用作环境变量、命令行参数或者存储卷中的配置文件。ConfigMap将你的环境配置信息和容器镜像解耦,便于应用配置的修改。使用场景:通过Configmap给pod定义全局环境变量通过Confi......
  • UI组件
    DevExpressGrapeCityInfragisticsRadzenSmartHTMLElementsSyncfusionTelerik......
  • vue3+vite build打包删除控制台打印
    很简单,直接在vite.config.ts里面添加配置即可:exportdefault({mode})=>defineConfig({build:{minify:"terser",terserOptions:{compress:{//生产环境时移除consoledrop_console:true,drop_debugger:true,......
  • 【React工作记录六十五】ant design子组件渲染不能及时渲染
     目录前言导语核心代码总结前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷导语antdesign子组件......
  • Vue重用组件
    1、是什么?这里主要是简单入门使用一下,复杂高阶的用法笔者暂时还没了解到Vue重用组件是指可以被多个Vue实例重复使用的组件。这些组件可以包含自定义的状态和事件处理程序,并且可以在整个应用程序中共享。重用组件可以减少代码冗余,提高开发效率,并使代码更加模块化和可维护。在Vue......
  • drf-视图组件
    目录一视图之两个视图基类1.1APIView使用APIView+序列化类+Response写接口1.2GenericAPIView[通用视图类]1.2.1主要的属性或方法1.2.2推导GenericAPIView的封装1.2.3写5个接口二5个视图扩展类2.1方法2.2自己封装2.3源码2.4具体使用,写5个接口三9个视图子类3.1介绍1)Cre......
  • drf-路由组件
    目录一路由Routers1.1什么情况下使用自动生成路由1.2自动生成路由使用方法1.3代码演示视图函数路由一路由Routers路由写法有多种-原始写法-映射的写法:path('books/',BookView.as_view({'get':'list','post':'create'}))-自动生成路由1.1什么情况下使用自动生成路......
  • drf之登录功能,认证组件,权限组件,频率组件
    目录一、登录功能表模型视图类回顾路由二、认证组件认证组件使用步骤(固定用法)三、权限组件权限类的使用步骤四、频率组件频率类的使用步骤一、登录功能表模型classUserInfo(models.Model):name=models.CharField(max_length=32)password=models.CharField(max_......
  • django配置文件作用,drf 登录功能,drf认证组件,drf权限组件,drf频率组件
    django配置文件作用:    drf登录功能:view内:from.modelsimportUserInfo,UserTokenfromrest_framework.viewsetsimportViewSetimportuuidfromrest_framework.responseimportResponsefromrest_framework.decoratorsimportactionclassUser......