03Vue的组件化开发

2021-02-01  本文已影响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. 父组件向子组件传值
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>
字符串 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>
上一篇下一篇

猜你喜欢

热点阅读