分享VUE3编写组件高级技巧,优雅!
在这里,主要分享一些平时写VUE组件,会用到一些技巧,会让代码得到很大的简化,可以节省很多脑力体力。
1、v-bind=“$attrs”
这是首推的一个技巧写法,特别在拓展开源组件时,无缝使用开源组件各种props值时,简直不要太爽。
比如element-ui组件中的select组件,就有一个让人痛恨的点,就是options数据无法配置,必须得手动引入option组件才行,如下:
<el-select v-model="value" placeholder="Select" size="large">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
身为一个优秀前端前端佬,这哪里能忍!
技巧随之用来,我们就可以使用上面这个,创建一个自定义select组件,既能享用原组件的各种配置属性和事件
(P.S. 如果需要组件上使用自定义事件,比如change事件,属性上定义为’onChange': ()=>{}。),也可以自定义一些功能,创建一个customSelect.vue:
<el-select v-model="selectedValue" v-bind="attts">
<el-option v-for="(item) in customOptions" v-bind="item"/>
</el-select>
这样在动态引用这个组件,就能使用自定义的customOptions这个属性。
上面例子主要说明,v-bind="$attr"的好处。但还是得多说一句,上面例子中的一些缺点。
- 无法直接使用el-select对外暴露的方法;
- 无法直接使用el-select的slot分发;
然后需要注意一个点,得在customSelect.vue组件中,设置inheritAttrs为false,防止数据在组件上一层层透传下去。
2、improt { h } from vue
h为vue中的渲染函数,主要用来创建虚拟 DOM 节点 (vnode)。对应参数,可以戳这里,看官方详细正宗介绍。
对应这个连接中,有很多渲染函数的介绍。这系列有一个很大的特点,那就是用的魔怔了,就会一不小心把VUE变成“React”,损失掉VUE框架中的一些优点。
自由度非常高。仅仅针对这个H函数举例,还是援用上面的例子,实现如下(代码片段):
<scirpt>
import {defineComponent, h} from 'vue'
import {ElSelect, ElOption} from 'element-plus'
export default definComponent({
name:'DynamicSelect',
props:{
options:{
type:Array,
required:true,
default:() => []
}
},
setup(props) {
return () = >
h(ElSelect, () =>
props.options.map(options =>
h(ElOption, {
key:option.value,
label:option.label,
value:option.value,
})
)
)
}
})
<script>
足够清爽,简单。
3、render
render,用于编程式地创建组件虚拟 DOM 树的函数。解释链接,可以戳这里。
废话不多说,直接以上面的例子,用render方式撸一遍。
<!-- <template>
<div>1</div>
<template> -->
<scirpt>
import {defineComponent, h} from 'vue'
import {ElSelect, ElOption} from 'element-plus'
export default definComponent({
name:'DynamicSelect',
props:{
options:{
type:Array,
required:true,
default:() => []
}
},
render(_ctx) {
return () = >
h(ElSelect, () =>
_ctx.options.map(options =>
h(ElOption, {
key:option.value,
label:option.label,
value:option.value,
})
)
)
}
})
<script>
不能说实现的方式跟上面相似,简直说是一模一样。主要在于render做了template要做的事,但相比较template少一层解析,理论上会比template更高效。
需要注意一点,这里放出官网的描述:
如果一个组件中同时存在
render
和template
,则render
将具有更高的优先级
正常理解的话,是render渲染出的vnode会高于template解析出的vnode,同时存在,render会覆盖掉template。
但在经过VUE的v3.3.4版本中操作,template会覆盖掉render。所以这个优先级,猜测可能是render会优先解析,具体得翻源码,待理解后继续更新。
4、getCurrentInstance
这个是获取当前组件实例的方法,属于核弹级别的方法,也属于VUE3官网文档中翻不到的东西。
但鲁迅说的好,路走的多了,那就成路了。如果社区用的人多了,那么它就有可能提上去!
言归正传,那么拿了这个组件的实例,能干什么呢?
那可干的事情,就可多可多了。
比如改个,调个组件方法,这都算小儿科,完全不用担心这里readOnly
,那里ReadonlyReactiveHandler
。
猛一点,直接硬插,换个上下文。
再猛的,先假设:组件实例 === 组件,组件 === VUE,VUE === YYX写的,然后你写了一点代码+VUE,是不是由此可得,你的代码 》 VUE ,进而证明 你 》 YYX。嗯?
5、extends
先来一段官方的介绍:
从实现角度来看,
extends
几乎和mixins
相同。通过extends
指定的组件将会当作第一个 mixin 来处理。然而,
extends
和mixins
表达的是不同的目标。mixins
选项基本用于组合功能,而extends
则一般更关注继承关系。
缺点上,第1节有提一个,但还有一个不算是缺点的缺点,相同属性和方法会直接覆盖被继承的组件(钩子函数不会被覆盖),主要在于是否熟悉被继承的组件中的逻辑。用的好就很好,用的不行,就真的很不行。
如果还是用上面的例子作为例子,实现方法如下:
<scirpt>
import {defineComponent, createVNode, render, getCurrentInstance } from 'vue'
import {ElSelect, ElOption} from 'element-plus'
export default definComponent({
name:'DynamicSelect',
extends:ElSelect,
props:{
options:{
type:Array,
required:true,
default:() => []
}
},
setup(props) {
return ElSelect.setup(props, context)
},
mounted(){
const curInstance = getCurrentInstance()
const container = doucment.createElement('div')
this.$props.options.forEach(options => {
const vNode = createVNode(ElOption,{
key:option.value,
label:option.label,
value:option.value,
})
})
const currrentProvides = curInstance?.provides
if(currrentProvides){
// 将ELSelect的Provides,传入到ElOption中
reflect.set(curInstance?.appContext,'provides',{...currrentProvides})
}
vNode.appContext = curInstance?.appContext
render(vNode,container)
this.$el.appendChild(container)
}
})
<script>
但这种,确实是为了实现那个例子而写的代码。有些可以作为参考。
暂时分享这些,欢迎前端佬们拍砖。
来源:juejin.cn/post/7450836153258049572