Vue3---组件通信(笔记)

474 阅读4分钟

Vue3的组件通信主要分为以下几种

  • 父子通信(props和自定义事件this.$emit)
  • 非父子组件通信
    • Provide/Inject
      
    • Mitt全局事件总线
      
  • 插槽Slot
  • expose/ref(传的是属性或者方法)
  • attrs(非props属性集合)
  • v-model
  • Vuex 仓库

一、 父子通信(props和自定义事件this.$emit)

在父组件向子组件传值的时候用的是props

通过在组件上注册一些自定义的attribute,父组件给这些attribute赋值,子组件通过props获取attribute的名称来获取对应的值。
props有两种用法,一种是字符串数组props:['attr名称']
一种是对象的用法,对象类型的时候,我们除了获取attribute名称的同时,指定它传递的类型,默认值,是否必须传值等等。
type的类型都可以是什么呢? String、Number、Boolean、Array、Object、Date、Function、Symbol

image.png

子组件向父组件传递数据

在子组件中监听事件,传递数据,this.$emit("事件监听",参数)
父组件中触发事件 <子组件 @事件监听="方法">

父组件:
<template>
  <div>
  //以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中
      <HelloWorld @addnum="add"/>
  <p>父组件的数值为:{{num}}</p>
  <button @click="active">改变num的值</button>
  </div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
  name: 'App',
  components: {
    HelloWorld
  },
  data() {
    return {
      num:0
    }
  },
  methods: {
    add(num){
      this.num=num
    },
    active(){
      this.num = 15
    }
  },
}
</script>

子组件:
<template>
  <div>
    <button @click="btnclick">传值</button>
    <p>子组件中num的值:{{num}}</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      num:10
    }
  },
  methods: {
    btnclick(){
      console.log("传值");
      // 定义好触发事件的名称
      this.$emit("addnum",this.num)
    }
  },
}
</script>

image.png

二、非父子组件通信

provide与inject

官方文档:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。

project和inject主要是用于深度嵌套的时候:
祖先元素给子孙元素传值,不用关心传递者和接收者 image.png
这个时候,使用props来逐级传递可以但是没有必要,所以我们就是用到了provide和inject

image.png 但是使用对象类型的provide的时候,this不是组件实例的this,而是script的this,所以我们参考data数据的类型,使用provide的时候,一律使用函数类型,这样的话,this指的就是组件实例的this

import HelloWorld from './components/HelloWorld.vue'
import {computed} from 'vue';
export default {
  name: 'App',
  components: {
    HelloWorld
  },
  data() {
    return {
      num:18777 
    }
  },
  methods: {
    numactive(){
      this.num = 265
    }
  },
  provide(){
    return{
      name:"康博璇",
    age:18,
    height:165,
    // 这里的this的script中的this,所以将provide写成一个函数
    // this是provide()中的this,这个this就是组件实例
    // 但是,这个num并不是双向变换的,不是响应式的
    // num:this.num
    num: computed(()=>this.num)
    // computed 函数返回的是一个ref对象
    }
  }
}
</script>

Mitt全局事件总线

image.png 在Vue3中,事件总线bus被取消了,如果我们需要继续使用事件总线的话,我们就要通过第三方库mitt
安装库: npm install mitt
封装工具eventbus.js :

image.png 在项目中使用 import emitter from './eventbus'

发送:
<template>
  <div>
    <p>Hello组件</p>
    <button @click="btnhello">Hello</button>
    <p>name:{{age}}</p>
  </div>
</template>

<script>
import emitter from './事件总线的使用/eventbus.js';
export default {
  name: 'HelloWorld',
  methods: {
    btnhello(){
      console.log("Hello中的按钮被点击了");
      //发送事件why,传递参数name
      emitter.emit("why",{name:"kangboxuan"})
      emitter.emit("kebi",{name:"kebi"})
    }
  },
}


接收:
<template>
    <div>
        <p>{{name}}</p>
    </div>
</template>
<script>
import emitter from './eventbus';

