前言

在前面两篇文章已经详细介绍了 Vue 的语法及实例属性,看完之后基本就对 Vue 有个基本的掌握了,接下来这篇咱们就来讲讲 Vue 的组件!内容包括组件开发,组件复用,组件的数据传递,组件间的事件通知,通过插槽进行内容分发,动态组件等!

什么是组件

首先我们先来说说什么是组件:组件简单来说就是可复用的 Vue 实例,每个组件都是一个 Vue 实例!所以他们与 Vue 实例一样,有相同的属性,只有 el 属性是根实例所有特有的!

由于组件是可复用的,通常我们将组件当成是自定义元素来用:

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div id="app">
<div>
<Button text="按钮1" @click="clickBtn(1)" />
<Button :text="btnText" @click="clickBtn(2)" />
</div>
</div>

<script>
import Button from './Button';

new Vue({
el: document.getElementById('app'),
data: function() {
return {
btnText: '按钮2'
};
},
components: {
Button,
},
methods: {
clickBtn: function(type) {
console.log(type)
}
},
})
</script>

子组件 - button组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- {% raw %} -->
<div>
<button>{{ text }}</Button>
</div>
<!-- {% endraw %} -->
<script>
export default {
data: function() {
return {};
},
props: ['text'],
methods: {
click: function() {
this.$emit('click');
}
},
}
</script>

想上面例子里的 Button 就是一个可复用的组件,当我们开发好组件之后,在父组件内引入并注册之后,就可以将其当成是自定义标签一样重复使用!

组件注册

上面的例子简单的介绍了什么是组件,接下来说说组件的注册!组件的注册有两种类型,分别是局部注册全局注册

全局注册:顾名思义,就是注册在全局 Vue 上的,一旦注册之后,在所有 Vue实例上都可以直接使用:

1
2
3
4
5
6
import Vue from 'vue';

Vue.component('Button', {
props: ['text'],
...
})

局部注册:就是在使用时,在父组件上直接注册,就像一开始介绍什么是组件时候的例子,使用的就是局部注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Button from './Button';

new Vue({
el: document.getElementById('app'),
data: function() {
return {
btnText: '按钮2'
};
},
// 使用局部注册 注册组件
components: {
Button,
},
methods: {
clickBtn: function(type) {
console.log(type)
}
},
})

数据传递及事件通知

Vue 里,组件通过 prop 从父组件向下传递给子组件:

Input组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div>
<input v-model="value" />
</div>
<script>
export default {
data: function() {
return {};
},
props: ['value'],
methods: {
click: function() {
this.$emit('click');
}
},
}
</script>

上面的例子中,value 属性就是父组件通过 prop 传递进来的,那么也就是说,在组件中,有些需要的外部数据,都可以使用 prop 传递进来;同样,上面的例子中只是展示了怎么将数据传递近组件,那么如果数组在组件中发生了改变,我们要怎么将发生的变化传递给外面了?

这里就要用到上面说的事件传递了,接着上面例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div>
<input v-model="val" @input="input" />
</div>

<script>
export default {
data: function() {
return {
val: ''
};
},
props: ['value'],
// 监控父组件传入的值 并初始化组件的值
watch: {
value: function(val) {
this.val = val;
}
}
methods: {
// 向外传递input事件
input: function(event) {
this.$emit('input', this.val);
}
},
}
</script>

上面的例子我们可以看到,我们在组件内部input的值的输入变化时,通过 $emit 方法向外进行了传递,并且将最新值当做参数给传递出去了,那么在外面我们就可以使用 @input 事件监听组件内部值的变化了,当然在这种表单元素且通过 input 事件进行传递的,我们可以直接使用 v-model 指令进行双向绑定:

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<div id="app">
<div>
<!-- 使用prop进行传递 使用input事件监听变化 -->
<Input :value="value1" @input="input" />

<!-- 使用v-model指令进行双向绑定 -->
<Input v-model="value2" />
</div>
</div>

<script>
import Input from './Input';

new Vue({
el: document.getElementById('app'),
data: function() {
return {
value1: '',
value2: '',
};
},
components: {
Input,
},
methods: {
input: function(value) {
this.value1 = value;
}
},
})

