第四節:Vuejs組件及組件之間的交互


 一. 組件及其交互

1.組件的注冊

(1).全局注冊

   Vue.component('組件名稱', { }) 第1個參數是標簽名稱,第2個參數是一個選項對象。

選項參數包括

  data:必須是一個function,然后要return,在return里面聲明變量。

  template: 用``符號包裹

  methods: 聲明方法

注意事項:

  A. 模板必須是單個根元素

  B. 如果使用駝峰式命名組件,那么在使用組件的時候,只能在另一個組件字符串模板中用駝峰的方式使用組件,在普通標簽中直接使用的話,必須加短橫線。如: HelloWord模板,在body里用的時候必須<hello-word>,而在btn-counter組件中,則可以直接使用<HelloWord>

總之:不推薦使用駝峰命名,建議小寫+短橫線。

       Vue.component('HelloWord', {
                template: `<div>我是HelloWord組件</div>`
            });
            Vue.component('btn-counter', {
                data: function() {
                    return {
                        count: 0
                    }
                },
                template: `
                    <div>
                      <div>{{count}}</div>
                      <button @click="handle">點擊了{{count}}次</button>
                      <HelloWord></HelloWord>
                    </div>
                `,
                methods: {
                    handle: function() {
                        this.count += 3;
                    }
                }
            });
<p>1.全局組件</p>
<btn-counter></btn-counter>
<hello-word></hello-word>

(2).局部組件

  在Vue實例中components進行聲明局部組件,僅供該實例使用。

 

2.父組件向子組件傳值

  父組件發送形式是以屬性的形式綁定在子組件上,可以直接往里傳值,也可以用:符號進行綁定變量。然后子組件用屬性props接收,props是一個數組。

注意事項:

  A.在props使用駝峰形式,在頁面中需要使用短橫線的形式字符串, 而在字符串模板中則沒有這個限制。

  PS:這里面不演示了,總之在vue中不建議使用駝峰命名。

  B.比如p3是props里聲明的一個屬性,在使用的時候 p3:'true' 這種情況p3是string類型; 而 :p3:'true',這種情況p3是布爾類型。

3.子組件向父組件傳值

  子組件用 $emit() 觸發事件,第一個參數為 自定義的事件名稱 第二個參數為需要傳遞的數據(這里最多只能傳遞一個參數,但他可以是一個對象哦,對象里面包括很多屬性,如下:),父組件用v-on(或者@) 監聽子組件的事件,這里用固定命名 $event 來獲取子組件傳遞過來的參數。

 this.$emit('change-num', {
     id: id,
     type: 'change',
     num: 10
   });

4.兄弟組件之間的交互

  兄弟之間傳遞數據需要借助於事件中心,通過事件中心傳遞數據,提供事件中心 var hub = new Vue()。

  (1).傳遞數據方,通過一個事件觸發hub.$emit(方法名,傳遞的數據),這里可以傳遞多個數據哦

  (2).接收數據方,通過mounted(){} 鈎子中 觸發hub.$on()方法名,這里可以接收多個數據哦

  (3).銷毀事件 通過hub.$off()方法名銷毀之后無法進行傳遞數據

5.插槽

  組件的最大特性就是復用性,而用好插槽能大大提高組件的可復用能力,在template中使用<slot>標簽

(1).匿名插槽

   使用時,組件標簽中嵌套的內容(包含html)會替換掉slot; 如果不傳值 ,則使用 slot 中的默認值。

(2).具名插槽

  使用時,通過slot屬性來指定, 這個slot的值必須和下面slot組件得name值對應上 如果沒有匹配到 則放到匿名的插槽中

特別注意:具名插槽的渲染順序,完全取決於模板中的順序,而不是取決於父組件中元素的順序!

(3).作用域插槽

  A. 作用域插槽使用場景

    a.父組件對子組件加工處理

    b.既可以復用子組件的slot,又可以使slot內容不一致

  B.作用域插槽的使用

    a.子組件模板中,<slot>元素上有一個類似props傳遞數據給組件的寫法msg="xxx

    b.插槽可以提供一個默認內容,如果如果父組件沒有為這個插槽提供了內容,會顯示默認的內容。 如果父組件為這個插槽提供了內容,則默認的內容會被替換掉

