组件的封装涉及到了组件之间的相互通信,这里面包含了更多的知识点,而这篇所总结的只是针对父子组件的通信,不涉及非父子组件之间的通信。
父子组件通信:
!三要素 *
父组件对子组件传递参数,子组件需要用到props来接收参数。
一般使用:以字符串的形式列出prop,或者指定prop类型
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
// OR
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
但是通常在项目开发中,我们需要对组件的prop进行验证。通常你知道这些prop的类型,只要有一个验证需求没有被满足,就会报错。在团队开发中,这很重要。
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
//对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的。
例:在之前用vue-cli3搭建好的项目中创建 /components/MyCard.vue
/components/MyCard.vue
<template>
<div class="g-card">
<div class="u-header">{{data.header}}div>
<div class="u-banner">{{data.banner}}div>
<div class="u-footer">{{data.footer}}div>
div>
template>
<script>
export default {
props: {
data: {
type: Object,
default: () => {//对象默认值必须从一个工厂函数获取。
return {};
}
}
}
};
script>
HomePage.vue
<template>
<div class="g-main">
<x-header title="主页" :left-options="{showBack:false}">x-header>
<div class="u-block">
<h1>组件封装h1>
div>
<my-card :data="cardData">my-card>
div>
template>
<script>
import MyCard from "./components/MyCard";//引入子组件
export default {
data() {
return {
cardData: {
header: "header",
banner: "banner",
footer: "footer"
}
};
},
components: {
MyCard//注册子组件
},
};
script>
子组件向父组件传值需要用到$emit:https://cn.vuejs.org/v2/api/index.html#vm-emit
vm.$emit( eventName, […args] )
触发当前实例上的事件。附加参数都会传给监听器回调。
也就是说,$emit需要向父组件传递一个事件名字,可以在这个事件上附加参数,而父组件作为监听器或者说调用者就可以接收到该事件附加的参数。
例:将上面的代码修改一下
/components/MyCard.vue
<template>
<div class="g-card">
<div class="u-header">{{data.header}}div>
<div class="u-banner">{{data.banner}}div>
<div class="u-footer" @click="clickFooter">clickdiv>
div>
template>
<script>
export default {
data() {
return {
msg: "hello newman"
};
},
props: {
data: {
type: Object,
default: () => {
return {};
}
}
},
methods: {
clickFooter() {
this.$emit("active", this.msg);
}
}
};
script>
通过v-on监听active事件
HomePage.vue
<my-card :data="cardData" @active="handleClick">my-card>
methods里写handleClick方法
handleClick(val) {
alert(val);
},
父组件中的逻辑要放在父组件处理,子组件基于父组件的数据做的逻辑放在子组件中处理; 这样既降低了耦合性,也保证了各自的数据不被污染。
是vue内置组件:https://cn.vuejs.org/v2/api/index.html#slot
<slot> 元素作为组件模板之中的内容分发插槽。<slot> 元素自身将被替换。
通过插槽分发内容:https://cn.vuejs.org/v2/guide/components.html#通过插槽分发内容
例:
/components/MyCard.vue
//修改上面的代码
<div class="g-card">
<slot name="header">slot>
<slot name="banner">slot>
<slot name="footer">slot>
div>
HomePage.vue
<my-card class="g-card">
<div class="u-header" slot="header">slot-headerdiv>
<div class="u-banner" slot="banner">slot-bannerdiv>
<div class="u-footer" slot="footer">slot-footerdiv>
my-card>
例:
/components/MyCard.vue
<div class="g-card">
<slot>slot>
div>
HomePage.vue
<my-card>
<div class="u-header">headerdiv>
<div class="u-banner">bannerdiv>
<div class="u-footer">footerdiv>
my-card>
关于组件封装父子组件通信的基本知识大致就是这些,在工作中清楚地知道父子组件如何通信真的很重要,至少可以让你会用别人写的插件或者UI框架,知道组件封装的这些知识原理,自己也可以动手封装组件。