组件库Button 按钮
Breezliscript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import { ref, computed, inject } from 'vue'; import type { ButtonProps, ButtonEmits, ButtonInstance } from './types'; import { BUTTON_GROUP_CTX_KEY } from './constants'; import { throttle } from 'lodash-es'; import VrIcon from '../Icon/Icon.vue';
defineOptions({ name: 'VrButton', });
const props = withDefaults(defineProps<ButtonProps>(), { tag: 'button', nativeType: 'button', useThrottle: true, throttleDuration: 500, });
const emits = defineEmits<ButtonEmits>(); const slots = defineSlots(); const buttonGroupCtx = inject(BUTTON_GROUP_CTX_KEY, void 0);
const _ref = ref<HTMLElement>(); const size = computed(() => buttonGroupCtx?.size ?? props.size ?? ''); const type = computed(() => buttonGroupCtx?.type ?? props.type ?? ''); const disabled = computed(() => props.disabled || buttonGroupCtx?.disabled || false); const hasDefaultSlot = computed(() => !!slots.default); const iconStyle = computed(() => ({ marginRight: hasDefaultSlot.value ? '6px' : '0px', }));
const handleBtnClick = (e: MouseEvent) => { emits('click', e); };
const handleBtnClickThrottle = throttle( handleBtnClick, props.throttleDuration );
defineExpose<ButtonInstance>({ ref: _ref, disabled, size, type, loading: computed(() => props.loading), click: handleBtnClick, });
|
关键解释
defineOptions
最终导出组件名为 VrButton
,在 packages\core\components.ts 中以以下形式导入
1
| import {VrButton} from '@veyra/components'
|
withDefaults
给 defineProps
绑定默认值
接口
1 2 3 4 5
| interface ButtonProps{ tag?: string | Component size?: 'default' | 'large' | 'small' ... }
|
1 2 3 4 5 6
| >const props = withDefaults(defineProps<ButtonProps>(), { tag: 'button', nativeType: 'button', useThrottle: true, throttleDuration: 500, >})
|
defineEmits
子向父发射事件
子组件
1 2 3 4 5 6 7 8 9
| const emits = defineEmits<ButtonEmits>()
const handleBtnClick = (e: MouseEvent) => { emits('click', e) } const handleBtneCLickThrottle = throttle( handleBtnClick, props.throttleDuration )
|
父组件 用户使用组件
1
| <vr-... @click="Click" /> //当接收到子组件发射过来的事件之后,触发父组件自己的'Click'函数
|
1.点击后 子组件的 @click
被触发,将一个 MouseEvent
类型事件传入 handleBtnClick
或 handleBtneCLickThrottle
函数
2.emits('click', e)
向父组件发射 'click'
事件
3.父组件通过@click响应到 'click'
事件, 开始执行父组件中自定义的 Click
事件
defineSlots
1
| const slots = defineSlots()
|
1 2 3
| const iconStyle = computed(() => ({ marginRight: slots.default ? '6px' : '0px', }))
|
检测组件是否有默认插槽, 有则插入样式 marginRight: 6px
template
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <component :is="tag" ref="_ref" class="vr-button" :class="{ [`vr-button--${type}`]: type, [`vr-button--${size}`]: size, 'is-plain': plain, 'is-round': round, 'is-circle': circle, 'is-disabled': disabled, 'is-loading': loading, }" :disabled="disabled || loading" :type="tag === 'button' ? nativeType : void 0" :autofocus="autofocus" @click="useThrottle ? handleBtnClickThrottle : handleBtnClick" > <template v-if="loading"> <slot name="loading"> <vr-icon class="loading-icon" :icon="loadingIcon ?? 'spinner'" :style="iconStyle" size="1x" spin /> </slot> </template> <vr-icon v-if="icon && !loading" :icon="icon" size="1x" :style="iconStyle" /> <slot></slot> </component>
|
关键解释
<component></component>
是vue内置的动态组件
通过:is可控制组件的最终类型
例如<component :is="button"></component>
的最终渲染结果为<button></button>
ref="_ref"
首先,ref
属性的作用是将 **DOM 元素 **或 子组件实例 绑定 到对应的 响应式变量 上
ref="_ref"
将渲染的 DOM 元素(例如 <button>
或 <a>
)绑定到 _ref
变量
组件挂载后,_ref.value
就会指向这个 DOM 元素
使用 defineExpose
将 _ref
暴露给父组件,允许父组件访问子组件的 DOM 元素
例如,父组件可以调用子组件按钮的 focus()
方法
1 2
| ><vr-button ref="myButton" /> ><button @click="focus">focus vr-button</button>
|
1 2 3 4 5 6 7
| >import { ref } from 'vue'
>const myButton = ref(null)
>function focus() { myButton.value.ref.focus() >}
|
当用户点击父组件的按钮时,子组件的按钮会获得焦点
:class
使用对象语法动态绑定类名
例如文中两种
当 type
存在时(如 primary
、success
),添加类名 er-button--primary
'is-plain': plain
当 plain 为 true 时,添加类名 is-plain
constants.ts
1 2 3 4 5 6 7 8
| import type { InjectionKey } from "vue"; import type { ButtonGroupContext } from "./types";
export const BUTTON_GROUP_CTX_KEY: InjectionKey<ButtonGroupContext> = Symbol("buttonGroupContext");
|
关键解释
举个例子
1 2 3 4 5 6 7 8 9 10 11
|
import { provide } from "vue"; import { BUTTON_GROUP_CTX_KEY } from "./constants"; export default { setup() { const buttonGroupContext = { size: "large", type: "primary", }; provide(BUTTON_GROUP_CTX_KEY, buttonGroupContext); return {}; }, };
|
1 2 3 4 5 6 7 8 9 10 11
|
import { inject } from "vue"; import { BUTTON_GROUP_CTX_KEY } from "./constants"; export default { setup() { const buttonGroupContext = inject(BUTTON_GROUP_CTX_KEY); console.log(buttonGroupContext); return {}; }, };
|
types.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import { type Component, type ComputedRef, type Ref } from 'vue' export type ButtonType = 'primary' | 'success' | 'warning' | 'danger' | 'info' export type NativeType = 'button' | 'submit' | 'reset' export type ButtonSize = 'default' | 'large' | 'small'
export interface ButtonProps { tag?: string | Component type?: ButtonType size?: ButtonSize plain?: boolean round?: boolean circle?: boolean disabled?: boolean autofocus?: boolean nativeType?: NativeType icon?: string loading?: boolean loadingIcon?: string useThrottle?: boolean throttleDuration?: number }
export interface ButtonGroupProps { size?: ButtonSize type?: ButtonType disabled?: boolean }
export interface ButtonGroupContext { size?: ButtonSize type?: ButtonType disabled?: boolean }
export interface ButtonEmits { (e: 'click', value: MouseEvent): void }
export interface ButtonInstance { ref: Ref<HTMLButtonElement | void> disabled: ComputedRef<boolean> size: ComputedRef<string> type: ComputedRef<string> }
|