• VUE基础知识五:组件的分类、组件间传值、事件调用等


    组件可以分为局部组件和全局组件,定义方式如下:

    // 局部组件定义方式
    const Conter = {
    	
    }
    const app = Vue.createApp({
    	components:{conter:Conter} //通过声明components来引用conter局部组件
    });
    // 全局组件定义方式
    app.component('hello-world',{});
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    全局组件在定义组件名称的时候,采用小写字母,如果涉及多个单词,使用-来连接;

    局部组件在定义名称的时候,采用首字母大写的方式,如果涉及多个单次,采用驼峰式命名。

    在引用局部组件的时候,可以直接使用原名称,也可直接指定名称,如下:

    components:{Counter,HelloWorld}
    // 在使用的时候Vue会自动检测名称,并将Counter首字母改为小写,最终为counter
    // 当时驼峰命名的时候,Vue也会自动将字母转为小写,多字母之间用中划线连接
    template:`<div><conter/><hello-world/></div>`
    
    • 1
    • 2
    • 3
    • 4

    父组件传值到子组件

    • 静态传参:通过非动态绑定的属性进行传值,传入的值提前写入,传到子组件统一转为String类型
    • 动态传参:通过v-bind来动态绑定属性传值,传入的值可动态改变,子组件接收的数据类型与传入的数据类型相同

    子组件在接收值的时候可以做数据类型判断、限定等操作。如下:

    // 直接接收数据
    app.component('counter',{
    	props:['content']
    })
    // 加入类型判断
    app.component('counter',{
    	props:{
    		counter:{
    			type:Number, // 指定类型是数值类型
    			required:true, // 是否必须传入
    			default:123, // 当不满足需求的时候,采用此默认值
    			validator:function(value){ // value表示传入的数据
    				// 参数校验逻辑,最终返回一个boolean类型的结果
    			}
    		}
    	}
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    type可取的类型有:StringBooleanArrayObjectFunctionSymbol

    在传多个数据的时候,可以使用以下两种方式:

    • 方式一:
    data(){
    	return {
    		params:{
    			a:"1",
                 b:"2",
                 c:"3",
    		}
    	}
    },
    template:`<div><counter v-bind="params"/></div>`
    // 此时,Vue会自动将params内a、b、c赋值到props中对应的属性
    app.component('counter',{
    	props:['a','b','c'],
    	template:`<div>{{a}}-{{b}}-{{c}}</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 方式二:
    data(){
    	return {
    		params:{
    			a:"1",
                 b:"2",
                 c:"3",
    		}
    	}
    },
    template:`<div ><counter :params="params"/></div>`
    app.component('counter',{
    	props:['params'],
    	template:`<div>{{params.a}}-{{params.b}}-{{params.c}}</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    由于html不支持驼峰规则,所有在定义标签属性的时候,涉及多个单词,需要用-连接,此时传值就存在一个问题,需要规避,如下:

    data(){
    	return {
    		params:1234
    	}
    },
    template:`<div ><counter :counter-params="params"/></div>` //定义采用短横线连接的方式,不支持驼峰规则
    app.component('counter',{
    	props:['counter-params'],
    	template:`<div>{{counter-params}}</div>`
    })
    // 如果采用上面的方式来接收传入的值就会出现counter-params是NaN
    // 规避方法:将接收值改成驼峰规则。如下:
    app.component('counter',{
    	props:['counterParams'],
    	template:`<div>{{counterParams}}</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    单向数据流

    概念:子组件可以使用父组件传递的数据,但是不能修改传递过来的数据。

    如果子组件的确需要修改传过来的数据,这个时候可以将传过来的数据复制到新的变量内,通过修改新的变量来实现。如下:

    data(){
    	return {
    		param:1234
    	}
    },
    template:`<div ><counter :param="param"/></div>`
    app.component('counter',{
    	props:['param'],
    	data(){
    		return{
    			myCount:this.param // 复制数据
    		}
    	}
    	template:`<div @click="myCount += 1">{{myCount}}</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Non-props特性

    在父组件给子组件传值的时候,如果子组件没有通过props接收时,Vue会自动将传入的属性和值添加到子组件template顶层dom对象中。如下:

    data(){
    	return {
    		param:1234
    	}
    },
    template:`<div ><counter :param="param"/></div>`
    app.component('counter',{
    	template:`<div>Counter</div>`
    })
    // 浏览器渲染后的效果如下:
    <div param="1234">Counter</div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如果此时不想Vue自动给顶层dom添加属性,可以用inheritAttrs:false来关闭此特性。如下:

    data(){
    	return {
    		param:1234
    	}
    },
    template:`<div ><counter :param="param"/></div>`
    app.component('counter',{
    	inheritAttrs:false,
    	template:`<div>Counter</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果子组件存在多个并列的顶层dom对象,Vue是无法自动实现添加属性的机制,可以手动的添加。如下:

    data(){
    	return {
    		param:1234
    	}
    },
    template:`<div ><counter :param="param" message="111"/></div>`
    app.component('counter',{
    	template:`
    		<div :param="$attrs.param">Counter1</div>  //param属性
    		<div :message="$attrs.message">Counter2</div> //message属性
    		<div v-bind="$attrs">Counter2</div>` // 所有属性
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    不仅如此,还可以通过生命周期函数来获取属性和属性值,如下:

    app.component('counter',{
    	mounted(){
    		console.log(this.$attrs.param)
    	}, 
    	template:`
    		<div :param="$attrs.param">Counter1</div>  //param属性
    		<div :message="$attrs.message">Counter2</div> //message属性
    		<div v-bind="$attrs">Counter2</div>` // 所有属性
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    父子组件通过事件传递数据

    正常的数据流是父组件将数据传给子组件,而子组件是没有修改父组件属性值的权限,只能将属性值复制到子组件内进行操作,但是可以通过事件的方式将子组件改变的数据传给父组件,由父组件进行修改操作。如下:

    const app = Vue.createApp({
    	data(){
    		return {
    			count:123
    		}
    	},
    	methods:{
    		addOneHandle(num){ // num是接收传递来的参数,可多个,与传入对应即可
    			this.count = this.count + num; 
    		}
    	}
    	template:`<counter :count="count" @add-one="addOneHandle"/>`
    });
    
    app.component("counter",{
    	props:['count'],
    	methods:{
    		addOneHandle(){
    			this.$emit('addOne',2);// 调用父节点的add-one事件,传入一个参数2
    		}
    	},
    	template:`<div @click="addOneHandle">{{count}}</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    注意点:

    • 在使用this.$emit('addOne',2)的时候,调用事件的名称采用驼峰命名规则,@add-one父标签中命名使用中划线连接,因为html不支持驼峰规则规则命名
    • 在调用父组件的事件时,传入的参数个数无限制,多个参数依次向后添加即可
    • 可以通过emits属性来统一管理记录调用父组件的事件集合,如下:
    app.component("counter",{
    	props:['count'],
    	emits:['addOne'] // 这里既可以使用数组的方式,也可使用对象的方式,对象的方式可以引入函数做逻辑判断
    	methods:{
    		addOneHandle(){
    			this.$emit('addOne',2);// 调用父节点的add-one事件,传入一个参数2
    		}
    	},
    	template:`<div @click="addOneHandle">{{count}}</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    父子组件的双向绑定

    在同一个组件里面,数据可以通过v-model进行双向绑定,在父子组件之间也能通过这种方式实现双向绑定。如下:

    const app = Vue.createApp({
    	data(){
    		return {
    			count:123,
    			other:234
    		}
    	},
    	template:`<counter v-model:count="count" v-model:other="other"/>`
    });
    
    app.component("counter",{
    	props:['count','other'],
    	methods:{
    		addOneHandle(){
    			this.$emit('update:count',this.count + 1);
    			this.$emit('update:other',this.other + 1);
    		}
    	},
    	template:`<div @click="addOneHandle">{{count}}-{{other}}</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    上面这种方式可以适用于多个参数,即多个v-model:xxx="xxx",如果是单个参数,可采用下面的方式简化:(不建议)

    const app = Vue.createApp({
    	data(){
    		return {
    			count:123
    		}
    	},
    	template:`<counter v-model="count" />`
    });
    
    app.component("counter",{
    	props:['modelValue'],
    	methods:{
    		addOneHandle(){
    			this.$emit('update:modelValue',this.modelValue + 1);
    		}
    	},
    	template:`<div @click="addOneHandle">{{modelValue}}</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    插槽分类讲解

    • 基本插槽:通过在引用子组件的时候,传递dom对象给子组件的一种方式,如下:
    const app = Vue.createApp({
        template:`<counter>
    		<div>dom</div>
        </counter>`
    });
    
    app.component("counter",{
    	template:`<div>
    		<slot></slot> // 通过slot标签,将父组件counter内的所有dom引入到此位置
    	</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 具名插槽:如果父组件传递多个dom,这些dom需要放在子组件中的不同位置,直接采用基本插槽的全部引用,就会存在问题,这里就引入了具名组件。
    const app = Vue.createApp({
        template:`<counter>
            <template v-slot:header>
    			<div>header</div>
            </template>
    	    <template v-slot:footer> // 使用template标签作为占位符,这里的v-slot:footer可简化为#footer
    			<div>footer</div>
            </template>
        </counter>`
    });
    
    app.component("counter",{
    	template:`<div>
            <slot name="header"></slot> //将header的标签放到body前
    		<div>
                body content
            </div>
            <slot name="footer"></slot> //将footer的标签放到body后
    	</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 作用域插槽:直接上代码如下:
    const app = Vue.createApp({
        template:`<counter v-slot="slotProps"> // 接收子组件绑定的所有属性数据
    		<div>{{slotProps.index}}-{{slotProps.item}}</div>// 渲染接收到的数据
        </counter>`
    });
    
    app.component("counter",{
    	data(){
    		return {
    			list:[1,2,3],
    		}
    	},
    	template:`<div>
    		<slot v-for="(item,index) in list" :item="item" :index="index"/> //将遍历出来的数据绑定到slot上
    	</div>`
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    动态组件

    component标签及其固定属性is来控制子组件的加载情况,实现动态组件的效果,代码如下:

        const app = Vue.createApp({
            data(){
                return {
                    showItem:"one-item"
                }
            },
            methods:{
                changeItem(){ // 通过点击事件,修改需要展示的组件
                    this.showItem = this.showItem === "one-item" ? "two-item": "one-item";
                }
            },
            template: `
                <component :is="showItem"/> //通过is属性,切换显示one或者two组件
                <button @click="changeItem">切换</button>
            `
        })
        app.component("one-item",{
            template:`<div>this is one-item</div>`
        })
        app.component("two-item",{
            template:`<div>this is two-item</div>`
        })
        app.mount("#root");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    异步组件

    组件异步显示,详细见示例代码:

    const app = Vue.createApp({
        template: `
            <div>this is static div</div>
            <async-item /> // 加载子组件
        `
    })
    app.component("async-item", Vue.defineAsyncComponent(() => {
        return  new Promise((resolve,reject) => {
            setTimeout(()=>{
                resolve({
                    template:`<div>this is async template</div>`
                })
            },4000); // 4秒后加载
        })
    }))
    app.mount("#root");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    华为云云耀云服务器L实例评测|华为云上安装监控服务Prometheus三件套安装
    【密码学】第三章、分组密码
    使用html画一个键盘
    JavaScript 62 JavaScript 版本 62.7 Internet Explorer - Edge
    贪吃蛇和俄罗斯方块
    在 Java 中,如何创建泛型对象与泛型数组
    nginx acess日志找不到访问记录问题
    微信小程序原理
    Kubernetes v1.21.5部署xxl-job 2.3.1
    Java_网络多线程
  • 原文地址:https://blog.csdn.net/millery22/article/details/125536951