一、什么是组件
所谓的组件,其实就是我们通过vue自定义的一些标签。在自定义的标签中,其实已经封装好了一些自定义的特定的HTML代码。
你也许会疑问,为什么要特意的将HTML代码封装起来。其实是这样的,我们使用组件一方面可以减少HTML的代码(视觉上是这样的),另一方面可以极大的提高代码的复用。
你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。通过组件可以使你的页面更加的结构化,利于维护。
 正如上图所示,通常一个页面会以一棵嵌套的组件树的形式来组织。
二、为什么需要组件
组件是Vue中的一个重要概念,是一个可以重复使用的Vue是可以复用的Vue实例,它拥有独一无二的组件名称,它可以扩展HTML元素,以组件名称的方式作为自定义的HTML标签。
因为组件是可复用的Vue实例,所以它们与new Vue()接收相同的选项,例如data,computed、watch、methods以及生命周期钩子等。仅有的例外是像el这样根实例特有的选项。
在一个绝大多数的系统网页中,网页都包含header、menu、body、footer等部分。像上述的那些东西可能会在多个页面也重复使用,我们可以将其分装成一个一个的组件,那么当需要使用到的时候,引用这个组件即可。
总结下来:
- 提高开发效率
- 方便重复使用
- 简化调试步骤
- 提升项目的可维护性
- 便于多人协同开发
三、创建一个组件
我们现在尝试着创建一个简单的组件,如下:
component():创建组件的方法
Vue.component('my-component', {
template: '<li>我是 寒江coder</li>'
})
let app = new Vue({
el:"#app"
})
至此,我们就已经创建了一个简单的组件,其名为 my-component。下面的简单的使用该组件。
<ul>
<my-component></my-component>
</ul>

注意: 对于组件的使用,我有话要说。因为我们的组件是在通过Vue创建的,若想让组件生效,需要通过el:"#app"绑定一个根标签,将我们定义好的组件在Vue可以识别的根标签中使用。
四、组件的复用
因为上面说过,组件的意义之一就是实现代码的复用,接下来将会介绍一下,如何实现组件的复用。
我们先定义一个按钮组件:
data:这里的和之前的Vue({…})中的data是不同的。在Vue({…})中的data是一个对象(data:{…});而在这里的data必须是一个方法(data:function(){…})
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
new Vue({ el: '#components-demo' })
<div id="components-demo">
<button-counter></button-counter>
</div>

你可以将组件进行任意次数的复用:
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>

我们可以发现,被复用的组件之间是独立的(当点击按钮时,每个组件都会各自独立维护它的 count)。因为你每用一次组件,就会有一个它的新实例被创建。
五、通过 Prop 向子组件传递数据
Prop 是你可以在组件上注册的一些自定义 属性(attribute )。当一个值传递给一个 prop 属性的时候,它就变成了那个组件实例的一个 property。
比如说,我们定义一个“博文组件”,如下:
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
+'<div>...</div>'
})
我们都知道一篇博文必然要有它的标题,那么我们就可以通过props给这个“博文组件”定义一个“标题”属性“title”。
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。
我们可以像这样直接给自定义属性传递信息,如下:
<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>

但是通常情况下,我们所需要的数据都是从数据库中层批量的获取到的。我们模拟了一下通常情况下数据的渲染情况,如下: 你所需的数据可能会被存放在data中。
new Vue({
el: '#blog-post-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
})
那么我们可以在使用自定义组件时使用v-for和v-bind进行数据的获取与绑定,如下:
<blog-post
v-for="post in posts"
v-bind:title="post.title"
></blog-post>
 v-bind:title=“post.title”:从通过v-bind成功绑定title属性,我们可以看到通过pops定义的属性它确实变成了组件实例的一个 property。
在一个博文中,我们不可能只有一个标题,肯定还有文章和发布时间,那么我们是否可以仿照上面的内容添加一个“内容”呢?如下的方式是不对的
Vue.component('blog-post', {
props: ['title',"content"],
template:`
<h2>{{title}}</h2>
<div v-html='content'></div>
`
})
new Vue({
el: '#components-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue',content:"啥的哈达哈打电话IU是洒水" },
{ id: 2, title: 'Blogging with Vue',content:"我回答碎碎念我肯定帅是ASH其实撒"}
]
}
})
<div id="components-demo">
<blog-post
v-for="post in posts"
v-bind:title="post.title"
v-bind:content="post.content"
></blog-post>
</div>

