[vue3] Study Notes--Component Communication Method

Learning vue3 is always inseparable from vue2

Summary of communication methods of vue2 components:

1、props:可以实现父子组件,子父组件,甚至兄弟组件通信
2、自定义事件:实现子父组件通信
3、全局事件总线$bus:可以实现任意组件通信
4、pubsub:发布订阅模式实现任意组件通信
5、vuex: 集中式状态管理容器,实现任意组件通信
6、ref:父组件获取子组件实例VC,获取子组件的响应式数据及方法
7、slot:插槽(默认插槽,具名插槽,作用域插槽),实现父子组件通信

Summary of communication methods of vue3 components:

1、props
2、自定义事件
3、全局事件总线
4、v-model
5、useAttrs方法
6、ref与$parent
7、provide与inject
8、pinia组合式API
9、插槽

The communication method of vue3 components is as follows:

1. Parent-child component communication props

The props data is read-only, passed from the parent component to the child component, and cannot be directly changed inside the child component.
The child component needs to use the defineProps method to accept the data passed from the parent component.

parent component

<template>
  <div class="box">
    <Child  :msg="msg"></Child>
  </div>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
import {
    
     ref } from "vue";
let msg= ref(10000);
</script>

Subassembly

<template>
  <div class="son">  
       <p>{
    
    {
    
    msg}}</p>
  </div>
</template>
<script setup lang="ts">
let props = defineProps(['msg']); //数组|对象写法都可以
</script>

2. Custom event value passing

There are two types of events in the vue framework: native DOM events and custom events. The original DOM events allow users to interact with web pages, such as: click, dbclick, mouseenter, mouseleave, etc. Custom events can implement child components to pass data to parent
components

If the native DOM event is used directly on the component tag of the parent component, it can be changed to a native DOM event through the .native modifier in vue2, and it is a native event in vue3, which is equivalent to the event bound to the root node of the child component. Any content in the child component can trigger this event.

<Event1 @click="handler2"></Event1>

There is no this in vue3, no component instance, use the defineEmits method to return a function to trigger a custom event
parent component

<template>
  <div>
   <!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
    <Event2 @xxx="handler3"  @click="handler4"></Event2>
  </div>
</template>
<script setup lang="ts">
//引入子组件
import Event2 from './Event2.vue';
const handler3 = (param1,param2)=>{
    
    
    console.log(param1,param2);
}
const handler4 = (param1,param2)=>{
    
    
     console.log(param1,param2);
}
</script>

Subassembly

<template>
  <div class="child">
    <p>我是子组件2</p>
    <button @click="handler">点击我触发自定义事件xxx</button>
    //直接在模板区触发,当在子组件中定义事件名为click,在父组件中即为自定义事件,不再是原生DOM事件
    <button @click="$emit('click','AK47','J20')">点击我触发自定义事件click</button>
  </div>
</template>

<script setup lang="ts">
let $emit = defineEmits(['xxx','click']);
//按钮点击回调
const handler = () => {
    
    
  //第一个参数:事件类型 第二个|三个|N参数即为注入数据
    $emit('xxx','东风导弹','航母');
};
</script>

3. Brother component pass value

In the vue3 framework, there is no vue constructor, resulting in no VM, no prototype object, and the combined API writing method setup does not have this, and the plug-in mitt can be used to implement the global event bus function.
mitt official website address

npm install --save mitt

Create a new bus folder in the src directory – in the index.ts file

//引入mitt插件:mitt一个方法,方法执行会返回bus对象
import mitt from 'mitt';
const $bus = mitt();
export default $bus;

emit triggers an event, on receives parameters

Subcomponent 1

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

insert image description here
Subcomponent 2

<template>
  <div class="child2">
     <button @click="handler">赠送车</button>
  </div>
</template>

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

4. v-model parent-child component communication

Parent-child component communication is not possible in vue2, and vue3 is equivalent to doing the work of props and emit at the same time
insert image description here

5. useAttrs method

The useAttrs method can get the attributes and events on the component tag.
If you use props at the same time, the priority of props is higher than useAttrs. The props cannot get the useAttrs method after accepting the useAttrs method.
The useAttrs method can not only get the native events on the component tag, but also the custom events.

The parent component, the parent component does not need to pass values ​​to the child component, the type, size, icon, native event and custom event on the component tag can be obtained through the useAttrs method

<template>
  <div>
    <!-- 自定义组件 -->
    <HintButton type="primary" size="small" :icon="Edit" title="编辑按钮" @click="handler" @xxx="handler"></HintButton>
  </div>
</template>

Subassembly

<template>
  <div :title="title">
  //对象格式:{
    
    type:'primary',size:'small',icon:'Edit',title:'编辑按钮'}
     <el-button :="$attrs"></el-button>   
  </div>
</template>
//引入useAttrs方法:获取组件标签身上属性与事件
import {
    
    useAttrs} from 'vue';
//此方法执行会返回一个对象
let $attrs = useAttrs();

If you receive title with props

let props =defineProps(['title']);

