手记

Vue组件通信应知必知

前言

本章我们来学习Vue组件通信中的可以算是所有内容,在此之前,您最好掌握Vue的基础语法、指令等内容,同时也建议您查看我其他的文章进行补充。

组件通信

父子组件关系


通过上图顺带给大家说明了父子组件的实现原理,以及组件间传值传DOM的实现思路,那么我们看看Vue的代码来感受一下

父向子传值

模板部分(此处传值也能使用组件内的变量)

<div id="app">
    	<!-- 传递一个字符串常量haha -->
         <son v-bind:text="'haha'"/>
</div>

js部分

        // 子组件
        var Son = {
            // 要接收的字段名称
            props:['text'], 
            template:`
                <div>
                    {{ text }}
                </div>`
        };
        Vue.component('son',Son);
        // 父组件
        new Vue({
            el:'#app'
        });

显示结果很显然是子组件的haha

子向父通信

试想一种情况,由父组件控制子组件的显示,而从子组件中点击X来实现子组件的隐藏,那么实际的控制权确实在父组件

要处理这个问题,大家第一反应应该就是相当通过子组件的点击事件,拿到父组件内控制显示与隐藏的变量并更改就可以了

代码如下

// 隐藏按钮点击函数
methods:{
	clickChild(){
        this.$parent.isShow = false;
    }
}

实现是对的,不过

在框架中,我们最好抛开以前的DOM思想,否则我们会无法体会到框架带给我们的思想,而永远沉寂在面向过程中。。。

事件通信机制

在Vue中我们用@xxx来给原生DOM元素绑定事件,比如:click、input等。。

那么同时,我们也可以给子组件绑定自定义事件,不论他叫什么名字。举例:

<!-- 父组件 -->
<son v-show="isShow" @callme="callmeFn"/>
// 父组件js
methods:{
	callmeFn(params){
		this.isShow = false;
    }
}

接下来子组件只用触发这个事件就好了,从低耦合的角度来说,是不是父子各玩各的呢?

// 子组件点击函数中,触发父组件为其绑定的事件
this.$emit('callme','还可以传递参数')

子向父值同步扩展

对于刚才的例子,我们实际上无需父组件和子组件函数的执行,只需要在子组件点击时同步父组件的isShow的值即可,那么可以这样写

<!-- 父组件内 -->
<son v-show="isShow" :isShow.sync="isShow"/>

从子组件中的代码你可以看出原理还是基于事件

// 子组件内
this.$emit('update:isShow','value')

通过Vue提供的事件对象来实现多层级别的组件通信

要做的只有类似这样3步

  • 创建一个共同通信的对象
let connector = new Vue();
  • 在要触发事件的函数中注册事件
connector.$on('事件名',function(params){ });
  • 在目标事件中触发以上事件
connector.$emit('事件名','value');

事件原理实现

大家对于on,emit可能已经看得比较痛苦了,有懵逼的吧?那话不多说,直接上源码实现,相信会让你感觉好一点

	var store = {
			once:{},
			fns:{},
			on(key,fn,isOnce){
				this.fns[key]?this.fns[key].push(fn):this.fns[key] = [fn];
			},
			emit(key,value){
				let t = this.fns[key];
				let o = this.once[key];
				if (!t)return;
				t.forEach(fn=>{
					fn(value);
				});
				if (o)	this.off(key);
			},
			off(key) {
					delete this.fns[key];
			},
			once(key,fn){
				this.once[key] = true;
				this.on(key,fn);
			}
		}

原理其实就是一个对象用key存储着事件名,用value存储着函数!

传递DOM元素

看下面的功能,9宫格的6个,看看这些组件样子一样却又各不相同,因此,我们可以尝试由父组件给他们不同的DOM来体现他们的不同,子组件只需要做一些对他们公共特点的编写即可

如果子元素的大部分所有内容由父元素来定制,子组件内尽量单一、简单,该组件就算是低耦合、高内聚了。

那么子组件中给父组件留下的内容区域如何来实现呢? 答案是:slot(插槽)

slot实现步骤

  1. 在子组件中留坑
<!-- 子组件 -->
<div>
    <!--一个默认的坑-->
    <slot></slot>
</div>
  1. 在父组件中传入DOM
<!-- 父组件 -->
<!-- 使用子组件 -->
<son>
	<!--传递的DOM-->
    <template v-slot:default>
        <button>给子的按钮</button>
    </template>
</son>
  1. 当然也可以具名传递
<!-- 子组件 -->
<div>
    <!-- 定制上半部分 -->
    <slot name="head"></slot>
    <!-- 定制下半部分 -->
    <slot name="foot"></slot>
</div>
  1. 父组件也需要说清楚名字
<!-- 父组件 -->
<!-- 使用子组件 -->
<son>
	<!--传递的DOM-->
    <template v-slot:head>
        <button>给头的按钮</button>
    </template>
     <template v-slot:foot>
        <button>给底部的按钮</button>
    </template>
</son>

最后说说一般人说不清楚的内容

思路整理: 这一组ul+li我们把他想象成一个组件,他接收一个数组,通过v-for帮我们渲染,数据如下

// 父组件数据
todos: [
    	{
            id: 0,
            text: 'ziwei0',
            isComplete: true
        },
        {
            text: 'ziwei1',
            id: 1,
            isComplete: true
        },
        {
            text: 'ziwei2',
            id: 2,
            isComplete: false
        },
        {
            text: 'ziwei3',
            id: 3,
            isComplete: false
        }
];

父组件把该对象传递给子组件

<son :todos="todos"></son>

剩下的就交给子组件,让它来处理具体的业务吧! 对吗?

不是说好了,让子组件功能尽量单一,高内聚、低耦合的么,它都处理业务了,还谈啥公共组件呢,复用性不高,pass!

因此我们将业务判断功能留给父组,子组件这么写

 <!--子组件-->
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      <slot>
      </slot>
    </li>
  </ul>

那么问题来了,父组件是不是要传递DOM作为slot的值呢?

	<!--父组件-->
	<son :todos="todos">
        <template v-slot:default>
            <span></span>
            <span>内容</span>
        </template>
	</son>

糟糕,父组件把todos都传递进去了,可现在要通过每一个todo来做业务处理啊,看张图

因此,我们使用2.6的新功能slotProps,看slot 传值

 <!--子组件-->
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      <slot :todo="todo">
      </slot>
    </li>
  </ul>

看父组件如何接收,看template那里(固定写法) slotProps相当于内置变量

<!--父组件-->
	<son :todos="todos">
				<template v-slot:default="slotProps">
                    <span v-if="slotProps.todo.isComplete"></span>
                	<span>{{slotProps.todo.text}}</span>
           	    </template>
			</son>

搞定!如果你喜欢我的文章,可以关注我,如果忍不住想天天看到我,可以联系我加入我们的前端讨论群

0人推荐
随时随地看视频
慕课网APP