造成< div>{{content}}< /div>无法正常显示的原因是每个组件必须只有一个根元素。你可以将模板的内容包裹在一个父元素内,来修复这个问题,如下
Vue.component('blog-post', {
props: ['title',"content"],
template:`
<div> <!--这个div就是“父元素”-->
<h2>{{title}}</h2>
<div v-html='content'></div>
</div>
`
})

上述的解决方案虽然解决了问题,但是我们的博文不只需要标题和内容,还需要发布日期、评论等等,这样的话其中会使得整个组件的v-bind使用的次数非常的多。为每个相关的信息定义一个 prop 会变得很麻烦
我们可以重构一下posps的内容,因为在Vue中的data中是有一个post属性的,post属性中包含着我们需要的标题和内容甚至是发布日期、评论等等。
我们可以在props中只是设置一个post属性,只绑定一个post属性,在模版(template)中通过post.title、post.content获取需要的内容。如下:
Vue.component('blog-post', {
props: ["post"],
template:`
<div>
<h2>{{post.title}}</h2>
<div v-html='post.content'></div>
</div>
`
})
new Vue({
el: '#components-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue',content:"啥的哈达哈打电话IU是洒水" },
{ id: 2, title: 'Blogging with Vue',content:"我回答碎碎念我肯定帅是ASH其实撒"}
]
}
})
<div id="components-demo">
<blog-post
v-for="post in posts"
v-bind:post="post"
></blog-post>
</div>

六、通过插槽分发内容
我们都知道,自定义组件其实就是一个标签,在HTML中的标签我们是可以在标签体中传值的,如下:
<div>这是在div的标签体中传入的值</div>
但是在我们自定义的标签体中,传值是没有任何的效果的,是不会显示的。如下:
<div id="components-demo">
<blog-post>传值是没有任何的效果的</blog-post>
</div>
为了解决上述的问题,Vue 自定义的 < slot> 元素让这变得非常简单,我可以在template中使用 < slot> 用来接收标签体中的内容。
Vue.component('blog-post', {
template:`
<div>
<slot></slot>
</div>
`
})
new Vue({
el: '#components-demo'
})
<div id="components-demo">
<blog-post>这次就有效果了</blog-post>
</div>

七、动态组件
Vue 中我们可以通过 < component> 元素加一个特殊的 is 属性来实现当前使用哪一个组件。
<component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent 可以包括
在进行具体的操作之前,我们需要了解几个新的Vue属性
components:用于创建模板使用的,它和Vue.component(组件名,{})的效果是一样的。在其中定义组件的格式是 组件名:{}。components中可以定义多个组件,属于局部组件。
computed:是计算属性的;。它会根据所依赖的数据动态显示新的计算结果, 该计算结果会被缓存起来。可以接收方法的返回值,或者运算表达式的结果。
下面我们来进行具体的操作一下
new Vue({
el: '#components-demo',
data:{
index:0,
arr:['home','post','archive'],
},
components: {
home:{template:'<div>我是主页</div>'},
post:{template:'<div>我是提交页</div>'},
archive:{template:'<div>我是存档页</div>'},
},
computed:{
currentComponent:function(){
return this.arr[this.index]
}
},
methods:{
change:function(){
this.index = (++this.index)%3
}
}
})
<div id="components-demo">
<button v-on:click="change">切换页面</button>
<component v-bind:is="currentComponent"></component>
</div>
当点击按钮的时候,会帝爱用change()方法,将index进行加一取模。
is="currentComponent"会调用currentComponent属性,该属性表示当前的组件名,进而实现当前使用哪个组件。   
八、注意事项
有些 HTML 元素,诸如 < ul>、< ol>、< table> 和 < select>,对于哪些元素可以出现在其内部是有严格限制的。
而有些元素,诸如 < li>、< tr> 和 < option>,只能出现在其它某些特定的元素内部。
通过上述的描述,我知道,在< table>中我们自定义的组件时不会被识别的
<div id="components-demo">
<table>
<myRow></myRow>
</table>
</div>
<script>
new Vue({
el: '#components-demo',
components: {
myRow:{template:'<div>我是主页</div>'},
}
})
</script>
上述的< myRow>是不会被识别的。
为此,vue给出了解决方案,我们可以通过 is 属性来声明那些组件在特殊的父标签中有效
<div id="components-demo">
<table>
<tr is="myRow"></tr>
</table>
</div>
< tr is=“myRow”>表示组件myRow作为tr进行渲染
因此就解决了特殊标签中的组件无法被渲染的问题。
|