基本示例
组件的定义:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter',{
data:function(){
return{
count:0
}
},
template:'<button v-on:click="count++">点击次数{{count}}</button>'
})
组件的使用:
<div id="components-demo">
<button-counter></button-counter>
</div>
组件的实例化:
new Vue({ el: '#components-demo' })
因为组件是可复用的Vue实例,所以与new Vue接收的参数相同,例如data、computed、watch、methods等。唯一的不同是 el 这样根实例特有的选项。
组件的复用
你可以将组件进行任意次数的复用:
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
点击按钮时,每个组件都会独立维护它的data值,因为每用一次组件就会有个新的实例被创建。
#data必须是一个函数
一个组件的data选项必须是一个函数,每个实例单独维护一份返回对象独立的拷贝。
这样才能不影响到其他实例的data属性值。
组件的组织
通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
为了能在模板中使用,这些组件必须先进行注册才能被识别,这里有 全局注册 和 局部注册 两种注册方式。我们的组件都是通过Vue.component进行全局注册的。
Vue.component('my-component-name', {
// ... options ...
})
全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
通过Prop向子组件传递数据
Prop 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。
Vue.component('test-counter',{
props:["msg"],
template:'<p>{{msg}}</p>'
})
一个组件默认可以有任意数量的Prop,任何值都可以传递给Prop,在组件实例中访问这个值就像访问data中的值一样。
<div id="test" >
<test-counter msg="test01"></test-counter>
<test-counter msg="test02"></test-counter>
</div>
也可以使用v-for动态渲染元素
<div id="test" >
<test-counter v-for="post in posts" v-bind:msg="post.msg"></test-counter>
</div>
new Vue({
el:"#test",
data:{
posts:[
{msg:"test01"},
{msg:"test02"},
{msg:"test03"}
]
}
})
单个根元素
模板中的元素必须包含在一个根节点里面,否则Vue 会显示一个错误,并解释道 every component must have a single root element (每个组件必须只有一个根元素)可以将模板的内容包裹在一个父元素内:
<div class="blog-post">
<h3>{{ title }}</h3>
<div v-html="content"></div>
</div>
当组件变得越来越复杂的时候,为每个相关信息定义一个prop会变得很麻烦,我们可以重构成一个单独的porp属性:
Vue.component('test-counter',{
props:["port"],
template:'<p>{{port.title}}-{{port.auth}}</p>'
})
<test-counter v-for="post in posts" v-bind:port="post"></test-counter>
new Vue({
el:"#test",
data:{
post:[
{title:"test01",auth:"aush1"},
{title:"test02",auth:"aush1"},
{title:"test03",auth:"aush2"}
]
}
})
现在,不论何时为 post 对象添加一个新的属性,它都会自动地在
监听子组件事件
我们开发组件的时候,部分子组件会有和父组件沟通的需求,但是子组件不能直接使用父组件的属性和方法。
Vue提供了一个自定义事件的系统来解决这个问题,父级组件可以像处理 native DOM 事件一样通过v-on来监听子组件实例的任意事件:
<div id="test" v-bind:style="{fontSize:fontSize+'px'}" >
<test-counter v-on:add-size="fontSize+=1" v-for="post in posts" v-bind:port="post"></test-counter>
</div>
同时子组件可以调用内建的 $emit 方法 并传入事件名称来触发一个事件,方法名称必须为全小写,Vue内部会转换大小写:
Vue.component('test-counter',{
props:["port"],
template:'<p>{{port.title}}-{{port.auth}}<button v-on:click="$emit(\'add-size\')">add</button></p>'
})
实例化的时候父级定义一个属性值
new Vue({
el:"#test",
data:{
posts:[
{title:"test01",auth:"aush1"},
{title:"test02",auth:"aush1"},
{title:"test03",auth:"aush2"}
],
fontSize:12
}
})
#使用事件抛出一个值
有时候需要在子组件中抛出特定值,这时可以使用 $emit 函数的第二的参数来提供这个值
<button v-on:click="$emit('add-size',1)">add</button>
父组件监听事件的时候,通过 $event 访问抛出的这个值:
<test-counter
v-on:add-size="fontSize+=$event"
...
></test-counter>
如果事件处理函数是一个方法
<test-counter v-on:add-size="addFontSize" v-for="post in posts" v-bind:port="post"></test-counter>
那么这个值将会作为第一个参数传入这个方法:
methods:{
addFontSize:function(size){
this.fontSize+=size;
}
}
#组件上使用v-model
自定义事件可以用于创建支持 v-model 的自定义输入组件。
<input v-model="searchText">
等价于:
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
当运用到组件上时:
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
为了让其正常工作这个组件内的input必须
- 将其value属性绑定到名称叫做value的prop上
- 在其input事件被触发的时候,通过自定义的input事件抛出
代码如下:
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
现在 v-model 就应该可以在这个组件上完美地工作起来了:
<custom-input v-model="searchText"></custom-input>
通过插槽分发内容
和 HTML 元素一样,我们经常需要向一个组件传递内容
<alert-box>
Something bad happened.
</alert-box>
Error! Something bad happened.
Vue 自定义的 ==
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
我们只要在需要的地方加入插槽就行了
动态组件
有时候在不同组件之间动态切换是非常有用的,比如在多标签界面中。
可以通过component元素加上特殊的is特性来实现
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
例子中currentTabComponent 可以包括:
- 已注册组件的名字,或
- 一个组件的选项对象
解析dom模板注意事项
在html中如<ul>
、<select>
、<ol>
、和<table>
等标签对于哪些元素可以出现在内部有严格限制,而有些元素如<li>
、<option>
、<tr>
等职能出现在特定元素内部。
这会导致我们使用这些约束性的元素时遇到一些问题
<table>
<blog-post-row></blog-post-row>
</table>
这个自定义组件<blog-post-row>
会被作为无效的内容提升到外部,并导致最终渲染结果出错,我们可以使用特殊的is
特性来解决这个问题
<table>
<tr is="blog-post-row"></tr>
</table>
若我们 从以下来源使用模板就没有这种限制
- 字符串(如:
template:''
) - 单文件组件(.vue)
<script type="text/x-template">