之前发现了一个非常好的学习项目,技术栈很新,于是就学习了一下思路,自己实现了一遍。果然,人还是得逼着自己,最近的学习节奏还算不错,再做完项目后来总结一下,编写并完善文档,这不就形成了一套解决方案吗?尽管这只是一个较简单的项目,很多复杂组件还未加入,但其中的思路还是很有启发性的。

项目地址:https://github.com/Fridolph/my-element-plus

my-element-plus 学习相关组件及源码,仅供学习参考

文档地址:https://fridolph.github.io/my-element-docs/

起步

所谓万事开头难,在决定好技术栈后,最重要的就是进行项目构建。Vue3 + Vite 一把梭确实爽!

  • eslint
  • postcss
  • vitest 直接可用
  • volar
  • vite
  • vitepress

相关依赖安装好后,直接按官方推荐设置 vite.config.ts 即可
根据经验,在做这种组件库项目时我发现可能需要花费更多时间(当然也可以使用 TDD 测试驱动开发)。但我更喜欢先把组件开发完成再去补充测试……理清需求和目的,逐步实现,效果会更好。

目录结构

对于这种项目,推荐的最佳实践是直接照搬大型开源项目的目录结构。当然,由于这个项目相对小且简单,也可以更加灵活一些,去掉那种复杂的模块结构,按照组件进行分类即可。

配色参考

色彩 - 组件设计前重要的一环。

中国色

https://peiseka.com/zhongguochuantongse.html

bootstrap

https://getbootstrap.com/docs/5.3/customize/color

tailwind.css

https://tailwindcss.com/docs/customizing-colors

可以将颜色先写到 styles 里的变量中,现在更推荐 CSS 的变量

styles > var.css

之前一直用 scss、stylus ,现在原生 css 已经很强大了,浏览器支持也很好,所以我们直接用 css vars

兼容性肯定要做的, autoprefixer 不可少,所以还是上 PostCSS。它是一个功能比较单一的工具,它提供了一种方式用 JavaScript 代码来处理 CSS。它负责把 CSS 代码解析成抽象语法树结构(Abstract Syntax Tree,AST),再交由插件来进行处理。

编写一个组件

如:Button - 按钮。大概是按以下思路来进行的

需求分析

Button 组件大部分关注样式,没有其他交互。可在其上自行绑定事件

根据分析,暂得以下具体属性列表:(button.type.ts)

  • type 样式
  • plain
  • round
  • circle
  • disabled
  • icon
  • loading

确定项目文件结构:

  • 从简单入手,这里就都放到 components/Button 下了
    • Button(components) 后续组件都参照这个文件结构来
    • Button.vue
    • style.css
    • types.ts
    • Button.test.ts
  • 以学习为主没必要过度设计,过于纠结细节
  • 该组件是否能完成基本功能
  • 是否需要暴露实例 或 expose 属性或方法等

编写测试代码:

  • 测试基本功能展示 OK
  • 传递 prop 后能否正常展示
  • 测试交互事件能否触发

总结遇到的一些问题:

  • 编码过程遇到的 bug
  • 样式能否实现
  • 其他实现方式 (锦上添花)

每当完成一个组件,应立即更新 vitepress 对应的组件文档:

一个组件文档工作量很少,且能有效反馈让进度可控,一定要坚持。一旦堆多了,后期反而文字类工作才是让程序员最头大的

表单组件

感觉这里总结得还成,就写到博客里了。

表单是用户和网站交互最重要的一部分。

表单有很多控件,这里选了几个简单的模拟实现,就单独整理写到这里了. 表单包含 输入框, 单选框, 下拉选择, 多选框 等用户输入的组件。 使用表单,您可以收集、验证和提交数据。

典型表单:

最基础的表单包括各种输入表单项,比如:

  • input
  • select
  • radio
  • checkbox

在每一个 form 组件中,你需要一个 form-item 字段作为输入项的容器,用于获取值与验证值。

按照原型图整理的简单需求:

  • 自定义 UI
    • 整理可自定义
    • 用户可自定义渲染多种类型的表单元素
    • 用户可自定义提交区域内容
  • 验证时机
    • 表单元素默认 blur 时验证,可自定义
    • 整个表单在点击提交时应全部验证
  • 验证规则
    • 每个 input 可配置多条规则
    • 规则可自定义

组件结构设计

1
2
3
4
const formOptions = {
name: { key: 'name', value: '', rules: [], ... }
[otherKey]: { key: 'xxx', value: '', rules: [], ... }
}

但这种用法过于繁杂,不灵活,在使用上体现不出结构,会让 js 臃肿,所以用 slot 的形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<form :options="formOptions" />

<form>
<FormItem
label="label"
name="name">
<input />
</FormItem>
<FormItem
label="label2"
name="name2">
<select />
</FormItem>
</form>

开发步骤:

  1. 根据结构,实现基础布局,完成 demo
  2. 添加初始化数据,绑定数据
  3. 添加验证
  4. 事件交互,验证等
  5. 不断完善

实现验证功能的思路

验证类型:

  • 表单 Form 整体验证
  • 单个表单 FormItem 的验证

表单中每项循环验证一次 every 即为表单验证结果

单个验证实现思路:

  • 从父组件 Form 获取对应 option、value
  • 在 FormItem 组件中实现

第三方库:async-validator

关于打包

为什么要打包?

  • 模块化
  • 可维护性
  • 可复制性

模块化定义:以功能块为单位进行程序设计,实现其求解算法的方法称为模块化。模块化的目的是为了降低程序复杂度,使程序设计、调试和维护等操作简单化。

打包什么类型的文件

  • CommonJs, ES6 modules 需要特殊的打包工具支持
  • AMD 已过时
  • 浏览器直接使用 (UMD)Universal Module Definition(但不支持 tree shaking)
    • 通用 JS 格式
    • 兼容 Common.js、AMD、浏览器

综上:

首要格式 - ES Module ,并且提供支持 TypeScript 的 type 文件
备选方案 - UMD

lib 相关设置

对应 vite.config.ts 中需要设置 build.lib 相应类型(参考文档配置即可)

我们可为每个组件建立一个 index.ts ,例如 Button

1
2
3
4
5
6
7
8
9
import type { App } from 'vue'
import Button from './Button.vue'

Button.install = (app: App) => {
app.component(Button.name, Button)
}

export default Button
export * from './types'

在src和main.ts同级目录下新建一个index.ts用来导入组件,将各组件注册到Vue实例中,这样打包出来就可通过import形式导入了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const components = [
// 组件
Button,
...
// 方法
createMessage,
closeAllInstances,
...
]

function install(app: App) {
components.forEach((component) => {
app.component(component.name, component)
})
}

export { 组件 }
export default install

发布遇到的坑

最近 npm 淘宝源 证书过期了,所以得改地址 = = 之前老是遇到依赖安装问题,都是这些小问题造成的,一不注意会卡很久

1
npm config set registry https://registry.npmmirror.com

但在发布时需要登录npm 又得把淘宝源去掉

1
npm config set registry https://registry.npmjs.org

npm login 登录不上 - - 困扰很久,结果发现是网络问题,手机开热点,电脑连手机的,登npmjs就可以了,发布完记得改回来。

最后

虽然看起来很简单,但折腾了几周才终于搞定。实际上,大部分时间花在改样式和配置上,而真正完成功能反而挺快的。本来也想试试TailWindCss,但发现还有些应对不了的问题,于是干脆改到了履历项目里(是的,先挖个坑告诉自己必须填)。

加油,2024!只要行动起来,就是好的开始。