組件 (Component) 是 Vue.js 最強大的功能之一。組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器爲它添加特殊功能。html
全部的 Vue 組件同時也都是 Vue 的實例,因此可接受相同的選項對象 (除了一些根級特有的選項) 並提供相同的生命週期鉤子。 vue
組件定義2步驟:
a、建立組件構造器
Vue.extend()定義要渲染的HTMLapp
b、註冊組件
Vue.component()定義組件名和構造器函數
調用Vue.extend()建立的是一個組件構造器,構造器有一個選項對象,選項對象的template屬性用於定義組件要渲染的HTML; ui
調用Vue.component()註冊組件時,須要提供2個參數:組件的標籤名 和 組件構造器;註冊的組件要掛載到某個Vue實例下,不然它不會生效; this
Vue.extend() 和 Vue.component():因爲 Vue 自己是一個構造函數,Vue.extend() 是一個類繼承方法,它用來建立一個 Vue 的子類並返回其構造函數; 而Vue.component() 的做用在於:創建指定的構造函數與 ID 字符串間的關係,從而讓 Vue.js 能在模板中使用它;直接向 Vue.component() 傳遞 options 時,它會在內部調用 Vue.extend()。 spa
全局組件定義2個步驟:
// 1.建立組件構造器
let Profile = Vue.extend({
// 1.1 模板選項
template: `
<div>
<input type="date">
<p>今天已是冬天了!</p>
</div>
`
});component
// 2. 註冊一個全局的組件,任何實例均可以使用它
Vue.component('my-date', Profile);
全局組件能夠直接在每個vue實例中使用。htm
// 1.建立組件構造器
let Profile = Vue.extend({
// 1.1 模板選項
template: `
<div>
<input type="date">
<p>今天已是冬天了!</p>
</div>
`
});
//二、註冊到一個指定的Vue實例中,這樣就只能在某一個實例中使用這個組件了
new Vue({
el: '#app',
//經過註冊到特定的實例,來達到局部應用,本例中只能應用在id=app的實例中
components: {
'my-date': Profile,
}
});對象
注意:
全局組件須要單獨註冊;
局部組件必須註冊到特定的實例中,以components屬性的方式;
// 1. 子組件構造器
let Child1 = Vue.extend({
template: `<img src="img/img_01.jpg" width="200">`
});
let Child2 = Vue.extend({
template: `<p>我認爲本身很美!</p>`
});
//若是想單獨使用子組件,必須單獨註冊。這樣就能夠做爲一個單獨的全局組件使用。
Vue.component('child', Child1);
// 2. 父組件構造器
Vue.component('parent', {
components: {
'my-child1': Child1,
'my-child2': Child2
},
template:
`
<div>
<my-child1></my-child1>
<my-child2></my-child2>
</div>
`
});
//三、使用
<div id="app">
<parent></parent>
//child爲全局組件,能夠單獨使用!
<child></child>
</div>
<body>
<div id="app">
<my-div></my-div>
</div>
<template id="my_div">
//template裏面只能有一個根,也就是全部內容都包含在一個div裏面!
<div>
<div>我是MT!</div>
<input type="date">
<img src="img/img_02.jpg" alt="" width="400">
</div>
</template>
<script src="js/vue.js"></script>
<script>
// 1. 實例化組件
Vue.component('my-div', {
template: '#my_div'
});
new Vue({
el: '#app',
});
</script>
</body>
<body> <div id="app"> <my-div></my-div> </div> <script type="text/x-template" id="my_div"> <div> <img src="img/img_02.jpg" alt="" width="200"> <p>我是花姑娘!</p> </div> </script> <script src="js/vue.js"></script> <script> // 1. 實例化組件 Vue.component('my-div', { template: '#my_div' }); new Vue({ el: '#app', }); </script> </body>
注意事項:
1. 組件掛載數據必須是函數,返回一個對象
Vue.component('my-btn', { template: '#my_btn', data(){ return { counter: 0 } } });
組件掛載數據,必須是一個函數,且返回一個對象,這樣子組件被任何對象引用時,data就互不干涉,都是一個獨立的對象,能夠隨時修改,數據都互不影響。
二、組件的template中,必須包含在一個根下面
<template id="my_div"> //template裏面只能有一個根,也就是全部內容都包含在一個div裏面! <div> <div>我是MT!</div> <input type="date"> <img src="img/img_02.jpg" alt="" width="400"> </div> </template> <template id="my_div"> //這是錯誤的,不能有多個根元素! <div>我是MT!</div> <input type="date"> <img src="img/img_02.jpg" alt="" width="400"> </template>
a、父子通訊
<body>
<div id="app">
//第三步、父組件給子組件傳值,單層傳值無需動態綁定。父子傳值,讓子組件更活起來!
<my-div message="今天要下雨" imgsrc="img/img_02.jpg"></my-div>
<my-div message="明天要下冰雹" imgsrc="img/img_01.jpg"></my-div>
</div>
<template id="my_div">
//第一步:子組件定義接受的值
<div>
<h1>{{message}}</h1>
<img :src="imgsrc" width="200" alt="">
</div>
</template>
<script src="js/vue.js"></script>
<script>
// 1. 建立組件,
//第二步、經過props定義從父組件可以接受的值
Vue.component('my-div', {
template: '#my_div',
props: ['message', 'imgsrc']//注意,要支持駝峯式imgSrc的寫法,須要在template和父組件裏面修改爲img-src或img-Src。
});
new Vue({
el: '#app',
data: {
msg: '今天的天氣很好!'
}
});
</script>
</body>
<body>
//四、祖父組件使用和傳值
<div id="app">
<my-parent :imgtitle="title" :imgsrc="img"></my-parent>
</div>
<template id="my_img">
<img :src="imgsrc" width="200">
</template>
<template id="my_title">
<h2>{{title}}</h2>
</template>
//三、父組件定義和傳值
<template id="my_parent">
<div>
<child1 :childimg="imgsrc"></child1>
<child2 :childtitle="imgtitle"></child2>
</div>
</template>
<script src="js/vue.js"></script>
<script>
// 1. 子組件的實例
let Child1 = Vue.extend({
template: '#my_img',
props: ['childmyimg'] #父組件傳遞屬性
});
let Child2 = Vue.extend({
template: '#my_title',
props: ['childtitle']
});
// 2. 子組件註冊到父組件
Vue.component('my-parent', {
props: ['imgtitle', 'imgsrc'], #曾祖父傳遞的屬性
components: {
'child1': Child1,
'child2': Child2
},
template: '#my_parent'
});
new Vue({
el: '#app',
data: {
title: '我是否是很漂亮',
img: 'img/img_01.jpg'
}
});
</script>
</body>
祖父組件=>父組件=>子組件,注意事項:
一、多層傳值必須是動態綁定的方式 :
二、相對子組件必須經過props屬性接受父組件傳過來的數據
子組件通知給外界,他執行了什麼操做,此時,外界會作出相應的操做。
<body>
<div id="app">
<!--父組件經過on監聽total事件,一旦執行total事件,則會修改Vue實例中的數據,這樣就達到了在父組件中屢次調用子組件,數據都是獨立的-->
<my-btn @total="allcounter()"></my-btn>
<my-btn @total="allcounter()"></my-btn>
<my-btn @total="allcounter()"></my-btn>
<my-btn @total="allcounter()"></my-btn>
<my-btn @total="allcounter()"></my-btn>
<my-btn @total="allcounter()"></my-btn>
<my-btn @total="allcounter()"></my-btn>
<p>全部按鈕一共點擊了{{totalCounter}}次</p>
</div>
<template id="my_btn">
<button @click="total()">點擊了{{counter}}次</button>
</template>
<script src="js/vue.js"></script>
<script>
//定義子組件
Vue.component('my-btn', {
template: '#my_btn',
data(){
return {
counter: 0 //注意:在子組件中掛載數據,必須返回對象,這樣在任何地方調用子組件都是獨立的,數據互不干涉。
}
},
methods: {
total(){
this.counter += 1;
// 通知外界,我觸發了total方法,而後他就會調用後面的allcounter(),這樣就把全部的點擊次數加起來了
this.$emit('total');
}
}
});
new Vue({
el: '#app',
data: {
totalCounter: 0
},
methods: {
allcounter(){
this.totalCounter += 1;
}
}
});
</script>
</body>
使用 $on(eventName) 監聽事件
使用 $emit(eventName) 觸發事件
slot的意思是插槽,其目的在於讓組件的可擴展性更強。打個比方說:假如父組件須要在子組件內放一些DOM,那麼這些DOM是顯示、不顯示、在哪一個地方顯示、如何顯示,就是slot分發負責的。
注意:若是使用子組件時,在裏面添加DOM,默認這些Dom是不會顯示的,由於子組件根本不認識。例如:
<child><div>我想在子組件中顯示,可是顯示不出來</div></child>
插槽就像電路板,是用來方便插入內容的。slot分爲匿名插槽和具名插槽。
匿名插槽就像公交車,誰均可以上,任何人均可以使用這個插槽,很相似娛樂圈中的女明星啦;
具名插槽則是私家車,一我的對應一輛車,一個內容對應一個插槽,這個就是生活中的良家婦女啦。
<body>
<div id="app">
<my-slot>
<!--date會覆蓋默認的插槽-->
<input type="date">
</my-slot>
</div>
<template id="my_slot">
<div id="panel">
<h2 class="panel-header">插槽的頭部</h2>
<!--預留一個插槽-->
<slot>能夠替換任何標籤,默認顯示提示的內容</slot>
<footer>插槽的尾部</footer>
</div>
</template>
<script src="js/vue.js"></script>
<script>
Vue.component('my-slot', {
template: '#my_slot'
});
new Vue({
el: '#app',
data: {
msg: '今天的天氣很好!'
}
});
</script>
</body>
<body>
<div id="app">
<my-computer>
<!--只能根據slot的name插入對應的數據-->
<div slot="cpu">Inter Core i8</div>
<img slot="gpu" src="img/img_02.jpg" width="100" alt="">
<div slot="memory">128G</div>
<input type="color" slot="hard-drive">
</my-computer>
</div>
<template id="my_computer">
<div id="main">
<slot name="cpu">這裏是插cpu的</slot>
<slot name="gpu">這裏是插gpu的</slot>
<slot name="memory">這裏是插內存條的</slot>
<slot name="hard-drive">這裏是插硬盤的</slot>
</div>
</template>
<script src="js/vue.js"></script>
<script>
Vue.component('my-computer', {
template: '#my_computer'
});
new Vue({
el: '#app',
data: {
msg: '今天的天氣很好!'
}
});
</script>
</body>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <div> <input type="text" v-model="inputValue"> <button @click="handleClickBtn">添加</button> </div> <ul> <todo-item v-for="(item,index) in list" :content="item" :key="item" :index="index" :msg="msg" @delete="handleParentDelete" > {{item}} </todo-item> </ul> </div> <script src="js/vue.js"></script> <script> //定義全局組件todoItem var todoItem = { //子組件接受來自父組件的傳值,能夠直接使用 props: ['content', 'index', 'msg'], template: "<li @click='handleItemDelete'>{{content}}-{{msg}}</li>", methods: { handleItemDelete: function () { //當點擊子組件時,會向外觸發delete事件,同時帶上對應的參數,父組件監聽到delete被觸發後,則會執行對應的方法 this.$emit('delete', this.index) } }, } // 1. 建立Vue的實例 let vm = new Vue({ el: '#app', data: { list:[], inputValue:'', msg:'我是來自父組件的msg' }, components:{ //全局組件註冊到vm實例中 todoItem }, methods:{ handleClickBtn:function(){ if(this.inputValue){ this.list.push(this.inputValue); this.inputValue = ''; }else{ alert('輸入內容不能爲空!'); } }, handleParentDelete:function(id){ //$emit傳給父組件的參數,在這裏接受。 alert(id); this.list.splice(id,1); }, } }); </script> </body> </html>
VUE的組件其實也是一個Vue實例,她具有vue的全部屬性和方法。
解決h5的一些小bug
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <table> <tbody> //tbody標籤直接使用row會在h5中有問題,解析後會跳到table外面去 <row></row> <row></row> <row></row> //修改成: <tr is="row"></tr> <tr is="row"></tr> <tr is="row"></tr> //ul標籤裏面須要使用li,select標籤中須要使用option </tbody> </table> </div> <script src="js/vue.js"></script> <script> Vue.component('row',{ template:'<tr><td>this is a row</td></tr>' }) // 1. 建立Vue的實例 let vm = new Vue({ }); </script> </body> </html>
組件中的data必須是一個函數,返回一個對象
data:function(){ return { number:0 } },
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <counter ref="one" @change="handleChange"></counter> <counter ref="two" @change="handleChange"></counter> <div>{{total}}</div> </div> <script src="js/vue.js"></script> <script> Vue.component('counter',{ template:'<div @click="handleClick">{{number}}</div>', data:function(){ return { number:0 } }, methods:{ handleClick:function(){ this.number++ this.$emit('change') } } }) // 1. 建立Vue的實例 let vm = new Vue({ el:'#app', data:{ total:0 }, methods:{ handleChange:function () { this.total = this.$refs.one.number + this.$refs.two.number } } }); </script> </body> </html>
父組件傳值給子組件,子組件最好不要修改父組件的數據,由於加入父組件傳遞的是一個對象,可能被多個子組件反覆的修改,致使數據混亂。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <counter :count='1' @change='handleChange'></counter> <counter :count='2' @change='handleChange'></counter> <span>{{total}}</span> </div> <script src="js/vue.js"></script> <script> var counter={ props:['count'], template:"<div @click='handleClick'>{{number}}</div>", data:function(){ return { //拷貝一個父本出來 number:this.count } }, methods:{ handleClick:function(){ //注意:避免直接修改父組件傳過來的數據 //this.count++ //每次步長爲1 this.number += 1 //每次子組件被點擊後都告知父組件觸發對應的事件 this.$emit('change',1) } } } // 1. 建立Vue的實例 let vm = new Vue({ el: '#app', components:{ counter }, data:{ total:3 }, methods:{ handleChange:function(num){ this.total+=num } } }); </script> </body> </html>
子組件對父組件傳過來的數據進行約束檢測,則爲props特性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <child content="hello world"></child> </div> <script src="js/vue.js"></script> <script> Vue.component('child',{ props:{ content:{ type:String, required:false, default:'default value', validator:function (value) { return (value.length>5) } } }, template:'<div>{{content}}</div>', }) // 1. 建立Vue的實例 let vm = new Vue({ el:'#app', }); </script> </body> </html>
props特色:
父組件向子組件傳遞數據,可是在子組件沒有明確接受,則不能經過{{}}使用,也稱之爲非props
非props特色:
正常狀況下,在父組件中綁定事件,咱們只能在子組件經過this.$emit('events')進行觸發後執行:
<child @click='handleClick'></child>
若是須要在父組件中也生效,須要經過給父組件綁定原生事件:
<child @click.native='handleClick'></child>
完整示例以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <child @click.native='handleClick'></child> </div> <script src="js/vue.js"></script> <script> Vue.component('child',{ template:'<div>Child</div>', }) // 1. 建立Vue的實例 let vm = new Vue({ el: '#app', methods:{ handleClick:function(){ alert('aaa') } } }); </script> </body> </html>