组件是指实现应用中局部功能代码(html
、css
、js
等)和资源(图片、音频、视频等)的集合。
传统的Web应用存在以下缺点:
- 依赖关系混乱,不易于维护。
- 代码复用率不高。
CSS文件可以通过外部引入,不存在复用率不高的问题。JS的模块化解决了JS代码复用率不高的问题。而Html想要复用却不是那么容易。
- 模块化应用:当应用中的JS都以区分模块的形式来编写,那么这个应用就是一个模块化的应用。
- 组件化应用:当应用中的功能都是以多组件的方式来编写,那么这个应用就是一个组件化的应用。
使用Vue组件可以扩展HTML元素,封装可重用的代码,解决了上述问题。
使用组件系统可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
在Vue中,组件有两种编写形式:
- 非单文件组件:一个文件中包含有其它多个组件。
- 单文件组件:一个文件中只包含有1个组件。
在Vue中使用组件共分为3步:
- 定义(创建)组件。
- 注册组件:
- 局部注册。
- 全局注册。
- 使用组件(组件标签)。
组件的注册
组件有两种注册方式:
-
局部注册:在创建Vue实例时,在其配置对象中的
components
配置项中定义。例如:
<div id="#app"> <!-- 3. 使用组件 --> <hello></hello> </div> <script type="text/javascript"> // 1. 创建组件 const hello = Vue.extend({ template: ` <div> <h2>{{msg}}</h2> </div> `, data() { return { msg: 'Hello Vue.js!', } }, }); const vm = new Vue({ el: "#app", // 2. 注册组件(局部注册) components: { 'hello': hello, // hello, // 简写形式 }, }); </script>
在
components
中,组件是以key: value
的形式进行注册。key
作为组件的名称,而value
使用的是组件的实例对象。局部注册的组件只能在当前的Vue实例或组件实例中使用。
-
全局注册:使用Vue原型对象提供的
Vue.component()
方法。Vue.component()
接收两个参数:- 参数1:注册组件时使用的
key
。 - 参数2:组件的实例对象。
如上例,将
hello
组件改为全局注册:<div id="#app"> <!-- 3. 使用组件 --> <hello></hello> </div> <script type="text/javascript"> // 1. 创建组件 const hello = Vue.extend({ template: ` <div> <h2>{{msg}}</h2> </div> `, data() { return { msg: 'Hello Vue.js!', } }, }); // 2. 注册组件(全局注册) Vue.component('hello', hello) const vm = new Vue({ el: "#app", }); </script>
- 参数1:注册组件时使用的
组件的使用与命名
组件在使用时,直接以<component-name>
的形式使用。切确地说,component-name
是组件在注册时传入的key
。
如上例中:
<div id="#app">
<!-- 3. 使用组件 -->
<hello></hello>
</div>
其实组件标签可以使用闭合标签的形式编写。如<user></user>
可以写为<user/>
。但是这种用法最好在Vue脚手架中使用。因为不使用脚手架的情况下,闭合标签可能会调职后续组件不能被成功渲染。
关于组件名可以采用以下两种命名方式:
- kebab-case(烤肉串式):用
-
连接多个单词。 - CamelCase(大驼峰命名法,需要Vue脚手架支持):将每个单词的首字母设为大写形式。
在定义组件名时需要注意:
组件名尽可能回避Html中已有的元素名称(Vue会报错)。
可以使用
name
配置项指定组件在开发者工具中展示的名称。如上例:
const hello = Vue.extend({ name: 'hello-vue', template: ` <div> <h2>{{msg}}</h2> </div> `, data() { return { msg: 'Hello Vue.js!', } }, })
那么无论组件名被定义成什么,
hello
组件在Vue开发者工具中展示的都是hello-vue
这个名称。
非单文件组件
如下是一个组件的示例:
<div id="app">
<h2>{{msg}}</h2>
<hr>
<!-- 3. 编写组件标签 -->
<site></site>
<hr>
<!-- 3. 编写组件标签 -->
<user></user>
</div>
<script type="text/javascript">
// 1. 创建site组件
const site = Vue.extend({
template: `
<div>
<h2>网站名称:{{name}}</h2>
<h2>网站地址:{{url}}</h2>
</div>
`,
data() {
return {
name: 'Linner\'s Blog',
url: 'blog.linner.asia',
}
},
});
// 1. 创建user组件
const user = Vue.extend({
template: `
<div>
<h2>用户姓名:{{name}}</h2>
<h2>用户年龄:{{age}}</h2>
</div>
`,
data() {
return {
name: '张三',
age: 18,
}
},
});
new Vue({
el: "#app",
data: {
msg: 'Hello!'
},
// 2. 注册组件(局部注册)
components: {
site,
user,
},
});
</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
:使用简写形式定义:
const site = {
template: `
<div>
<h2>网站名称:{{name}}</h2>
<h2>网站地址:{{url}}</h2>
</div>
`,
data() {
return {
name: 'Linner\'s Blog',
url: 'blog.linner.asia',
}
},
}
使用简写形式时,Vue.extend()
由vm
来调用。
Vue.extend()
返回的是一个VueComponent()
构造函数。并且每次调用Vue.extend()
时,返回的都是一个新的VueComponent()
。
Vue.extend()
的部分源码如下。Vue.extend()
在每次被调用时,都会定义一个新的VueComponent()
。
Vue.extend = function (extendOptions) {
/* ... */
// VueComponent() 是在 Vue.extend() 中现场定义的
var Sub = function VueComponent(options) {
this._init(options);
};
/* 对Sub进行一系列处理... */
return Sub;
};
组件配置
使用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
配置项即可。
例如:
<div id="root"></div>
<script type="text/javascript">
// 定义user组件
const user = {
template: `
<div>
<h2>用户姓名:{{name}}</h2>
<h2>用户年龄:{{age}}</h2>
</div>
`,
data() {
return {
name: '张三',
age: 18,
}
},
}
// 定义site组件
const site = {
template: `
<div>
<h2>网站名称:{{name}}</h2>
<h2>网站地址:{{url}}</h2>
<user></user>
</div>
`,
data() {
return {
name: 'Linner\'s Blog',
url: 'blog.linner.asia',
}
},
// 注册组件(局部)
components: {
user,
},
}
// 定义hello组件
const hello = {
template: `
<h1>{{msg}}</h1>
`,
data() {
return {
msg: 'Hello Vue.js',
}
},
}
// 定义app组件
const app = {
template: `
<div>
<hello></hello>
<hr>
<site></site>
</div>
`,
components: {
hello,
site,
},
}
const vm = new Vue({
el: "#root",
template: `
<app></app>
`,
// 注册组件(局部)
components: {app},
});
</script>
组件实例对象
在组件的一些配置项中(如data
、methods
、watch
、computed
等)的函数,this
通常指向的是VueComponent()
创建出来的实例对象。而使用new Vue()
时,如上的一些配置项中的函数,它们的this
通常指向的是Vue实例对象(vm
)。
由VueComponent()
创建出来的实例对象即为组件实例对象,又可称为VueComponent
对象(在代码中可使用缩写形式vc
)。
虽然
vc
的结构和vm
十分相似(vc
也使用了数据代理和数据劫持),但是它们是两种不同的对象。
在使用组件标签时,每一个组件标签都对应着一个组件实例。每一个组件实例都是由它对应的VueComponent()
构造函数所创建。
例如:
<div id="app">
<site></site>
<hr>
<user></user>
<user></user>
</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 项目开发。
评论