组件是可复用的Vue实例,因此它们与new Vue接收相同的选项,如data、computed、watch、methods以及生命周期钩子等。仅有的例外是像el这样跟实例特有的选项。
组件是用来实现局部(特定)功能效果的代码(html/css/js)和资源(mp3/mp4/ttf/zip)的集合等
组件的优势:可复用代码,简化项目编码,提高运行效率;
组件的分类:单文件组件(一个文件中只包含有一个组件,大型项目中多用此方法)、非单文件组件(一个文件中包含有n个组件);局部组件;全局组件
创建组件:
首先说一下局部组件:
通常使用vue.extend来创建组件(注意和继承分开,继承是extends),且其中的配置项有v一般ue实例的配置项一样,只是不能写el(因为组件创建的时候是给很多地方服务的,所有的组件都要被一个vm管理,由vm决定服务于哪个容器);
若是在组件中写了el配置项,则会报如下错误:
另外组件中的data配置项不能像之前一样,写成对象形式,要将其包装为函数形式(见之前说过data有两种写法),并将其用return返回(若用对象形式定义data配置项,则被引用组件的多个页面中,若其中一个页面的data被修改了,则其它页面的data也会与之对应修改),如:
<script>
let data = {
A1: 0,
A2: 1
}
const x1 = data;
const x2 = data;
</script>
用对象式定义data,修改x1,则x2也对应被修改了;
但若将其定义为函数式,则被调用的函数生成的实例则是独立的,互不影响;
<script>
function data() {
return {
A1: 0,
A2: 1
}
}
const x1 = data();
const x2 = data();
</script>
一个完整的组件创建示例如下:
// 局部组件创建
const students = Vue.extend({
data() {
return {
studentName: '小明',
studentClass: '三年级'
}
}
})
一般情况下,会把Vue.extend省略,如下:
const students = {
data() {
return {
studentName: '小明',
studentClass: '三年级'
}
}
}
注册局部组件
上面说过,组件统一由new Vue创建出的vue实例(一般设置为vm)管理,由vm配置管理哪些组件,使用配置项components注册组件;
<script>
// 创建vm
const vm = new Vue({
el: '#root',
// 注册局部组件
components: {
students: students
}
})
</script>
components配置项存放的依然是key:value值,其中key是指被作为标签使用的组件名,value值是被定义的组件名称,若key和value一样的话,可以省略:
<script>
// 创建vm
const vm = new Vue({
el: '#root',
// 注册局部组件
components: {
students
}
})
</script>
编写组件标签
将上一步中的components的key值作为标签使用即可,如上述为students,
<body>
<div id='root'>
<!-- 编写组件标签 -->
<students></students>
</div>
<script>
// 构建局部组件
const students = {
data() {
return {
studentName: '小明',
studentClass: '三年级'
}
}
}
// 创建vm
const vm = new Vue({
el: '#root',
// 注册局部组件
components: {
students
}
})
</script>
</body>
此时由于students组件中未定义template配置项,会报如下错误:
修正如下:
<body>
<div id='root'>
<!-- 编写组件标签 -->
<students></students>
</div>
<script>
// 构建局部组件
const students = {
data() {
return {
studentName: '小明',
studentClass: '三年级'
}
},
template: `
<span>{{studentName}}</span>
<span>{{studentClass}}</span>`
}
// 创建vm
const vm = new Vue({
el: '#root',
// 注册局部组件
components: {
students
}
})
</script>
</body>
依然报错,
提示很明显,意思是组件只能包含一个root元素,所以可以将模板的内容包裹在一个父元素内,来修复这个问题,
<body>
<div id='root'>
<!-- 编写组件标签 -->
<students></students>
</div>
<script>
// 构建局部组件
const students = {
data() {
return {
studentName: '小明',
studentClass: '三年级'
}
},
template: `
<div>
<span>{{studentName}}</span>
<span>{{studentClass}}</span>
</div>`
}
// 创建vm
const vm = new Vue({
el: '#root',
// 注册局部组件
components: {
students
}
})
</script>
</body>
一个简单的局部组件已完成,页面显示如下:
组件标签要也可以写成自闭合标签,如上述组件可写成<students/>
但是使用这种标签书写方式,一般要配合脚手架使用,否则该标签只会被渲染一次,后面的组件标签并不会被解析;
<body>
<div id='root'>
<!-- 编写组件标签 -->
<students/>
<students/>
<students/>
</div>
<script>
// 构建局部组件
const students = {
data() {
return {
studentName: '小明',
studentClass: '三年级'
}
},
template: `
<div>
<span>{{studentName}}</span>
<span>{{studentClass}}</span>
</div>`
}
// 创建vm
const vm = new Vue({
el: '#root',
// 注册局部组件
components: {
students
}
})
</script>
</body>
如上,写了三个<students/>组件,但页面只会解析一个,显示如下:
另外,关于组件标签名的定义方法,可分为多种方式:
若标签由一个单词组成,可写成全是由小写字母组成的名称或者是首字母是大写字母的英文名称,如students、Students
若标签由多个单词组成,则也可分为两种方式,
kebab-case命名方式:即短横线分隔命名方式(都是小写),如my-school;
PascalCase命名方式:即首字母为大写的驼峰命名法,如MySchool;
再来说一下全局组件:
全局组件即定义在全局使用的组件,在创建时与局部组件不一样,并且在vm中也不用再注册局部组件,其余保持一致;
全局组件使用Vue.component('组件名',组件)来创建组件;
<body>
<div id='root'>
<!-- 编写组件标签 -->
<students/>
</div>
<script>
// 构建全局变量
Vue.component('students', {
data() {
return {
studentName: '小明',
studentClass: '三年级'
}
},
template: `
<div>
<span>{{studentName}}</span>
<span>{{studentClass}}</span>
</div>`
})
const vm = new Vue({
el: '#root',
})
</script>
</body>
组件的嵌套
一个组件下含有另一个组件,称为组件的嵌套;如在school组件中嵌套student组件:
<body>
<div id='root'>
<!-- 编写组件标签 -->
<school></school>
</div>
<script>
// 构建局部变量
const student = Vue.extend({
name: 'student',
data() {
return {
studentName: '小明',
studentClass: '三年级'
}
},
template: `
<div>
<span>{{studentName}}</span>
<span>{{studentClass}}</span>
</div>`
})
const school = Vue.extend({
data() {
return {
schollName: '光明',
schoolClass: '高中'
}
},
components: {
student
},
// 注册其包含的组件
template: `
<div>
<span>{{schollName}}</span>
<span>{{schoolClass}}</span>
<student></student>
</div>`
})
const vm = new Vue({
el: '#root',
components: {
school
}
})
</script>
</body>
渲染出来的结构层级如下:
上述所有讲解都是针对于非单文件组件而言的,对于单文件组件而言,是xxxx.vue文件,但是浏览器肯定是无法识别该类型的文件,需要配合webpack和vue脚手架解析称为.js文件,才能被识别使用;
一般而言,.vue文件有两种命名方式:
对于单个单词命名文件,有全小写和首字母大写两种方式,如school.vue和School.vue两种;
对于多个单词命名文件,有短横线连接方式和首字母大写两种方式,如my-school和MySchool.vue
推荐使用School.vue和MySchool.vue方式
vue文件支持三种标签书写:
<template>
<!-- 此时是文件结构 -->
</template>
<script>
// 此处是组件交互相关的代码
</script>
<style>
/*此处是组件的样式 */
</style>
template标签并不会改变结构,渲染后并没有该template标签
<template>
<div class="demo">
<h2>学校名称: {{schoolName}}</h2>
<h2>学校地址: {{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
const school = Vue.extend({
data() {
return {
schoolName: '小学',
address: '这里'
}
},
methods: {
showName() {
alert(this.schoolName)
}
}
})
</script>
<style>
.demo {
color: red;
}
</style>
写出另外一个student组件:
<template>
<div class="demo">
<h2>学生姓名: {{studentName}}</h2>
<h2>年龄: {{age}}</h2>
</div>
</template>
<script>
const student = Vue.extend({
data() {
return {
studentName: '张三',
age: 11
}
}
})
</script>
<style>
.demo {
color: blue;
}
</style>
要暴露上述两个组件给其它调用,一般使用export default
<script>
const school = Vue.extend({
data() {
return {
schoolName: '小学',
address: '这里'
}
},
methods: {
showName() {
alert(this.schoolName)
}
}
})
export default school
</script>
<script>
const student = Vue.extend({
data() {
return {
studentName: '张三',
age: 11
}
}
})
export default student
</script>
又可以精简成:
<script>
export default {
name: 'school', // name一般与文件名一样
data() {
return {
schoolName: '小学',
address: '这里'
}
},
methods: {
showName() {
alert(this.schoolName)
}
}
}
</script>
<script>
export default {
name: 'student', // name一般与文件名一样
data() {
return {
studentName: '张三',
age: 11
}
}
}
</script>
使用app.vue组件来管理所有的组件,在app.vue中引入上述组件,
<template>
<div>
<!-- 依然要保证只有一个根元素 -->
<school></school>
<student></student>
</div>
</template>
<script>
import school from '/school'
import student from '/student'
export default {
name: 'app',
components: {
school,
student
}
}
</script>
<style>
</style>
创建vm实例(main.js)管理app组件:
import app from '/app.vue'
new Vue({
el: '#root',
components: { app }
})
main.js文件又称为项目的入口文件;
最后创建一个容器index.html,与vm实例关联:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>最后一步啦!</title>
</head>
<body>
<div id="#root">
</div>
<script src="js/vue.js"></script>
<!-- 引入vue.js -->
<script src="main.js"></script>
<!-- 引入main.js -->
</body>
</html>
最后运行html文件,发现报错:
是因为浏览器无法解析import语法,因此还是需要将项目放在脚手架里使用