[vue3] Notas de estudio: método de comunicación de componentes

Aprender vue3 siempre es inseparable de vue2

Resumen de los métodos de comunicación de los componentes de vue2:

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

Resumen de los métodos de comunicación de los componentes de vue3:

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

El método de comunicación de los componentes vue3 es el siguiente:

1. Elementos de comunicación entre padres e hijos

Los datos de accesorios son de solo lectura, se pasan del componente principal al componente secundario y no se pueden cambiar directamente dentro del componente secundario.
El componente secundario debe usar el método defineProps para aceptar los datos pasados ​​del componente principal.

componente padre

<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>

Subensamblaje

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

2. Paso de valor de evento personalizado

Hay dos tipos de eventos en el marco vue: eventos DOM nativos y eventos personalizados. Los eventos DOM originales permiten a los usuarios interactuar con páginas web, como: clic, dbclick, mouseenter, mouseleave, etc. Los eventos personalizados pueden implementar componentes secundarios para pasar datos a los componentes principales
.

Si el evento DOM nativo se usa directamente en la etiqueta del componente principal, se puede cambiar a un evento DOM nativo a través del modificador .native en vue2, y es un evento nativo en vue3, que es equivalente al evento vinculado al nodo raíz del componente secundario Cualquier contenido en el componente secundario puede desencadenar este evento.

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

No hay esto en vue3, no hay instancia de componente, use el método defineEmits para devolver una función para activar un
componente principal de evento personalizado

<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>

Subensamblaje

<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. Valor de aprobación del componente Brother

En el marco vue3, no hay un constructor vue, lo que da como resultado que no haya VM, ni objeto prototipo, y la configuración del método de escritura de API combinado no tiene esto, y el complemento mitt se puede usar para implementar la función de bus de eventos global.
dirección del sitio web oficial del guante

npm install --save mitt

Cree una nueva carpeta de bus en el directorio src, en el archivo index.ts

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

emit desencadena un evento, on recibe parámetros

Subcomponente 1

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

inserte la descripción de la imagen aquí
Subcomponente 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. comunicación de componente padre-hijo v-model

La comunicación de componentes padre-hijo no es posible en vue2, y vue3 es equivalente a hacer el trabajo de accesorios y emitir al mismo tiempo
inserte la descripción de la imagen aquí

5. método useAttrs

El método useAttrs puede obtener los atributos y eventos en la etiqueta del componente.
Si usa accesorios al mismo tiempo, la prioridad de los accesorios es mayor que la de useAttrs. Los accesorios no pueden obtener el método useAttrs después de aceptar el método useAttrs.
El método useAttrs no solo puede obtener los eventos nativos en la etiqueta del componente, sino también los eventos personalizados.

El componente principal, el componente principal no necesita pasar valores al componente secundario, el tipo, el tamaño, el ícono, el evento nativo y el evento personalizado en la etiqueta del componente se pueden obtener a través del método useAttrs

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

Subensamblaje

<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();

Si recibe título con accesorios

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

No se pueden obtener accesorios después de aceptar el método useAttrs.
inserte la descripción de la imagen aquí
Tanto los eventos nativos como los eventos personalizados se pueden obtener a través del método useAttrs.
inserte la descripción de la imagen aquí

6. ref y $padre

ref obtiene el nodo DOM real, puede obtener la instancia del componente secundario VC
$parent puede obtener la instancia del componente principal dentro del componente secundario

1. ref
obtiene la instancia del componente secundario a través de ref en el componente principal

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

Después de obtener la instancia del subcomponente, los datos internos del componente se cierran al mundo exterior y otros no pueden acceder a ellos. Si desea acceso externo, debe exponerlo al mundo exterior a través del método defineExpose. En el
subcomponente

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

})

Después de la exposición, puede cambiar los datos del componente secundario en el
componente principal .

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

2. $parent
está dentro del subcomponente, y $parent se pasa al parámetro del evento de clic (método de escritura fijo)

Subensamblaje

<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;
}

Imprimir $parent
inserte la descripción de la imagen aquí
no obtiene los datos del componente principal, por lo que también debe exponerse en el componente principal
.

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

En este momento, imprima de nuevo y habrá datos
inserte la descripción de la imagen aquí

7. proporcionar e inyectar

Puede realizar la comunicación de los componentes de próxima generación, proporcionar (proporcionar), inyectar (inyectar)
1. Componentes de antepasados: los componentes de antepasados ​​proporcionan datos a los componentes de descendientes

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

El método provide tiene dos parámetros, clave y valor.

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

2. Se pueden usar los componentes descendientes, subcomponentes/componentes nietos y se puede modificar el valor

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

8. API de selección de pinia

