1 什麼是元件¶
當頁面功能十分複雜時,將頁面各功能化整為零,拆分成多個不同的小部分,這樣便於分析,也有利於程式碼的複用,在vue中,我們將劃分後的每個小部分稱為元件。元件化是vue裡面最強的功能,可以擴充套件html,封裝重用的程式碼。在使用vue時,我們可以將一個完整的頁面拆分為多個元件,每個元件都用於實現頁面的一個功能塊,每一個元件又可以進行細分,從而將頁面整個應用抽象成一顆元件樹,如下圖所示。
(1)Vue.extend():
- 呼叫Vue.extend()建立的是一個元件構造器。
- 通常在建立元件構造器時,傳入template代表我們自定義元件的模板。
- 該模板就是使用到元件的地方要使用的HTML程式碼。
- 事實上,這種寫法在Vue2.0以上的版本的文件中幾乎已經看不到了,很少會直接使用,但是在很多資料中會提到這種方式,而且這種方式是其他建立元件方式的基礎。
(2)Vue.component():
- 呼叫Vue.component()是將剛才的元件構造器租車位一個元件,並且給它起一個元件的標籤名稱。
- 需要傳遞兩個引數:一是註冊元件的標籤名,二是元件構造器
(3)元件必須掛載在某個Vue例項下,在例項對應的容器之外是不會生效的。
一個基本的元件使用示例如下:
<div id="root">
<hello></hello>
<hello></hello>
</div>
<hello></hello> <!-- 此處因為在容器外,使用無效-->
<script type="text/javascript">
Vue.config.productionTip = false
//第一步:建立hello元件
const hello = Vue.extend({
template:`
<div class="demo">
<h2>Hello, {{lang}}</h2>
</div>
`,
data(){
return {
lang:'python' }
},
})
//第二步:全域性註冊元件
Vue.component('hello',hello)
//第三步:建立vm
new Vue({
el:'#root',
})
</script>
3 全域性元件與區域性元件¶
根據註冊方式的不同,可以將元件分為全域性元件和區域性元件。在上文中,我們註冊元件的方式就是全域性元件,這種註冊方式的好處元件鍵可以再多個vue例項中使用。
<!-- 準備好一個容器-->
<div id="root">
<hello></hello>
<hello></hello>
</div>
<!-- 準備好第二個容器-->
<div id="root2">
<hello></hello>
<hello></hello>
</div>
<hello></hello> <!-- 此處因為在容器外,使用無效-->
<script type="text/javascript">
Vue.config.productionTip = false
//第一步:建立hello元件
const hello = Vue.extend({
template:`
<div class="demo">
<h2>Hello, {{lang}}</h2>
</div>
`,
data(){
return {
lang:'python' }
},
})
//第二步:全域性註冊元件,可以再多個容器中使用
Vue.component('hello',hello)
//第三步:建立vm
new Vue({
el:'#root',
})
new Vue({
el:'#root2',
})
</script>
區域性元件是指在例項化Vue時,使用components關鍵字進行配置,從而完成組成,這種方式註冊的元件只能在Vue例項對應的容器內部使用:
<!-- 準備好一個容器-->
<div id="root">
<hello></hello>
<hello></hello>
</div>
<!-- 準備好第二個容器-->
<div id="root2">
<hello></hello> <!-- 因為是區域性元件,此處不生效-->
</div>
<script type="text/javascript">
Vue.config.productionTip = false
//第一步:建立hello元件
const hello = Vue.extend({
template:`
<div class="demo">
<h2>Hello, {{lang}}</h2>
</div>
`,
data(){
return {
lang:'python'
}
},
})
new Vue({
el:'#root',
components: { //第二步:區域性註冊元件,僅可以在
hello
}
})
new Vue({
el:'#root2',
})
</script>
在實際開發中,還是區域性元件使用得更多。
4 元件巢狀(父元件與子元件)¶
Vue中的元件是允許巢狀的,且這是一種十分常用的編碼方式。如下所示,建立兩個元件:hello1和hello2,hello1在hello2建立時,在hello2中進行註冊,從而實現了兩者的巢狀關係,hello2是父元件,hello1是子元件:
<!-- 準備好一個容器-->
<div id="root">
<hello2></hello2>
<hello2></hello2>
<hello2></hello2>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
// 建立hello1元件
const hello1 = Vue.extend({
template:`
<div class="demo1">
<h2>{{msg}}</h2>
</div>
`,
data(){
return {
msg:'我是元件:hello1'
}
},
})
// 建立hello2元件
const hello2 = Vue.extend({
template:`
<hello1></hello1>
`,
components:{
hello1
}
})
new Vue({
el:'#root',
components: {
hello2
}
})
</script>
5 註冊元件語法糖¶
上文中說到元件的使用分為三步:建立元件構造器、註冊元件、使用元件,其中前兩步可以使用語法糖的形式進行合併,從而簡化程式碼。如下所示,使用全域性語法糖建立並註冊元件hello1,使用區域性語法糖建立並註冊元件hello2:
<!-- 準備好一個容器-->
<div id="root">
<hello1></hello1>
<hello2></hello2>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
// 全域性元件語法糖
Vue.component('hello1',{
template:`
<div class="demo1">
<h2>{{msg}}</h2>
</div>
`,
data(){
return {
msg:'我是元件:hello1'
}
},
})
new Vue({
el:'#root',
components: {
hello2:{ // 區域性註冊方式語法糖
template:`
<div class="demo2">
<h2>{{msg}}</h2>
</div>
`,
data(){
return {
msg:'我是元件:hello2'
}
},
}
}
})
</script>
6 單檔案元件¶
單檔案元件是指將建立元件的各種配置存放於單獨的檔案,需要使用時,單獨引入元件,方便管理。當檔案元件的編寫分為三個部分,如下所示,分別為template、script、style,分別用於編寫模板、配置指令碼和樣式:
<template>
</template>
<script>
</script>
<style>
</style>
繼續上文例子,定義兩個元件:hello1和hello2,hello1是hello2的子元件,hello2又是根元件App的子元件。我們將hello1的內容寫入名為“hello1.vue”的檔案中,內容如下:
<template>
<div class="demo1">
<h2>{{msg}}</h2>
</div>
</template>
<script>
export default {
name: 'hello1',
data(){
return {
msg:'我是元件:hello1'
}
},
}
</script>
<style>
.demo1{
background:#f0d4c2;
}
</style>
hello2中因為要使用hello1所以,需要引入hello1.vue。hello2.vue檔案內容如下:
<template>
<div class="demo2">
<h2>{{msg}}</h2>
<hello1></hello1>
</div>
</template>
<script>
import hello1 from './hello1.vue'
export default {
name: 'hello2',
data(){
return {
msg:'我是元件:hello2'
}
},
components:{
hello1
}
}
</script>
<style>
.demo2{
background:#f19e66;
}
</style>
根元件App檔案app.vue內容如下:
<template>
<div id="app">
<hello2></hello2>
</div>
</template>
<script>
import hello2 from './components/hello2'
export default {
name: 'App',
components: {
hello2
}
}
</script>
<style>
#app{
width:600px;
height:300px;
background:#a4a5a7;
margin-top:60px;
}
</style>
7 透過 Prop 向子元件傳遞資料¶
在子元件中定義props屬性,可以用來接收父元件傳遞過來的變數,注意,props以陣列的形式存在。例如,我們根據父元件傳遞的不同變數值,顯示不同內容, 子元件內容如下
<template>
<div class="demo1">
<h4>{{person}} 的年齡是:{{age}}</h4>
</div>
</template>
<script>
export default {
name: 'hello1',
props: ['person', 'age'] // 接受兩個值,注意,就算是一個值,也是陣列
}
</script>
父元件內容如下:
<template>
<div class="demo2">
<hello1 person="張三" age=23></hello1>
<hello1 person="李四" age=34></hello1>
<hello1 person="王五" age=45></hello1>
</div>
</template>
<script>
import hello1 from './hello1.vue'
export default {
name: 'hello2',
components:{
hello1
}
}
</script>
當然,還可以結合v-for對父元件的中的變數進行遍歷,在遍歷過程中,建立多個子元件,並向子元件傳遞值。此時,子元件hello1.vue檔案內容如下:
<template>
<div class="demo1">
<h4>{{perObj.name}} 的年齡是:{{perObj.age}}</h4>
</div>
</template>
<script>
export default {
name: 'hello1',
props: ['perObj']
}
</script>
父元件內容如下:
<template>
<div class="demo2">
<hello1
v-for="per in persons"
:key="per.id"
:perObj=per
>
</hello1>
</div>
</template>
<script>
import hello1 from './hello1.vue'
export default {
name: 'hello2',
data(){
return {
persons: [
{ id: 1, name: '張三', age: 23 },
{ id: 2, name: '李四', age: 34 },
{ id: 3, name: '王五', age: 45 }
]
}
},
components:{
hello1
}
}
</script>
8 監聽子元件事件(向父元件傳遞資料)¶
監聽子元件事件是指子元件事件被觸發,出發後的動作需要與父元件資料產生互動。Vue中有一個原則,資料屬於哪個元件,最好由該元件進行處理,也就是說,子元件需要對父元件資料進行修改時,需要呼叫父元件函式。那麼這時候,就需要將父元件的函式,傳遞到子元件,傳遞的方法還是透過prop。同時因為是子元件中呼叫父元件函式,可以在呼叫時,透過引數形式將子元件資料傳遞到父元件中。
繼續上述例子,在每一行後面新增一個按鈕,每次點選按鈕後,對應的年齡欄位加1。因為子元件顯示的persons資料來源於父元件,所以,點選按鈕後,需要呼叫父元件的函式對persons中各物件的age欄位進行修改。
父元件hello2.vue程式碼如下:
<template>
<div class="demo2">
<!--注意:這裡透過:changeAge="changeAge" 將父元件函式傳遞到了元件-->
<hello1
v-for="per in persons"
:key="per.id"
:perObj=per
:changeAge="changeAge"
>
</hello1>
</div>
</template>
<script>
import hello1 from './hello1.vue'
export default {
name: 'hello2',
data(){
return {
persons: [
{ id: 1, name: '張三', age: 23 },
{ id: 2, name: '李四', age: 34 },
{ id: 3, name: '王五', age: 45 }
]
}
},
components:{
hello1
},
methods: {
changeAge(id){ // 在父元件中定義函式修改父元件中的資料
this.persons.forEach((per)=>{
if(per.id === id) per.age += 1
});
console.log('id值為:', id, 'age值加1')
}
},
}
</script>
子元件hello1.vue內容如下:
<template>
<div class="demo1">
<h4>{{perObj.name}} 的年齡是:{{perObj.age}}</h4>
<!-- 此處呼叫從父元件中傳過來的函式 -->
<button @click="changeAge(perObj.id)">修改年齡</button>
</div>
</template>
<script>
export default {
name: 'hello1',
props: ['perObj', 'changeAge'], // 在子元件中接收父元件傳遞過來的函式
}
</script>
9 在元件上使用v-model¶
v-model也可以在元件標籤上使用。如下所示,在父元件上使用子元件標籤時,使用v-model雙向繫結lang屬性。
<template>
<div class="demo2">
<h3>{{ lang }}</h3>
<hello1 v-model="lang"></hello1>
</div>
</template>
<script>
import hello1 from './hello1.vue'
export default {
name: 'hello2',
data(){
return {
lang: 'python'
}
},
components:{
hello1
}
}
</script>
在子元件上,有兩點需要注意,一是子元件標籤上,不能再使用v-model進行雙向繫結,而是使用v-bind進行繫結,第二是“@input”呼叫emit函式繼續出發v-model機制內建的input事件修改父元件的lang屬性值。只要子元件上綁的是v-model,那麼,emit就一定要出發input事件。
<template>
<div class="demo1">
<label>開發語言</label>
<input type="text"
v-bind="lang"
@input="$emit('input', $event.target.value)"
>
</div>
</template>
<script>
export default {
name: 'hello1',
props: ['lang'], // 在子元件中接收父元件傳遞過來的函式
}
</script>