完整代碼如下:

  1 <!DOCTYPE html>
  2 <html>
  3     <head>
  4         <meta charset="utf-8">
  5         <title>06-組件及交互</title>
  6         <style type="text/css">
  7             p {
  8                 font-size: 20px;
  9                 color: #0000FF;
 10                 font-weight: bold;
 11             }
 12             .current {
 13               color: orange;
 14             }
 15         </style>
 16     </head>
 17     <body>
 18         <div id="myApp">
 19             <p>1.全局組件</p>
 20             <btn-counter></btn-counter>
 21             <hello-word></hello-word>
 22             <p>2.局部組件</p>
 23             <ypfzj1></ypfzj1>
 24             <p>3.父組件向子組件傳值</p>
 25             <father-child p1="ypf1" :p2="p2" p3="true"></father-child>
 26             <father-child p1="ypf1" :p2="p2" :p3="true"></father-child>
 27             <p>4.子組件向父組件傳值</p>
 28             <div :style="{fontSize:myFontSize+'px'}">我是內容,等着被控制</div>
 29             <child-father @exchangebig='handle1($event)'></child-father>
 30             <p>5.兄弟組件相互交互</p>
 31             <brother-one></brother-one>
 32             <brother-two></brother-two>
 33             <button @click="destoryHandle">銷毀事件</button>
 34             <p>6.1 匿名插槽</p>
 35             <my-tips1>您超標了</my-tips1>
 36             <my-tips1>用戶名不正確</my-tips1>
 37             <my-tips1></my-tips1>
 38             <p>6.2 具名插槽</p>
 39             <my-tips2>
 40                 <div slot='header'>我是header</div>
 41                 <div>我是內容1</div>
 42                 <div>我是內容2</div>
 43                 <div slot='footer'>我是footer</div>
 44             </my-tips2>
 45             <my-tips2>
 46                 <div slot='footer'>我是footer</div>                
 47                 <div>我是內容1</div>
 48                 <div slot='header'>我是header</div>
 49                 <div>我是內容2</div>            
 50             </my-tips2>
 51             <p>6.3 作用域插槽</p>
 52             <my-tips3 :list='list'>
 53              <template slot-scope='slotProps'>
 54                 <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
 55                 <span v-else>{{slotProps.info.name}}</span>
 56               </template>
 57             </my-tips3>
 58         </div>
 59 
 60         <script src="js/vue.min.js" type="text/javascript" charset="utf-8"></script>
 61         <script type="text/javascript">
 62             //全局組件
 63             Vue.component('HelloWord', {
 64                 template: `<div>我是HelloWord組件</div>`
 65             });
 66             Vue.component('btn-counter', {
 67                 data: function() {
 68                     return {
 69                         count: 0
 70                     }
 71                 },
 72                 template: `
 73                     <div>
 74                       <div>{{count}}</div>
 75                       <button @click="handle">點擊了{{count}}次</button>
 76                       <HelloWord></HelloWord>
 77                     </div>
 78                 `,
 79                 methods: {
 80                     handle: function() {
 81                         this.count += 3;
 82                     }
 83                 }
 84             });
 85             //父組件向子組件中傳值
 86             Vue.component('father-child', {
 87                 props: ['p1', 'p2', 'p3'],
 88                 data: function() {
 89                     return {
 90                         msg: '我是用來看父組件向子組件中傳值的'
 91                     }
 92                 },
 93                 template: `
 94                     <div>
 95                         <div>{{msg+"--"+p1+"--"+p2+"--"+p3}}</div>
 96                         <div>p3的類型為:{{typeof p3}}</div>
 97                     </div>                `
 98             });
 99             
