写在开始
我想写这个系列很久了,困窘于自身经验不足与与生俱来的拖延症,一直没有动笔,但是在 2024 的开始,我打算完成这个系列,算是对这两年工作的一个总结跟分享,这个系列是什么?是我依据工作经验与公司中后台项目中常出现的问题,复盘的一系列的中后台开发最佳实践。
技术栈的介绍
工欲善其事,必先利其器,init 项目前我们需要进行开发工具的选取。我个人将需要工具分为两类,一类是基本固定死的,基本是 Vue3 中后台开发的最佳实践的组合拳了,另外一类是可以进行考虑的。
固定工具栈
Vue3 + typescript + axios + pinia + vue-router + sass/less + vite + pnpm + reset/normalize
里面大多都是官方的工具生态包,关于是否引入 typescript,我个人认为是十分有必要的,尤其是在团队协同开发的中后台项目,能够合理地推断业务数据的类型,并且通过链式可选运算符访问到接下来的内容,不得不说是种很舒服的体验👍。pnpm 就不过多介绍,一个更加优秀的包管理工具。
其他工具栈
这里分享的都是一些有封装好的技术工具,这些工具并不影响项目主流程的进行,它们的服务对象并不是产品,而是开发者,用来提高我们的开发体验,可有可无。
tailwind
为什么不直接用行内样式呢?对比来看,tailwind 相比行内样式的表达能力更强,行内样式的冗余也不利于 HTML 的维护,同时行内样式增加的是 HTML 文件的大小,tailwind 增加的是样式文件的大小,HTML 的体积成本相比样式文件更加昂贵。
原子化 CSS 方案见仁见智,对我来说,tailwind 解决了 CSS 封装的命名负担,回首来看,我维护的大多项目中,可复用的 class 样式只占很小一部分,剩下的都只是为了 BEM 而去命名,因而我选择脱离这种命名模式转投 tailwind。
但使用 tailwind 仍有一些主要的注意点
- tailwind 固执己见的基础样式
tailwind 引入的样式不同于 normalize 跟 reset ,tailwind 在抹平不同浏览器样式不统一的基础上,还对一些元素的默认样式进行了覆盖。
比如 list 进行了无样式化处理
一些元素被重新定义为块级元素
如果你并不期望这种样式,可以通过修改配置文件关闭这种样式文件的引入。
- CSS 到 tailwind 快速转化技巧
一般情况下,Tailwind CSS IntelliSense 这个插件基本满足日常开发了,但是对一些你想批量转换的 CSS 样式,又不想一条一条手打的话,可以通过这个工具进行快速转换
https://transform.tools/css-to-tailwind
- tailwind 的字符串格式化技巧
使用原子化的过程中还容易导致的一个问题是,目前代码格式化工具对字符串之间的空格是无能为力的,这就导致了这样丑陋的 class 写在代码中,需要人工去调整空格间隙。
目前并社区并没有好的解决方案,虽然有相关提案的讨论,但我并不认为 ESlint 作为一个质量检测工具应该加入这些功能。
我目前解决方案是将原子化 class 填入数组,这样就可直接通过 Prettier 格式化了
- 不要为了原子化而去原子化
什么意思,但对于样式是否需要全局原子化,tailwind 提供了 apply 语法以供开发者封装一些自定义的原子化的样式,但是我并不建议这样做
apply 需要在样式文件里面注册 CSS 类
但是在 CSS 文件里面写一些原子化的 class 类名并不是一个很好的注意,与其这样,倒不如直接写样式属性封装 class。
使用 tailwind 并不意味着就不能再封装样式了,事实上,在表现一些父元素 hover、focus,子元素高亮的场景时,将这些表现单独封装到一个样式文件内部还是十分有必要的。
loadash
lodash 提供了日常开发常用的一些工具函数,例如 debounce、throttle、get 等,
跟 lodash 类似的工具包还有 ramda、underscorejs 等
我时常用的,比如对某些嵌套对象内部实现不清楚的,直接一个 get,也不用那么多条件判断与链式兜底了
虽然大部分函数大家自己手写也能实现,但是相比于其他同事实现的工具函数,用这种经过广泛的使用和测试的工具库更加值得信赖。
vue use
VueUse 是基于Composition API 的实用函数集合
比如 useFullscreen、useToggle、useStorage 都是一些十分好用的 hooks,这一些我也在我的开源项目 vue-tsx-admin 中进行了实践。
pinia-plugin-persistedstate
对于 pinia 下的某些全局数据,我既期望响应化,也期望持久化(浏览器 reload 全局状态不丢失),这样我就需要再使用 pinia 的时候还需要对某些状态进行持久化的管理,就以深色浅色主题色切换为例,除了写渲染状态切换逻辑外,还需要关注状态在 localStorage 的存取。
export enum LocalStorageKey {
localeKey = 'vtsc-locale',
applicationTheme='vtsc-theme'
}
export interface ApplicationState {
theme: ApplicationTheme
}
export enum ApplicationTheme {
lingt = 'light',
dark = 'dark'
}
const getSupportTheme = (