Vue组件简析
1.简介
组件(Component)是 Vue.js 最强大的功能之一。
组件可以扩展 HTML 元素,封装可重用的代码。
组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
组件是可复用的 Vue 实例,它们与 new Vue
接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。仅有的例外是像 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
选项必须是一个函数,它返回的是一个对象,内含数据键值对,因此每个实例可以维护一份被返回对象的独立的拷贝,而不是像这样:
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>
|
最终的效果如图:
其中在外面创建的子组件,是一个独立的子组件,可以用于直接传入其它父组件
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
| 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
| 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
李炎恢教程