100             //子組件向父組件傳值
101             Vue.component('child-father', {
102                 props: [],
103                 data: function() {
104                     return {
105                         msg: '我是用來看父組件向子組件中傳值的'
106                     }
107                 },
108                 template: `
109                     <div>
110                         <button @click='$emit("exchangebig",5)'>增大測試1</button>
111                         <button @click='$emit("exchangebig",10)'>增大測試2</button>
112                     </div>                `
113             });
114             //兩個兄弟組件
115             //事件中心
116             var hub = new Vue();
117             Vue.component('brother-one', {
118                 data: function() {
119                     return {
120                         num: 0
121                     }
122                 },
123                 template: `
124                     <div>
125                         <div>borther1:{{num}}</div>
126                         <div>
127                             <button @click='handle'>控制兄弟brother2</button>
128                         </div>
129                     </div>
130                 `,
131                 methods: {
132                     handle: function() {
133                         //傳遞數據方,通過一個事件觸發hub.$emit(方法名,傳遞的數據) 觸發兄弟組件的事件
134                         hub.$emit('yChild2Event', 2, 1);
135                     }
136                 },
137                 //Dom創建后
138                 mounted: function() {
139                     //接收數據方,通過mounted(){} 鈎子中 觸發hub.$on(方法名)
140                     hub.$on('yChild1Event', (val1, val2) => {
141                         this.num += val1 + val2;
142                     });
143                 }
144             })
145             Vue.component('brother-two', {
146                 data: function() {
147                     return {
148                         num: 0
149                     }
150                 },
151                 template: `
152                     <div>
153                         <div>borther2:{{num}}</div>
154                         <div>
155                             <button @click='handle'>控制兄弟brother1</button>
156                         </div>
157                     </div>
158                 `,
159                 methods: {
160                     handle: function() {
161                         //傳遞數據方,通過一個事件觸發hub.$emit(方法名,傳遞的數據) 觸發兄弟組件的事件
162                         hub.$emit('yChild1Event', 4, 3);
163                     }
164                 },
165                 //Dom創建后
166                 mounted: function() {
167                     //接收數據方,通過mounted(){} 鈎子中 觸發hub.$on(方法名)
168                     hub.$on('yChild2Event', (val1, val2) => {
169                         this.num += val1 + val2;
170                     });
171                 }
172             })
173             //匿名插槽
174             Vue.component('my-tips1',{
175                 template:`
176                     <div>
177                       <strong>提醒:</strong>
178                       <slot>默認內容</slot>
179                       <slot>默認內容1</slot>
180                     </div>
181                 `,
182             })
183             //具名插槽
184             Vue.component('my-tips2', {
185               template: `
186                 <div>
187                     <div>我是具名插槽</div>
188                     <slot name='header'></slot>
189                     <slot></slot>
190                     <slot name='footer'></slot>
191                 </div>
192               `
193             });
194             //作用域插槽
195             Vue.component('my-tips3', {
196               props: ['list'],
197               template: `
198                 <div>
199                   <li :key='item.id' v-for='item in list'>
200                     <slot :info='item'>{{item.name}}</slot>
201                   </li>
202                 </div>
203               `
204             });
205             
206             //放在下面的局部組件里
207             var ypfzj1 = {
208                 data: function() {
209                     return {
210                         msg: '我是局部組件1'
211                     }
212                 },
213                 template: '<div>{{msg}}</div>'
214             };
215 
216             //Vm實例
217             var vm = new Vue({
218                 el: '#myApp',
219                 data: {
220                     p2: 'ypf2',
221                     myFontSize: 12,
222                     list: [{
223                       id: 1,
224                       name: 'apple'
225                     },{
226                       id: 2,
227                       name: 'orange'
228                     },{
229                       id: 3,
230                       name: 'banana'
231                     }]
232                 },
233                 methods: {
234                     handle1: function(val) {
235                         this.myFontSize += val;
236                     },
237                     //銷毀事件
238                     destoryHandle: function() {
239                         hub.$off('yChild1Event');
240                         hub.$off('yChild2Event');
241                     }
242                 },
243                 components: {
244                     'ypfzj1': ypfzj1,
245                 }
246             });
247         </script>
248     </body>
249 </html>
View Code

運行效果:

 

 二. 購物車案例

1.需求分析

  實現購物車商品列表的加載,數量的增加和減少、物品的刪除、及其對應總價的變化。

效果圖如下:

