组件是指实现应用中局部功能代码(html、css、js等)和资源(图片、音频、视频等)的集合。
传统的Web应用存在以下缺点:
- 依赖关系混乱,不易于维护。
- 代码复用率不高。
CSS文件可以通过外部引入,不存在复用率不高的问题。JS的模块化解决了JS代码复用率不高的问题。而Html想要复用却不是那么容易。
- 模块化应用:当应用中的JS都以区分模块的形式来编写,那么这个应用就是一个模块化的应用。
- 组件化应用:当应用中的功能都是以多组件的方式来编写,那么这个应用就是一个组件化的应用。
使用Vue组件可以扩展HTML元素,封装可重用的代码,解决了上述问题。
使用组件系统可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
在Vue中,组件有两种编写形式:
- 非单文件组件:一个文件中包含有其它多个组件。
- 单文件组件:一个文件中只包含有1个组件。
在Vue中使用组件共分为3步:
- 定义(创建)组件。
- 注册组件:
- 局部注册。
- 全局注册。
- 使用组件(组件标签)。
组件的注册
组件有两种注册方式:
-
局部注册:在创建Vue实例时,在其配置对象中的
components配置项中定义。例如:
1<div id="#app"> 2 <!-- 3. 使用组件 --> 3 <hello></hello> 4</div> 5<script type="text/javascript"> 6 // 1. 创建组件 7 const hello = Vue.extend({ 8 template: ` 9 <div> 10 <h2>{{msg}}</h2> 11 </div> 12 `, 13 data() { 14 return { 15 msg: 'Hello Vue.js!', 16 } 17 }, 18 }); 19 20 const vm = new Vue({ 21 el: "#app", 22 // 2. 注册组件(局部注册) 23 components: { 24 'hello': hello, 25 // hello, // 简写形式 26 }, 27 }); 28</script>在
components中,组件是以key: value的形式进行注册。key作为组件的名称,而value使用的是组件的实例对象。局部注册的组件只能在当前的Vue实例或组件实例中使用。
-
全局注册:使用Vue原型对象提供的
Vue.component()方法。Vue.component()接收两个参数:- 参数1:注册组件时使用的
key。 - 参数2:组件的实例对象。
如上例,将
hello组件改为全局注册:1<div id="#app"> 2 <!-- 3. 使用组件 --> 3 <hello></hello> 4</div> 5<script type="text/javascript"> 6 // 1. 创建组件 7 const hello = Vue.extend({ 8 template: ` 9 <div> 10 <h2>{{msg}}</h2> 11 </div> 12 `, 13 data() { 14 return { 15 msg: 'Hello Vue.js!', 16 } 17 }, 18 }); 19 20 // 2. 注册组件(全局注册) 21 Vue.component('hello', hello) 22 23 const vm = new Vue({ 24 el: "#app", 25 }); 26</script> - 参数1:注册组件时使用的
组件的使用与命名
组件在使用时,直接以<component-name>的形式使用。切确地说,component-name是组件在注册时传入的key。
如上例中:
1<div id="#app">
2 <!-- 3. 使用组件 -->
3 <hello></hello>
4</div>
其实组件标签可以使用闭合标签的形式编写。如<user></user>可以写为<user/>。但是这种用法最好在Vue脚手架中使用。因为不使用脚手架的情况下,闭合标签可能会调职后续组件不能被成功渲染。
关于组件名可以采用以下两种命名方式:
- kebab-case(烤肉串式):用
-连接多个单词。 - CamelCase(大驼峰命名法,需要Vue脚手架支持):将每个单词的首字母设为大写形式。
在定义组件名时需要注意:
组件名尽可能回避Html中已有的元素名称(Vue会报错)。
可以使用
name配置项指定组件在开发者工具中展示的名称。如上例:
1const hello = Vue.extend({ 2 name: 'hello-vue', 3 template: ` 4 <div> 5 <h2>{{msg}}</h2> 6 </div> 7 `, 8 data() { 9 return { 10 msg: 'Hello Vue.js!', 11 } 12 }, 13})那么无论组件名被定义成什么,
hello组件在Vue开发者工具中展示的都是hello-vue这个名称。
非单文件组件
如下是一个组件的示例:
1<div id="app">
2 <h2>{{msg}}</h2>
3 <hr>
4 <!-- 3. 编写组件标签 -->
5 <site></site>
6 <hr>
7 <!-- 3. 编写组件标签 -->
8 <user></user>
9</div>
10<script type="text/javascript">
11 // 1. 创建site组件
12 const site = Vue.extend({
13 template: `
14 <div>
15 <h2>网站名称:{{name}}</h2>
16 <h2>网站地址:{{url}}</h2>
17 </div>
18 `,
19 data() {
20 return {
21 name: 'Linner\'s Blog',
22 url: 'blog.linner.asia',
23 }
24 },
25 });
26
27 // 1. 创建user组件
28 const user = Vue.extend({
29 template: `
30 <div>
31 <h2>用户姓名:{{name}}</h2>
32 <h2>用户年龄:{{age}}</h2>
33 </div>
34 `,
35 data() {
36 return {
37 name: '张三',
38 age: 18,
39 }
40 },
41 });
42
43 new Vue({
44 el: "#app",
45 data: {
46 msg: 'Hello!'
47 },
48 // 2. 注册组件(局部注册)
49 components: {
50 site,
51 user,
52 },
53 });
54</script>
创建组件
非单文件组件的创建使用的是Vue.extend()方法。
Vue.extend()的使用与Vue实例的构造函数(new Vue())非常类似,它们都需要传入一个配置对象。并且在Vue构造函数的配置对象中使用的大多配置项,在Vue.extend()的配置对象中同样也能使用(如data、methods等)。
但需要注意的是,Vue.extend()的配置对象中不能使用el配置项;并且它的data配置项需要使用函数式来定义,然后通过在data()中return一个对象来作为该组件的data对象。
在定义非单文件组件时,可以将const component = Vue.extend(options)简写为const component = options。
如上,将site:使用简写形式定义:
1const site = {
2 template: `
3 <div>
4 <h2>网站名称:{{name}}</h2>
5 <h2>网站地址:{{url}}</h2>
6 </div>
7 `,
8 data() {
9 return {
10 name: 'Linner\'s Blog',
11 url: 'blog.linner.asia',
12 }
13 },
14}
使用简写形式时,Vue.extend()由vm来调用。
Vue.extend()返回的是一个VueComponent()构造函数。并且每次调用Vue.extend()时,返回的都是一个新的VueComponent()。
Vue.extend()的部分源码如下。Vue.extend()在每次被调用时,都会定义一个新的VueComponent()。
1Vue.extend = function (extendOptions) {
2 /* ... */
3
4 // VueComponent() 是在 Vue.extend() 中现场定义的
5 var Sub = function VueComponent(options) {
6 this._init(options);
7 };
8
9 /* 对Sub进行一系列处理... */
10 return Sub;
11};
组件配置
使用Vue.extend()创建组件,还需要在配置对象中启用template配置。template的作用是,定义组件的布局结构,也就是组件中可供复用的HTML代码。
在使用
template的时候需要注意,template的内容必须仅包含1个根元素,并且不能使用<template>标签作为根元素。
使用Vue.extend()的注意项:
-
组件配置项与Vue实例配置项基本相同。但是在组件配置中,不能使用
el配置。组件时作为可以复用的Vue实例,所以它们与根实例(Vue实例)接收的配置项基本相同(包括
data、computed、watch、methods、components以及生命周期钩子等)。但是所有的组件最终都要被一个根实例vm所管理,由vm决定它们要服务于哪个容器。并且考虑到组件的复用,所以不使用el,而是使用template来配置可复用的组件模板。 -
组件中的
data要配置成一个函数。防止一个组件被多次使用时,存在数据的引用关系。
data使用函数来定义,可以确保每个组件实例都可以维护一份由data返回对象的独立拷贝。
组件的嵌套
组件的嵌套使用很简单,直接在组件的配置对象中使用components配置项即可。
例如:
1<div id="root"></div>
2<script type="text/javascript">
3 // 定义user组件
4 const user = {
5 template: `
6 <div>
7 <h2>用户姓名:{{name}}</h2>
8 <h2>用户年龄:{{age}}</h2>
9 </div>
10 `,
11 data() {
12 return {
13 name: '张三',
14 age: 18,
15 }
16 },
17 }
18
19 // 定义site组件
20 const site = {
21 template: `
22 <div>
23 <h2>网站名称:{{name}}</h2>
24 <h2>网站地址:{{url}}</h2>
25 <user></user>
26 </div>
27 `,
28 data() {
29 return {
30 name: 'Linner\'s Blog',
31 url: 'blog.linner.asia',
32 }
33 },
34 // 注册组件(局部)
35 components: {
36 user,
37 },
38 }
39
40 // 定义hello组件
41 const hello = {
42 template: `
43 <h1>{{msg}}</h1>
44 `,
45 data() {
46 return {
47 msg: 'Hello Vue.js',
48 }
49 },
50 }
51
52 // 定义app组件
53 const app = {
54 template: `
55 <div>
56 <hello></hello>
57 <hr>
58 <site></site>
59 </div>
60 `,
61 components: {
62 hello,
63 site,
64 },
65 }
66
67 const vm = new Vue({
68 el: "#root",
69 template: `
70 <app></app>
71 `,
72 // 注册组件(局部)
73 components: {app},
74 });
75</script>
组件实例对象
在组件的一些配置项中(如data、methods、watch、computed等)的函数,this通常指向的是VueComponent()创建出来的实例对象。而使用new Vue()时,如上的一些配置项中的函数,它们的this通常指向的是Vue实例对象(vm)。
由VueComponent()创建出来的实例对象即为组件实例对象,又可称为VueComponent对象(在代码中可使用缩写形式vc)。
虽然
vc的结构和vm十分相似(vc也使用了数据代理和数据劫持),但是它们是两种不同的对象。
在使用组件标签时,每一个组件标签都对应着一个组件实例。每一个组件实例都是由它对应的VueComponent()构造函数所创建。
例如:
1<div id="app">
2 <site></site>
3 <hr>
4 <user></user>
5 <user></user>
6</div>
其中所有的<site></site>都对应着一个site组件的VueComponent()构造函数,所有的<user></user>都对应着一个user组件的VueComponent()构造函数。也就是说,每一个组件都对应一个VueComponent()构造函数。
而上方的user组件被使用了两次,也就是两个<user></user>。其中每一个<user></user>都对应着一个使用user的VueComponent()所创建出来的VueComponent对象。也就是说,每使用一次<user></user>,Vue都会使用user的VueComponent()创建出来一个组件实例。即每一个组件标签对应着一个组件实例。
当组件被注册时(无论是局部注册还是全局注册),它们都会被vm所管理。并且当组件被使用时,它们的VueComponent对象都会被保存在vm.$children中。
vm.$children是数组类型,保存了实例中使用到的所有VueComponent对象。
而组件在使用子组件时,同vm一样,会将其保存在当前组件实例对象vc的$children(vc.$children)中。
Vue 原型链
Vue 的原型链如下:
所以在Vue中,Vue.extend(options).prototype.__proto__ === Vue.prototype(Vue.extend(options)返回的是VueComponent构造函数)。
单文件组件
单文件组件请查看 Vue 项目开发。
评论