Props cannot be obtained after accepting the useAttrs method.
insert image description here
Both native events and custom events can be obtained through the useAttrs method.
insert image description here

6. ref and $parent

ref gets the real DOM node, you can get the instance of the child component VC
$parent can get the instance of the parent component inside the child component

1. ref
gets the instance of the child component through ref in the parent component

<template>
  <div class="box">
    <Son ref="son"></Son>
  </div>
</template>
import {
    
    ref} from 'vue';
let money = ref(100000000);
//获取子组件的实例,与组件标签上的ref同名,不然拿不到实例
let son = ref();

After getting the instance of the subcomponent, the internal data of the component is closed to the outside world and cannot be accessed by others. If you want external access, you need to expose it to the outside world through the defineExpose method. In the
subcomponent

import {
    
    ref} from 'vue';
//子组件钱数
let money = ref(666);
defineExpose({
    
    
  money,

})

After exposure, you can change the data of the child component in the parent component.
Parent component

  <button @click="handler">修改</button>
  //父组件内部按钮点击回调
const handler = ()=>{
    
    
   //子组件钱数减去10
   son.value.money-=10;
 
}

2. $parent
is inside the subcomponent, and $parent is passed into the parameter of the click event, (fixed writing method)

Subassembly

<template>
  <div class="dau">  
     <button @click="handler($parent)">点击我</button>
  </div>
</template>
import {
    
    ref} from 'vue';
//子组件钱数
let money = ref(999999);
//子组件按钮点击回调
const handler = ($parent)=>{
    
    
   money.value+=10000;
   $parent.money-=10000;
}

Printing $parent
insert image description here
does not get the data of the parent component, so it needs to be exposed in the parent component as well.
Parent component

//对外暴露
defineExpose({
    
    
   money
})

At this time, print again, and there will be data
insert image description here

7. provide and inject

It can realize the communication of next-generation components, provide (provide), inject (inject)
1. Ancestor components: ancestor components provide data to descendant components

import {
    
     ref, provide } from "vue";
let car = ref("法拉利");

The provide method has two parameters, key and value

//两个参数:第一个参数就是提供的数据key
//第二个参数:祖先组件提供数据
provide("getData", car);

2. The descendant components, subcomponents/grandchildren components can be used and the value can be modified

 <button @click="updateCar">更新数据</button>
import {
    
    inject} from 'vue';
//注入祖先组件提供数据
//需要参数:即为祖先提供数据的key
let car = inject('getData');
const updateCar = ()=>{
    
    
   car.value  = '自行车';
}

8. pinia selection API

Pinia in vue3 is a big pineapple. Looking at such a cute cartoon image, it seems that learning the knowledge of pinia is not so boring.
Pinia Chinese document
insert image description here
pinia is similar to vuex in vue2
vuex: centralized management state container, which can realize communication between any components
Core concepts: state, mutations, actions, getters, modules
pinia: centralized management state container, can realize communication between any components
Core concepts: state, actions, getters

1. Create a new store folder under the src directory, create a new index.ts and modules files, and the directory is as follows:
insert image description here
index.ts

//创建大仓库
import {
    
     createPinia } from 'pinia';
//createPinia方法可以用于创建大仓库
let store = createPinia();
//对外暴露,安装仓库
export default store;

Introduce and use in main.ts

//引入仓库
import store from './store'
app.use(store)

After the introduction is successful, you can see
insert image description here
the pinia writing method: selective API and combined API
1. Selective API defines a small warehouse in modules, info

//defineStore 用于定义小仓库,从pinia中获取
//定义info小仓库
import {
    
     defineStore } from "pinia";
