【前端工程化-组件库】从0-1构建Vue3组件库(组件开发)
前言:知识储备
TSX 定义组件
选择 TSX
来开发组件,可以更加灵活地使用 render
函数,动态创建组件,同时也方便使用 TS
来控制参数/变量的类型。
强大的灵活度和控制力对于开发底层组件尤为重要!!!
使用 TSX 定义组件有几种常用的方法:
- 函数式写法
export default (props, ctx) => <div>button</div>
- defineComponent({render() {}})
export default defineComponent({
render() {
return <div>button</div>
}
})
- defineComponent({setup() {}})
export default defineComponent({
setup(props, ctx) {
return () => <div>button</div>
}
})
Vue3中JSX/TSX语法(API变化)
指令
内置指令
v-if
v-if
使用条件语句或三目运算符来代替:
<div>{ condition ? <span>A</span> : <span>B</span> }</div>
v-for
v-for
使用 map
来代替:
import { defineComponent, ref } from "vue";
const App = defineComponent({
setup(){
const list = ref<string[]>([1,2,3])
return () => {
list.value.map((data,index) => <p key={index}>{data}</p>)
}
}
});
扩展个知识点:
- 数组遍历常用
forEach
和map
,forEach
重在执行遍历,没有返回值;map
可以设置返回值。- 在 JSX/TSX 语法中最终是要返回 html 标签或对应内容,所以JSX中使用
map
方法来遍历。
自定义指令
自定义指令需要配置 directives
选项:
defineComponent({
directives: {
// 模版中使用 v-focus
focus: {
mounted(el) {
el.focus()
}
}
},
setup() {
return () => <input v-focus>
}
});
修饰符
借用 withModifiers
向事件处理函数添加修饰符:
import { withModifiers, defineComponent } from "vue";
const App = defineComponent({
setup() {
const count = ref(0);
const inc = () => {
count.value++;
};
return () => (
<div onClick={withModifiers(inc, ["self"])}>{count.value}</div>
);
}
});
插槽
JSX/TSX 中使用插槽,主要使用 v-slots
指令来实现:
- Parent.tsx
export default defineComponent({
setup() {
return () => (
<Child
v-slots={{
prefix: () => <i>prefix</i>, // 具名插槽
suffix: (props: Record<"name", string>) => <span>{props.name}</span>
}}
>
默认插槽内容
</Child>
);
}
});
- Child.tsx
const Child = defineComponent({
setup(props, { slots }) {
return () => (
<div>
默认插槽: {slots.default && slots.default()}
<br />
具名插槽: {slots.prefix && slots.prefix()}
<br />
作用域插槽:{slots.suffix && slots.suffix({ name: "suffix" })}
</div>
);
}
});
emit 子向父组件通信
JSX/TSX 中使用 emit 实现子组件向父组件传值,多了 emits
选型配置:
const Child = defineComponent({
emits: ["click"],
setup(props ,{ emit }) {
return () => (
<button onClick={() => {emit("click")}}>点我触发emit</button>
)
}
});
开发组件
Button组件
功能需求
组件开发
我们使用 TSX 语法来开发 Button 组件,代码如下:
packages/components/button/src/button.tsx (button 组件)
import { computed, defineComponent, toRefs } from "vue"
import { buttonProps, ButtonProps } from "./button-type"
import { getComponentCls } from "@vue3-ui/utils"
export default defineComponent({
name: "UButton",
props: buttonProps,
setup(props: ButtonProps, { slots }) {
const { type, size, disabled } = toRefs(props)
// class处理
const prefixCls = getComponentCls("button")
const classes = computed(() => [
prefixCls,
`${prefixCls}--${type.value}`,
`${prefixCls}--${size.value}`,
disabled.value ? "is-disabled" : ""
])
return () => {
const { tag: Component } = props
const defaultSlot = slots.default ? slots.default() : "按钮"
return (
<Component disabled={disabled.value} class={classes.value}>
{defaultSlot}
</Component>
)
}
}
})
packages/utils/index.ts
const CLASS_PREFIX = "u"
// 拼装组件className
export const getComponentCls = (componentName: string): string =>
`${CLASS_PREFIX}-${componentName}`
packages/components/button/src/button-type.ts (提取 button 属性定义)
import { ExtractPropTypes, PropType } from "vue"
export type buttonType =
| "default"
| "primary"
| "success"
| "info"
| "warning"
| "danger"
export type buttonSize = "small" | "default" | "large"
export const buttonProps = {
type: {
type: String as PropType<buttonType>,
default: "default"
},
size: {
type: String as PropType<buttonSize>,
default: "default"
},
disabled: {
type: Boolean,
default: false
},
tag: {
type: String as PropType<keyof HTMLElementTagNameMap>,
default: "button"
}
}
export type ButtonProps = ExtractPropTypes<typeof buttonProps>
至此,Button 组件的逻辑代码已完成,样式编码可以考虑接入CSS预编译器:Less、Sass、Stylus等。组件库里选择接入最早出现成熟稳定的 Sass
。
Sass 的优势如下:
- Sass完全兼容所有版本的CSS。
- 特性丰富,Sass拥有比其他任何CSS扩展语言更多的功能和特性。
- 技术成熟,功能强大。
- 行业认可,越来越多的人使用Sass。
- 社区庞大,大多数科技企业和成百上千名开发者为Sass提供支持。
- 较多优秀框架使用Sass,比如Element、Bootstrap、Bourbon等。
插件注册
Button 组件功能开发好,需要将其注册成插件导出,实现代码如下:
packages/components/install.ts
import { App } from "vue"
import { Vue3UIOption, installComponent } from "@vue3-ui/utils"
import { components } from "./components"
const Vue3UI = {
install(app: App, options?: Vue3UIOption) {
components.forEach(component => {
installComponent(app, component, options)
})
}
}
export default Vue3UI
packages/utils/index.ts
import type { App } from "vue"
export interface Vue3UIOption {
componentPrefix?: string
}
// 注册插件
export const installComponent = (
app: App,
component: any,
options?: Vue3UIOption
) => {
app.component(component.name, component)
}
组件使用
项目入口引入组件库即可使用预览,具体如下:
play/main.ts
import { createApp } from "vue"
import "./style.css"
import App from "./App.vue"
import Vue3UI from "@vue3-ui/components"
import "@vue3-ui/theme-chalk/src/index.scss"
createApp(App).use(Vue3UI).mount("#app")
总结
行文至此,我们已经完整开发了 Vue3 组件库的Button组件,其余组件后面会陆续补充完善,有兴趣参与的小伙伴欢迎到 github 协同开发👏👏 ps:组件库 github 地址:github.com/GGXXMM/vue3…
本专栏文章:
- 【前端工程化-组件库】从0-1构建Vue3组件库(概要设计)
- 【前端工程化-组件库】从0-1构建Vue3组件库(组件开发)
- 【前端工程化-组件库】从0-1构建Vue3组件库(单元测试)
- 【前端工程化-组件库】从0-1构建Vue3组件库(打包发布)