前言
组件化的思想可以说是可以渗透在前端开发中方方面面了,而组件化的思想也确实使我们的代码更加简洁、复用性更高之外,也让我们的项目更加的轻巧。需要注意的是,组件化的思想并不是Vue中所特有的,基本上当前三大前端框架的开发都秉持着这种理念。本篇文章将对Vue框架下的组件定义和使用进行讲解,希望对各位读者有所帮助。
一、认识组件化的思想
在定义和使用组件之前,我们不妨先来聊聊什么是组件化。我们知道,一个网站的页面往往是比较复杂的,如果我们把页面上的所有功能维护在一个文件中,那么随着功能的不断增多,内部的代码和样式可能会越来越复杂,也越来越不容易维护。
同时,我们可以发现一个网页上面的内容,其实很多都是可以划分为不同的大模块,大模块下可能还可以再细分出不同的小模块。组件化的思想也就由此而来,我们可以把讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就会容易许多。
以简书的首页为例,我们可以把简书的首页划分为不同的区域,分别拆成不同的组件,这样一旦我们需要改动某个功能的时候,我们只需要关注对应模块的改动即可。同时,我们还可以发现,其实部分组件内部的元素其实是大同小异的,比如简书内容区的文章,其实都是格式相近的内容,所以我们针对这种情况还可以进一步提取为一个组件。
组件化是Vue.js中的重要思想,它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。任何的应用都会被抽象成一颗组件树。
组件化思想的应用:
- 有了组件化的思想,我们在之后的开发中就要充分的利用它。
- 尽可能的将页面拆分成一个个小的、可复用的组件。
- 这样让我们的代码更加方便组织和管理,并且扩展性也更强。
二、注册组件的基本步骤
组件的使用可以分为三个步骤:创建组件构建器 -> 注册组件 -> 使用组件
(一)创建组件构建器
调用Vue.extend()创建的是一个组件构造器。通常在创建组件构造器时,传入template代表我们自定义组件的模板。
该模板就是在使用到组件的地方,要显示的HTML代码。事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。
// 创建组件
const myComponent = Vue.extend({
template:
`<div>
<h2>我是组件的标题</h2>
<h4>我是组件的内容</h4>
</div>`
})
(二)注册组件
调用Vue.component()
是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。所以需要传递两个参数:1、注册组件的标签名 2、组件构造器
// 全局注册组件,第一个参数为组件的名称
Vue.component('my-component',myComponent);
(三)使用组件
使用组件的话十分简单,只需要在APP实例下使用我们在步骤二定义的标签名即可。从结果图中我们可以看到,我们只是使用了定义的组件标签,组件的内容就已经可以展示出来了。可能有人读者会认为,这种做法有些多此一举,明明两行代码的事情,还需要经过组件定义、注册、使用这三个步骤才能实现。
确实,这种想法不无道理,但当我们需要多个地方渲染同一内容,而内容又比较多时,组件的威力就显现了出来。我们只需要使用一下组件的标签就可以完成复用!
<div id="app">
<my-component></my-component>
</div>
同时,我们刚刚提到了使用组件标签的前提是需要在APP
实例下,一般来说全局注册的组件只有在app实例下使用才有效,如果是在<div id="app">
标签外使用组件,那么是不能够被正确解析的。
三、全局组件和局部组件
(一)全局组件
之前我们在注册组件时,使用了Vue.component()
这种方式来实现,其实这种方式注册的组件也叫全局组件,而全局组件可以在任意Vue示例下使用。我们可以看一下下面这个例子:
我们一共定义了2个Vue实例,一个全局注册的自定义组件,我们分别在2个vue实例中和vue实例之外的地方使用全局组件。从最终的结果中可以发现,我们的全局组件可以正常地在两个vue实例中显示。
<body>
<div id="app1">
<my-component></my-component>
</div>
<div id="app2">
<my-component></my-component>
</div>
<my-component></my-component>
<script>
const myComponent = Vue.extend({
template:
`<div>
<h2>我是组件的标题</h2>
<h4>我是组件的内容</h4>
</div>`
})
// 全局注册组件,第一个参数为组件的名称
Vue.component('my-component',myComponent);
let app1 = new Vue({
el: '#app1',
})
let app2 = new Vue({
el: '#app2',
})
</script>
</body>
(二)局部组件
如果我们注册的组件是挂载在某个实例中, 那么这个组件就是一个局部组件。如何将组件挂载到实例中呢?
我们可以看一下下面的做法:
<body>
<div id="app1">
<my-component></my-component>
</div>
<div id="app2">
<my-component></my-component>
</div>
<my-component></my-component>
<script>
const myComponent = Vue.extend({
template:
`<div>
<h2>我是组件的标题</h2>
<h4>我是组件的内容</h4>
</div>`
})
// 注册局部组件
let app1 = new Vue({
el: '#app1',
components:{
'my-component': myComponent
}
})
let app2 = new Vue({
el: '#app2',
})
</script>
</body>
四、父组件和子组件
我们在第一节提到过,一个复杂的页面可以抽取为几个大的组件,而大的组件可能还可以抽取出几个小的组件。此时就涉及到组件中的一种很重要的关系,组件间的父子关系。常常表现为一个大的组件之中包含了几个小的组件,那么这个大的组件就是小组件的父组件。
我们可以看一下下面这个例子:父组件在app实例下注册和使用,子组件在父组件下注册和使用,在页面上也可以正常的使用。
<body>
<div id="app">
<parent-component></parent-component>
</div>
<script>
const childComponent = Vue.extend({
template:
`<div>
<h4>我是子组件的内容</h4>
</div>`
})
const parentComponent = Vue.extend({
template:
`<div>
<h2>我是父组件的标题</h2>
<h4>我是父组件的内容</h4>
<child-component></child-component>
</div>`,
components: {
'child-component': childComponent
}
})
// 注册局部组件
let app = new Vue({
el: '#app',
components:{
'parent-component': parentComponent
}
})
</script>
</body>
我们再看下面这个例子,子组件在父组件中注册,但是是在APP实例下使用,那么此时是无法正确解析出子组件的。因为当子组件注册到父组件的components时,Vue会编译好父组件的模块,该模板的内容已经决定了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了),所以<child-component></child-component>
是只能在父组件中被识别的。
<body>
<div id="app">
<parent-component></parent-component>
<child-component></child-component>
</div>
<script>
const childComponent = Vue.extend({
template:
`<div>
<h4>我是子组件的内容</h4>
</div>`
})
const parentComponent = Vue.extend({
template:
`<div>
<h2>我是父组件的标题</h2>
<h4>我是父组件的内容</h4>
</div>`,
components: {
'child-component': childComponent
}
})
// 注册局部组件
let app = new Vue({
el: '#app',
components:{
'parent-component': parentComponent
}
})
</script>
</body>
PS:其实这一小节除了想要讲述组件间的父子关系之外,更重要的还是想要提醒各位读者在使用组件时,一定要注意好组件的作用域,避免出现组件无法渲染的情况发生。
五、组件的简写方式
(一)简写组件的注册方式
我们之前定义组件时,采用的方式是Vue.extend()
来定义具体一个模板的内容,而后再进行全局或者局部的注册。但实际上,我们可以省去了调用Vue.extend()的步骤,直接使用一个对象来代替。
<body>
<div id="app">
<my-component></my-component>
</div>
<script>
// 注册局部组件
let app = new Vue({
el: '#app',
components: {
'my-component': {
template:
`<div>
<h2>我是组件的标题</h2>
</div>`,
}
}
})
</script>
</body>
(二)模板分离
相比于上面提到的简写注册组件的方式,模板分离也同样简化了我们定义组件的方式,模板分离将template
中的HTML分离出来写,然后挂载到对应的组件上,让我们的代码结构变得非常清晰。
我们可以看一下下面的这个案例,我们在上一小节的基础上,使用模板分离的方式再次简化了我们的代码。(其实也不算特别简化,但是template的存在使得代码结果比以前要清晰很多!)
<body>
<div id="app">
<my-component></my-component>
</div>
<template id="myComponent">
<div>
<h2>我是组件的标题</h2>
</div>
</template>
<script>
// 注册局部组件
let app = new Vue({
el: '#app',
components: {
'my-component': {
template: "#myComponent"
}
}
})
</script>
</body>
除了用template
标签之外,我们还可以使用script
标签来抽取我们的html
<body>
<div id="app">
<my-component></my-component>
</div>
<script type="text/x-template" id="myComponent">
<div>
<h2>我是组件的标题</h2>
</div>
</script>
<script>
// 注册局部组件
let app = new Vue({
el: '#app',
components: {
'my-component': {
template: "#myComponent"
}
}
})
</script>
</body>
六、组件的data
实际上,每个组件内部都可以维护属于自身的data,用于组件内部用于展示或者计算。这点相比大家都知道,这里要说的是对于data的定义方式。组件中的data属性必须是一个函数,原因很简单,Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。
这是什么意思呢?我们来看一下下面这个案例:
由于组件的data如果不是函数的话会报错,所以下面的案例我们使用了一个外部定义的obj来模拟data不为函数的结果。
<body>
<div id="app">
<my-component></my-component>
<my-component></my-component>
<my-component></my-component>
</div>
<template id="myComponent">
<div>
<button @click="add">+1</button>
<span>当前数量为:{{count}}</span>
</div>
</template>
<script>
const obj = {
count : 0
}
// 注册局部组件
let app = new Vue({
el: '#app',
components: {
'my-component': {
template: "#myComponent",
data(){
return obj
},
methods:{
add(){
this.count++;
}
}
}
}
})
</script>
</body>
正确的做法是让每个组件都有独立的data作用域
<body>
<div id="app">
<my-component></my-component>
<my-component></my-component>
<my-component></my-component>
</div>
<template id="myComponent">
<div>
<button @click="add">+1</button>
<span>当前数量为:{{count}}</span>
</div>
</template>
<script>
let app = new Vue({
el: '#app',
components: {
'my-component': {
template: "#myComponent",
data(){
return {
count : 0
}
},
methods:{
add(){
this.count++;
}
}
}
}
})
</script>
</body>