vue-组件机制

本文详细介绍了Vue.js的组件机制,包括全局和局部注册组件、props验证、以及插槽的使用。组件是Vue的核心功能,用于封装可复用的代码。在注册组件时,全局注册可在任何新创建的Vue实例中使用,而局部注册局限于vue实例内部。组件间的通信通过props从父到子传递,子组件通过$emit向父组件发送事件。此外,还讨论了props的验证规则以确保数据类型正确。最后,介绍了不同类型的插槽,包括普通插槽、具名插槽和作用域插槽,用于自定义组件内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用is特性进行了扩展的原生 HTML 元素。组件注册的时候需要为该组件指定各种参数。

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

<body>
   //模板
   <div id="app"></div>
   <script>
       new Vue({
           el:'#app',
           data:{},
           methods:{}
       })
   </script>    
</body>
<body>
   //挂载好的模板
   <div id="app"></div>
   <script>
      //全局声明
      Vue.component("my-header",{
        data(){
           return{
               num:10
           }
        },
        methods:{}
      });
      //实例对象 根组件
       new Vue({
           el:'#app',
           data:{},
           methods:{}
       })
   </script>    
</body>

以上两段代码不同之处就很明显了,第一段代码在根组件中,所以存在el,在组件中el只有一个,另一个不同在于在根组件中的data是对象,在我们声明的组件中,data必须为一个函数,可以使用function或者()调用,并且设置返回值。

全局注册

通过template来实现组件的复用,template代表该组建的模板,只能有一个直接孩子节点。

要想进行组件使用得先进行组件注册,全局注册可以使用Vue.component(tagName, options) 注册一个全局组件, 注册之后可以用在任何新创建的 Vue 根实例的模板中。

<body>
   //利用组件可以在模板中无限复用
   <div id="app">
       <my-header></my-header>
       <my-header></my-header>
       <my-header></my-header>
   </div>
   <script>
      //全局声明
      Vue.component("my-header",{
        data(){//函数
           return{//函数中必须有return
               num:10
           }
        },
        methods:{
            handler(){
                this.num++;//返回值num++
            }
        },
        //template代表该组建的模板 只能有一个直接孩子节点
        //将想要复用的元素封装起来
        template:`
          <div>
           <p>11111-{{num}}</p>
           <p>22222</p>
           <button @click='handler'>点击</button>
           //定义了一个点击事件的方法handler,每个点击事件是独立的
          </div>
        `
      });
      //实例对象
       new Vue({
           el:'#app',
           data:{//对象
                   
               },
           methods:{}
       })
   </script>   

局部注册

注册在vue实例声明中,使用components:{},和data,methods,computed(计算属性)同级

<body>
   <div id="app">
        <my-header></my-header>
        <my-footer></my-footer>
   </div>
   <script>
       new Vue({
           el:'#app',
           data:{},
           methods:{},
           components:{
               //多个组件声明
               //属性名即为组件名称,属性值为参数
               'my-footer':{
                   data(){
                       return{}
                   },
                   template:`
                    <div>hello-footer</div>
                   `
               },
               'my-header':{
                   data(){
                       return{
                           num:0
                       }
                   },
                   methods:{
                       handler(){
                           this.num++;
                       }
                   },
                   template:`
                   <div>
                        <p>11111-{{num}}</p>
                        <p>22222</p>
                        <button @click='handler'>点击</button>
                   </div>
                   `
               }
           }
       })
   </script>    
</body>

但是兄弟级组件之间不能嵌套使用,可以使用全局嵌套。

<body>
   <div id="app">
        <my-header></my-header>
        <!-- <my-footer></my-footer> -->
   </div>
   <script>
       Vue.component('my-footer',{
                   data(){
                       return{}
                   },
                   template:`
                    <div>hello-footer</div>
                   `
               });
       new Vue({
           el:'#app',
           data:{},
           methods:{},
           components:{
               //多个组件声明
               //属性名即为组件名称,属性值为参数
               
               'my-header':{
                   data(){
                       return{
                           num:0
                       }
                   },
                   methods:{
                       handler(){
                           this.num++;
                       }
                   },
                   template:`
                   <div>
                        <p>11111-{{num}}</p>
                        <p>22222</p>
                        <my-footer></my-footer>
                        <button @click='handler'>点击</button>
                   </div>
                   `
               }
           }
       })
   </script>    
</body>

那么组件之间如何传值呢?

父-->子:props[]

在父组件的template中定义所需要的自定义属性,属性值为传输的内容

<my-footer  msg='hello-父组件给子组件的内容'></my-footer>

然后在子组件中添加一个与data,methods同级的属性props[],props[]代表的是子组件希望从父组件中接收的属性,多个属性用逗号隔开

props:["msg"]

子--->父:$emit();

在子组件中使用事件绑定触发父级的自定义事件,$emit("name",data,data1);然后在父组件中绑定自定义事件,在自定义事件的处理程序中,参数为传递的变量

