• 第四节: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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    千氪公开课第一期|如何实现写作收益的最大化?-千氪
    2018年十大区块链投资机构盘点-千氪
    vscode 问题
    jquery点击输入框提示文字消失输入文字变色
    IE8兼容css3
    点击特定按钮,文本框实现输入状态变化
    h5页面在iOS上的问题解决
    纯css三角提示框,兼容IE8
    适配问题
    webstorm配置编译sass
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/12326466.html
Copyright © 2020-2023  润新知