本文是介绍独立组件开发的系列文章的第一篇。
Vuejs
无疑是当下最火热的前端框架,而它最精髓的,正是它的组件与组件化。写一个 Vue
项目,就是在写一个个的组件,掌握了 Vue
组件的各种开发模式与技巧,再复杂的业务场景也可轻松拿捏。
组件的分类
Vue
组件分成三类:
页面组件
由 vue-router
产生的每个页面,它本质上也是一个组件(.vue),它承载当前页面的 HTML 结构,同时会有数据获取、数据整理等业务逻辑。
页面组件整个文件相对较大,但一般不会有 props
选项和 自定义事件
,因为它作为路由的渲染,不会被复用,因此也不会对外提供接口。
在我们平时的项目开发中,写的大部分代码都是这类的组件。多人开发时,每人维护自己的页面,很少有交集。这类组件相对是最好写的,因为主要是还原设计稿,完成需求,不需要太多模块和架构设计上的考虑。
独立组件
它不包含业务,是独立、具体功能的组件,比如下拉框、日期选择器等。
这类组件作为项目的基础控件,会被大量使用,因此组件的 API 进行过高强度的抽象,可以通过不同配置实现不同的功能。
大部分的互联网都有自己的组件使用规范或组件库,但是要开发和维护一套像 element ui
、ant design
这样的组件库,投入的人力和精力还是很重的,所以出于成本考虑,很多项目都会使用已有的开源组件库。
独立组件的开发难度要远高于页面组件,因为它的侧重点是 API 的设计、兼容性、性能、以及复杂的功能。
这类组件对 JavaScript
的编程能力有比较高的要求,也包含非常多的技巧与经验。比如在不依赖 Vuex
等第三方库(因为独立组件,无法依赖其它库)的情况下,各组件间的通信等等问题。
业务组件
它与独立组件的区别是,业务组件只在当前项目中会用到,不具有通用性,而且会包含一些业务,比如数据请求;而独立组件不含业务,在任何项目中都可以使用,功能单一,比如一个具有数据校验功能的输入框。
业务组件更像是介于第一类和第二类之间,在开发上也与独立组件类似,但依赖于项目,你可以使用项目中引入的第三方库,比如 Vuex
、axios
、lodash
等,所以它的开发难度相对独立组件要容易点,但也有必要考虑组件的可维护性和复用性。
因此,独立组件的开发是最能体现一个前端开发者的Vue
水平的,本文也将重点介绍独立组件的各种开发模式和技巧。
Vue组件的三个 API:prop、event、slot
一个再复杂的组件,都是由三部分组成的:prop、event、slot
,它们构成了 Vue 组件的 API。
如果你开发的是一个独立组件,那一定要事先设计好这三部分,因为组件一旦发布,后面再修改 API 就很困难了,使用者都不希望一个库的接口经常变换。
如果你阅读别人写的组件,也可以从这三个部分展开,它们可以帮助你快速了解一个组件的所有功能。
属性 prop
prop
定义了这个组件有哪些可配置的属性,组件的核心功能也都是它来确定的。
写独立组件时,props
最好用对象
的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值,这点在组件开发中很重要,然而很多人却忽视,直接使用 props 的数组用法,这样的组件往往是不严谨的。
比如我们封装一个按钮组件 <v-button>
:
<template>
<button :class="'v-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>
使用组件:
<v-button size="large"></v-button>
<v-button disabled></v-button>
组件定义了两个属性:尺寸 size
和是否禁用 disabled
。
size
使用 validator
进行了值的自定义验证,也就是说,从父级传入的 size
,它的值必须是指定的 small、large、default 中的一个,默认值是 default
,如果传入这三个以外的值,都会抛出一条警告。
组件里定义的 props
,都是单向数据流,只能通过父级修改,组件自己不能修改 props 的值,只能修改定义在 data 里的数据,非要修改,也是通过后面介绍的自定义事件通知父级,由父级来修改。
在使用组件时,也可以传入一些标准的 html
属性,比如 id、class:
<v-button id="btn1" class="btn-submit"></v-button>
这样的 html
属性,在组件内的 <button>
元素上会继承,并不需要在 props
里再定义一遍。
这个特性是默认支持的,如果不期望开启,在组件选项里配置inheritAttrs: false
就可以禁用了。
插槽 slot
如果要给上面的按钮组件 <v-button>
添加一些文字内容,就要用到组件的第二个 API:插槽 slot
,它可以分发组件的内容:
<template>
<button :class="'i-button-size' + size" :disabled="disabled">
<slot></slot>
</button>
</template>
这里的 <slot>
节点就是指定的一个插槽的位置,这样在组件内部就可以扩展内容了:
<v-button>按钮 1</v-button>
<v-button>
<strong>按钮 2</strong>
</v-button>
当需要多个插槽时,会用到具名 slot
,比如我们再增加一个 slot,用于设置另一个图标组件:
<template>
<button :class="'i-button-size' + size" :disabled="disabled">
<slot name="icon"></slot>
<slot></slot>
</button>
</template>
<v-button>
<v-icon slot="icon" type="checkmark"></v-icon>
按钮 1
</v-button>
这样,父级内定义的内容就会出现在组件对应的 slot
里,没有写名字的,就是默认的 slot
。
在组件的 <slot>
里也可以写一些默认的内容,这样在父级没有写任何 slot
时,它就会出现:
<slot>提交</slot>
自定义事件 event
现在给组件 <v-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
来监听:
<v-button @on-click="handleClick"></v-button>
上面的 click
事件,是在组件内部的 <button>
元素上声明的,这里还有另一种方法,直接在父级声明,但为了区分原生事件和自定义事件,要用到事件修饰符 .native
,所以上面的示例也可以这样写:
<v-button @click.native="handleClick"></v-button>
如果不写 .native
修饰符,那上面的 @click
就是自定义事件 click,而非原生事件 click(原生事件是指在html标签上定义的click事件,在组件上定义的事件为自定义事件),但我们在组件内只触发了 on-click
事件,而不是 click
,所以直接写 @click
会监听不到。
总结
本文复习了 Vue
组件的核心知识点prop、slot、evnet
,虽然没有完全覆盖 Vue
的 API,但对于组件开发来说已经足够了。
prop
定义了组件有哪些功能,开发组件首先需要明确prop
,推荐使用对象
的形式来写prop
。
slot
分发组件的内容,让用户可以自定义展示内容,极大的扩展了组件的使用范围。
event
组件内的数据改变,只能通过父级改变,保证数据的单向流动的。
在下一篇文章,我们将介绍独立组件之间的通信,看看它们在不依赖于第三库的情况下是如何通信的。
标签:slot,Vue,自定义,父级,prop,组件,click From: https://blog.51cto.com/u_16078169/6209572