• Vue组件


    一、组件概述

    1.什么是组件?

    组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。

    所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。

      

    二、组件定义

    1、组件定义步骤

    组件定义2步骤:
    a、创建组件构造器
    Vue.extend()定义要渲染的HTML

    b、注册组件
    Vue.component()定义组件名和构造器

     

    调用Vue.extend()创建的是一个组件构造器,构造器有一个选项对象,选项对象的template属性用于定义组件要渲染的HTML;

    调用Vue.component()注册组件时,需要提供2个参数:组件的标签名 和 组件构造器;注册的组件要挂载到某个Vue实例下,否则它不会生效;

    Vue.extend() 和 Vue.component():由于 Vue 本身是一个构造函数,Vue.extend() 是一个类继承方法,它用来创建一个 Vue 的子类并返回其构造函数; 而Vue.component() 的作用在于:建立指定的构造函数与 ID 字符串间的关系,从而让 Vue.js 能在模板中使用它;直接向 Vue.component() 传递 options 时,它会在内部调用 Vue.extend()。

      

     

    2、全局组件

    全局组件定义2个步骤:
    // 1.创建组件构造器
      let Profile = Vue.extend({
    // 1.1 模板选项
      template: `
        <div>
        <input type="date">
        <p>今天已经是冬天了!</p>
        </div>
        `
     });

    // 2. 注册一个全局的组件,任何实例都可以使用它
      Vue.component('my-date', Profile);
    全局组件可以直接在每一个vue实例中使用。

     

    3、局部组件

    // 1.创建组件构造器
      let Profile = Vue.extend({
    // 1.1 模板选项
      template: `
        <div>
        <input type="date">
        <p>今天已经是冬天了!</p>
        </div>
        `
      });
    //2、注册到一个指定的Vue实例中,这样就只能在某一个实例中使用这个组件了
      new Vue({
        el: '#app',
    //通过注册到特定的实例,来达到局部应用,本例中只能应用在id=app的实例中
        components: {
          'my-date': Profile,
        }
        });

     注意:

    • 全局组件需要单独注册;

    • 局部组件必须注册到特定的实例中,以components属性的方式;

     

    三、父子组件

    // 1. 子组件构造器
            let Child1 = Vue.extend({
                template: `<img src="img/img_01.jpg" width="200">`
            });
    let Child2 = Vue.extend({
                template: `<p>我认为自己很美!</p>`
            });
    //如果想单独使用子组件,必须单独注册。这样就可以作为一个单独的全局组件使用。
    Vue.component('child', Child1);
    
    // 2. 父组件构造器
            Vue.component('parent', {
                components: {
                    'my-child1': Child1,
                    'my-child2': Child2
                },
                template:
                    `
                      <div>
                          <my-child1></my-child1>
                          <my-child2></my-child2>
                      </div>
                    `
            });
    //3、使用
    <div id="app">
            <parent></parent>
        //child为全局组件,可以单独使用!
            <child></child>
        </div>
    

      

    四、组件简化

    1、template方式简化

    <body>
        <div id="app">
            <my-div></my-div>
        </div>
    
        <template id="my_div">
        //template里面只能有一个根,也就是所有内容都包含在一个div里面! <div> <div>我是MT!</div> <input type="date"> <img src="img/img_02.jpg" alt="" width="400"> </div> </template> <script src="js/vue.js"></script> <script> // 1. 实例化组件 Vue.component('my-div', { template: '#my_div' }); new Vue({ el: '#app', }); </script> </body>

      

    2、script方式简化

    <body>
        <div id="app">
            <my-div></my-div>
        </div>
    
        <script type="text/x-template" id="my_div">
            <div>
                <img src="img/img_02.jpg" alt="" width="200">
                <p>我是花姑娘!</p>
            </div>
        </script>
    
        <script src="js/vue.js"></script>
        <script>
    
            // 1. 实例化组件
            Vue.component('my-div', {
                template: '#my_div'
            });
    
            new Vue({
               el: '#app',
            });
        </script>
    </body>

    注意事项:

    1. 组件挂载数据必须是函数,返回一个对象
    
    
     Vue.component('my-btn', {
                template: '#my_btn',
                data(){
                    return {
                        counter: 0
                    }
                }
            });
    
    
    

      

    
    

     组件挂载数据,必须是一个函数,且返回一个对象,这样子组件被任何对象引用时,data就互不干涉,都是一个独立的对象,可以随时修改,数据都互不影响。 

     2、组件的template中,必须包含在一个根下面

    <template id="my_div">
        //template里面只能有一个根,也就是所有内容都包含在一个div里面!
            <div>
                <div>我是MT!</div>
                <input type="date">
                <img src="img/img_02.jpg" alt="" width="400">
            </div>
        </template>
    
    
    
    <template id="my_div">    
           //这是错误的,不能有多个根元素!
                <div>我是MT!</div>
                <input type="date">
                <img src="img/img_02.jpg" alt="" width="400">
        </template>
    

      

    五、组件间通讯

    1、单层通讯

    a、父子通信
    <body>
        <div id="app">
         //第三步、父组件给子组件传值,单层传值无需动态绑定。父子传值,让子组件更活起来!
            <my-div message="今天要下雨" imgsrc="img/img_02.jpg"></my-div>
            <my-div message="明天要下冰雹" imgsrc="img/img_01.jpg"></my-div>
        </div>
    
        <template id="my_div">
      //第一步:子组件定义接受的值
            <div>
                <h1>{{message}}</h1>
                <img :src="imgsrc" width="200" alt="">
            </div>
        </template>
    
        <script src="js/vue.js"></script>
        <script>
            // 1. 创建组件,
      //第二步、通过props定义从父组件能够接受的值
            Vue.component('my-div', {
                template: '#my_div',
                props: ['message', 'imgsrc']//注意,要支持驼峰式imgSrc的写法,需要在template和父组件里面修改成img-src或img-Src。
            });
    
            new Vue({
               el: '#app',
                data: {
                   msg: '今天的天气很好!'
                }
            });
        </script>
    </body>
    

      

    2、多层通讯

    <body>
    //4、祖父组件使用和传值
    <div id="app">
        <my-parent :imgtitle="title" :imgsrc="img"></my-parent>
    </div>
    
    <template id="my_img">
        <img :src="imgsrc" width="200">
    </template>
    
    <template id="my_title">
        <h2>{{title}}</h2>
    </template>
    
    //3、父组件定义和传值
    <template id="my_parent">
        <div>
            <child1 :childimg="imgsrc"></child1>
            <child2 :childtitle="imgtitle"></child2>
        </div>
    </template>
    
    <script src="js/vue.js"></script>
    <script>
    
        // 1. 子组件的实例
        let Child1 = Vue.extend({
            template: '#my_img',
            props: ['childmyimg']  #父组件传递属性
        });
    
        let Child2 = Vue.extend({
            template: '#my_title',
            props: ['childtitle']
        });
    
        // 2. 子组件注册到父组件
        Vue.component('my-parent', {
            props: ['imgtitle', 'imgsrc'],  #曾祖父传递的属性
            components: {
                'child1': Child1,
                'child2': Child2
            },
            template: '#my_parent'
        });
    
        new Vue({
            el: '#app',
            data: {
                title: '我是不是很漂亮',
                img: 'img/img_01.jpg'
            }
        });
    </script>
    </body>

    祖父组件=>父组件=>子组件,注意事项:
    1、多层传值必须是动态绑定的方式 :
    2、相对子组件必须通过props属性接受父组件传过来的数据

     

    六、自定义事件

    子组件通知给外界,他执行了什么操作,此时,外界会做出相应的操作。
    <body>
        <div id="app">
           <!--父组件通过on监听total事件,一旦执行total事件,则会修改Vue实例中的数据,这样就达到了在父组件中多次调用子组件,数据都是独立的-->
            <my-btn @total="allcounter()"></my-btn>
            <my-btn @total="allcounter()"></my-btn>
            <my-btn @total="allcounter()"></my-btn>
            <my-btn @total="allcounter()"></my-btn>
            <my-btn @total="allcounter()"></my-btn>
            <my-btn @total="allcounter()"></my-btn>
            <my-btn @total="allcounter()"></my-btn>
    
            <p>所有按钮一共点击了{{totalCounter}}次</p>
        </div>
    
        <template id="my_btn">
             <button @click="total()">点击了{{counter}}次</button>
        </template>
    
        <script src="js/vue.js"></script>
        <script>
    //定义子组件
            Vue.component('my-btn', {
                template: '#my_btn',
                data(){
                    return { 
                        counter: 0  //注意:在子组件中挂载数据,必须返回对象,这样在任何地方调用子组件都是独立的,数据互不干涉。
                    }
                },
                methods: {
                    total(){
                        this.counter += 1;
    
                        // 通知外界,我触发了total方法,然后他就会调用后面的allcounter(),这样就把所有的点击次数加起来了
                        this.$emit('total');
                    }
                }
            });
    
            new Vue({
               el: '#app',
                data: {
                    totalCounter: 0
                },
                methods: {
                   allcounter(){
                       this.totalCounter += 1;
                   }
                }
            });
        </script>
    </body>
    

      

    使用 $on(eventName) 监听事件

    使用 $emit(eventName) 触发事件

    七、插槽slot

    slot的意思是插槽,其目的在于让组件的可扩展性更强。打个比方说:假如父组件需要在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、如何显示,就是slot分发负责的。

    注意:如果使用子组件时,在里面添加DOM,默认这些Dom是不会显示的,因为子组件根本不认识。例如:

    <child><div>我想在子组件中显示,但是显示不出来</div></child>

    插槽就像电路板,是用来方便插入内容的。slot分为匿名插槽和具名插槽。
    匿名插槽就像公交车,谁都可以上,任何人都可以使用这个插槽,很类似娱乐圈中的女明星啦;
    具名插槽则是私家车,一个人对应一辆车,一个内容对应一个插槽,这个就是生活中的良家妇女啦。

    1、匿名插槽

    <body>
        <div id="app">
            <my-slot>
                <!--date会覆盖默认的插槽-->
                <input type="date">
            </my-slot>
        </div>
    
        <template id="my_slot">
            <div id="panel">
                <h2 class="panel-header">插槽的头部</h2>
                <!--预留一个插槽-->
                <slot>可以替换任何标签,默认显示提示的内容</slot>
                <footer>插槽的尾部</footer>
            </div>
        </template>
    
        <script src="js/vue.js"></script>
        <script>
            Vue.component('my-slot', {
                template: '#my_slot'
            });
    
            new Vue({
               el: '#app',
                data: {
                   msg: '今天的天气很好!'
                }
            });
        </script>
    </body>
    

      

    2、具名插槽

    <body>
        <div id="app">
            <my-computer>
     		<!--只能根据slot的name插入对应的数据-->
                <div slot="cpu">Inter Core i8</div>
                <img slot="gpu" src="img/img_02.jpg" width="100" alt="">
                <div slot="memory">128G</div>
                <input type="color" slot="hard-drive">
            </my-computer>
        </div>
    
        <template id="my_computer">
            <div id="main">
                <slot name="cpu">这里是插cpu的</slot>
                <slot name="gpu">这里是插gpu的</slot>
                <slot name="memory">这里是插内存条的</slot>
                <slot name="hard-drive">这里是插硬盘的</slot>
            </div>
        </template>
    
        <script src="js/vue.js"></script>
        <script>
            Vue.component('my-computer', {
                template: '#my_computer'
            });
    
            new Vue({
               el: '#app',
                data: {
                   msg: '今天的天气很好!'
                }
            });
        </script>
    </body>
    

      

    八、综合案例

    组件以及父子组件传值

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    <div id="app">
        <div>
            <input type="text" v-model="inputValue">
            <button @click="handleClickBtn">添加</button>
        </div>
        <ul>
            <todo-item
                    v-for="(item,index) in list"
                    :content="item"
                    :key="item"
                    :index="index"
                    :msg="msg"
                    @delete="handleParentDelete"
            >
                {{item}}
    
            </todo-item>
        </ul>
    </div>
    
    <script src="js/vue.js"></script>
    <script>
        //定义全局组件todoItem
        var todoItem = {
            //子组件接受来自父组件的传值,可以直接使用
            props: ['content', 'index', 'msg'],
            template: "<li @click='handleItemDelete'>{{content}}-{{msg}}</li>",
            methods: {
                handleItemDelete: function () {
                    //当点击子组件时,会向外触发delete事件,同时带上对应的参数,父组件监听到delete被触发后,则会执行对应的方法
                    this.$emit('delete', this.index)
                }
            },
        }
        // 1. 创建Vue的实例
        let vm = new Vue({
            el: '#app',
            data: {
                list:[],
                inputValue:'',
                msg:'我是来自父组件的msg'
            },
            components:{
                //全局组件注册到vm实例中
                todoItem
            },
            methods:{
                handleClickBtn:function(){
                    if(this.inputValue){
                        this.list.push(this.inputValue);
                        this.inputValue = '';
                    }else{
                        alert('输入内容不能为空!');
                    }
                },
                handleParentDelete:function(id){
                    //$emit传给父组件的参数,在这里接受。
                    alert(id);
                    this.list.splice(id,1);
                },
    
            }
        });
    </script>
    </body>
    </html>
    

     VUE的组件其实也是一个Vue实例,她具备vue的所有属性和方法。

      

    九、组件的注意事项

    1、is属性

    解决h5的一些小bug

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    <div id="app">
        <table>
            <tbody>
                //tbody标签直接使用row会在h5中有问题,解析后会跳到table外面去
                <row></row>
                <row></row>
                <row></row>
                //修改为:
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
                //ul标签里面需要使用li,select标签中需要使用option
            </tbody>
        </table>
    </div>
    
        <script src="js/vue.js"></script>
        <script>
    
            Vue.component('row',{
                template:'<tr><td>this is a row</td></tr>'
            })
    
            // 1. 创建Vue的实例
            let vm = new Vue({
    
    
            });
        </script>
        </body>
        </html>
    

      

    2、子组件的data

     组件中的data必须是一个函数,返回一个对象

            data:function(){
                    return {
                        number:0
                    }
                },        
    

    3、ref使用

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <div id="app">
            <counter ref="one" @change="handleChange"></counter>
            <counter ref="two" @change="handleChange"></counter>
            <div>{{total}}</div>
        </div>
    
        <script src="js/vue.js"></script>
        <script>
    
            Vue.component('counter',{
                template:'<div @click="handleClick">{{number}}</div>',
                data:function(){
                    return {
                        number:0
                    }
                },
                methods:{
                    handleClick:function(){
                        this.number++
                        this.$emit('change')
                    }
                }
    
            })
    
            // 1. 创建Vue的实例
            let vm = new Vue({
                el:'#app',
                data:{
                    total:0
                },
                methods:{
                    handleChange:function () {
                        this.total = this.$refs.one.number + this.$refs.two.number
                    }
                }
    
            });
        </script>
        </body>
        </html>
    

      

    4、父子传值

    父组件传值给子组件,子组件最好不要修改父组件的数据,因为加入父组件传递的是一个对象,可能被多个子组件反复的修改,导致数据混乱。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
       <div id="app">
           <counter :count='1' @change='handleChange'></counter>
           <counter :count='2' @change='handleChange'></counter>
           <span>{{total}}</span>
       </div>
    
    <script src="js/vue.js"></script>
    <script>
        var counter={
          props:['count'],
          template:"<div @click='handleClick'>{{number}}</div>",
          data:function(){
            return {
              //拷贝一个父本出来
              number:this.count
            }
          },
          methods:{
            handleClick:function(){
              //注意:避免直接修改父组件传过来的数据
              //this.count++
    
              //每次步长为1
              this.number += 1
              //每次子组件被点击后都告知父组件触发对应的事件
              this.$emit('change',1)
            }
          }
        }
        // 1. 创建Vue的实例
        let vm = new Vue({
            el: '#app',
            components:{
              counter
            },
            data:{
              total:3
            },
            methods:{
              handleChange:function(num){
    
                this.total+=num
              }
            }
    
        });
    </script>
    </body>
    </html>
    

    5、props和非props特性

     子组件对父组件传过来的数据进行约束检测,则为props特性

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <div id="app">
            <child content="hello world"></child>
        </div>
    
        <script src="js/vue.js"></script>
        <script>
    
            Vue.component('child',{
                props:{
                    content:{
                        type:String,
                        required:false,
                        default:'default value',
                        validator:function (value) {
                            return (value.length>5)
                        }
                    }
                },
                template:'<div>{{content}}</div>',
    
            })
    
            // 1. 创建Vue的实例
            let vm = new Vue({
                el:'#app',
    
            });
        </script>
        </body>
        </html>
    

      

    props特点:

    • 1、父组件传递过来
    • 2、子组件必须验证
    • 3、子组件可以使用
    • 4、不会渲染在子组件中

    父组件向子组件传递数据,但是在子组件没有明确接受,则不能通过{{}}使用,也称之为非props

    非props特点:

    • 1、父组件传递过来
    • 2、子组件没明确写在props里面接受
    • 3、子组件不能通过{{}}使用
    • 4、会渲染在子组件当中,比如上面的content='hello wold‘会在子组件元素的html属性中渲染’。

    6、给组件绑定原生事件

    正常情况下,在父组件中绑定事件,我们只能在子组件通过this.$emit('events')进行触发后执行:

    <child @click='handleClick'></child>
    如果需要在父组件中也生效,需要通过给父组件绑定原生事件:
    <child @click.native='handleClick'></child>
    完整示例如下:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
       <div id="app">
          <child @click.native='handleClick'></child>
       </div>
    
    <script src="js/vue.js"></script>
    <script>
        Vue.component('child',{
          template:'<div>Child</div>',
    
        })
        // 1. 创建Vue的实例
        let vm = new Vue({
            el: '#app',
            methods:{
              handleClick:function(){
                alert('aaa')
              }
            }
    
        });
    </script>
    </body>
    </html>
    

      

  • 相关阅读:
    NOIP 2017逛公园(记忆化搜索)
    NOIP 2012疫情控制 (二分+倍增+贪心)
    NOIP 2005过河(DP+路径压缩)
    P1198 [JSOI2008]最大数
    [Noip2016]蚯蚓
    [六省联考2017]期末考试
    六省联考:组合数问题
    蒜头君的兔子
    bzoj1015 [JSOI2008]星球大战starwar
    luogu P3370 【模板】字符串哈希
  • 原文地址:https://www.cnblogs.com/skyflask/p/10907415.html
Copyright © 2020-2023  润新知