Vue-组件二次封装
本次对
el-input
进行简单封装进行演示
- 封装很简单,就给激活样式的边框(主要是功能)
本次封装主要使用到vue自带的几个对象
- $attrs:获取绑定在组件上的所有属性
- $listeners: 获取绑定在组件上的所有函数方法
- $slots: 获取应用在组件内的所有插槽
1、属性传递
element 的input组件有很多属性,
- 想要实现在封装好后的组件上使用
el-input
组件的属性,会直接传递到el-input
组件上,包括v-model
。- 在组件中,可以使用
this.$attrs
获取所有绑定在组件上的属性(不包括方法)- 这样,我们就可以在封装的组件内,使用
v-bind="$attrs"
,直接把属性传递到内部组件上。- 在下列案例中,由于
v-model
是:value 和 @input
两个组合的语法糖,$attrs
只能获取属性,所以只能传递:value
属性
1.1、父组件
<template>
<div class="wrapper">
<my-input v-model="val">
</my-input>
</div>
</template>
<script>
import MyInput from '@/components/MyInput'
export default {
components: {
MyInput,
},
data() {
return {
val: '111',
}
},
methods: {
inputChange(val){
console.log(val);
}
}
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 10vh;
}
</style>
1.2、子组件
<template>
<el-input v-bind="$attrs"></el-input>
</template>
<script>
export default {
created() {
console.log("attrs:",this.$attrs);
}
}
</script>
<style lang="scss" scoped>
::v-deep {
.el-input__inner:focus {
border-color: red;
}
}
</style>
1.3、效果
- 这时候给输入框输入值是无效的,因为目前只能把
value
属性绑定到el-input
上,并没有把input
函数绑定上去,所以不能修改父组件传过来的value
的值。
2、方法传递
element的组件,也有很多方法,比如:change等函数
- 想要实现在封装好后的组件上使用
el-input
组件的方法,会直接传递到el-input
组件上。- 在组件中,可以使用
this.$listeners
获取所有绑定在组件上的属性(不包括属性)- 这样,我们就可以在封装的组件内,使用
v-on="$listeners"
,直接把方法传递到内部组件上。- 在下列案例中,由于
v-model
是:value 和 @input
两个组合的语法糖,$listeners
只能获取属性,所以结合上面$attrs
jiu可以完整的实现v-model
的效果了
2.1、父组件
<template>
<div class="wrapper">
<my-input v-model="val" @change="inputChange">
</my-input>
</div>
</template>
<script>
import MyInput from '@/components/MyInput'
export default {
components: {
MyInput,
},
data() {
return {
val: '111',
}
},
methods: {
inputChange(val){
console.log("inputChange:", val);
}
}
}
</script>
2.2、子组件
<template>
<el-input v-bind="$attrs" v-on="$listeners"></el-input>
</template>
<script>
export default {
created() {
console.log("attrs:",this.$attrs);
console.log("listeners:",this.$listeners);
}
}
</script>
<style lang="scss" scoped>
::v-deep {
.el-input__inner:focus {
border-color: red;
}
}
</style>
2.3、效果
这时候搭配
$attrs
就可以实现v-model
的完整效果了,以及@change
函数也会传递过去
3、插槽传递
element的组件,也包括了很多的插槽
- 想要给封装好后的组件,使用的插槽,传递到
el-input
中- 在组件中,可以使用
this.$slots
获取所有绑定在组件上的插槽- 这样,我们就可以在封装的组件内,使用
v-for="(val, key) in $slots"
,所有插槽,遍历放到组件中,当作组件的插槽- 注意插槽传参也要处理(我这里没处理)
3.1、父组件
<template>
<div class="wrapper">
<my-input v-model="val" @change="inputChange">
<template slot="prepend">Http://</template>
<el-button slot="append" icon="el-icon-search"></el-button>
</my-input>
</div>
</template>
<script>
import MyInput from '@/components/MyInput'
export default {
components: {
MyInput,
},
data() {
return {
val: '111',
}
},
methods: {
inputChange(val){
console.log("inputChange:", val);
}
}
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 10vh;
}
</style>
3.2、子组件
<template>
<el-input v-bind="$attrs" v-on="$listeners">
<template v-for="(val, key) in $slots">
<slot :name="key"></slot>
</template>
</el-input>
</template>
<script>
export default {
created() {
console.log("attrs:",this.$attrs);
console.log("listeners:",this.$listeners);
console.log("slots",this.$slots);
}
}
</script>
<style lang="scss" scoped>
::v-deep {
.el-input__inner:focus {
border-color: red;
}
}
</style>
3.3、效果
4、ref伪传递(适用于vue3)
- 为什么说伪传递呢,因为在vue中,根本就拿不到外层组件的
ref
属性,所以只能另换思路- 你要用ref,无非就是想调用组件里面的函数。那我封装的组件里面,可以把被封装的组件的函数,直接提取出来,当作我封装组件的函数即可实现
- 适用于Vue3,vue2会卡死
4.1、父组件
<template>
<div class="wrapper">
<my-input ref="muInput" v-model="val" @change="inputChange">
<template slot="prepend">Http://</template>
<el-button slot="append" icon="el-icon-search"></el-button>
</my-input>
</div>
</template>
<script>
import MyInput from '@/components/MyInput'
export default {
components: {
MyInput,
},
data() {
return {
val: '111',
}
},
mounted() {
this.$refs.muInput.focus()
},
methods: {
inputChange(val){
console.log("inputChange:", val);
}
}
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 10vh;
}
</style>
4.2、子组件
<template>
<el-input ref="input" v-bind="$attrs" v-on="$listeners">
<template v-for="(val, key) in $slots" #[key]>
<slot :name="key"></slot>
</template>
</el-input>
</template>
<script>
export default {
mounted() {
console.log("attrs:",this.$attrs);
console.log("listeners:",this.$listeners);
console.log("slots",this.$slots);
for (const [key, value] of Object.entries(this.$refs.input)) {
this[key] = value
}
}
}
</script>
<style lang="scss" scoped>
::v-deep {
.el-input__inner:focus {
border-color: red;
}
}
</style>
5、插槽传递补充22
- $slots可以获取所有应用在子组件上的插槽。但是仅限于子组件不使用插槽进行传参(作用域插槽)只能捕获到具名插槽。
- 当需要使用作用域插槽时,就会发现,
$solts
捕获不到这个插槽了。- 这时候,就需要使用
$scopedSlots
,它就等于具名插槽 + 作用域插槽,捕获的更加全面。- 上面我们只是使用
$slots
传递具名插槽,如果插槽需要传参,则无法使用,我们要换成更加全面的$scopedSlots
5.1、子组件
<template>
<el-input v-bind="$attrs" v-on="$listeners">
<template v-for="(val, key) in $scopedSlots" :scope="scope">
<slot :name="key" :data="scope"></slot>
</template>
</el-input>
</template>
<script>
export default {
created() {
console.log("attrs:",this.$attrs);
console.log("listeners:",this.$listeners);
console.log("slots",this.$slots);
console.log("scopedSlots",this.$scopedSlots);
}
}
</script>
<style lang="scss" scoped>
::v-deep {
.el-input__inner:focus {
border-color: red;
}
}
</style>