組件
什么是組件?
組件(Component)是 Vue.js 最強大的功能之一。組件可以擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素, Vue.js 的編譯器為它添加特殊功能。在有些情況下,組件也可以是原生 HTML 元素的形式,以 js 特性擴展。
例子

舉個小例子:豆瓣電影排行榜
此例子包含文章列表,分頁,這里把列表和分頁分別當作單獨的組件,加上父頁面組件一共是3個
父組件:app.vue
子組件:list.vue pagination.vue
接口用的是豆瓣的jsonp,https://api.douban.com/v2/movie/top250
首先在main.js引入父組件,引入less,引入VueResource,實例化vue
//main.js
import Vue from 'vue'
import app from './example1/app.vue'
import './example1/style.less'
import VueResource from 'vue-resource'
Vue.use(VueResource)
new Vue({
render: h => h(app)
}).$mount('#app')
從分頁開始,在src/example1/components文件夾下建立一個pagination.vue文件
<!--pagination.vue-->
<template>
<div class="text-center">
<ul class="pagination">
<li></li>
</ul>
</div>
</template>
<script type="text/javascript">
export default {
data () {
return {
}
},
props: {
},
methods: {
},
computed: {
},
components: {
},
mounted () {
}
}
</script>
通過props從父組件接收數(shù)據(jù)。組件可以為props指定驗證要求。如果未指定驗證要求,Vue會發(fā)出警告,如果不需要驗證,可以用數(shù)組形式
//pagination.vue
props: {
//當前頁碼
now: {
type: Number,
default: 1
},
//總頁數(shù)
all: {
type: Number,
default: 1,
required: 1
}
}
父組件app.vue引入組件,并且把數(shù)據(jù)傳給子組件
<!--app.vue-->
<!--代碼片段-->
<pagination :now="page.now" :all="page.all"></pagination>
<!--代碼片段-->
<script type="text/javascript">
import pagination from './components/pagination.vue'
export default {
data () {
return {
page: {
now: 1,
all: 10
}
}
},
components: {
pagination
}
}
</script>
根據(jù)傳入的數(shù)據(jù)計算分頁數(shù)據(jù)
//pagination.vue
computed: {
pages () {
var pages = [];
if(this.now > 1) pages.push({
text: '上一頁',
num: this.now - 1
});
if(this.now - 1 > 2) pages.push({
text: 1,
num: 1
});
if(this.now - 1 > 3) pages.push({
text: '...',
num: 0
});
for (var i = 1; i <= this.all; i++) {
if(this.now - i < 3 && this.now >= i || i - this.now < 3 && this.now <= i){
pages.push({
text: i,
num: i
});
}
}
if(this.all - this.now > 3) pages.push({
text: '...',
num: 0
});
if(this.all - this.now > 2) pages.push({
text: this.all,
num: this.all
});
if(this.now < this.all) pages.push({
text: '下一頁',
num: this.now + 1
});
return pages;
}
}
用v-for把分頁渲染出來
<!--pagination.vue-->
<template>
<ul class="pagination">
<li v-for="page in pages" :class="{ 'active' : page.num == now }">
<a v-if="page.num" @click="toPage(page.num)">{{ page.text }}</a>
<span v-else>{{ page.text }}</span>
</li>
</ul>
</template>
子組件通過自定義事件向父組件傳事件和數(shù)據(jù)
//pagination.vue
methods: {
toPage (page) {
this.$emit('toPage', page);
}
}
父組件監(jiān)聽這個事件,然后調(diào)用方法changePage
<!--app.vue-->
<pagination :now="page.now" :all="page.all" @toPage="changePage"></pagination>
<script>
//代碼片段
methods: {
changePage (page) {
this.page.now = page
}
}
//代碼片段
</script>
至此完成了父組件把數(shù)據(jù)丟給子組件處理,子組件把操作反饋給父組件這一過程
列表組件list.vue
<!--list.vue-->
<template>
<div>
<slot name="title"></slot>
<div class="list clearfix row">
<div class="col-xs-6" v-for="item in items">
<div class="item">
<div class="img">
</div>
<slot name="year" :year="item.year">
<div class="year">{{ item.year }}</div>
</slot>
<div class="title">{{ item.title }}</div>
<div class="dis">{{ item.original_title }}</div>
</div>
</div>
</div>
</div>
</template>
<script type="text/javascript">
export default {
data () {
return {
}
},
props: ['items']
}
</script>
來點動態(tài)的數(shù)據(jù)
<!--app.vue-->
<template>
<div class="container">
<div class="loading" v-show="loading"></div>
<list :items="items">
<h1 class="text-center" slot="title">豆瓣電影排行榜</h1>
<template slot="year" scope="props">
<div class="year red">{{ props.year }}</div>
</template>
</list>
<pagination :now="page.now" :all="page.all" @toPage="changePage"></pagination>
</div>
</template>
<script type="text/javascript">
import list from './components/list.vue'
import pagination from './components/pagination.vue'
export default {
data () {
return {
loading: 0,
params: {
//每頁顯示條數(shù)
count: 10,
//從第幾條開始顯示,初始0
start: 0,
//總條數(shù)
total: 250
},
items: []
}
},
methods: {
getItems () {
this.loading = 1;
this.$http.jsonp('https://api.douban.com/v2/movie/top250', {
params: this.params
}).then(response => {
if(response.ok) {
this.items = response.data.subjects;
this.params = {
count: response.data.count,
start: response.data.start,
total: response.data.total
}
}
}, response => {
console.log(response);
}).finally(function(){
this.loading = 0;
})
},
changePage (page) {
this.params.start = (page - 1) * this.params.count;
this.getItems();
}
},
components: {
list,
pagination
},
computed: {
page () {
return {
now: this.params.start / this.params.count + 1,
all: Math.ceil(this.params.total / this.params.count)
}
}
},
mounted () {
this.getItems();
}
}
</script>
幾個點:
prop傳遞數(shù)據(jù)
組件實例的作用域是孤立的。這意味著不能并且不應(yīng)該在子組件的模板內(nèi)直接引用父組件的數(shù)據(jù)。可以使用 props 把數(shù)據(jù)傳給子組件。
類似于用 v-bind 綁定 HTML 特性到一個表達式,也可以用 v-bind 動態(tài)綁定 props 的值到父組件的數(shù)據(jù)中。每當父組件的數(shù)據(jù)變化時,該變化也會傳導給子組件:
自定義事件
父組件可以在使用子組件的地方直接用 v-on 來監(jiān)聽子組件觸發(fā)的事件
slot分發(fā)內(nèi)容
組件不知道它的掛載點會有什么內(nèi)容。掛載點的內(nèi)容是
由父組件決定的;組件很可能有它自己的模版
為了讓組件可以組合,我們需要一種方式來混合父組件的內(nèi)容與子組件自己的模板。這個過程被稱為 內(nèi)容分發(fā) (或 “transclusion” 如果你熟悉 Angular)。Vue.js 實現(xiàn)了一個內(nèi)容分發(fā) API ,參照了當前 Web 組件規(guī)范草案,使用特殊的
<slot>元素作為原始內(nèi)容的插槽。
通過slot插槽將標題插入到子組件
作用域插槽
父組件模板的內(nèi)容在父組件作用域內(nèi)編譯;子組件模板的內(nèi)容在子組件作用域內(nèi)編譯
作用域插槽是一種特殊類型的插槽,用作使用一個(能夠傳遞數(shù)據(jù)到)可重用模板替換已渲染元素。
在子組件中,只需將數(shù)據(jù)傳遞到插槽,就像你將 prop 傳遞給組件一樣
通過作用域插槽將子組件數(shù)據(jù)傳給父組件,再作為自定義模板重新插入到子組件
計算
豆瓣接口接收參數(shù)和設(shè)計的不同,pages需要通過computed計算