Vue组件简析

1.简介

组件(Component)是 Vue.js 最强大的功能之一。

组件可以扩展 HTML 元素,封装可重用的代码。

组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:

img

组件是可复用的 Vue 实例,它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

通常一个应用会以一棵嵌套的组件树的形式来组织,例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册。接下来介绍一下这两种注册方式。


2.全局注册

全局注册分为三部分:

2.1.构建模板对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--html代码用反引号括起来-->
const htmlElement=Vue.extend({
data() {
return {
count : 0
}
},
template:`
<button v-on:click="count++">
<h3>注册全局组件</h3>
点击量:{{count}}
</button>
`
})

上面的Vue.extend()是一个基础构造器(全局 API),意图创建一个子类;

一个组件的 data 选项必须是一个函数,它返回的是一个对象,内含数据键值对,因此每个实例可以维护一份被返回对象的独立的拷贝,而不是像这样:

1
2
3
data: {
count: 0
}

2.2.注册全局组件

1
Vue.component('lhy',htmlElement);

上面的Vue.component()是注册组件,参数 1 为组件名称,参数 2 为.extend();详情看官方文档API部分

3.视图输出

1
2
3
4
<div id="app">
<lhy>
</lhy>
</div>

这样一个全局组件就注册好了。

除了这一种方式之外我们也可以省略掉 Vue.extend()这一步,将构造内容直接作为参数传递

像这样:

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 id="app">
<lhy></lhy>
</div>


Vue.component('lhy', {

data() {

return {

count : 0

}

},

template :

`
<button v-on:click="count++">
点击量:{{count}}
</button>

`
});

3.局部注册

刚刚的方式,是组件的全局注册方式,意味其它 Vue 实例也可以访问

如果想要只限某个 Vue 实例,可以设置局部注册方法

3.1.创建一个组件

1
2
3
4
5
6
7
8
9
10
const htmlElement={
data(){
return{
count:0,
}
},
template:`
<button @click="count++">点击次数{{count}}</button>
`
}

3.2.绑定组件注册

1
2
3
4
5
6
const app=new Vue({
el:'#app',
components:{
'lhy':htmlElement
},
})

在局部注册中,不用extend()方法,因为extend是全局API


4.组件的嵌套

组件和组件之间可以嵌套,形成一种父子关系的组件

例如,我们先注册两个父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--第一个父组件-->
Vue.component('f1',{
data() {
return {
message: '一号父组件'
}
},
template:`<div>
{{message}}
</div>`
,
});
<!--第二个父组件-->
Vue.component('f2',{
data() {
return {
message: '二号父组件'
}
},
template:`<div>
{{message}}
</div>`
,
});

Vue.Compnents 第二个参数其实就是 Vue 构造子集,可以继续嵌套。

我们可以在原来的基础上,对第一个父组件进行嵌套

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!--在外面定义一个子组件-->
const deson={

data(){
return{
message:'在外面定义的子组件'
}
},
template:`<div>
{{message}}
</div>
`
}

Vue.component('f1',{
data() {
return {
message: '一号父组件'
}
},
template:`<div>
<div>
{{message}}
</div>
<son1></son1>
<son2></son2>
</div>`,
components: {
<!--在里面定义一个子组件-->
'son1':{
data(){
return{
message:'在里面定义的子组件'
}
},
template:`<div>{{message}}</div>`
},
<!--绑定在外面定义的子组件-->
'son2':deson,
}
});
Vue.component('asshole',{
data() {
return {
message: '二号父组件'
}
},
template:`<div>{{message}}</div>`,
});

而html为

1
2
3
4
<div id="app">
<f1></f1>
<f2></f2>
</div>

最终的效果如图:

image-20210726173939968

其中在外面创建的子组件,是一个独立的子组件,可以用于直接传入其它父组件


5.props

5.1.props通信

组件的父子关系中,当设置一个独立组件时,这个组件就是 new Vue()的子组件

当我们需要通过子组件显示父组件 data 值的时候,需要通过 props 属性传值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 这个组件是 Vue 实例的子组件
const childComponent = {
template : `
<div>{{text}}</div>
`,
props : ['text']
};

//根组件
const app = new Vue({
el : '#app',
data : {message:'Hello'},
components : {
'html-a' : childComponent
},
});

在这里父组件中data里面message的值会通过v-bind传入到子组件的html中

1
2
3
4
<div id="app">
//使用 v-bind 才能将父组件的 data 值绑定到子组件使用;
<html-a v-bind:text="message"></html-a>
</div>

5.2.props类型

从父组件给子组件通信,子组件不单单可以得到字符串类型,还可以是其它类型

1
2
3
4
5
6
7
8
9
10
11
//数据对象
const dataObj = {
message : 'Hello, Vue!',
array : [1,2,3,4,5],
object : {
name : 'Mr.Lee',
age : 100
},
flag : true,
number : 200,
};
1
2
3
4
5
6
7
8
9
10
<html-a v-bind:text="message"></html-a>
<html-a v-bind:text="flag"></html-a>
<html-a v-bind:text="number"></html-a>
<html-a v-bind:text="array[4]"></html-a>
<html-a v-bind:text="number"></html-a>
<html-a v-bind:text="array"></html-a>
<html-a v-bind:text="object"></html-a>
<html-a v-bind:text="object.name"></html-a>
<html-a v-bind:text="message + ' + ' + number"></html-a>
<html-a v-bind:text="flag" v-if="flag"></html-a>

上面的子类中,text 包含了所有的父组件的值

而这种方式只能在绑定里组合更多的值,而不能在模版中组合更多的值

5.3.组件的单向数据流

父组件的 data 值更新后通过 props 选项交给子组件进行渲染,反之则不行;

这就是单向数据流(单向下行绑定),不能通过子组件来改变父组件的状态;

这样做的是为了防止父组件发生改变后,数据流变得难以理解;

父组件更新时,子组件所有 props 值也会更新,你不能改变子组件的 props 值;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//父组件
const app = new Vue({
el : '#app',
data : {
count : 0
},
components : {
'html-a' : {
data() {
return {
message : '子组件',
}
},
template : `
<button v-on:click="count++">{{count}}</button>
`,
props : ['count'],
}
},
})

以上的代码从子组件改变从父组件传下来的值,这样会正确改变了 props 的值,也渲染到视图中,但控制台报错。

因为我们不可以直接修改 props 值,可以通过数据或计算属性来解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//其中的count是父组件中data里面的值,将count传给子组件里面的childCount
components : {
'html-a' : {
data() {
return {
message : '子组件',
childCount : this.count
}
},
template : `
<button v-on:click="childCount++">{{childCount}}</button>
`,
props : ['count']
}
},
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
//通过使用计算属性和方法数据更改
components : {
'html-a' : {
data() {
return {
message : '子组件',
childCount : this.count
}
},
template : `
<button
v-on:click="clickChildCount">{{changeChildCount}}</button>
`,
props : ['count'],
computed: {
changeChildCount() {
return this.childCount;
},
},
methods : {
clickChildCount() {
this.childCount++;
}
}
}
}

参考资料:

Vue官方文档
菜鸟教程vue.js
李炎恢教程