1、Vue組件化開發思想。
引述:組件化規范Web Components。
1)、我們希望盡可能多的重用代碼。
2)、自定義組件的方式不太容易(html、css、js)。
3)、多次使用組件可能導致沖突。
4)、Web Components通過創建封裝好功能的定制元素解決上述問題。
5)Vue部分實現了上述Web Components規范。
2、Vue組件注冊。Vue組件注冊注意事項。
1)、data必須是一個函數。分析函數與普通對象的對比,Vue的data是一個對象,區別於組件的data是一個函數。組件的data是函數,可以形成一個閉包的環境,這可以保證每一個組件都可以擁有一份獨立的數據。
2)、組件模板內容必須是單個根元素,分析演示實際的效果,比如多個div包了多個button標簽。類比Vue實例的el容器中。
3)、組件模板內容可以是模板字符串。模板字符串需要瀏覽器提供支持(ES6語法規則)。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <div v-text="msg"></div> 10 11 <!-- Vue組件注冊成功之后,就可以使用了 --> 12 <!-- 這個組件是一個子組件,因為Vue也是一個組件,子組件寫到Vue父組件當中,形成父子關系。 --> 13 <button-counter></button-counter> 14 15 <!-- 組件的重用,可以拷貝多份,每個組件相互獨立,不互相影響的。 --> 16 <button-counter></button-counter> 17 <button-counter></button-counter> 18 </div> 19 20 <script src="vue.js" type="text/javascript"></script> 21 <script type="text/javascript"> 22 // Vue的組件化開發,Vue的注冊。參數一是組件的名稱,參數二是組件的內容,是一個對象。 23 // Vue.component('', { 24 // data: '', //data表示組件數據。 25 // template: '', // template表示組件模板內容。可以做數據的綁定、分支、事件的操作、循環的結構都可以在這里面使用。 26 // }); 27 // Vue的組件化開發,Vue的注冊,下面的語法就將組件注冊成功了。 28 // Vue.component('button-counter', { 29 // data: function() { 30 // // 提供一個具體的對象,對象當中存放的具體的數據 31 // return { 32 // count: 0, 33 // } 34 // }, 35 // // template: '<button @click="count++">點擊了{{count}}次</button>', 36 // // 組件模板內容必須是單個根元素,分析演示實際的效果 37 // template: '<div><button @click="counts">點擊了{{count}}次</button><button @click="counts">點擊了{{count}}次</button></div>', 38 // methods: { 39 // counts: function() { 40 // this.count++; 41 // } 42 // } 43 // }) 44 //--------------------------------------------------------------------------------------- 45 Vue.component('button-counter', { 46 data: function() { 47 // 提供一個具體的對象,對象當中存放的具體的數據 48 return { 49 count: 0, 50 } 51 }, 52 // template: '<button @click="count++">點擊了{{count}}次</button>', 53 // 組件模板內容可以是模板字符串。模板字符串需要瀏覽器提供支持(ES6語法規則) 54 template: ` 55 <div> 56 <button @click="counts">點擊了{{count}}次</button> 57 <button @click="counts">點擊了{{count}}次</button> 58 </div> 59 `, 60 methods: { 61 counts: function() { 62 this.count++; 63 } 64 } 65 }) 66 67 // 創建Vue對象 68 var vm = new Vue({ 69 el: '#app', 70 data: { // 對象,區別於組件的data是一個函數。 71 msg: 'hello world!', 72 }, 73 methods: {} 74 }); 75 </script> 76 </body> 77 </html>
3、Vue組件注冊,組件名稱方法。
1)、短橫線方式。Vue.component('button-counter', {/** ... */});
2)、駝峰方式,駝峰方式要用到根組件里面,需要換成短橫線方式。如果使用駝峰式命名組件,那么在使用組件的時候,只能在字符串模板中用駝峰的方式使用組件,但是在普通的標簽模板中,必須使用短橫線的方式使用組件。Vue.component('buttonCounter', {/** ... */});
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <Hello-world></Hello-world> 10 <div v-text="msg"></div> 11 12 <!-- Vue組件注冊成功之后,就可以使用了 --> 13 <!-- 這個組件是一個子組件,因為Vue也是一個組件,子組件寫到Vue父組件當中,形成父子關系。 --> 14 <button-counter></button-counter> 15 16 <!-- 組件的重用,可以拷貝多份,每個組件相互獨立,不互相影響的。 --> 17 <button-counter></button-counter> 18 <button-counter></button-counter> 19 </div> 20 21 <script src="vue.js" type="text/javascript"></script> 22 <script type="text/javascript"> 23 // Vue的組件化開發,Vue的注冊。參數一是組件的名稱,參數二是組件的內容,是一個對象。 24 // Vue.component('', { 25 // data: '', //data表示組件數據。 26 // template: '', // template表示組件模板內容。可以做數據的綁定、分支、事件的操作、循環的結構都可以在這里面使用。 27 // }); 28 // Vue的組件化開發,Vue的注冊,下面的語法就將組件注冊成功了。 29 // Vue.component('button-counter', { 30 // data: function() { 31 // // 提供一個具體的對象,對象當中存放的具體的數據 32 // return { 33 // count: 0, 34 // } 35 // }, 36 // // template: '<button @click="count++">點擊了{{count}}次</button>', 37 // // 組件模板內容必須是單個根元素,分析演示實際的效果 38 // template: '<div><button @click="counts">點擊了{{count}}次</button><button @click="counts">點擊了{{count}}次</button></div>', 39 // methods: { 40 // counts: function() { 41 // this.count++; 42 // } 43 // } 44 // }) 45 46 Vue.component('HelloWorld', { 47 data: function() { 48 return { 49 msg: '您好,Vue!', 50 } 51 }, 52 template: '<div>{{msg}}</div>', 53 }); 54 55 //--------------------------------------------------------------------------------------- 56 Vue.component('buttonCounter', { 57 data: function() { 58 // 提供一個具體的對象,對象當中存放的具體的數據 59 return { 60 count: 0, 61 } 62 }, 63 // template: '<button @click="count++">點擊了{{count}}次</button>', 64 // 組件模板內容可以是模板字符串。模板字符串需要瀏覽器提供支持(ES6語法規則) 65 template: ` 66 <div> 67 <button @click="counts">點擊了{{count}}次</button> 68 <button @click="counts">點擊了{{count}}次</button> 69 <HelloWorld></HelloWorld> 70 </div> 71 `, 72 methods: { 73 counts: function() { 74 this.count++; 75 } 76 } 77 }) 78 79 // 創建Vue對象 80 var vm = new Vue({ 81 el: '#app', 82 data: { // 對象,區別於組件的data是一個函數。 83 msg: 'hello world!', 84 }, 85 methods: {} 86 }); 87 </script> 88 </body> 89 </html>
4、Vue組件注冊,局部組件注冊。
1)、局部組件,只能在注冊他的父組件當中使用,在別的組件中是不可以使用的。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <!-- 使用局部組件 --> 10 <component-a></component-a> 11 <component-b></component-b> 12 <component-c></component-c> 13 </div> 14 15 <script src="vue.js" type="text/javascript"></script> 16 <script type="text/javascript"> 17 /* 通過這種方式注冊的組件只能在父組件中使用,在別的組件中是使用不了的。 */ 18 var componentA = { 19 data: function() { 20 return { 21 msg: 'hello world componentA.', 22 } 23 }, 24 template: '<div>{{msg}}</div>', 25 }; 26 var componentB = { 27 data: function() { 28 return { 29 msg: 'hello world componentB.', 30 } 31 }, 32 template: '<div>{{msg}}</div>', 33 }; 34 var componentC = { 35 data: function() { 36 return { 37 msg: 'hello world componentC.', 38 } 39 }, 40 template: '<div>{{msg}}</div>', 41 }; 42 43 // 創建Vue對象 44 var vm = new Vue({ 45 el: '#app', 46 data: { // 對象,區別於組件的data是一個函數。 47 48 }, 49 methods: { 50 51 }, 52 // Vue的局部組件注冊。 53 components: { 54 // 將局部組件通過components注冊進來 55 'componentA': componentA, 56 'componentB': componentB, 57 'componentC': componentC 58 } 59 }); 60 </script> 61 </body> 62 </html>
5、Vue組件注冊,Vue調式工具vue-devtools用法。
1)、克隆地址,https://github.com/vuejs/vue-devtools.git
2)、安裝依賴包npm install。
3)、構建npm run build。生成一個Chrome擴展的包。
4)、打開Chrome擴展頁面。加載構建的包。
5)、選中開發者模式。
6)、加載已解壓的擴展,選擇shells/chrome。
6、Vue組件,組件之間的關系,包含父子關系(祖孫關系)和兄弟關系。組件間數據交互。
1)、父組件向子組件傳值。
a、組件內部通過props接收傳遞過來的值,它的值是一個數組,數組中可以包含很多的屬性,這些屬性都是從父組件傳輸過來的。
b、父組件通過屬性將值傳遞給子組件。通過靜態傳遞和動態綁定傳遞屬性。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <!-- 父組件打印 --> 10 <div>{{pmsg}}</div> 11 <!-- 子組件的使用,父組件以屬性的方式將值傳遞給子組件 --> 12 <menu-item title="我是來自父組件的標題" concent="我是父組件的內容!"></menu-item> 13 <!-- 父組件給子組件動態傳值 --> 14 <menu-item v-bind:title="title" concent="我是父組件的內容!"></menu-item> 15 <menu-item :title="title" concent="我是父組件的內容!"></menu-item> 16 </div> 17 18 <script src="vue.js" type="text/javascript"></script> 19 <script type="text/javascript"> 20 // 創建一個組件,父組件向子組件傳值。 21 Vue.component('menu-item', { 22 props: ['title', 23 'concent' 24 ], // 子組件接收父組件傳遞的值 25 data: function() { 26 return { 27 msg: '子組件本身的數據' 28 } 29 }, 30 template: '<div>{{msg + " : " + title + " " + concent}}</div>', 31 }); 32 33 // 創建Vue對象 34 var vm = new Vue({ 35 el: '#app', 36 data: { // 對象,區別於組件的data是一個函數。 37 pmsg: '父組件中的內容', 38 title: '我是來自父組件的標題!', 39 }, 40 methods: { 41 42 }, 43 44 }); 45 </script> 46 </body> 47 </html>
2)、props屬性名規則。
a、在props中使用駝峰形式,在html模板中需要使用短橫線的形式。因為dom元素的屬性不區分大小的,如果傳遞駝峰形式就出現問題了。
b、字符串形式的模板中沒有這個限制。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <!-- 父組件打印 --> 10 <div>{{pmsg}}</div> 11 <!-- 子組件的使用,父組件以屬性的方式將值傳遞給子組件 --> 12 <menu-item menu-title="我是來自父組件的標題" concent="我是父組件的內容!"></menu-item> 13 14 <!-- 在html模板中需要使用短橫線的形式 --> 15 <!-- 父組件給子組件動態傳值,title值是動態傳值,是父組件里面定義的變量 --> 16 <menu-item v-bind:menu-title="title" concent="我是父組件的內容!"></menu-item> 17 <menu-item :menu-title="title" concent="我是父組件的內容!"></menu-item> 18 </div> 19 20 <script src="vue.js" type="text/javascript"></script> 21 <script type="text/javascript"> 22 // 創建一個組件,父組件向子組件傳值。 23 Vue.component('third-component', { 24 // 子組件接收父組件傳遞的值 25 props: ['thirdTitle'], 26 // 子組件接收父組件傳遞的值menuTitle、concent就可以使用了。 27 // 這里面指定的就是字符串形式的模板。這里面可以使用駝峰形式的數據是沒有問題的。 28 template: '<div>{{thirdTitle}}</div>', 29 }); 30 Vue.component('menu-item', { 31 // 子組件接收父組件傳遞的值 32 props: ['menuTitle', 33 'concent' 34 ], 35 data: function() { 36 return { 37 msg: '子組件本身的數據' 38 } 39 }, 40 // 子組件接收父組件傳遞的值menuTitle、concent就可以使用了。 41 // 這里面指定的就是字符串形式的模板。這里面可以使用駝峰形式的數據是沒有問題的。 42 template: '<div>{{msg + " : " + menuTitle + " " + concent}}<third-component thirdTitle="hello"></third-component></div>', 43 }); 44 45 // 創建Vue對象 46 var vm = new Vue({ 47 el: '#app', 48 data: { // 對象,區別於組件的data是一個函數。 49 pmsg: '父組件中的內容', 50 title: '我是來自父組件的標題!', 51 }, 52 methods: { 53 54 }, 55 56 }); 57 </script> 58 </body> 59 </html>
3)、props屬性值規則。
a、字符串String。
b、數值Number。
c、布爾值Boolean。
d、數組Array。
e、對象Object。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <!-- 第二步、在父組件進行打印輸出 --> 10 <div>{{pmsg}}</div> 11 12 <!-- 第二步、在子組件進行引用父組件的數據pstr --> 13 <menu-item v-bind:pstr2='pstr' v-bind:pnum2='pnum' v-bind:pboo2='pboo' v-bind:parr2='parr' v-bind:pobj2='pobj'> 14 </menu-item> 15 16 <br /> 17 <menu-item :pstr2='pstr' :pnum2='pnum'></menu-item> 18 <br /> 19 20 <!-- pnum2前面不加冒號就是字符串類型的,加上冒號就是數值number類型 --> 21 <menu-item v-bind:pstr2='pstr' pnum2='pnum'></menu-item> 22 </div> 23 24 <script src="vue.js" type="text/javascript"></script> 25 <script type="text/javascript"> 26 // 父組件向子組件傳值props屬性名類型 27 Vue.component('menu-item', { 28 /* 第三步、在子組件進行引用父組件傳來的數據 */ 29 props: ['pstr2', 'pnum2', 'pboo2', 'parr2', 30 'pobj2' 31 ], 32 template: ` 33 <div> 34 <div>{{pstr2}}</div> 35 <div>{{typeof pnum2}}</div> 36 <div>{{pboo2}}</div> 37 <div>{{parr2}}</div> 38 <ul> 39 <li v-bind:key='index' v-for='(item,index) in parr2'>{{item}}</> 40 </ul> 41 <div> 42 <span>{{pobj2.name}}</span> 43 <span>{{pobj2.age}}</span> 44 </div> 45 </div> 46 ` 47 }); 48 49 // 創建Vue對象 50 var vm = new Vue({ 51 el: '#app', 52 data: { // 對象,區別於組件的data是一個函數。 53 pmsg: '父組件中內容', // 第一步、在父組件中進行打印顯示 54 pstr: 'hello', // 第一步、在子組件進行打印顯示,字符串類型 55 pnum: 12, //數組類型 56 pboo: true, //布爾類型 57 parr: ['apple', 'orange', 'banana'], //數組類型 58 pobj: { 59 name: '張颯颯', 60 age: 25, 61 }, 62 }, 63 methods: { 64 65 }, 66 }); 67 </script> 68 </body> 69 </html>
7、Vue組件,子組件向父組件傳值。
1)、props傳值數據原則,單向數據流,意思就是只允許父組件向子組件傳遞數據,而不允許子組件直接操作props中的數據。如果子組件直接操作props中的數據,數據的控制邏輯就比較復雜,不容易進行控制,單向數據流處理邏輯比較清晰,所以推薦使用單向數據流。
2)、Vue子組件向父組件傳值的方式,是通過子組件通過自定義事件向父組件傳遞信息。$emit方法名稱攜帶一個參數,這個參數名稱就是自定義事件,這個事件就可以傳遞給父組件,父組件需要監聽這個事件,父組件通過v-on:事件名稱,直接綁定處理事件的名稱,后面跟着事件處理邏輯。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <!-- 第二步、在父組件進行打印輸出 --> 10 <!-- v-bind:style綁定樣式字體大小,可以通過子組件向父組件傳遞信息,通過自定義事件 --> 11 <div v-bind:style='{fontSize:fontSize2 + "px"}'>{{pmsg}}</div> 12 13 <!-- 第二步、在子組件進行引用父組件的數據pstr --> 14 <menu-item v-bind:parr2='parr' v-on:enlarge-text='handle'></menu-item> 15 <br /> 16 <menu-item v-bind:parr2='parr' @enlarge-text='handle'></menu-item> 17 </div> 18 19 <script src="vue.js" type="text/javascript"></script> 20 <script type="text/javascript"> 21 // 父組件向子組件傳值props屬性名類型 22 Vue.component('menu-item', { 23 /* 第三步、在子組件進行引用父組件傳來的數據 */ 24 props: ['parr2'], 25 template: ` 26 <div> 27 <ul> 28 <li v-bind:key='index' v-for='(item,index) in parr2'>{{item}}</li> 29 </ul> 30 <button @click='parr2.push("lemon")'>點擊</button> 31 32 <button @click='$emit("enlarge-text")'>擴大父組件中字體大小</button> 33 </div> 34 ` 35 /* <button @click='$emit("")'>擴大父組件中字體大小</button>中的$emit("")固定方法名稱,參數是自定義事件的名稱 */ 36 /* 然后在父組件中進行自定義事件的監聽, */ 37 }); 38 39 // 創建Vue對象 40 var vm = new Vue({ 41 el: '#app', 42 data: { // 對象,區別於組件的data是一個函數。 43 pmsg: '父組件中內容', // 第一步、在父組件中進行打印顯示 44 parr: ['apple', 'orange', 'banana'], //數組類型 45 fontSize2: 10, 46 }, 47 methods: { 48 // Vue子組件向父組件傳值的方式 49 handle: function() { 50 // 擴大字體大小 51 this.fontSize2 += 1; 52 } 53 }, 54 }); 55 </script> 56 </body> 57 </html>
3)、子組件通過自定義事件向父組件傳值。子組件通過自定義事件向父組件傳遞信息,$emit方法名稱可以攜帶兩個參數,第二個參數可以是傳遞給父組件的參數。在父組件中通過$event接收到子組件傳輸的數據,$event是固定寫法。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <!-- 第二步、在父組件進行打印輸出 --> 10 <!-- v-bind:style綁定樣式字體大小,可以通過子組件向父組件傳遞信息,通過自定義事件 --> 11 <div v-bind:style='{fontSize:fontSize2 + "px"}'>{{pmsg}}</div> 12 13 <!-- 第二步、在子組件進行引用父組件的數據pstr --> 14 <menu-item v-bind:parr2='parr' v-on:enlarge-text='handle($event)'></menu-item> 15 <br /> 16 <!-- $event是固定寫法,接收子組件傳遞的參數值 --> 17 <menu-item v-bind:parr2='parr' @enlarge-text='handle($event)'></menu-item> 18 </div> 19 20 <script src="vue.js" type="text/javascript"></script> 21 <script type="text/javascript"> 22 // 父組件向子組件傳值props屬性名類型 23 Vue.component('menu-item', { 24 /* 第三步、在子組件進行引用父組件傳來的數據 */ 25 props: ['parr2'], 26 template: ` 27 <div> 28 <ul> 29 <li v-bind:key='index' v-for='(item,index) in parr2'>{{item}}</li> 30 </ul> 31 <button @click='parr2.push("lemon")'>點擊</button> 32 33 <button @click='$emit("enlarge-text",100)'>擴大父組件中字體大小</button> 34 </div> 35 ` 36 /* <button @click='$emit("")'>擴大父組件中字體大小</button>中的$emit("")固定方法名稱,參數是自定義事件的名稱 */ 37 /* 然后在父組件中進行自定義事件的監聽, */ 38 /* $emit固定方法,第二個參數可以是子組件向父組件傳遞的參數值 */ 39 }); 40 41 // 創建Vue對象 42 var vm = new Vue({ 43 el: '#app', 44 data: { // 對象,區別於組件的data是一個函數。 45 pmsg: '父組件中內容', // 第一步、在父組件中進行打印顯示 46 parr: ['apple', 'orange', 'banana'], //數組類型 47 fontSize2: 10, 48 }, 49 methods: { 50 // Vue子組件向父組件傳值的方式 51 handle: function(val) { 52 // 擴大字體大小 53 this.fontSize2 += val; 54 } 55 }, 56 }); 57 </script> 58 </body> 59 </html>
8、Vue組件,組件間數據交互,非父子組件間傳值。
1)、單獨的事件中心管理組件間的通信。比如兄弟組件之間的數據交互,此時使用props和自定義事件就不好使了,此時就要使用事件中心的管理模式。比如組件A和組件B需要通過事件中心,組件A觸發事件,組件B監聽事件中心的事件,反之亦然。
2)、監聽事件與銷毀事件。事件中心通過var eventHub = new Vue()即可。eventHub.$on('add-todo',addTodo)用於監聽事件,參數1是自定義事件的名稱,參數2是事件函數。eventHub.$off('add-todo')用於銷毀時間事件,參數1是事件的名稱。
3)、觸發事件。eventHub.$emit('add-todo',id);參數一,觸發指定的事件名稱,參數二是攜帶的參數。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <div> 10 <span>事件中心的事件銷毀</span> 11 <button @click="handle">銷毀</button> 12 </div> 13 14 <br /> 15 <menu-tom></menu-tom> 16 17 <br /> 18 <!-- 兄弟組件,相互傳遞數據 --> 19 <menu-jack></menu-jack> 20 </div> 21 22 <script src="vue.js" type="text/javascript"></script> 23 <script type="text/javascript"> 24 // 提供Vue的事件中心,在事件中心可以進行事件的監聽 25 var hub = new Vue(); 26 27 // 兄弟之間組件數據傳遞 28 Vue.component('menu-tom', { 29 data: function() { 30 return { 31 num: 0, 32 } 33 }, 34 template: ` 35 <div> 36 <div>Tom:{{num}}</div> 37 <div> 38 <button @click='handle'>點擊</button> 39 </div> 40 </div> 41 `, 42 /* 點擊按鈕觸發事件 */ 43 methods: { 44 handle: function() { 45 // 觸發兄弟組件的事件 46 hub.$emit('jack-event', 5); 47 } 48 }, 49 // Vue的鈎子函數,Vue聲明周期里面的,mounted鈎子函數一旦被觸發,模板就就緒了,即可以對模板進行操作了。 50 mounted: function() { 51 // 監聽事件,箭頭函數,接收對方傳遞過來的數據 52 hub.$on('tom-event', (val) => { 53 // 拿到這對方的數據之后,進行累加操作,val是兄弟組件傳遞過來的 54 this.num += val; 55 }); 56 } 57 }); 58 59 // 兄弟組件 60 Vue.component('menu-jack', { 61 data: function() { 62 return { 63 num: 0, 64 } 65 }, 66 template: ` 67 <div> 68 <div>Jack:{{num}}</div> 69 <div> 70 <button @click='handle'>點擊</button> 71 </div> 72 </div> 73 `, 74 /* 點擊按鈕觸發事件 */ 75 methods: { 76 handle: function() { 77 // 觸發兄弟組件的事件 78 hub.$emit('tom-event', 10); 79 } 80 }, 81 // Vue的鈎子函數,Vue聲明周期里面的,mounted鈎子函數一旦被觸發,模板就就緒了,即可以對模板進行操作了。 82 mounted: function() { 83 // 監聽事件,箭頭函數,接收對方傳遞過來的數據 84 hub.$on('jack-event', (val) => { 85 // 拿到這對方的數據之后,進行累加操作,val是兄弟組件傳遞過來的 86 this.num += val; 87 }); 88 } 89 }); 90 91 // 創建Vue對象 92 var vm = new Vue({ 93 el: '#app', 94 data: { // 對象,區別於組件的data是一個函數。 95 96 }, 97 methods: { 98 handle: function() { 99 // 事件中心的事件銷毀 100 hub.$off('tom-event'); 101 hub.$off('jack-event'); 102 } 103 }, 104 }); 105 </script> 106 </body> 107 </html>
9、Vue組件,組件插槽。組件插槽的作用。
1)、父組件向子組件傳遞內容,這個內容是模板的內容。上面分析的是數據的交互。
2)、子組件使用<slot></slot>預留插槽,可以將父組件的中標簽之間的內容展示出來。
3)、插槽的位置位於子組件的模板中,使用<slot></slot>表示,語法固定。
4)、使用的時候,使用這個組件的時候通過標簽中的內容傳遞給<slot></slot>表示。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <menu-alert>此處有Bug!!!</menu-alert> 10 <menu-alert>此處有Warn!!!</menu-alert> 11 </div> 12 13 <script src="vue.js" type="text/javascript"></script> 14 <script type="text/javascript"> 15 // 提供Vue的事件中心,在事件中心可以進行事件的監聽 16 var hub = new Vue(); 17 18 // 兄弟之間組件數據傳遞 19 Vue.component('menu-alert', { 20 /* 插槽的內容是在組件標簽的中間傳遞過來的,如果不傳遞內容,這里有默認顯示 */ 21 template: ` 22 <div> 23 <strong>Error:</strong> 24 <slot>默認內容!</slot> 25 </div> 26 `, 27 }); 28 29 // 創建Vue對象 30 var vm = new Vue({ 31 el: '#app', 32 data: { // 對象,區別於組件的data是一個函數。 33 34 }, 35 methods: { 36 37 38 }, 39 }); 40 </script> 41 </body> 42 </html>
10、Vue組件,組件插槽。具名插槽用法。
1)、在子組件模板定義的時候,使用<slot name="插槽名稱"></slot>來定義,也可以省略name的屬性定義,就是默認插槽。
2)、在父組件中使用的時候,在標簽上面使用slot="插槽名稱"就可以使用該插槽了。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <div id="app"> 9 <menu-alert> 10 <p slot='header'>標題信息</p> 11 </menu-alert> 12 <menu-alert> 13 <p>詳細內容1</p> 14 <p>詳細內容2</p> 15 </menu-alert> 16 <menu-alert> 17 <p slot='footer'>底部信息</p> 18 </menu-alert> 19 20 <br /> 21 22 <menu-alert> 23 <!-- template臨時包裹內容,並不會渲染到界面上,可以同時把多條內容填充到文本中 --> 24 <template slot='header'></template> 25 <p>標題信息1</p> 26 <p>標題信息2</p> 27 </menu-alert> 28 <menu-alert> 29 <p>詳細內容1</p> 30 <p>詳細內容2</p> 31 </menu-alert> 32 <menu-alert> 33 <template slot='footer'> 34 <p>底部信息1</p> 35 <p>底部信息2</p> 36 </template> 37 38 </menu-alert> 39 </div> 40 41 <script src="vue.js" type="text/javascript"></script> 42 <script type="text/javascript"> 43 // 提供Vue的事件中心,在事件中心可以進行事件的監聽 44 var hub = new Vue(); 45 46 // 兄弟之間組件數據傳遞 47 Vue.component('menu-alert', { 48 /* 插槽的內容是在組件標簽的中間傳遞過來的,如果不傳遞內容,這里有默認顯示 */ 49 template: ` 50 <div> 51 <header> 52 <slot name='header'></slot> 53 </header> 54 <main> 55 <slot></slot> 56 </main> 57 <footer> 58 <slot name='footer'></slot> 59 </footer> 60 </div> 61 `, 62 }); 63 64 // 創建Vue對象 65 var vm = new Vue({ 66 el: '#app', 67 data: { // 對象,區別於組件的data是一個函數。 68 69 }, 70 methods: { 71 72 73 }, 74 }); 75 </script> 76 </body> 77 </html>
11、Vue組件,組件插槽。作用域插槽。
1)、應用場景,父組件對子組件的內容進行加工處理。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 <style type="text/css"> 7 .orange { 8 color: orange; 9 } 10 </style> 11 </head> 12 <body> 13 <div id="app"> 14 <!-- 應用場景,父組件對子組件的內容進行加工處理。 --> 15 <fruit-list v-bind:list2='list'> 16 <!-- <template slot-scope='slotProps'> --> 17 <!-- 父組件提供template標簽,來進行插槽的填充,slot-scopek屬性可以得到子組件中綁定的那個屬性,即子組件綁定的v-bind:info='item'的屬性 --> 18 <template slot-scope='slotProps'> 19 <!-- 就得到了子組件的數據了,就可以開始對子組件的數據進行加功處理了。 --> 20 <strong v-bind:class="orange2" v-if='slotProps.info.id==2'> 21 {{slotProps.info.name}} 22 </strong> 23 24 </template> 25 26 </fruit-list> 27 <br /> 28 </div> 29 30 <script src="vue.js" type="text/javascript"></script> 31 <script type="text/javascript"> 32 // 提供Vue的事件中心,在事件中心可以進行事件的監聽 33 var hub = new Vue(); 34 35 // 兄弟之間組件數據傳遞 36 Vue.component('fruit-list', { 37 props: ['list2'], 38 /* 插槽的內容是在組件標簽的中間傳遞過來的,如果不傳遞內容,這里有默認顯示 */ 39 template: ` 40 <div> 41 <li v-bind:key='item.id' v-for='(item,id) in list2'> 42 <slot v-bind:info='item'>{{item.name}}</slot> 43 </li> 44 </div> 45 `, 46 }); 47 48 // 創建Vue對象 49 var vm = new Vue({ 50 el: '#app', 51 data: { // 對象,區別於組件的data是一個函數。 52 orange2: 'orange', 53 list: [{ 54 id: 1, 55 name: 'apple', 56 }, { 57 id: 2, 58 name: 'orange', 59 }, { 60 id: 3, 61 name: 'banana', 62 }, ] 63 }, 64 methods: { 65 66 67 }, 68 }); 69 </script> 70 </body> 71 </html>
12、基於組件的案例,按照組件化方式實現業務需求。
1)、根據業務功能進行組件化划分。
a、標題組件,展示文本。
b、列表組件,列表展示,商品數量變更,商品刪除。
c、結算組件,計算商品金額。
2)、功能實現步驟。
a、實現整體布局和樣式效果。
b、划分獨立的功能組件。
c、組合所有的子組件形成整體結構。
d、逐個實現各個組件的功能,包含標題組件,列表組件,結算組件。
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 <style type="text/css"> 7 .container {} 8 9 .container .cart { 10 width: 300px; 11 /*background-color: lightgreen;*/ 12 margin: auto; 13 } 14 15 .container .title { 16 background-color: lightblue; 17 height: 40px; 18 line-height: 40px; 19 text-align: center; 20 /*color: #fff;*/ 21 } 22 23 .container .total { 24 background-color: #FFCE46; 25 height: 50px; 26 line-height: 50px; 27 text-align: right; 28 } 29 30 .container .total button { 31 margin: 0 10px; 32 background-color: #DC4C40; 33 height: 35px; 34 width: 80px; 35 border: 0; 36 } 37 38 .container .total span { 39 color: red; 40 font-weight: bold; 41 } 42 43 .container .item { 44 height: 55px; 45 line-height: 55px; 46 position: relative; 47 border-top: 1px solid #ADD8E6; 48 } 49 50 .container .item img { 51 width: 45px; 52 height: 45px; 53 margin: 5px; 54 } 55 56 .container .item .name { 57 position: absolute; 58 width: 90px; 59 top: 0; 60 left: 55px; 61 font-size: 16px; 62 } 63 64 .container .item .change { 65 width: 100px; 66 position: absolute; 67 top: 0; 68 right: 50px; 69 } 70 71 .container .item .change a { 72 font-size: 20px; 73 width: 30px; 74 text-decoration: none; 75 background-color: lightgray; 76 vertical-align: middle; 77 } 78 79 .container .item .change .num { 80 width: 40px; 81 height: 25px; 82 } 83 84 .container .item .del { 85 position: absolute; 86 top: 0; 87 right: 0px; 88 width: 40px; 89 text-align: center; 90 font-size: 40px; 91 cursor: pointer; 92 color: red; 93 } 94 95 .container .item .del:hover { 96 background-color: orange; 97 } 98 </style> 99 </head> 100 <body> 101 <div id="app"> 102 <div class="container"> 103 <my-cart></my-cart> 104 </div> 105 </div> 106 <script type="text/javascript" src="js/vue.js"></script> 107 <script type="text/javascript"> 108 var CartTitle = { 109 props: ['username'], 110 template: ` 111 <div class="title">{{username }} 的商品</div> 112 ` 113 }; 114 var CartList = { 115 props: ['lists'], 116 template: ` 117 <div> 118 <div v-bind:key='id' v-for='(item,id) in lists' class="item"> 119 <img v-bind:src="item.img" /> 120 <div class="name">{{item.name}}</div> 121 <div class="change"> 122 <a href="" @click.prevent='sub(item.id)'>-</a> 123 <input type="text" class="num" v-bind:value='item.num' @blur='changeNum(item.id,$event)' /> 124 <a href="" @click.prevent='add(item.id)'>+</a> 125 </div> 126 <div class="del" @click='del(item.id)'>×</div> 127 </div> 128 </div> 129 `, 130 methods: { 131 del: function(id) { 132 // 不建議直接在子組件中操作props: ['lists'],的數據。將id傳給父組件,讓父組件進行刪除。 133 console.log(id); 134 // 將id傳遞給父組件,通過自定義事件的方式向父組件傳遞信息 135 this.$emit('cart-del', id); 136 }, 137 /* 通過事件對象$event可以將參數傳遞過來 */ 138 changeNum: function(id, event) { 139 // 在父組件進行處理商品數量的變更,告訴父組件改的數量也要傳遞過去。 140 console.log(id, event.target.value); 141 // 觸發自定義事件 142 this.$emit('change-num', { 143 id: id, 144 num: event.target.value, 145 type: 'change', 146 }); 147 }, 148 sub: function(id) { 149 this.$emit('change-num', { 150 id: id, 151 type: 'sub', 152 }); 153 }, 154 add: function(id) { 155 this.$emit('change-num', { 156 id: id, 157 type: 'add', 158 }) 159 }, 160 } 161 }; 162 var CartTotal = { 163 props: ['lists'], 164 template: ` 165 <div class="total"> 166 <span>總價:{{totalPrice}}</span> 167 <button>結算</button> 168 </div> 169 `, 170 // 計算屬性 171 computed: { 172 totalPrice: function() { 173 // 計算商品的總價,遍歷數組,讓單價乘以數量再累加。 174 var total = 0; 175 this.lists.forEach(item => { 176 // 計算出總價 177 total += item.price * item.num; 178 }); 179 // 將計算的總價返回。 180 return total; 181 } 182 } 183 }; 184 185 // 全局組件,這個全局組件里面包含三個局部子組件。 186 Vue.component('my-cart', { 187 data: function() { 188 return { 189 uname: '張颯颯', 190 list: [{ 191 id: 1, 192 name: 'TCL彩電', 193 price: 1000, 194 num: 1, 195 img: 'img/a.jpg' 196 }, { 197 id: 2, 198 name: '機頂盒', 199 price: 1000, 200 num: 1, 201 img: 'img/b.jpg' 202 }, { 203 id: 3, 204 name: '海爾冰箱', 205 price: 1000, 206 num: 1, 207 img: 'img/c.jpg' 208 }, { 209 id: 4, 210 name: '小米手機', 211 price: 1000, 212 num: 3, 213 img: 'img/d.jpg' 214 }, { 215 id: 5, 216 name: 'PPTV電視', 217 price: 1000, 218 num: 2, 219 img: 'img/e.jpg' 220 }] 221 } 222 }, 223 /* 通過父組件監聽子組件的刪除事件,將id傳輸給delCart方法的$event參數 */ 224 template: ` 225 <div class="cart"> 226 <cart-title v-bind:username='uname'></cart-title> 227 <cart-list v-bind:lists='list' v-on:cart-del='delCart($event)' @change-num='changeNum($event)'></cart-list> 228 <cart-total v-bind:lists='list'></cart-total> 229 </div> 230 `, 231 components: { 232 'cart-title': CartTitle, 233 'cart-list': CartList, 234 'cart-total': CartTotal, 235 }, 236 methods: { 237 delCart: function(id) { 238 console.log(id); 239 // 通過id刪除list集合中的數據,第一步,找到id所對應數據的索引,第二步,根據索引刪除對應的數據。 240 // 第一步,找到id所對應數據的索引。 241 var index = this.list.findIndex(item => { 242 return item.id == id; 243 }); 244 245 // 第二步,根據索引刪除對應的數據 246 this.list.splice(index, 1); 247 }, 248 changeNum: function(val) { 249 console.log(val); 250 // 分為三種情況,輸入域變更,加好變更,減號變更。 251 if (val.type == 'change') { 252 // 將子組件傳遞過來的數據num更新list中對應的數據 253 this.list.some(item => { 254 // 如果集合里面的和子組件傳遞過來的匹配相等就進行更新即可。 255 if (item.id == val.id) { 256 // 此時就完成了更新 257 item.num = val.num; 258 // 終止遍歷 259 return true; // 返回true表示終止遍歷 260 } 261 }); 262 } else if (val.type == 'add') { 263 this.list.some(item => { 264 // 如果集合里面的和子組件傳遞過來的匹配相等就進行更新即可。 265 if (item.id == val.id) { 266 // 此時就完成了更新 267 item.num += 1; 268 // 終止遍歷 269 return true; // 返回true表示終止遍歷 270 } 271 }); 272 } else if (val.type == 'sub') { 273 this.list.some(item => { 274 // 如果集合里面的和子組件傳遞過來的匹配相等就進行更新即可。 275 if (item.id == val.id) { 276 // 此時就完成了更新 277 item.num -= 1; 278 // 終止遍歷 279 return true; // 返回true表示終止遍歷 280 } 281 }); 282 } 283 } 284 } 285 }); 286 287 var vm = new Vue({ 288 el: '#app', 289 data: { 290 291 } 292 }); 293 </script> 294 </body> 295 </html>
效果,如下所示: