Vue.js学习笔记——11.组件化开发(二)
Vue.js学习笔记——12.组件化开发(三)
一、组件化的基本使用
如果我们将一个页面所有的处理逻辑全部放在一起,处理起来就会变得十分复杂,而且不利于后续的管理以及扩展。但如果我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么整个页面的管理和维护就会变得非常容易了。这就是组件化的思想,它提供了一种抽象,让我们可以开发出一个个独立的可复用的小组件来构造我们的应用。任何的应用都会被抽象成一个组件树。
组件的使用分成三个步骤:
- 调用Vue.extend()方法创建组件构造器
- 调用Vue.component(‘组件标签名’,构造器名)方法注册组件
- 在Vue实例的作用范围内使用组件
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器
const cpn = Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>`
})
// 2.注册组件
Vue.component(`my-cpn`,cpn)
const app = new Vue({
el:"#app"
})
</script>
</body>
</html>
执行结果:
可以看到在第一步创建组件构造器时在template模板属性内传入了一组件模板。要注意在template中传入的是ES6的模板字符串,使用的不是单引号也不是双引号,而是Tab键上方的``,区别是它可以支持换行。
第二步注册了组件后就可以在Vue实例挂载的区域内多次使用使用组件了,注意第一二不要写在Vue实例之前,也不能在Vue挂载的区域外使用,否则不起效果。
二、全局组件与局部组件
按照刚刚的方法注册的组件是全局组件,意味着可以在多个Vue实例下使用。如果需要创建只能在特定Vue实例中使用的组件,则要在Vue实例中的components属性中注册组件。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 3.使用组件 -->
<mycpn></mycpn>
<mycpn></mycpn>
</div>
<div id="app2">
<mycpn></mycpn>
<mycpn></mycpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器
const cpn = Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>`
})
// 2.注册全局组件,意味着可以在多个Vue实例下使用
// Vue.component(`mycpn`,cpn)
const app = new Vue({
el:"#app",
components:{
//2.在Vue实例中注册局部组件 组件标签名:构造器名
mycpn:cpn
}
})
const app2 = new Vue({
el:"#app2"
})
</script>
</body>
</html>
执行结果:
可以看到在app实例下的局部组件,只能在app实例中使用,在app2实例中并没有解析出来。
在实际开发中,局部组件比较常用,因为一般只会创建一个Vue实例,在实例中注册局部组件即可。
三、父组件与子组件
不止在Vue实例中可以使用components注册组件,在组件构造器中也能使用components注册组件,这样构造的组件就互为父子关系。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 4.使用组件 -->
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器(子组件)
const cpnC = Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>`
})
//2.在组件构造器内注册组件(父组件)
const cpnC2 = Vue.extend({
template:`
<div>
<h2>我是标题2</h2>
<p>我是内容2</p>
<cpn1></cpn1>
</div>`,
components:{
cpn1:cpnC
}
})
const app = new Vue({
el:"#app",
components:{
//3.在Vue实例中注册全局组件 组件标签名:构造器名
cpn2:cpnC2
}
})
</script>
</body>
</html>
执行结果:
在页面中只使用了一个cpn2标签,可在cpn2中的cpn组件也被成功解析出来,此时cpn2就是父组件,cpn为子组件。由于此时cpn只在cpn2中注册,只能在cpn2的作用域使用,所以在页面中不能单独使用,如果需要使用cpn则必须为它注册全局或局部组件。
Vue实例可以看成所有组件的根组件。
四、注册组件的语法糖与组件抽离的写法
Vue2.x版本提供了注册组件的语法糖,直接将模板传入component()中,可以省去extend(),但在Vue内部还是通过extend()实现的,只不过是将一二步创建和注册合为一步而已。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<cpn></cpn>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器
// const cpnC = Vue.extend({
// template:`
// <div>
// <h2>我是标题</h2>
// <p>我是内容</p>
// </div>`
// })
// 2.注册组件
// Vue.component(`cpn`,cpnC)
Vue.component('cpn',{
template:`
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>`
})
const app = new Vue({
el:"#app",
components:{
//在Vue实例中注册局部组件
cpn2:{
template:`
<div>
<h2>我是标题2</h2>
<p>我是内容2</p>
</div>`
}
}
})
</script>
</body>
</html>
执行结果:
无论是全局组件还是局部组件都可以通过语法糖来注册,这也是现在都看不到extend()方法的原因。
即使如此,在script中写html标签时还是显得臃肿,而且在模板字符串中写html时操作也并不方便,使用就要使用组件抽离的写法,直接在html中写模板。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<!--模板标签-->
<template id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册全局组件
// Vue.component(`cpn`,{
// template:`#cpn`
// })
const app = new Vue({
el:"#app",
//注册局部组件
components:{
cpn:{
template: `#cpn`
}
}
})
</script>
</body>
</html>
执行结果:
只需在template标签中写模板并添加id属性,在注册时即可直接通过id来使用模板。
五、组件的data属性
组件是一个单独功能模块的封装,也拥有自己的data属性,并且组件不能直接访问Vue实例中的data,组件有自己保存数据的地方,就是组件自己的data。组件的data和Vue实例中的data不一样,它不能是一个对象类型,它是一个函数并且返回一个对象。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<!--模板标签-->
<template id="cpn">
<div>
<h2>{{title}}</h2>
<p>我是内容</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册全局组件
Vue.component(`cpn`,{
template:`#cpn`,
//data是一个函数,返回一个对象,数据存放在返回的对象中
data(){
return{
title:`我是标题123`
}
}
})
const app = new Vue({
el:"#app",
})
</script>
</body>
</html>
执行结果:
组件的data是一个函数,返回一个对象,数据存放在返回的对象中。
但为什么组件的data必须是个函数呢? 这个问题面试时经常会问到。
举个栗子:现在我想要把之前学习v-on时写的计数器Vue.js学习笔记——4.v-on指令的使用封装成一个组件,以便进行多次使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<!--模板标签-->
<template id="cpn">
<div>
<h2>计数器:{{num}}</h2>
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册全局组件
Vue.component(`cpn`,{
template:`#cpn`,
//data是一个函数,返回一个对象,数据存放在返回的对象中
data(){
return{
num:0
}
},
methods:{
add(){
this.num++
},
sub(){
this.num--
}
}
})
const app = new Vue({
el:"#app",
})
</script>
</body>
</html>
执行结果:
可以看到,尽管使用了三次计数器组件,但是点击各自的按钮后他们之间不会相互影响每个组件都有属于自己的num,这就data为什么要是函数,这样就确保每次使用组件,data都返回了一个不同的对象,指向不同的地址空间,避免了他们之间相互影响造成的共享变量的安全问题,确保了组件的复用性。
如果希望组件共享变量(其实不会这么希望的,因为组件不是这么用的)可以在注册组件前创建一个全局对象,在data中返回这个对象即可:
const obj = {
num:0
}
Vue.component(`cpn`,{
template:`#cpn`,
//data是一个函数,返回一个对象,数据存放在返回的对象中
data(){
return obj
},
methods:{
add(){
this.num++
},
sub(){
this.num--
}
}
})