2. 實戰演練 

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4   <meta charset="UTF-8">
  5   <title>組件之購物車案例</title>
  6   <style type="text/css">
  7     .container {
  8     }
  9     .container .cart {
 10       width: 300px;
 11       margin: auto;
 12     }
 13     .container .title {
 14       background-color: lightblue;
 15       height: 40px;
 16       line-height: 40px;
 17       text-align: center;
 18       /*color: #fff;*/  
 19     }
 20     .container .total {
 21       background-color: #FFCE46;
 22       height: 50px;
 23       line-height: 50px;
 24       text-align: right;
 25     }
 26     .container .total button {
 27       margin: 0 10px;
 28       background-color: #DC4C40;
 29       height: 35px;
 30       width: 80px;
 31       border: 0;
 32     }
 33     .container .total span {
 34       color: red;
 35       font-weight: bold;
 36     }
 37     .container .item {
 38       height: 55px;
 39       line-height: 55px;
 40       position: relative;
 41       border-top: 1px solid #ADD8E6;
 42     }
 43     .container .item img {
 44       width: 45px;
 45       height: 45px;
 46       margin: 5px;
 47     }
 48     .container .item .name {
 49       position: absolute;
 50       width: 90px;
 51       top: 0;left: 55px;
 52       font-size: 16px;
 53     }
 54 
 55     .container .item .change {
 56       width: 100px;
 57       position: absolute;
 58       top: 0;
 59       right: 50px;
 60     }
 61     .container .item .change a {
 62       font-size: 20px;
 63       width: 30px;
 64       text-decoration:none;
 65       background-color: lightgray;
 66       vertical-align: middle;
 67     }
 68     .container .item .change .num {
 69       width: 40px;
 70       height: 25px;
 71     }
 72     .container .item .del {
 73       position: absolute;
 74       top: 0;
 75       right: 0px;
 76       width: 40px;
 77       text-align: center;
 78       font-size: 40px;
 79       cursor: pointer;
 80       color: red;
 81     }
 82     .container .item .del:hover {
 83       background-color: orange;
 84     }
 85   </style>
 86 </head>
 87 <body>
 88   <div id="app">
 89     <div class="container">
 90       <my-cart></my-cart>
 91     </div>
 92   </div>
 93   <script type="text/javascript" src="js/vue.js"></script>
 94   <script type="text/javascript">
 95     
 96     var CartTitle = {
 97       props: ['uname'],
 98       template: `
 99         <div class="title">{{uname}}的商品</div>
100       `
101     }
102     
103     var CartList = {
104       props: ['list'],
105       template: `
106         <div>
107           <div :key='item.id' v-for='item in list' class="item">
108             <img :src="item.img"/>
109             <div class="name">{{item.name}}</div>
110             <div class="change">
111               <a href="" @click.prevent='sub(item.id)'></a>
112               <input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
113               <a href="" @click.prevent='add(item.id)'></a>
114             </div>
115             <div class="del" @click='del(item.id)'>×</div>
116           </div>
117         </div>
118       `,
119       methods: {
120         changeNum: function(id, event){
121             //向父組件傳值
122           this.$emit('change-num', {
123             id: id,
124             type: 'change',
125             num: event.target.value
126           });
127         },
128         sub: function(id){
129             //向父組件傳值
130           this.$emit('change-num', {
131             id: id,
132             type: 'sub'
133           });
134         },
135         add: function(id){
136             //向父組件傳值
137           this.$emit('change-num', {
138             id: id,
139             type: 'add'
140           });
141         },
142         del: function(id){
143           // 把id傳遞給父組件
144           this.$emit('cart-del', id);
145         }
146       }
147     }
148     
149     var CartTotal = {
150       props: ['list'],
151       template: `
152         <div class="total">
153           <span>總價:{{total}}</span>
154           <button>結算</button>
155         </div>
156       `,
157       computed: {
158         total: function() {
159           // 計算商品的總價
160           var t = 0;
161           this.list.forEach(item => {
162             t += item.price * item.num;
163           });
164           return t;
165         }
166       }
167     }
168    
169     Vue.component('my-cart',{
170       data: function() {
171         return {
172           uname: '張三',
173           list: [{
174             id: 1,
175             name: 'TCL彩電',
176             price: 1000,
177             num: 1,
178             img: 'img/a.jpg'
179           },{
180             id: 2,
181             name: '機頂盒',
182             price: 1000,
183             num: 1,
184             img: 'img/b.jpg'
185           },{
186             id: 3,
187             name: '海爾冰箱',
188             price: 1000,
189             num: 1,
190             img: 'img/c.jpg'
191           },{
192             id: 4,
193             name: '小米手機',
194             price: 1000,
195             num: 1,
196             img: 'img/d.jpg'
197           },{
198             id: 5,
199             name: 'PPTV電視',
200             price: 1000,
201             num: 2,
202             img: 'img/e.jpg'
203           }]
204         }
205       },
206       template: `
207         <div class='cart'>
208           <cart-title :uname='uname'></cart-title>
209           <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
210           <cart-total :list='list'></cart-total>
211         </div>
212       `,
213       components: {
214         'cart-title': CartTitle,
215         'cart-list': CartList,
216         'cart-total': CartTotal
217       },
218       methods: {
219         changeNum: function(val) {
220           // 分為三種情況:輸入域變更、加號變更、減號變更
221           if(val.type=='change') {
222             // 根據子組件傳遞過來的數據,跟新list中對應的數據
223             this.list.some(item=>{
224               if(item.id == val.id) {
225                 item.num = val.num;
226                 // 終止遍歷
227                 return true;
228               }
229             });
230           }else if(val.type=='sub'){
231             // 減一操作
232             this.list.some(item=>{
233               if(item.id == val.id) {
234                 item.num -= 1;
235                 // 終止遍歷
236                 return true;
237               }
238             });
239           }else if(val.type=='add'){
240             // 加一操作
241             this.list.some(item=>{
242               if(item.id == val.id) {
243                 item.num += 1;
244                 // 終止遍歷
245                 return true;
246               }
247             });
248           }
249         },
250         delCart: function(id) {
251           // 根據id刪除list中對應的數據
252           // 1、找到id所對應數據的索引
253           var index = this.list.findIndex(item=>{
254             return item.id == id;
255           });
256           // 2、根據索引刪除對應數據
257           this.list.splice(index, 1);
258         }
259       }
260     });
261    
262     
263     
264     var vm = new Vue({
265       el: '#app',
266       data: {
267 
268       }
269     });
270 
271   </script>
272 </body>
273 </html>
View Code

 

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM