组件基础
基本示例
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
<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>
- 注意:
data
必须是一个函数
data: function () {
return {
count: 0
}
}
组件的组织
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。
通过Prop向子组件传递数据
<div id="app">
<!-- 方法一 -->
<blog-post message="我是message"></blog-post>
<hr />
<!-- 方法二 -->
<blog-post
v-for="(item, index) in post"
:key="index"
:message="item"
></blog-post>
</div>
<script>
Vue.component('blog-post', {
props: ['message'],
template: '<h3>{{message}}</h3>'
})
let vm = new Vue({
el: '#app',
data: function () {
return {
post: ['java', 'python', 'vue']
}
}
})
</script>
单个根元素
如果你在模板中尝试这样写,Vue 会显示一个错误,并解释道 every component must have a single root element (每个组件必须只有一个根元素)。你可以将模板的内容包裹在一个父元素内,来修复这个问题
每个定义的组件都必须包含在唯一的一个根元素内。
监听子组件事件
-
父级组件可以通过
v-on
监听子组件实例的任意事件<blog-post ... v-on:enlarge-text="postFontSize += 0.1" ></blog-post>
-
同时子组件可以通过调用内建的
$emit
方法并传入事件名称来触发一个事件<button v-on:click="$emit('enlarge-text')"> Enlarge text </button>
示例
<div id="app" :style="{fontSize:postFontSize+'em'}">
<blog-post
v-for="(item, index) in post"
:key="index"
:blogs="item"
@enlarge-text="item.postFontSize++"
:style="{fontSize:item.postFontSize+'em'}"
></blog-post>
<div :style="{fontSize:postFontSize+'em'}">aaaaa</div>
<button @click="postFontSize++">全部放大</button>
</div>
<script>
Vue.component('blog-post', {
props: ['blogs'],
template: `<div>
<h3>name : {{ blogs.name }}</h3>
<span>book is {{ blogs.book }}</span>
<button @click="$emit('enlarge-text')">放大此处</button>
<hr />
</div>`
})
let vm = new Vue({
el: '#app',
data: function () {
return {
post: [
{ name: 'David', book: 'Flowers', postFontSize: 1 },
{ name: 'Alice', book: 'trees', postFontSize: 1 },
{ name: 'Bob', book: 'Fishes', postFontSize: 1 }
],
postFontSize: 1
}
}
})
</script>
使用事件抛出一个值
有的时候用一个事件来抛出一个特定的值是非常有用的。例如我们可能想让 <blog-post>
组件决定它的文本要放大多少。这时可以使用 $emit
的第二个参数来提供这个值:
<button v-on:click="$emit('enlarge-text', 0.1)">
Enlarge text
</button>
然后当在父级组件监听这个事件的时候,我们可以通过 $event
访问到被抛出的这个值:
<blog-post
...
v-on:enlarge-text="postFontSize += $event"
></blog-post>
或者,如果这个事件处理函数是一个方法:
<blog-post
...
v-on:enlarge-text="onEnlargeText"
></blog-post>
那么这个值将会作为第一个参数传入这个方法:
methods: {
onEnlargeText: function (enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
在组件上使用v-model
自定义事件也可以用于创建支持 v-model
的自定义输入组件。记住:
<input v-model="searchText">
等价于:
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
当用在组件上时,v-model
则会这样:
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
为了让它正常工作,这个组件内的 <input>
必须:
- 将其
value
attribute 绑定到一个名叫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>
通过插槽分发内容
<div id="app">
<blog-post>Good things have happened!</blog-post>
</div>
<script>
Vue.component('blog-post', {
props: ['post'],
template: `
<div>
<strong>Great!</strong>
<h3><slot></slot></h3>
</div>`
})
let vm = new Vue({
el: '#app',
data: function () {
return {}
}
})
</script>
动态组件
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里
可以通过 Vue 的
<component>
元素加一个特殊的is
attribute 来实现:
:is
后面的内容可以包括:
- 已注册组件的名字,或
- 一个组件的选项对象
方法一:直接通过methods方法
<div id="app">
<button @click="changeComponent">change</button>
<hr />
<component :is="componentId[times]"></component>
</div>
<script>
Vue.component('one', {
template: `<h1>I am one</h1>`
})
Vue.component('two', {
template: `<h1>I am two</h1>`
})
Vue.component('three', {
template: `<h1>I am three</h1>`
})
let vm = new Vue({
el: '#app',
data: function () {
return {
componentId: ['one', 'two', 'three'],
times: 0
}
},
methods: {
changeComponent: function () {
if (this.times != 2) {
this.times++
} else {
this.times = 0
}
}
}
})
</script>
方法二:通过computed计算属性
<div id="app">
<button @click="changeComponent">change</button>
<hr />
<component :is="currentComponentId"></component>
</div>
<script>
Vue.component('one', {
template: `<h1>I am one</h1>`
})
Vue.component('two', {
template: `<h1>I am two</h1>`
})
Vue.component('three', {
template: `<h1>I am three</h1>`
})
let vm = new Vue({
el: '#app',
data: function () {
return {
componentId: ['one', 'two', 'three'],
times: 0
}
},
computed: {
currentComponentId: function () {
return this.componentId[this.times]
}
},
methods: {
changeComponent: function () {
this.times == 2 ? (this.times = 0) : this.times++
}
}
})
</script>
解析DOM模板时的注意事项
有些 HTML 元素,诸如
<ul>
、<ol>
、<table>
和<select>
,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如<li>
、<tr>
和<option>
,只能出现在其它某些特定的元素内部。
<div id="app">
<ol>
<li is="blog-post"></li>
</ol>
</div>
<script>
Vue.component('blog-post', {
props: ['post'],
template: `<div>
<li>aaa</li>
<li>bbb</li>
<li>ccc</li>
</div>`
})
let vm = new Vue({
el: '#app',
data: function () {
return {}
}
})
</script>
如果我们从以下来源使用模板的话,这条限制是*不存在*的:
- 字符串 (例如:
template: '...'
) - 单文件组件 (
.vue
) <script type="text/x-template">