You are on page 1of 9

基础:Vue.

js 组件的三个 API:
prop、event、slot
如果您已经对 Vue.js 组件的基础⽤法了如指掌,可以跳过本⼩
节,不过当做复习稍读⼀下也⽆妨。

组件的构成
⼀个再复杂的组件,都是由三部分组成的:prop、event、slot,它
们构成了 Vue.js 组件的 API。如果你开发的是⼀个通⽤组件,那⼀
定要事先设计好这三部分,因为组件⼀旦发布,后⾯再修改 API 就
很困难了,使⽤者都是希望不断新增功能,修复 bug,⽽不是经常
变更接⼝。如果你阅读别⼈写的组件,也可以从这三个部分展开,它
们可以帮助你快速了解⼀个组件的所有功能。

属性 prop

prop 定义了这个组件有哪些可配置的属性,组件的核⼼功能也都是
它来确定的。写通⽤组件时,props 最好⽤对象的写法,这样可以
针对每个属性设置类型、默认值或⾃定义校验属性的值,这点在组件
开发中很重要,然⽽很多⼈却忽视,直接使⽤ props 的数组⽤法,
这样的组件往往是不严谨的。⽐如我们封装⼀个按钮组件 <i-
button>:
<template>
<button :class="'i-button-size' + size"
:disabled="disabled"></button>
</template>
<script>
// 判断参数是否是其中之⼀
function oneOf (value, validList) {
for (let i = 0; i < validList.length; i++) {
if (value === validList[i]) {
return true;
}
}
return false;
}

export default {
props: {
size: {
validator (value) {
return oneOf(value, ['small', 'large',
'default']);
},
default: 'default'
},
disabled: {
type: Boolean,
default: false
}
}
}
</script>

使⽤组件:
<i-button size="large"></i-button>
<i-button disabled></i-button>

组件中定义了两个属性:尺⼨ size 和 是否禁⽤ disabled。其中


size 使⽤ validator 进⾏了值的⾃定义验证,也就是说,从⽗级
传⼊的 size,它的值必须是指定的 small、large、default 中的⼀
个,默认值是 default,如果传⼊这三个以外的值,都会抛出⼀条警
告。

要注意的是,组件⾥定义的 props,都是单向数据流,也就是只能
通过⽗级修改,组件⾃⼰不能修改 props 的值,只能修改定义在
data ⾥的数据,⾮要修改,也是通过后⾯介绍的⾃定义事件通知⽗
级,由⽗级来修改。

在使⽤组件时,也可以传⼊⼀些标准的 html 特性,⽐如


id、class:

<i-button id="btn1" class="btn-submit"></i-


button>

这样的 html 特性,在组件内的 <button> 元素上会继承,并不需


要在 props ⾥再定义⼀遍。这个特性是默认⽀持的,如果不期望开
启,在组件选项⾥配置 inheritAttrs: false 就可以禁⽤了。

插槽 slot

如果要给上⾯的按钮组件 <i-button> 添加⼀些⽂字内容,就要⽤


到组件的第⼆个 API:插槽 slot,它可以分发组件的内容,⽐如在上
⾯的按钮组件中定义⼀个插槽:
<template>
<button :class="'i-button-size' + size"
:disabled="disabled">
<slot></slot>
</button>
</template>

这⾥的 <slot> 节点就是指定的⼀个插槽的位置,这样在组件内部


就可以扩展内容了:

<i-button>按钮 1</i-button>
<i-button>
<strong>按钮 2</strong>
</i-button>

当需要多个插槽时,会⽤到具名 slot,⽐如上⾯的组件我们再增加
⼀个 slot,⽤于设置另⼀个图标组件:

<template>
<button :class="'i-button-size' + size"
:disabled="disabled">
<slot name="icon"></slot>
<slot></slot>
</button>
</template>

<i-button>
<i-icon slot="icon" type="checkmark"></i-icon>
按钮 1
</i-button>

这样,⽗级内定义的内容,就会出现在组件对应的 slot ⾥,没有写


名字的,就是默认的 slot。
在组件的 <slot> ⾥也可以写⼀些默认的内容,这样在⽗级没有写
任何 slot 时,它们就会出现,⽐如:

<slot>提交</slot>

