从0到1 开发一个自己的ui库

背景:由于最近业务开发,发现有很多业务场景相似,所以想搞一个自己的ui组件库。在此记录一下。使用技术框架:vite + ts + vue3。

可以从我的项目里把代码拖出来跑跑看看,我的示例里面写了3个组件,按钮、自动滚动列表、自动滚动表格。

仓库地址:https://github.com/lishengqin/standard-vue-cockpit-ui

ui库的文档地址:https://lishengqin.github.io/standard-vue-cockpit-ui/

下面图片是项目中install这个库后,查看这个库的目录格式,以及引入组件使用的效果。

      

1. 首先执行命令行生成vue项目,然后把sass sass-loader naive-ui等依赖都安装上

npm create vite@latest standard-vue-cockpit-ui -- --template vue

2. 由于是用ts写的,所有相关的操作要加上:

 1)加上【tsconfig.json】文件

 2)启动文件里的plugin要加上【vueJsx】,配置【esbuild】

3. 确定书写的格式:

  创建一个【packages】文件夹,与src在同一级,packages里面就是放置组件的,一个组件对应一个文件夹,举例,我现在开发一个【LsqButton】按钮组件,那我创建文件的目录为:  

         

4. 文件内容

index.scss---样式文件,就不展开说了;

 LsqButton.tsx

 1 import { defineComponent, PropType } from "vue";
 2 import { ButtonType } from "./index"
 3 import "./index.scss"
 4 export const lsqButtonProps = {
 5   /* 按钮类型 */
 6   type: {
 7     type: String as PropType<ButtonType>,
 8     default: "primary",
 9     validator(value: string) {
10       return ["primary", "text"].includes(value)
11     }
12   },
13   /* 文字颜色 */
14   color: {
15     type: String,
16     default: "fff"
17   },
18   bottomLine: {
19     type: Boolean,
20     default: false
21   }
22 }
23 export const lsqButtonEmits = {
24   /* 点击触发 */
25   click: () => { }
26 }
27 export const lsqButtonSlots = {
28   /* 自定义按钮文字插槽 */
29   default: () => true
30 }
31 export default defineComponent({
32   name: "lsq-button",
33   props: lsqButtonProps,
34   emits: lsqButtonEmits,
35   setup(props, { emit, slots }) {
36     let className = {
37       "lsq-button": true,
38       ['lsq_button_' + props.type]: true,
39       'lsq_button_text_border': props.type === 'text' && props.bottomLine
40     }
41     let style = {
42       color: props.color
43     }
44     const onClick = () => {
45       emit("click");
46     }
47     return () => (<div class={className} style={style} onClick={onClick}>{slots.default && slots.default()}</div>)
48   }
49 })

index.ts -- 关键的文件

 1 export type ButtonType = "primary" | "text"
 2 import _lsqButton from "./LsqButton"
 3 import { withInstall } from "../withInstall"
 4 export const LsqButton = withInstall(_lsqButton)
 5 export default LsqButton
 6 declare module "vue" {
 7   export interface GlobalComponents {
 8     LsqButton: typeof LsqButton
 9   }
10 }

同时还需要在 【packages】文件夹中写2个文件,【withInstall.ts】【index.ts】

【withInstall.ts】文件是为了install我们的组件,【index.ts】是将组件汇集,然后可以抛出去方便引入。

withInstall.ts

 1 import { App } from "vue/dist/vue";
 2 
 3 type EventShim = {
 4   new(...args: any[]): {
 5     $props: {
 6       onClick?: (...args: any[]) => void;
 7     };
 8   };
 9 };
10 
11 export type Index<T> = T & {
12   install(app: App): void;
13 } & EventShim;
14 
15 const camelizeRE = /-(\w)/g;
16 
17 export function withInstall<T>(options: T) {
18   (options as Record<string, unknown>).install = (app: App) => {
19     const { name } = options as unknown as { name: string };
20     //@ts-ignore
21     app.component(`-${name}`.replace(camelizeRE, (_, c) => c.toUpperCase()), options);
22   };
23   return options as Index<T>;
24 }

index.ts

import { App } from 'vue'

import LsqButton from "./lsq-button/index";
const components = [
  LsqButton
]
const install = (app: App): void => {
  components.map(one => one.install(app))
}
// 按需加载
export {
  LsqButton
}
// 完整加载
export default {
  install
}

 index.ts 文件对于组件 2种抛出方式,这样便于用户按需使用或全量引用。其实vue.use(),它的本质就是在执行参数中的install方法,里面的按需加载抛出方法不说了,那个完整加载的思路,就是我们自己写一个installAllComponents方法,这个方法其实就是将所有的组件都install执行,然后我再抛出一个对象:{ install : 封装的installAllComponents },然后用户再全量引用的时候,vue.use(allComponents),本质就是再执行 allComponents.install(),也就是执行我们上面定义的【installAllComponents】方法。

posted @ 2022-12-16 11:52  蛙仔  阅读(573)  评论(0编辑  收藏  举报