美文网首页
03Vue的组件化开发

03Vue的组件化开发

作者: 攻城老狮 | 来源:发表于2021-02-01 12:27 被阅读0次

    Vue的组件化开发


    组件注册

    1. 全局组件注册的语法
    Vue.component(组件名称,{
        data: 组件数据,
        template: 组件模板内容
    });
    
    注意:
    1.data必须是一个函数(目的是形成闭包环境,使数据独立)
    2.组件模板的内容必须是单根元素
    3.组件模板内容可以是模板字符串(ES6)
    4.组件命名方式
    其中,若使用驼峰的方式命名,则只能在字符串模板中使用,在普通的标签模板中还必须转换为短横线的方式(全部字母小写,并使用短横线相连)
        * 短横线方式
    Vue.component("my-component",{});
        * 驼峰方式
    Vue.component("MyComponent",{});
    
    1. 组件用法
    1.可直接将名称作为标签,在html中使用
    例:
    <div id="app">
        <button-counter></button-counter>
    </div>
    2.组件可以充分使用,并且组件之间的数据相互隔离互不影响
    <div id="app">
        <button-counter></button-counter>
        <button-counter></button-counter>
        <button-counter></button-counter>
    </div>
    

    实例:使用组件的方式,实现点击按钮触发数据自增的功能。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <button-counter></button-counter>
            <button-counter></button-counter>
            <button-counter></button-counter>
        </div>
        <script src="js/jquery-3.4.1.js"></script>
        <script src="js/vue.js"></script>
        <script>
           Vue.component("button-counter",{
               data: function(){
                    return {
                        num: 0
                    }
               },
               template: "<button @click='handle'>点击了{{num}}次</button>",
               methods: {
                    handle: function(){
                        this.num++;
                    }
               }
           });
           var vm = new Vue({
               el: "#app",
               data: {
               }
          });
        </script>
    </body>
    
    </html>
    
    1. 局部组件注册

    局部组件只能在注册的父容器中使用,在其他组件中无法使用。此处,与自定义指令和过滤器类似。

    var ComponentA = {};
    var ComponentB = {};
    var ComponentC = {};
    new Vue({
       el: "#app",
       components: {
           'component-a':ComponentA,
           'component-b':ComponentB,
           'component-c':ComponentC
       }
    });
    

    实例:通过局部组件注册几个组件,并调用测试。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <hello-yorick></hello-yorick>
            <hello-tom></hello-tom>
        </div>
        <script src="js/jquery-3.4.1.js"></script>
        <script src="js/vue.js"></script>
        <script>
            var helloYorick = {
                data: function(){
                    return {
                        msg: "Hello yorick"
                    };
                },
                template: "<div>{{msg}}</div>"
            };
            var helloTom = {
                data: function(){
                    return {
                        msg: "Hello tom"
                    };
                },
                template: "<div>{{msg}}</div>"
            };
            var vm = new Vue({
                el: "#app",
                data: {
                },
                components:{
                    "hello-yorick": helloYorick,
                    "hello-tom": helloTom
                }
            });
        </script>
    </body>
    
    </html>
    

    组件间的数据交互

    1. 父组件向子组件传值
    • 组件内部通过props接收传递过来的数值
    Vue.component("menu-item",{
       props:["title"],
       template: "<div>{{title}}</div>"
    });
    
    注意:
    1.若在props中使用驼峰形式,则普通标签模板中需要使用短横线的形式传递。而在字符串形式的模板中没有此限制。
    2.props是单向数据流。
    
    • 父组件通过属性将值传递给子组件,有以下三种传递方式
    1.传递静态的值
    <menu-item title="来自父组件的数据"></menu-item>
    2.传递动态的值
    <menu-item :title="ptitle"></menu-item>
    3.两者结合使用
    <menu-item :title="ptitle" content="父组件传递的静态content值"></menu-item>
    

    实例:通过父组件向子组件传递数据,并显示数据内容。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
           <div>{{fmsg}}</div>
           <menu-item title="父组件传递的静态title值"></menu-item>
           <menu-item :title="ptitle"></menu-item>
           <menu-item :title="ptitle" content="父组件传递的静态content值"></menu-item>
        </div>
        <script src="js/jquery-3.4.1.js"></script>
        <script src="js/vue.js"></script>
        <script>
            Vue.component("menu-item",{
                props: ["title","content"],
                data: function(){
                    return {
                        smsg: "子组件内容"
                    }
                },
                template: "<div>{{smsg + '---' + title + '----' + content}}</div>"
            });
            var vm = new Vue({
                el: "#app",
                data: {
                    ptitle: "父组件传递的动态title值",
                    fmsg: "父组件的内容"
                }
            });
        </script>
    </body>
    
    </html>
    
    • props传递不同类型数据
    字符串 String
    数值 Number(通过v-bind绑定的属性,未绑定的为String类型)
    布尔值 Boolean(通过v-bind绑定的属性,未绑定的为String类型)
    数组 Array
    对象 Object
    

    实例:父组件向子组件传递不同类型的数据,并显示。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
           <menu-item :pstr="pstr" :pnum="233" :pboo="true" :parr="parr" :pobj="pobj"></menu-item>
        </div>
        <script src="js/jquery-3.4.1.js"></script>
        <script src="js/vue.js"></script>
        <script>
            Vue.component("menu-item",{
                props: ["pstr","pnum","pboo","parr","pobj"],
                template: `
                    <div>
                        <div>{{pstr}}</div>
                        <div>{{typeof pnum + '---' + pnum}}</div>
                        <div>{{typeof pboo + '---' + pboo}}</div>
                        <ul>
                            <li :key='index' v-for='(item,index) in parr'>{{item}}</li>
                        </ul>
                        <div>
                            <span>{{pobj.uname}}</span>
                            <span>{{pobj.age}}</span>
                        </div>
                    </div>
                `
            });
            var vm = new Vue({
                el: "#app",
                data: {
                   pstr: "String",
                   parr: ["apple","lemon","banana"],
                   pobj: {
                       uname: "alice",
                       age: 13
                   }
                }
            });
        </script>
    </body>
    
    </html>
    
    1. 子组件向父组件传值
    • 子组件通过自定义事件向父组件传递信息
    <button v-on:click="$emit('enlarge-text')">
        扩大字体
    </button>
    
    • 父组件需要监听子组件传递的事件
    <menu-item v-on:enlarge-text="fontSize+=0.1"></menu-item>
    

    实例:通过子组件向父组件传递信息,使得父组件的文本大小增加。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <div :style="{fontSize:fontSize+'px'}">{{fmsg}}</div>
            <menu-item v-on:enlarge-text="handle"></menu-item>
        </div>
        <script src="js/jquery-3.4.1.js"></script>
        <script src="js/vue.js"></script>
        <script>
            Vue.component("menu-item",{
               template: `
                    <button @click="$emit('enlarge-text')">点击按钮</button>
               `
            });
            var vm = new Vue({
                el: "#app",
                data: {
                  fmsg: "父文本内容",
                  fontSize: 10
                },
                methods: {
                    handle: function(){
                        this.fontSize += 5;
                    }
                }
            });
        </script>
    </body>
    
    </html>
    
    • 子组件向父组件传递信息时携带参数
    <button v-on:click="$emit('enlarge-text',0.1)">
        扩大字体
    </button>
    
    • 父组件获取子组件传递的参数
    <menu-item v-on:enlarge-text="fontSize+=$event"></menu-item>
    

    实例:改进上述实例代码,实现将增加的值由子组件传递过来。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <div :style="{fontSize:fontSize+'px'}">{{fmsg}}</div>
            <menu-item v-on:enlarge-text="handle($event)"></menu-item>
        </div>
        <script src="js/jquery-3.4.1.js"></script>
        <script src="js/vue.js"></script>
        <script>
            Vue.component("menu-item",{
               template: `
                    <button @click="$emit('enlarge-text',5)">点击按钮</button>
               `
            });
            var vm = new Vue({
                el: "#app",
                data: {
                  fmsg: "父文本内容",
                  fontSize: 10
                },
                methods: {
                    handle: function(val){
                        this.fontSize += val;
                    }
                }
            });
        </script>
    </body>
    
    </html>
    
    1. 兄弟组件之间的数据传递
    • 单独的事件中心负责管理组件间的通信
    var hub = new Vue();
    
    • 监听事件与销毁事件
    hub.$on("add-todo",addTodo);
    hub.$off("add-todo");
    
    • 触发事件
    hub.$emit("add-todo",id);
    

    实例:通过兄弟组件之间的数据传递,实现1号控制2号的数据变化,2号控制1号的数据变化。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <test-tom></test-tom>
            <test-jerry></test-jerry>
            <div>
                <button @click="handle">销毁事件</button>
            </div>
        </div>
        <script src="js/jquery-3.4.1.js"></script>
        <script src="js/vue.js"></script>
        <script>
            var hub = new Vue();
            Vue.component("test-tom",{
                data: function(){
                    return {
                        num: 0
                    }
                },
                template: `
                    <div>
                        <div>TOM:{{num}}</div>
                        <button @click='handle'>点击</button>
                    </div>
                `,
                methods: {
                    handle: function(){
                        hub.$emit("jerry-event",2);
                    }
                },
                mounted: function(){
                    hub.$on("tom-event",(val)=>{
                        this.num += val;
                    });
                }
            });
            Vue.component("test-jerry",{
                data: function(){
                    return {
                        num: 0
                    }
                },
                template: `
                    <div>
                        <div>JERRY:{{num}}</div>
                        <button @click='handle'>点击</button>
                    </div>
                `,
                methods: {
                    handle: function(){
                        hub.$emit("tom-event",1);
                    }
                },
                mounted: function(){
                    hub.$on("jerry-event",(val)=>{
                        this.num += val;
                    });
                }
            });
            var vm = new Vue({
                el: "#app",
                data: {
                 
                },
                methods: {
                    handle: function(){
                        hub.$off("tom-event");
                        hub.$off("jerry-event");
                    }
                }
            });
        </script>
    </body>
    
    </html>
    

    组件插槽

    1. 组件插槽的基本使用
    • 插槽位置
    Vue.component("alert-box",{
        template: `
            <div>
                <strong>Error:</strong>
                <slot></slot>
            </div>
        `
    });
    
    • 插槽内容
    <alert-box>出现异常</alert-box>
    

    实例:使用插槽可以获取父组件中标签内部的数据。若数据为空,则会显示slot标签内的默认数据。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <alert-box>出现异常</alert-box>
            <alert-box></alert-box>
        </div>
        <script src="js/jquery-3.4.1.js"></script>
        <script src="js/vue.js"></script>
        <script>
            Vue.component("alert-box",{
                template: `
                    <div>
                        <strong>Error:</strong>
                        <slot>默认内容</slot>
                    </div>
                `
            });
            var vm = new Vue({
                el: "#app",
                data: { 
                }
            });
        </script>
    </body>
    
    </html>
    
    1. 具名插槽的用法
    • 具名插槽定义
    Vue.component("container",{
        template: `
            <div>
                <header>
                    <slot name='header'></slot>
                </header>
                <main>
                    <slot></slot>
                </main>
                <footer>
                    <slot name='footer'></slot>
                </footer>
            </div>
        `
    });
    
    • 具名插槽的内容
    1.单个标签添加slot属性
    <container>
        <h1 slot="header">标题内容</h1>
        <p>主要内容1</p>
        <p>主要内容2</p>
        <p slot="footer">底部内容</p>
    </container>
    2.多个标签添加slot属性
     <container>
         <template slot="header">
             <h1>标题内容1</h1>
             <h1>标题内容2</h1>
         </template>
         <p>主要内容1</p>
         <p>主要内容2</p>
         <template slot="footer">
             <p>底部内容1</p>
             <p>底部内容2</p>
         </template>
    </container>
    

    实例:按照插槽给予的名字,实施对应数据的绑定

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <container>
                <h1 slot="header">标题内容</h1>
                <p>主要内容1</p>
                <p>主要内容2</p>
                <p slot="footer">底部内容</p>
            </container>
    
    
            <container>
                <template slot="header">
                    <h1>标题内容1</h1>
                    <h1>标题内容2</h1>
                </template>
                <p>主要内容1</p>
                <p>主要内容2</p>
                <template slot="footer">
                    <p>底部内容1</p>
                    <p>底部内容2</p>
                </template>
            </container>
        </div>
        <script src="js/jquery-3.4.1.js"></script>
        <script src="js/vue.js"></script>
        <script>
            Vue.component("container",{
                template: `
                    <div>
                        <header>
                            <slot name='header'></slot>
                        </header>
                        <main>
                            <slot></slot>
                        </main>
                        <footer>
                            <slot name='footer'></slot>
                        </footer>
                    </div>
                `
            });
            var vm = new Vue({
                el: "#app",
                data: { 
                }
            });
        </script>
    </body>
    
    </html>
    
    1. 作用域插槽
    • 作用域插槽的定义
    <div>
        <li :key='item.id' v-for='item in list'>
            <slot :info='item'>
                {{item.name}}
            </slot>  
        </li>
    </div>
    
    • 作用域插槽的内容
    <container :list="list">
        <template slot-scope="slotProps">
            <strong v-if='slotProps.info.id==2' class="current">
                {{slotProps.info.name}}
            </strong>
            <span v-else>{{slotProps.info.name}}</span>
        </template>
    </container>
    

    实例:通过作用域插槽实现对子组件中某个数据内容进行样式的加工处理。

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Document</title>
    </head>
    <style type="text/css">
      .current {
        color: orange;
      }
    </style>
    <body>
      <div id="app">
        <container :list="list">
          <template slot-scope="slotProps">
            <strong v-if='slotProps.info.id==2' class="current">
              {{slotProps.info.name}}
            </strong>
            <span v-else>{{slotProps.info.name}}</span>
          </template>
        </container>
      </div>
      <script type="text/javascript" src="js/vue.js"></script>
      <script type="text/javascript">
        /*
          作用域插槽
        */
        Vue.component("container",{
          props:["list"],
          template: `
            <div>
              <li :key='item.id' v-for='item in list'>
                <slot :info='item'>
                  {{item.name}}
                </slot>  
              </li>
            </div>
          `
        });
        var vm = new Vue({
          el: '#app',
          data: {
            list: [{
              id: 1,
              name: 'apple'
            },{
              id: 2,
              name: 'orange'
            },{
              id: 3,
              name: 'banana'
            }]
          }
        });
      </script>
    </body>
    </html>
    
    

    案例

    购物车组件化开发案例

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Document</title>
      <style type="text/css">
        .container {
        }
        .container .cart {
          width: 300px;
          /*background-color: lightgreen;*/
          margin: auto;
        }
        .container .title {
          background-color: lightblue;
          height: 40px;
          line-height: 40px;
          text-align: center;
          /*color: #fff;*/  
        }
        .container .total {
          background-color: #FFCE46;
          height: 50px;
          line-height: 50px;
          text-align: right;
        }
        .container .total button {
          margin: 0 10px;
          background-color: #DC4C40;
          height: 35px;
          width: 80px;
          border: 0;
        }
        .container .total span {
          color: red;
          font-weight: bold;
        }
        .container .item {
          height: 55px;
          line-height: 55px;
          position: relative;
          border-top: 1px solid #ADD8E6;
        }
        .container .item img {
          width: 45px;
          height: 45px;
          margin: 5px;
        }
        .container .item .name {
          position: absolute;
          width: 90px;
          top: 0;left: 55px;
          font-size: 16px;
        }
    
        .container .item .change {
          width: 100px;
          position: absolute;
          top: 0;
          right: 50px;
        }
        .container .item .change a {
          font-size: 20px;
          width: 30px;
          text-decoration:none;
          background-color: lightgray;
          vertical-align: middle;
        }
        .container .item .change .num {
          width: 40px;
          height: 25px;
        }
        .container .item .del {
          position: absolute;
          top: 0;
          right: 0px;
          width: 40px;
          text-align: center;
          font-size: 40px;
          cursor: pointer;
          color: red;
        }
        .container .item .del:hover {
          background-color: orange;
        }
      </style>
    </head>
    <body>
      <div id="app">
        <div class="container">
          <cart></cart>
        </div>
      </div>
      <script type="text/javascript" src="js/vue.js"></script>
      <script type="text/javascript">
        
        var cartHeader = {
          props:["uname"],
          template: `
            <div>
              <div class="title">{{uname}}的商品</div>
            </div>
          `
        };
        var cartList = {
          props:["list"],
          template: `
            <div>
              <div class="item" :key="item.id" v-for="item in list">
                <img :src="item.img"/>
                <div class="name">{{item.name}}</div>
                <div class="change">
                  <a href="" @click.prevent="subNum(item.id)">-</a>
                  <input type="text" class="num" :value="item.num" @blur="changeNum(item.id,$event)" />
                  <a href="" @click.prevent="addNum(item.id)">+</a>
                </div>
                <div class="del" @click="del(item.id)">×</div>
              </div>
            </div>
          `,
          methods: {
            changeNum: function(id,event){
              this.$emit("change-num",{
                id:id,
                type: "change",
                num:event.target.value
              });
            },
            subNum: function(id){
              this.$emit("change-num",{
                id:id,
                type: "sub"
              });
            },
            addNum: function(id){
              this.$emit("change-num",{
                id:id,
                type: "add"
              });
            },
            del: function(id){
              this.$emit("del",id);
            }
          }
        };
        var cartFooter = {
          props: ["list"],
          template: `
            <div class="total">
              <span>总价:{{total}}</span>
              <button>结算</button>
            </div>
          `,
          computed: {
            total: function(){
              var sum = 0;
              this.list.forEach(item=>{
                sum += item.price * item.num;
              });
              return sum;
            }
          }
        };
        Vue.component("cart",{
          data: function(){
            return {
              uname: "yorick",
              list: [{
                id: 1,
                name: 'TCL彩电',
                price: 1000,
                num: 1,
                img: 'img/a.jpg'
              },{
                id: 2,
                name: '机顶盒',
                price: 1000,
                num: 1,
                img: 'img/b.jpg'
              },{
                id: 3,
                name: '海尔冰箱',
                price: 1000,
                num: 1,
                img: 'img/c.jpg'
              },{
                id: 4,
                name: '小米手机',
                price: 1000,
                num: 1,
                img: 'img/d.jpg'
              },{
                id: 5,
                name: 'PPTV电视',
                price: 1000,
                num: 2,
                img: 'img/e.jpg'
              }]
            }
          },
          template: `
            <div class="cart">
              <cart-header :uname='uname'></cart-header>
              <cart-list :list='list' @del="del($event)" @change-num="changeNum($event)"></cart-list>
              <cart-footer :list='list'></cart-footer>
            </div>
          `,
          components: {
            "cart-header": cartHeader,
            "cart-list": cartList,
            "cart-footer": cartFooter
          },
          methods: {
            del: function(id){
              this.list = this.list.filter(item=>{
                return item.id != id;
              });
            },
            changeNum: function(val){
              if(val.type == "change"){
                this.list.some(item=>{
                  if(item.id == val.id){
                    item.num = val.num;
                    return true;
                  }
                });
              }else if(val.type == "sub"){
                this.list.some(item=>{
                  if(item.id == val.id){
                    item.num--;
                    return true;
                  }
                });
              }else if(val.type == "add"){
                this.list.some(item=>{
                  if(item.id == val.id){
                    item.num++;
                    return true;
                  }
                });
              }
            }
          }
        });
        var vm = new Vue({
          el: '#app',
          data: {
            
          }
        });
    
      </script>
    </body>
    </html>
    

    相关文章

      网友评论

          本文标题:03Vue的组件化开发

          本文链接:https://www.haomeiwen.com/subject/llxdtltx.html