背景
目前所在业务线的前端 js 代码居多,且没什么规范,效率实在是太低了,蚌埠不住了,是时候改一改了
前言
程序开发规范,是软件工程中确保代码质量和团队协作效率的重要一环。通过规范,提升软件产品的稳定性和可靠性,降低维护成本,同时也有助于团队成员间的沟通交流和知识传承。
规范的优缺点
优点:
- 保障代码交付质量
- 降低沟通成本、阅读成本、理解成本
- 降低重构/迁移成本
缺点:
- 对团队成员的自觉性要求较高
- 对团队成员的技术水平有一定的要求
规范可分为
- 可控规范:可通过一些lint工具规范化
- 不可控规范:无法通过工具检测到的,eg:变量名,文件名,设计模式
规范方向
- 文件(夹)划分规范
- 文件(夹)命名规范
- git规范:commitlint
- lint规范:eslint,prettier,stylelint
- 代码规范
- ts规范
看个现状
改造前
改造后(还有优化空间,就打个样,是不是清晰了很多)
状态/类型的写法(每个类型用到的地方都要写一遍注释)
推行TS
官网对它的定义是:A Static Type Checker(静态类型检查器)
TypeScript is also a programming language that preserves the runtime behavior of JavaScript. TypeScript never changes the runtime behavior of JavaScript code.
译:TypeScript 也是一种保留了 JavaScript 运行时行为的编程语言。TypeScript 永远不会改变 JavaScript 代码的运行时行为
也就是说,即使 TypeScript 认为代码存在类型错误,也保证 js 以相同的方式运行。
定位
我把 ts 定位为代码规范的基础辅助工具,使用 ts 旨在提高开发效率和代码质量。
目的
- 类型提示
借助 IDE 工具的提示功能,更好更快的写代码
- 安全编程
当调整类型时,ts 通过类型报错将代码问题从运行时前置到编译时,大大降低 bug 率
- 规范代码
强类型的定义能够更好的规范代码逻辑,在大型项目中,规范比灵活更重要
- 降低阅读、维护、拓展成本
字段类型的定义,让开发人员能更高效的识别、修改、溯源
- 提高渐进式重构的安全性和效率
在代码重构阶段,类型检查能极大地保障代码的安全迁移调整
- 工具链基石
众多工具通过类型定义将第三方库联系起来,极大地降低了工具开发成本
开发视角的区别
js:聚焦于功能的实现,弱化了类型定义和入参设计
ts:聚焦于程序的设计,加强了类型定义和入参设计
TS学习推荐文章
文件夹分类
src
├───📁 api/
│ └───📁 common/ # 维度跟 router 保持一致
│ ├───📄 index.ts
│ └───📄 type.ts
├───📁 assets/
│ ├───📁 images/
│ └───📁 styles/
├───📁 components/
│ ├───📁 base/ # 公共基础组件
│ │ └───📁 LayoutPro/ # 文件名以大驼峰命名
│ │ ├───📄 index.scss
│ │ └───📄 index.tsx
│ └───📁 business/ # 公共业务组件
│ └───📁 OrgSelect/ # 文件名以大驼峰命名
│ ├───📄 index.scss
│ └───📄 index.tsx
│
├───📁 constants/
├───📁 hooks/
│ └───📄 useTableRequest.ts # hook以use开头,小驼峰命名
├───📁 mock/
├───📁 pages/
│ └───📁 Home/ # 维度跟 router 保持一致,文件名跟组件一致:大驼峰
│ ├───📄 index.scss
│ └───📄 index.tsx
├───📁 router/ # 以左侧一级菜单维度拆分
│ ├───📄 custom.ts
│ ├───📄 shopping.ts
│ └───📄 index.ts
├───📁 stores/
├───📁 types/ # 全局 ts
└───📁 utils/ # 文件以功能命名,eg:password.ts,day.ts, card.ts
代码规范
我们定义了一些命名规则来区分常规命名和统一认知
ts
- interface:大写 i 开头,大驼峰命名,eg:IProps
- type:大写 t 开头,大驼峰命名,eg:TPages
- enum:大写 e 开头,大驼峰命名,eg:EStatus
- 状态/类型的定义须用枚举定义,eg:EPageType, EOrderStatus
- 通常情况下使用 interface 定义类型,interface 不满足的情况下再使用 type
- type文件不写 js 代码
- 尽可能不写 any 类型
命名
- 常量名命名:全大写,下划线间隔
- 组件参数接口命名:以Props为后缀,eg:ITableProps
- 监听事件以命名:以handle为前缀,eg:handleClick
- 请求接口命名:小驼峰命名,以Api为后缀,eg:getListApi
- 请求接口参数/返回值命名:大驼峰命名,参数以Req为后缀,返回值以Res为后缀,eg:IGetListReq,IGetListRes
- 变量名尽可能的知名达意,eg:type ❌,pageType✅
代码
- 禁止使用嵌套的三元表达式
- 组件代码不允许超过 500 行(原本想定不超过300行的,考虑到成员水平,最终定为500行)
- 当参数大于3个时,须改造成对象传参(这一点在团队内分歧较大,因为成员的开发习惯是先写,参数不够了再加,而不是一开始就尽可能的考虑全面,所以加了后面这段话)除非函数的功能足够内聚和颗粒化,且参数含义清晰、参数结构简单稳定,可以保持使用单独参数。
- 不写class类组件
- 推荐代码层级:数据 -> 接口 -> 方法 -> 副作用 -> DOM(any: 看不见我,看不见我,走位~ 走位~)
css
- className全小写,以“_”连接
- 公共组件className以“组件名_”为前缀,不使用嵌套写法(方便覆盖样式)
- 尽可能不写内联样式,保障dom干净度
lint库选择
eslint,prettier:alloy
stylelint:stylelint-config-standard
为什么选择 alloy 做 lint 库,主要有以下几个原因:
- 理念:秉承着专业的事交给专业的库来做,alloy 将 eslint 和 prettier 区分开,各自负责相应的工作
- 即时:通过 GitHub Actions 即时更新版本,紧跟最新 eslint 版本,至于项目中什么时候更新,那决定权在自己手中
- 文档:可以通过 alloy 在线网站文档,随时查询规则
- 迁移:技术永远在迭代,今天是 eslint,明天是 oxlint,在后续工程化持续迭代的情况下,如何降低迁移的摩擦成本是一大考量,在保障代码规范的情况下,定义最少的规则,是最小的成本
详情可查看:github.com/AlloyTeam/e…,特别是“设计理念”下的“详细说明”
填坑
以上我们定义了不少规范,从lint规则、文件划分、命名规则等多个角度切入,规范如何落地也带了以下几个问题:
- 如何切入
- 如何降低规范上手成本
- 如何保障规范成效
解决办法:
- 挑选部分功能较少且较常维护的项目为试点项目,培养组员规范意识,后续再全面落实
- 因为我们全员用的都是 vscode,所以借助vscode的 snippets 能力,提供代码片段、页面模板等功能,在代码编程维度上降低组员对命名规则的上手成本,提高开发效率。这边推荐一个在线生成 snippets 代码的网站
- 在 code review 环节上前期加强对规范的审查意识,相互督促,在大家都有规范意识后,就可以着重 CR 代码逻辑和设计思路了