上面的例子中,我们使用 propvalue1 的值传递近组件,然后使用 input 事件监听变化,并将新值赋值给 value1,从效果上来说,可以实现和通过 v-model 进行双向绑定的 value2 的效果一模一样!实际上,在 v-model 内部也是通过 prop 绑定变量,通过 input 事件监听变化的!

组件间通信

父子组件通信:从上面的例子中我们可以看出,父组件与子组件之间通过 prop 将数据自上而下的传递进子组件内部,在由子组件由内向外抛出事件的方式完成父子组件间的数据交互,或者叫通信的!这就是我们常用的父子组件间通信的方式!当然这种方式在父子组件直接进行通信还是比较方便的,但是对于不同层级像隔比较多的组件间通信,再使用这种方式就比较麻烦了!

跨层级组件间通信:除父子组件之外,我们还会遇到很多不同层级的组件之间也许要进行通信的例子,如果还是用 prop 进行层层传递,再由事件进行层层的往外通知的方式,就得需要找到需要通信间的组件间的共同父组件,然后层层组件间进行传递消息,这种方式无疑是不合适的!这里我们可以借助 vuex 来完成不同组件间的通信,除此之外,我们还可以定义一个 event-bus 插件来完成通信工作;这里具体的使用方式就不在本篇文章中祥述了,在下篇文章我们会详细的介绍使用 vuexevent-bus 来进行跨层级组件间的通信!

通过插槽分发内容

在平常我们开发组件中,有时我们不仅需要从父组件传递数据进入组件中,还有可能需要从父组件中传递进来元素文字之类的内容,单纯的通过 prop 进行数据传递有时并不能满足我们的需求,或者说不够灵活;幸好 Vue 中有自定义 <slot> 元素可以进行内容分发,首先我们通过例子看一下 <slot> 的使用方法:

1
2
3
4
5
6
7
8
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})

如你所见,只需要在模板中加上 <slot></slot> 就可以了!那么在外边使用时,只需要将需要传递的内容放在组件中间就可以被传递进来:

1
2
3
4
5
6
7
8
9
10
<div>
<alert-box>
<!-- 这部分内容将被传递进组件插槽中 start -->
<div class="inner">
<h2>这是标题</h2>
<p>这是内容</p>
</div>
<!-- 这部分内容将被传递进组件插槽中 end -->
</alert-box>
</div>

具名插槽

除了上面例子中的用法之外,有时候我们需要多个插槽,分别传递不同的内容进入组件,这时候我们就可以使用带有名称的 slot 来进行传递:

1
2
3
4
5
6
7
8
9
10
11
12
Vue.component('pop-box', {
template: `
<div class="demo-alert-box">
<header>
<slot name="header"></slot>
</header>
<main>
<slot name="main"></slot>
</main>
</div>
`
})

那么在使用插槽时,我们只需要在父组件中,将需要传递的内容使用 slot="name" 属性指定插槽对应的名称即可:

1
2
3
4
5
6
7
8
9
<div>
<pop-box>
<!-- 这部分内容将被传递进组件的header插槽中 -->
<h2 slot="header">这是标题</h2>

<!-- 这部分内容将被传递进组件的main插槽中 -->
<p slot="main">这是内容</p>
</pop-box>
</div>

以上就是插槽及具名插槽的使用方法了,这东西说起来不太容易说清,但是通过上面的代码例子还是很容易就能看出来具体怎么使用的,用起来也并不复杂,而且插槽分发给我们组件带来了极大的灵活性,可以更有效的提高组件的可复用性,大家不防多尝试一下!

动态组件

有时候在我们平常的开发中,会遇到一些组件之间进行切换的情况,比如选项卡等场景,而在 Vue 中提供了 <component> 元素加 is 属性来实现这种动态组件:

1
2
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

在上述示例中,currentTabComponent 可以包括:

  • 已注册组件的名字,或
  • 一个组件的选项对象

结尾

本文详细介绍了组件相关的内容,讲到这里组件部分内容也就讲完了,以上内容都是本人结合自己的使用经验总结出来的,除了上面这些以外还有一些细节部分内容本文可能没有讲到,大家可以去官方文档 - 组件基础看更详细的介绍!

转载说明

本文允许全文转载,转载请注明来源:
平凡公子 - vue从入门到精通入门篇之组件开发