⾃定义事件 event

现在我们给组件 <i-button> 加⼀个点击事件,⽬前有两种写法,


我们先看⾃定义事件 event(部分代码省略):

<template>
<button @click="handleClick">
<slot></slot>
</button>
</template>
<script>
export default {
methods: {
handleClick (event) {
this.$emit('on-click', event);
}
}
}
</script>

通过 $emit,就可以触发⾃定义的事件 on-click ,在⽗级通过


@on-click 来监听:

<i-button @on-click="handleClick"></i-button>

上⾯的 click 事件,是在组件内部的 <button> 元素上声明的,这


⾥还有另⼀种⽅法,直接在⽗级声明,但为了区分原⽣事件和⾃定义
事件,要⽤到事件修饰符 .native,所以上⾯的示例也可以这样
写:
<i-button @click.native="handleClick"></i-button>

如果不写 .native 修饰符,那上⾯的 @click 就是⾃定义事件


click,⽽⾮原⽣事件 click,但我们在组件内只触发了 on-click
事件,⽽不是 click,所以直接写 @click 会监听不到。

组件的通信
⼀般来说,组件可以有以下⼏种关系:

A 和 B、B 和 C、B 和 D 都是⽗⼦关系,C 和 D 是兄弟关系,A 和


C 是隔代关系(可能隔多代)。组件间经常会通信,Vue.js 内置的
通信⼿段⼀般有两种:

ref:给元素或组件注册引⽤信息;
$parent / $children:访问⽗ / ⼦实例。

这两种都是直接得到组件实例,使⽤后可以直接调⽤组件的⽅法或访
问数据,⽐如下⾯的示例中,⽤ ref 来访问组件(部分代码省略):

// component-a
export default {
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}

<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); // 弹窗
}
}
</script>

$parent 和 $children 类似,也是基于当前上下⽂访问⽗组件或


全部⼦组件的。
这两种⽅法的弊端是,⽆法在跨级或兄弟间通信,⽐如下⾯的结构:

// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>

我们想在 component-a 中,访问到引⽤它的⻚⾯中(这⾥就是


parent.vue)的两个 component-b 组件,那这种情况下,就得配
置额外的插件或⼯具了,⽐如 Vuex 和 Bus 的解决⽅案,本⼩册不
再做它们的介绍,读者可以⾃⾏阅读相关内容。不过,它们都是依赖
第三⽅插件的存在,这在开发独⽴组件时是不可取的,⽽在⼩册的后
续章节,会陆续介绍⼀些⿊科技,它们完全不依赖任何三⽅插件,就
可以轻松得到任意的组件实例,或在任意组件间进⾏通信,且适⽤于
任意场景。

结语
本⼩节带您复习了 Vue.js 组件的核⼼知识点,虽然这并没有完全覆
盖 Vue.js 的 API,但对于组件开发来说已经⾜够了,后续章节也会
陆续扩展更多的⽤法。

基于 Vue.js 开发独⽴组件,并不是新奇的挑战,坦率地讲,它本质
上还是 JavaScript。掌握了 Vue.js 组件的这三个 API 后,剩下的便
是程序的设计。在组件开发中,最难的环节应当是解耦组件的交互逻
辑,尽量把复杂的逻辑分发到不同的⼦组件中,然后彼此建⽴联系,
在这其中,计算属性(computed)和混合(mixins)是两个重要
的技术点,合理利⽤,就能发挥出 Vue.js 语⾔的最⼤特点:把状态
(数据)的维护交给 Vue.js 处理,我们只专注在交互上。

当您最终读完本⼩册时,应该会总结出和笔者⼀样的感悟:Vue.js
组件开发,玩到最后还是在拼 JavaScript 功底。对于每⼀位使⽤
Vue.js 的开发者来说,阅读完本⼩册都可以尝试开发和维护⼀套属
于⾃⼰的组件库,并乐在其中,⽽且你会越发觉得,⼀个组件或⼀套
组件库,就是融合了前端精髓的产出。

扩展阅读
Vue 组件通信之 Bus
(https://juejin.im/post/5a4353766fb9a044fb080927)
Vuex 通俗版教程
(https://juejin.im/entry/58cb4c36b123db00532076a2)

You might also like