關於vue組件的一個小結
用vue進行開發到目前為止也有將近一年的時間了,在項目技術選型的時候隔壁組選 react的時候我們堅持使用vue作為前端的開發框架。雖然兩者思想上的差異不大,但是vue的語法在代碼的可讀性以及後期的維護成本更加的適合,而且最近看到Apache對react的相關許可限制;這裏不討論react和vue的對比哪個好,技術框架沒有最好的,只有適合項目才是最好的。
進入主題。。。。。。。
組件,是vue的核心之一。
我們可以把頁面各個子模塊看成一個組件,可以獨立拆分出來。這樣不僅維護變得簡單了,而且代碼復用性也高。
vue組件分為全局組件和局部組件。組件中屬性和vue實例基本類似,基本可以使用其所有屬性如computed,methods,components,filter,directive.....但data屬性不同,在組件中data是函數,而且數據需要return出來;因為組件可能會被引用多次,就會創建多次實例,如果data是個對象的話那引用到這個組件的地方都講公用一個data這樣就回造成了數據的汙染。如果使用data屬性函數返回一個對象的話就可以解決這個問題,每次引入到這個組件實例的時候就可以在data函數中返回一個初始的數據對象。
組件的介紹
全局組件
使用Vue.component進行全局註冊,所有vue實例都會共享此組件
1 <div id="app"> 2 {{msg}} 3 <const-comp></const-comp> 4 5 </div> 6 <script> 7 8 Vue.component(‘constComp‘, { 9 template: "<h3>我是全局組件</h3>" 10 11}); 12 13 new Vue({ 14 el: "#app", 15 data: { 16 msg: "hello component" 17 } 18 }); 19 20 </script>
局部組件
局部組件只能在引入當前的vue實例中有效,在當前vue實例中components屬性加上引入進來的組件實例即可
<div id="app"> {{msg}} <!-- <const-comp></const-comp> --> <local-comp></local-comp> </div> <script> /*Vue.component(‘constComp‘, { template: "<h3>我是全局組件</h3>" });*/ let localComp = { template: "<div>我是局部組件</div>" }; new Vue({ el: "#app", data: { msg: "hello component" }, components: { localComp } }); </script>
單文件組件(xxx.vue)
其實就是將寫在js中的組件提出到一個vue文件中寫而已,這樣組件更加的好維護以及閱讀性也會好,提取出來了相應的引入即可,不會顯得文件很多行很長。
其主要有<template></template><script></script><style></style>這三個標簽,每個標簽做自己的事。template就像我們在html中寫dom,script寫js代碼當前的組件實例,style寫組件樣式,註意:加上scoped即可使當前樣式只在當前組件生效,組件渲染的時候此組件的dom會加上data-v-xxx屬性來選擇當前組件樣式。如果沒加上scoped的話當前組件的樣式就會在引入這個組件的實例中造成影響
如我寫的一個found.vue文件demo
1 <template> 2 <div> 3 <div class="saerchDiv"> 4 <Search 5 @result-click="resultClick" 6 @on-change="changeResult" 7 :results="results" 8 v-model="searchVal" 9 position="absolute" 10 auto-scroll-to-top 11 top="46px" 12 @on-focus="onFocus" 13 @on-cancel="onCancel" 14 @on-submit="onSubmit" 15 ref="search"></Search> 16 </div> 17 18 </div> 19 </template> 20 <script> 21 import {Search} from ‘vux‘; 22 23 export default { 24 name:"found", 25 data() { 26 return { 27 msg:"found page", 28 // searchVal:"尋找更多好文章", 29 searchVal:"", 30 results:[] 31 } 32 }, 33 components:{ 34 Search 35 }, 36 methods:{ 37 resultClick(item) { //選中搜索 38 console.log(item.title); 39 }, 40 changeResult(val) { //獲取搜索關鍵字 41 console.log(val); 42 /*this.$http.get(‘‘).then(res => { 43 44 }).catch(err => { 45 46 });*/ 47 48 this.results = this.getResults(val); 49 }, 50 onFocus(){ 51 console.log("on focus"); 52 // this.searchVal = "" 53 }, 54 onCancel(){ 55 console.log("點擊取消按鈕"); 56 }, 57 onSubmit(){ 58 console.log("on submit"); 59 }, 60 getResults(keyword) { //暫時獲取假數據 61 let rsArr = []; 62 for(let i = 0; i < 6; i++) { 63 rsArr.push({ 64 title:keyword + (i+1), 65 other:i //文章id 66 }); 67 } 68 return rsArr; 69 } 70 71 } 72 } 73 </script> 74 <style scoped> 75 76 .saerchDiv { 77 height: .75rem; 78 font-size: .27rem; 79 } 80 81 </style>View Code
組件的通信
vue組件的通信是vue組件的核心,組件不僅僅是要把模板的內容進行復用;更主要的是組件間要進行通信;組件之間的通信數據傳遞是組件的生命力之一。
props單向數據流,父組件向子組件傳遞數據
props可以是一個數據類型也可以是一個數組,也可以是對象,對象下的數據有3個屬性type,default,require。其中default,require值都是布爾類型值。type有Number,String,Boolean,Array,Object,Function,Symbol。如果props數據是對象或數組時默認值default必須是一個函數來返回初始化數據。而且因為對象或數據是引用類型,指向的是同一個內存空間所以當props數據是這兩個類型時,數據改變時子組件內改變是會影響父組件的。
props數據類型及相關樣例
1 // props: [‘propsDataA‘], 2 props: { 3 propA: { 4 type: String, 5 default: "", 6 require: true 7 }, 8 propB: { 9 type: Number, 10 default: 1, 11 require: false 12 }, 13 propC: { 14 type: Array, 15 default: function() { 16 return []; 17 }, 18 require: true 19 }, 20 propD: { 21 type: Object, 22 default: function() { 23 return {}; 24 }, 25 require: true 26 }, 27 propE: { 28 type: Function, 29 fn: function(val) { //一個時間未滿兩位數前面補零驗證 30 return val > 9 ? val : ‘0‘ + val; 31 }, 32 require: true 33 }, 34 propF: { 35 type: Boolean, 36 default: false, 37 require: true 38 }, 39 propG: [String, Number], 40 propH: Number 41 }View Code
這裏稍微改動一下局部組件的代碼,父組件向子組件傳遞數據;
1 <div id="app"> 2 {{msg}} 3 <!-- <const-comp></const-comp> --> 4 <local-comp :props-a="info"></local-comp> 5 </div> 6 <script> 7 8 /*Vue.component(‘constComp‘, { 9 template: "<h3>我是全局組件</h3>" 10 11 });*/ 12 let localComp = { 13 template: "<div>我是局部組件<p>父組件傳過來的數據為-->{{propsA}}</p></div>", 14 props: { 15 propsA: { 16 type: String, 17 default: "", 18 require: true 19 } 20 } 21 }; 22 new Vue({ 23 el: "#app", 24 data: { 25 msg: "hello component", 26 info: "hello props" 27 }, 28 components: { 29 localComp 30 } 31 }); 32 33 </script>
自定義事件$emit,子組件向父組件通信
這裏還是在原來的基礎上改,子組件使用$emit自定義一個send事件向父組件發送數據
1 <div id="app"> 2 {{msg}} 3 <!-- <const-comp></const-comp> --> 4 <div>子組件數據為---->{{fromChildData}}</div> 5 <local-comp :props-a="info" @send="getChildData"></local-comp> 6 </div> 7 <script> 8 9 /*Vue.component(‘constComp‘, { 10 template: "<h3>我是全局組件</h3>" 11 12 });*/ 13 let localComp = { 14 template: "<div>我是局部組件15 <p>父組件傳過來的數據為-->{{propsA}}</p>16 <button @click=‘sendMsg‘>使用$emit子組件向父組件傳遞事件</button>17 </div>", 18 props: { 19 propsA: { 20 type: String, 21 default: "", 22 require: true 23 } 24 }, 25 data() { 26 return { 27 msg: "子組件數據" 28 } 29 }, 30 methods: { 31 sendMsg(evt) { 32 this.$emit(‘send‘, this.msg); 33 } 34 } 35 }; 36 new Vue({ 37 el: "#app", 38 data: { 39 msg: "hello component", 40 info: "hello props", 41 fromChildData: "" 42 }, 43 components: { 44 localComp 45 }, 46 methods: { 47 getChildData(val) { 48 this.fromChildData = val 49 } 50 } 51 }); 52 53 </script>
非子父組件通信,使用一個空的Vue實例作為一個事件總線監聽數據變化
這種場景用於組件之間不為子父層級關系的時候相關通信,我們使用的那個空vue實例裏也可以放vue的屬性。用這個空的vue實例來$emit自定義一個事件然後再用這個實例來$on監聽自定義事件,從而達到非子父組件之間的通信。(PS:這裏暫時不討論vuex),看demo代碼。demo例子使用了ref組件索引。
<div id="app"> {{message}} <component-a ref="a"></component-a> <component-a ref="b"></component-a> </div> <script> const bus = new Vue({}); Vue.component(‘component-a‘, { data () { return { msg: 1 } }, template: ‘<button @click="handleEvent">傳遞事件</button>‘, methods: { handleEvent () { this.$parent bus.$emit(‘on-message‘, ‘來自組件 com-a 的內容‘); } } }); const app = new Vue({ el: ‘#app‘, data: { message: ‘‘ }, mounted () { bus.$on(‘on-message‘, (msg) => { this.message = msg; }); this.$children.msg = 2; } }) </script>
slot組件內容分發
單個就默認slot,多個使用具名slot,$slots訪問對應slot,vue2.0新增;因為vue2使用render函數來渲染,所以需要使用this.$slots來訪問slot。this.$slots.xxx訪問具體的slot,即slot中name指定的值 值類型{ [name: string]: ?Array<VNode> }
如果需要給slot添加默認內容的時候直接在slot上寫就可以了,這個時候默認的slot內容所在的作用域就是其所在的組件實例,可以根據其所在的組件來控制slot默認的內容展示如:<slot>{{msg}}</slot>。如果沒有指定默認數據的話slot內容根據其父組件所在的作用域。
當 vue 組件中當需要組件混合使用的時候需要用到內容分發,內容不確定的時候需要用到slot內容分發。
看demo代碼;
1 <div id="app"> 2 <com-out> 3 <div slot="b">{{msgB}}</div> 4 <div slot="a">{{msgA}}</div> 5 6 </com-out> 7 </div> 8 <template id="co"> 9 <div> 10 hello 11 <slot name="a"></slot> 12 <slot name="b"></slot> 13 </div> 14 </template> 15 <script> 16 Vue.component(‘com-out‘, { 17 template: "#co", 18 mounted() { 19 console.log("com-out slot" + this.$slots.a[0]); 20 } 21 }); 22 new Vue({ 23 el: "#app", 24 data: { 25 msgA: ‘父組件數據a‘, 26 msgB: ‘父組件數據b‘ 27 } 28 }) 29 </script>
遞歸組件
遞歸組件要記住兩點:
1.遞歸組件必須要給組件設置name。
2.要在一個合適的時間(條件)跳出遞歸否則會報棧溢出異常。
1 <div id="app"> 2 <com :count="1"></com> 3 </div> 4 <template id="cr"> 5 <div><com :count="count + 1" v-if="count < 3"></com>{{count}}</div> 6 </template> 7 <script> 8 Vue.component(‘com‘, { 9 name: ‘comr‘, 10 template: "#cr", 11 props: { 12 count: { 13 type: Number, 14 default: 1 15 } 16 } 17 }) 18 new Vue({ 19 el: ‘#app‘ 20 }) 21 22 23 </script>
動態組件
vue動態組件其實就是在組件中使用:is屬性根據值來判斷顯示哪個組件。
1 <div id="app"> 2 <button @click="changeCom">點擊讓子組件顯示</button> 3 <com v-bind:is="activeCom"></com> 4 </div> 5 <script> 6 new Vue({ 7 el: ‘#app‘, 8 data: { 9 activeCom: "comA" 10 }, 11 methods: { 12 changeCom: function () { 13 let arr = ["comA", "comB", "comC"], index; 14 index = Math.ceil(Math.random()*arr.length); 15 this.activeCom = arr[index]; 16 } 17 }, 18 components: { 19 comA: { 20 template: "<div>組件comA</div>" 21 }, 22 comB: { 23 template: "<div>組件comB</div>" 24 }, 25 comC: { 26 template: "<div>組件comC</div>" 27 }, 28 } 29 }); 30 </script>
異步組件
異步組件在性能上有一定的優勢,不僅加快了渲染時間也減少了不必要的加載;在路由中經常用到
看demo;
<div id="app"> <child-component></child-component> </div> <script> Vue.component(‘child-component‘, function (resolve, reject) { window.setTimeout(function () { resolve({ template: ‘<div>異步組件的內容</div>‘ }) }, 2000) }); new Vue({ el: ‘#app‘ }) </script>
組件相關屬性
$nextTick 虛擬dom完成後觸發回調。進行dom操作。不過相關dom操作一般都建議放在指令中,或者自己自定義指令來進行操作,用合適的方式來做合適的事才能達到最優。
$ref當組件使用ref來作為索引時$ref獲取當前組件
關於vue組件的一個小結