在阅读文章之前,我已经默认你已经掌握了Vue3.0
的基本使用,文章中主要分析其中组件开发过程中的思路以及一些Api
的讲解。希望这篇文章能帮助你更加的了解Element3
和Vue3.0
的使用和组件的开发能带来一些帮助。
Template
废话也就不多赘述了,首先在上面所说的Element3
Git地址上拉取最新源码,之后在package
文件夹中找到button
文件夹下的Button.vue
,这个组件就是介绍第一个组件<el-button/>
。对于tempalte
方面就不多赘述了大概内容如下:
<template>
<button class="el-button"
:class="classes"
:type="nativeType"
:disabled="buttonDisabled || loading">
<i class="el-icon-loading" v-if="loading"></i>
<i :class="icon" v-else-if="icon"></i>
<span v-if="$slots.default">
<slot></slot>
</span>
</button>
</template>
上述结构中的loading
就不用多说了这里也是蛮通俗的,这个地方优先展示的是loading
,其次才是icon
图标,在Button
组件中还使用了$slots.default
该值指向的是插入当前组件的默认slot
,也就是常说的匿名slot
。这里用了一个很巧妙的手法就是,如果匿名组件存在的下才会使用slot
而不是直接吧slot
挂载组件中。这就很Nice~
props
button
动态绑定了calss、type、disabled
这个地方稍后再说,先标记一下,首先看下组件的入参(即:props
):
export default {
props: {
// 控制按钮大小
size: {
type: String,
validator(val) {
if (val === '') return true
return ['medium', 'small', 'mini'].indexOf(val) !== -1
}
},
// 控制按钮类型
type: {
type: String,
validator(val) {
return (
['primary', 'success', 'warning', 'danger', 'info', 'text']
.indexOf(val) !== -1
)
}
},
// 原生类型
nativeType: {
type: String,
default: 'button'
},
// 是否朴素按钮
plain: Boolean,
// 是否圆角按钮
round: Boolean,
// 是否圆形circle
circle: Boolean,
// 是否loading
loading: Boolean,
// 是否禁用
disabled: Boolean,
// 图标类名
icon: String
}
};
通过上面的源码中可以看出size
和type
参数使用了自定义校验器(validator
),如果没有仔细阅读过Vue
文档的同学可能对validator
会有些许的陌生,validator
可以在方法可以更加精确的规范参数的值,如size
中只能传入medium,small,mini
。validator
方法返回的是一个boolean
值,当没有传入规定的值,则会抛出错误无效的道具
。也就时说validator
会根据如果返回的值为true
则标识验证通过,返回错误即验证失败。
细心的小伙伴或许已经发现了,其中包含属性native-type
原生属性也挂载了上去,但是这个值并没有使用validator
进行值的校验,可能在Element3
开发人员可能为了方便以后的拓展吧,如果原声中native-type
添加其他的值依赖组件库的项目即可以直接使用,无需更改组件库部分,同时也不希望组件库去影响原生的某些属性。
笔者也看了一下文档在Element3
中Button
的文档中,提供autofocus
属性,但是在组件中却没有接收这个这个属性,我们这个时候再次看下HTML
部分,在tempalte
就直接是button
标签了,所以当我们在<el-button autofocus>
的时候autofocus
就已经被挂载上去了。
逻辑处理
介绍完参数部分之后接下来我们继续向下看一下setup
,经过Vue3.0
的改进setup
已经承载了页面中大部分的逻辑,所以el-button
也是自然。
import { useGlobalOptions } from '../../src/use/globalConfig';
import { toRefs, inject, computed } from 'vue'
export default {
setup(props) {
const { size, disabled } = toRefs(props)
const buttonSize = useButtonSize(size)
const buttonDisabled = useButtonDisabled(disabled)
const classes = useClasses({
props,
size: buttonSize,
disabled: buttonDisabled
})
return {
buttonDisabled,
classes
}
}
}
由于在setup
中是没有this
所以setup
的第一个参数即是props
传入的参数属性参数,但获取到的对象是一个普通对象无法完成双向绑定,通过toRefs
转换成了绑定对象。
buttonSize
在setup
对class
进行了统一的处理,那么我们就看下这部分的内容,方法中首先处理的是buttonSize
,具体代码如下。
const useButtonSize = (size) => {
// 获取当前组件实例
const globalConfig = useGlobalOptions()
return computed(() => {
const elFormItem = inject('elFormItem', {})
return size?.value || elFormItem.elFormItemSize || globalConfig.size
})
}
src/use/globalConfig
export function useGlobalOptions() {
// 获取当前组件实例
const instance = getCurrentInstance()
if (!instance) {
console.ware('useGlobalOptions must be call in setup function')
return
}
return instance.appContext.config.globalProperties.$ELEMENT || {}
}
在useButtonSize
方法中通过公用方法获取到当前组件的示例,如果当前组件的实例存在的话则通过全局属性,获取到当前组件的全局信息(组件全局信息实在初始化时手动配置的信息,名为$ELEMENT
),若没有会获取到全局信息,则默认返回一个空对象。之后通过inject
接收父级elFormItem
传递过来的数据,并传入一个空对象作为默认值。
完成上述操作之后,在useButtonSize
方法中通过计算属性,获取到size
属性值,其优先级为props > elFormItem > globalConfig
,最后将计算结果返回出去。
useButtonDisabled
处理完上述之后就处理了一下按钮的是否可用样式useButtonDisabled
,具体代码如下:
const useButtonDisabled = (disabled) => {
return computed(() => {
const elForm = inject('elForm', {});
return disabled?.value || elForm.disabled;
});
};
其实这里的处理逻辑和处理button
的size
是差不多的,这里对代码就不多赘述了,这里同样是返回了所获取到的值。这里接收的不再是elFormItem
的值,而是elForm
的值,通过这里可以得出,Element3
在elForm
传入button
会使整个表单全部都禁用。
注:关于inject所接收的值,这里就不多赘述,分析其组件时会进行讲解
useClasses
获取到所有的参数之后,则是通过useClasses
方法统一处理class
名称:
const useClasses = ({ props, size, disabled }) => {
return computed(() => {
return [
size.value ? `el-button--${size.value}` : '',
props.type ? `el-button--${props.type}` : '',
{
'is-plain': props.plain,
'is-round': props.round,
'is-circle': props.circle,
'is-loading': props.loading,
'is-disabled': disabled.value
}
]
})
}
关于class
动态渲染这里,使用了计算属性,而不是一一对应绑定的,这样做的好处是可以统一维护其class
的样式,还有点可以说的就是,在渲染class
的时候,使用的是数组嵌套对象的形式,可能笔者比较菜,还没有用过这种方式动态渲染class
,通过代码可以明确的看出useClasses
方法只是简单的通过计算数据对所有相关的class
名称进行了统一的处理。针不戳~
总结
上述代码已经是整个el-button
组件的全部,虽然是一个很简单的组件,但是却能在里面学到一些东西,比如Vue3.0
的Provide/Inject
,整体代码思路清晰代码结构简单,都是我们值得学习的地方。
哪有什么岁月静好,不过是有人替你负重前行。感谢为Element3
默默付出的程序大佬们。
如果大家感兴趣的话就点击下方连接,Star
一下吧。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。