Pinia en vue3 es una piña grande. Mirando una imagen de dibujos animados tan linda, parece que aprender el conocimiento de pinia no es tan aburrido.
Pinia Documento chino
inserte la descripción de la imagen aquí
pinia es similar a vuex en vue2
vuex: contenedor de estado de gestión centralizada, que puede realizar la comunicación entre cualquier componente
Conceptos básicos: estado, mutaciones, acciones, captadores, módulos
pinia: contenedor de estado de gestión centralizada, puede realizar la comunicación entre cualquier componente
Conceptos básicos: estado, acciones, captadores

1. Cree una nueva carpeta de almacenamiento en el directorio src, cree un nuevo index.ts y archivos de módulos, y el directorio es el siguiente:
inserte la descripción de la imagen aquí
index.ts

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

Introducir y usar en main.ts

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

Después de que la introducción sea exitosa, puede ver
inserte la descripción de la imagen aquí
el método de escritura pinia: API selectiva y API combinada
1. La API selectiva define un pequeño almacén en módulos, información

//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;

Usado en componentes, cuando los datos cambian, los datos en todos los componentes usados ​​cambiarán juntos

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

inserte la descripción de la imagen aquí
cambiar los datos

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

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

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

inserte la descripción de la imagen aquí
2. API combinada, definir pequeños almacenes en módulos, tareas pendientes

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

utilizado en el componente

  <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. Ranura

Hay tres tipos de ranuras: ranuras predeterminadas, ranuras con nombre y ranuras con ámbito

La ranura predeterminada, la ranura se coloca en el componente secundario y cualquier contenido agregado en la etiqueta del componente secundario del componente principal se mostrará en la posición de la ranura

1.
Subcomponente de ranura predeterminado:

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

componente padre

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

inserte la descripción de la imagen aquí
2. Ranura con nombre Una ranura con nombre
es una ranura con un nombre. El componente secundario mantendrá el agujero y el componente principal agregará contenido de acuerdo con el nombre de la ranura. El
método de escritura de la ranura en V es equivalente a #, y la ranura en V = a es equivalente a #a.

Subensamblaje

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

componente padre

<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>

inserte la descripción de la imagen aquí
3.
Ranuras de alcance Me tomó un poco más de tiempo entender las ranuras de alcance. Parece ser un poco diferente de la comprensión anterior de vue2. También puede ser que no presté especial atención a la implementación de las ranuras de alcance cuando usaba vue2.

Las ranuras con ámbito son ranuras que pueden pasar datos y los componentes secundarios pueden devolver datos al componente principal. El componente principal puede determinar la estructura o apariencia de los datos devueltos que se mostrarán dentro del componente secundario.

Por ejemplo, cuando encapsulamos un componente de lista con algunos cuadros de entrada de consulta o cuadros desplegables, una página de llamada necesita dos condiciones de consulta: nombre de usuario y departamento de usuario, y una página de llamada necesita dos condiciones de consulta: nombre de producto y número de producto. Excepto por diferentes condiciones de consulta, todo lo demás es igual. Nos es imposible encapsular muchos subcomponentes, por lo que no tiene sentido reutilizar. En este momento, la ranura del alcance se puede usar para pasar los datos al componente secundario en la página principal, y el componente secundario pasará los datos al componente principal a través de la ranura del alcance, y la etiqueta del componente secundario del componente principal se mostrará dentro de la etiqueta de la plantilla, para mostrar diferentes condiciones de consulta.

El componente principal pasa la matriz de todos al componente secundario

    <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 },
]);

Los componentes secundarios reciben y usan internamente

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

Cuando comenzamos a usar espacios con ámbito dentro de los subcomponentes, el objeto corresponde a la clave y el valor, $fila y $índice son equivalentes a la clave que definimos, seguidos del valor correspondiente.

<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>

Datos devueltos usando ranuras dentro de la etiqueta del componente secundario del componente principal

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

Puede haber dudas, por qué es tan complicado de escribir, tiene que pasarse desde el componente principal y luego pasarse. Si la página que llama de a necesita juzgar los dos colores verde y rojo, y la página que llama de b necesita el juicio de púrpura y azul, o incluso las páginas que llaman como c y d necesitan otros colores. Incluso las etiquetas envueltas necesitan etiquetas p, que necesitan etiquetas div, e incluso otras apariencias o estructuras que requieren diferentes visualizaciones

Tal vez a lo que estamos acostumbrados es a pasar una variable del componente principal al componente secundario y usar v-if, v-else-if para juzgar diferentes condiciones dentro del componente secundario. Puede usar cualquiera de los dos si no es muy complicado, y puede considerar slots con ámbito para los complicados.No es necesario usar un método de implementación más.

Supongo que te gusta

Origin blog.csdn.net/weixin_49668076/article/details/131498874
Recomendado
Clasificación