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

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

// 局部组件定义方式
const Conter = {
	
}
const app = Vue.createApp({
	components:{conter:Conter} //通过声明components来引用conter局部组件
});
// 全局组件定义方式
app.component('hello-world',{});

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

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

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

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

父组件传值到子组件

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

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

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

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>`
})
  • 方式二:
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>`
})

由于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>`
})

单向数据流

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

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

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>`
})

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>

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

data(){
	return {
		param:1234
	}
},
template:`<div ><counter :param="param"/></div>`
app.component('counter',{
	inheritAttrs:false,
	template:`<div>Counter</div>`
})

如果子组件存在多个并列的顶层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>` // 所有属性
})

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

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>` // 所有属性
})

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

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

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>`
})

注意点:

  • 在使用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>`
})

父子组件的双向绑定

在同一个组件里面,数据可以通过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>`
})

上面这种方式可以适用于多个参数,即多个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>`
})

插槽分类讲解

  • 基本插槽:通过在引用子组件的时候,传递dom对象给子组件的一种方式,如下:
const app = Vue.createApp({
    template:`<counter>
		<div>dom</div>
    </counter>`
});

app.component("counter",{
	template:`<div>
		<slot></slot> // 通过slot标签,将父组件counter内的所有dom引入到此位置
	</div>`
})
  • 具名插槽:如果父组件传递多个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>`
})
  • 作用域插槽:直接上代码如下:
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>`
})

动态组件

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");

异步组件

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

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");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿洞晓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值