<body>
   <div id="app">
        <my-header></my-header>
        <!-- <my-footer></my-footer> -->
   </div>
   <script>
       Vue.component("my-footer",{
        //子组件
        data(){
            return {
                substr:"my-footer"
            }
        },
        methods:{
        send(){
                this.$emit("accept",this.substr,"send1");//第一个参数为触发父亲哪一个事件  触发方法时要不要携带参数            
                }
            },
        props:["msg","str",'num'],//子组件希望从父组件中接受的属性
        template:`
            <div>
                <p @click='send'>click-send</p>
                hello-footer-{{msg}}
                <p>{{str}}</p>
                <p>{{num}}</p>
            </div>
        `
    
    });
    var header={//父组件
        data(){
            return {
                num:0,
                substr:''
            }
        },
        methods:{
            handler(){
                this.num++;
            },
            accept(data,data1){
                this.substr=data;
                console.log(data,data1);
            }
        },
        template:`
        <div>
            <p>{{substr}}</p>
            <p>one row--1111-{{num}}</p>
            <p>two row--2222</p>
            <my-footer v-on:accept='accept' msg='hello-父组件给子组件的内容' str='my-header' :num='num'></my-footer>
            <button @click='handler'>click</button>
        </div>`
    }
       new Vue({
           el:'#app',
           data:{},
           methods:{},
           components:{
               //多个组件声明
               //属性名即为组件名称,属性值为参数
               
               'my-header':header
               }
           })
   </script>    
</body>

下面是利用父子组件制作的一个小案例:有四个独立的计数器,可以进行加减操作,设置一个总计的计数器,计算四个计数器的总值。

代码效果如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script>
</head>
<body>
    <div id="app">
        <div>
           总计: {{total}}
        </div>
        <test @rr='rr' @aa='aa'></test>
        <test @rr='rr' @aa='aa'></test>
        <test @rr='rr' @aa='aa'></test>
        <test @rr='rr' @aa='aa'></test>
    </div>
    <script>
        let test={  //声明一个子组件
            template:`
                <div>
                    <div>数量:{{count}}</div>
                    <div>
                        <button @click='r'>减少</button>
                        <button @click='a'>增加</button>
                    </div>
                </div>
            `,
            data(){
                return {
                    count:0
                }
            },
            methods:{
                r(){
                    this.count--
                    this.$emit('rr')
                },
                a(){
                    this.count++
                    this.$emit('aa')

                }
            }

        }
        new Vue({//父组件
            el:'#app',
            components:{
                test
            },
            data:{
                total:0
            },
            methods:{
                rr(){
                    this.total--
                },
                aa(){
                    this.total++
                }
            }
        })
    </script>
</body>
</html>

props验证

如果传递的参数不符合定义的类型,vue会发出警告

<body>
   <div id="app">
       <parent></parent>
   </div> 
   <script>
        Vue.component("child",{
            data(){
                return {}
            },
            props:{
                str:String,
                str1:[String,Number],
                str2:{
                    type:String,
                    required:true
                },
                str3:{
                    type:Number,
                    default:99
                },
                str4:{
                    type:Object,
                    default:function(){
                        return {name:"tom"}
                    }
                },
                str5:{
                    validator(value){
                        return value.indexOf("o") != -1
                    }
                }
            },
            template:`
                <h3>子组件:{{str5}}</h3>
    
            `
        });
        new Vue({
            el:'#app',
            data:{},
            methods:{},
            components:{
                "parent":{
                    data(){
                        return {
                            str:"10",
                            str1:10,
                            str4:{
                                name:"lisi"
                            },
                            str5:'hell'
                        }
                    },
                    template:`
                        <div>
                            <h1>父组件:</h1>
                            <child :str='str' :str1='str1' :str2='str' :str3='str1' :str5='str5'></child>
                        </div>
                    `
                }
            }
        });
</script>
</body>

插槽

插槽允许我们在调用子组件的时候为子组件传递模板。

1)普通插槽:通过<slot></slot>标签声明一个区域,用来存放形参

2)具名插槽:在给slot起名字时通过name属性,在使用时,通过<template v-slot:name></template>指定绑定的插槽

3)作用域插槽:在slot标签中通过v-bind绑定插槽可以使用的属性,然后在template标签中使用v-slot:default=name,default是默认属性名,如果设置了自定义属性名需要更改。name:template标签中就是当前可以使用的作用域。

<body>
   <div id="app">
       <my-div>
           <!-- 利用template标签 不会增加盒子 但是可以操作元素 -->
           <template v-slot:default='scope'>
               <!-- v-slot:default 是默认属性名 -->
                {{scope.user.name}}
                {{scope.list}}
           </template>
            <template v-slot:header>
                你好
            </template> 
                slit2-----
            </template>   
       </my-div>
   </div>
   <script>
        new Vue({
            el:'#app',
            data:{},
            methods:{},
            components:{
                'my-div':{
                    data(){
                        return {
                            user:{
                                name:'lisi',
                                age:20
                            },
                            list:[1,2,3,4,5]
                        }
                    },
                    template:`
                        <div>
                            <p>hello</p>
                            <p>world</p>
                            /*通过插槽 设置形参*/
                            <p><slot :user='user' :list='list'>slot</slot></p>
                            <p><slot name='header'>slot</slot></p>
                            <p><slot name='footer'>slot</slot></p>
                            
                        </div>
                    `
                }
            }
        });
    </script>
</body>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值