//defineStore需要传递两个参数,第一个参数:小仓库名字  第二个参数:小仓库配置对象
//defineStore方法执行会返回一个函数,函数作用就是让组件可以获取到仓库数据
let useInfoStore = defineStore("info", {
    
    
    //存储数据:state,vuex中state是对象写法,pinia中是函数写法,函数返回的对象才是给组件提供的数据
    state: () => {
    
    
        return {
    
    
            count: 99,
            arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        }
    },
    actions: {
    
    
    //vuex中commit提交,mutation去修改,pinia中没有mutation,直接在action中修改
        //注意:函数没有context上下文对象,pinia中使用this获取state中的数据
        //没有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;

Used in components, when the data changes, the data in all used components will change together

import useInfoStore from "../../store/modules/info";
//获取小仓库对象
let infoStore = useInfoStore();
console.log(infoStore);

insert image description here
change the data

  <button @click="updateCount">点击我修改仓库数据</button>
  

```bash
//修改数据方法
const updateCount = () => {
    
    
//可以直接这么写:
//infoStore.count++
//用$patch方法用一个新的对象替换原来的对象
infoStore.$dispath({
    
    
count:1111
})

  //仓库调用自身的方法去修改仓库的数据,在info.ts的actions中定义这个方法
  infoStore.updateNum(66,77);
};

insert image description here
2. Combined API, define small warehouses in modules, todo

//定义组合式API仓库
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;

used in the component

  <p @click="updateTodo">{
    
    {
    
     todoStore.arr }}{
    
    {
    
    todoStore.total}}</p>
import useInfoStore from "../../store/modules/info";
//获取小仓库对象
let infoStore = useInfoStore();

//引入组合式API函数仓库
import useTodoStore from "../../store/modules/todo";
let todoStore = useTodoStore();

//点击p段落去修改仓库的数据
const updateTodo = () => {
    
    
  todoStore.updateTodo();
};

9. Slot

There are three types of slots: default slots, named slots, and scoped slots

The default slot, slot is placed in the child component, and any content added in the child component label of the parent component will be displayed at the slot position

1. Default slot
subcomponent:

<template>
  <div class="box">
    <h1>我是子组件默认插槽</h1>
    <!-- 默认插槽 -->
    <slot></slot> 
  </div>
</template>

parent component

<template>
  <div class="box">
<Test>
      <div>
        <pre>大江东去浪淘尽,千古分流人物</pre>
      </div>
    </Test>
      </div>
</template>

insert image description here
2. Named slot A named
slot is a slot with a name. The child component will keep the hole, and the parent component will add content according to the slot name. The
writing method of v-slot is equivalent to #, and v-slot=a is equivalent to #a

Subassembly

<template>
  <div class="box">
    <h1>具名插槽填充数据a</h1>
    <slot name="a"></slot>
    <h1>具名插槽填充数据b</h1>
    <slot name="b"></slot>
    <h1>具名插槽填充数据</h1>
  </div>
</template>

parent component

<template>
  <div>
    <h1>slot</h1>
    <Test1 :todos="todos">
      <template v-slot="{ $row, $index }">
        <p :style="{ color: $row.done ? 'green' : 'red' }">
          {
    
    {
    
     $row.title }}--{
    
    {
    
     $index }}
        </p>
      </template>
    </Test1>
    <Test>    
      <!-- 具名插槽填充a -->
      <template #a>
        <div>我是填充具名插槽a位置结构</div>
      </template>
      <!-- 具名插槽填充b v-slot指令可以简化为# -->
      <template #b>
        <div>我是填充具名插槽b位置结构</div>
      </template>
    </Test>
  </div>
</template>

insert image description here
3. Scope
slots It took me a little more time to understand scope slots. It seems to be a bit different from the previous understanding of vue2. It may also be that I did not pay special attention to the implementation of scope slots when using vue2.

Scoped slots are slots that can pass data, and child components can pass data back to the parent component. The parent component can determine the structure or appearance of the returned data to be displayed inside the child component.

For example, when we encapsulate a list component with some query input boxes or drop-down boxes, a call page needs two query conditions: user name and user department, and b call page needs two query conditions: product name and product number. Except for different query conditions, everything else is the same. It is impossible for us to encapsulate many subcomponents, so there is no meaning of reuse. At this time, the scope slot can be used to pass the data to the child component on the parent page, and the child component will pass the data back to the parent component through the scope slot, and the child component tag of the parent component will be displayed inside the template tag, so as to display different query conditions.

The parent component passes the todos array to the child component

    <Test1 :todos="todos">
    </Test1>
import {
    
     ref } from "vue";
//todos数据
let todos = ref([
  {
    
     id: 1, title: "吃饭", done: true },
  {
    
     id: 2, title: "睡觉", done: false },
  {
    
     id: 3, title: "打豆豆", done: true },
  {
    
     id: 4, title: "打游戏", done: false },
]);

Child components receive and use internally

<template>
  <div class="box">
    <ul>
      <li v-for="(item, index) in todos" :key="item.id">     
      </li>
    </ul>
  </div>
</template>
defineProps(["todos"]);

When we start using scoped slots inside subcomponents, the object corresponds to key and value, $row and $index are equivalent to the key we defined, followed by the corresponding value.

<template>
  <div class="box">
    <ul>
      <li v-for="(item, index) in todos" :key="item.id">
        <slot :$row="item" :$index="index"></slot>
      </li>
    </ul>
  </div>
</template>

Data returned using slots inside the parent component's child component tag

<Test1 :todos="todos">
      <template v-slot="{ $row, $index }">
        <p :style="{ color: $row.done ? 'green' : 'red' }">
          {
    
    {
    
     $row.title }}--{
    
    {
    
     $index }}
        </p>
      </template>
    </Test1>

There may be doubts, why it is so complicated to write, it has to be passed in from the parent component, and then passed out. If the calling page of a needs to judge the two colors of green and red, and the calling page of b needs the judgment of purple and blue, or even calling pages such as c and d need other colors. Even the wrapped tags need p tags, that needs div tags, and even other appearances or structures that require different displays

Maybe what we are used to is to pass a variable from the parent component to the child component, and use v-if, v-else-if to judge different conditions inside the child component. You can use either one if it is not very complicated, and you can consider scoped slots for complicated ones. It is not necessary to use one more implementation method.

Guess you like

Origin blog.csdn.net/weixin_49668076/article/details/131498874