export default {
    name: 'Vue3TextHome',
    created(){
        
        emitter.on("why",(name)=>{
            console.log("why",name);
        })
        emitter.on("kebi",(name)=>{
            console.log("kebi",name);
        })
    },
};
</script>




三、插槽slot

抽取共性保留,slot占位不同功能 image.png 在开发中,为了使组件具有更强得复用性,我们通常不会把特定的内容区写死,而是通过slot占位,在不同的调用情况下,由使用它的组件决定这个占位的地方到底显示什么样子的元素
默认插槽,多个插槽,具名插槽,动态插槽,作用域插槽\

1、默认插槽

在组件中通过slot占位,然后使用的时候,可以插入普通内容,HTML元素,组件元素等等,同时在slot中也可以放入默认内容,如果在其他组件中没有插入对应的元素,则显示默认内容:

  <div>
    <slot>
    //如果在使用其他组件的时候,没有插入内容,则会显示这段话,如果有插入的内容则会替换掉这段话
       <p>我是Myslot的默认内容</p>
    </slot>
  </div>
</template>


使用插槽:
<template>
  <div>
    <Myslot>
        我是替换插槽中的内容的
    </Myslot>
  </div>
</template>

<script>
import Myslot from "./components/Myslot.vue";
export default {
  name: "App",
  components: {
    Myslot
  },
};
</script>

2、多个插槽

在组件中可以有多个插槽,如果插槽没有自己的名称的话,默认情况下就会把每一个插槽都替换掉

3、具名插槽

在使用中,通常我们会给一个组件设置多个插槽,且每一个插槽都有它自己的用处,所以我们不可以将插槽写成一样的,那么我们应该如何区分不同的插槽呢?
这个时候就用到了具名插槽,直白说,就是给插槽起名字,在组件中使用插槽的时候,使用对应名字的插槽,就可以让我们把对应的内容插到对应的插槽中了
< slot>元素有一个特殊的 attribute(属性):name;一个不带 name 的slot,会带有隐含的名字default
在使用对应插槽的时候通过v-slot="name的值"来绑定对应的插槽

具名插槽:
<template>
  <div>
    <slot name="slotone">
      <!-- <p>我是Myslot的默认内容</p> -->
    </slot>
  </div>
</template>

使用具名插槽:
<template>
  <div>
    <Myslot>
    //通过v-slot:来绑定具名插槽
      <template v-slot:slotone> 
        我是来替换具名插槽的 
      </template>
    </Myslot>
  </div>
</template>

4、作用域插槽

在Vue中有渲染作用域的概念:
父级模板里的所有内容都是在父级作用域中编译的;
子模板里的所有内容都是在子作用域中编译的;
但是有的时候我们希望把父模板的东西通过插槽传给子模版,会发现是不可以的,因为受到作用域的限制

image.png

四、expose/ref(传的是属性或者方法)

子组件通过 expose暴露自身的方法和数据, 父组件通过 ref 获取到了子组件并调用其方法和数据

五、attrs(非props属性集合)

attrs传的是父传子的未被props接收的属性,包括class,style,id等等属性

image.png

传递数据:
<template>
    <div>
        <Child :title="title" content="哈哈哈哈" 
                class="aaa" kbx="kk"/>
    </div>
</template>

接收数据:
<template>
    <div>
        <p>我是Header的子组件</p>
        <p>{{title}}</p>
        <p>{{content}}</p>
        <div :class="$attrs.class">{{$attrs.class}}</div>
        <p>{{$attrs.kbx}}</p>
    </div>
</template>

<script>
export default {
    name: 'Vue3TextChild',
    //vue官网对于inheritAttrs的属性解释:默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。
    //就是不被继承的意思
    inheritAttrs:false,

    props:{
        title:String,
        content:{
            type:String,
            default:"默认值"
        },
        info:{
            type:Object,
            default(){
                return {name:"kangboxuan"}
            }
        }
    }
};
</script>

image.png

六、v-model

数据双向绑定

七、Vuex

仓库管理