不以规矩,不能成方圆。
本人有幸经历了团队从缺乏标准到逐渐规范
的一个过程,在此当做记录供大家参考。
本文从为什么需要规范
以及建立规范的重要性
出发,逐步引申出如何去建立适合自己团队的规范,详细讨论了前端具体涉及到的规范都有哪些,部分小节有具体的配置步骤和操作链接。
文中涉及到的所有配置均放在github上的 Demo[1] 中,觉得不错的点个赞 ❤️❤️❤️。
下面我们开始吧。
规范能给我们带来什么好处,如果没有规范会造成什么后果?这里主要拿代码规范来说。
统一代码规范的好处:
可读性、可维护性、可复用性、可移植性和可靠性
,这会从根本上降低开发成本
,也是最重要的一点。更加易于维护
,因为团队内任何人都可以快速理解并修改。尽早发现问题,甚至完全预防问题
,这将提高整个交付过程的效率。若不统一代码规范,可能会造成的后果:
代码风格不一,增加团队成员间的心理负担
,极端情况下,某段代码只有某个人能修改(俗称屎山)。适应不同的风格,会导致效率低下
(阅读代码是我们花费时间最多的地方)。在这件事上,很难达成一致是我认为最重要的原因。并且,仅仅只是拥有规范也是不够的:
我曾想通过会议讨论的方式来制定规范,但效果却差强人意。将失败的原因总结为大致几点:
开发中依然有不少人选择无视规则
。提出若干点优化建议后,没有对问题的优先级和侧重点进行划分
,导致实际效果并不好。经历了上述的挫败之后,经过反复复盘总结,决定换一种方式来执行:
通过文档记录(wiki等),寻找业内最佳解决方案
,在团队内进行统一。按照优先级和重要性进行排序划分,依次将问题纳入迭代
,每个迭代重点解决其中几个即好。没有规则只是为了规则,制定规范的目的并不是一定要按照某某标准来执行
,更多的是团队成员达成一致即可。经过两个月的迭代后,发现效果出奇的好,大家的规范意识普遍增强,当遇到规范问题时也不再畏畏缩缩,而是大胆的抛出在群里讨论。
大致可以划分成这几个方向:
这里可能有小伙伴有疑问了,开发流程规范不是项目经理定的吗️,跟我有什么关系?
这里想告诉大家的是,开发流程在一定程度上应该是由我们自己来掌控。不管是传统开发的模式还是敏捷开发的模式,对于开发者来说核心依旧是高质高效的完成用户提出的需求
。
笔者曾见过不少开发者在拿到产品经理的需求后就开始急匆匆的写代码,以此来体现他们的高效
,但往往却因为需求理解不到位和前期代码欠缺设计导致bug率高和返工。
如何找到适合自己的开发流程是需要依靠经验来支撑的,需要反复总结和思考,最终达到高质高效完成
的目的。
说一说笔者自己比较喜欢的开发流程:
image.png
在接收到需求后应第一时间去了解这个需求的背景是什么?
这么做到底有没有解决用户的痛点?或者说用户更深层次的需求是什么?如果团队的产品经理经验不丰富,往往可以在这个阶段砍掉很多不合理的需求(这一点真的很重要)
。
对于复杂大功能往往还需要进行技术方案调研
和技术方案设计
,并输出详细的设计文档。涉及到细节上,则需要将数据流走向、组件设计等通过脑图的形式呈现出来。
由于每个开发者的IDE不同,即使IDE相同也会因为每个人的配置不一样导致格式化的结果不一样。如何确保团队内开发人员采用统一的格式化配置呢?
这里给推荐大家使用 prettier[2],它内置了一套格式化的规则,具体配置:
1). 安装依赖:
npm install --save-dev --save-exact prettier
// or
yarn add --dev --exact prettier
复制代码
2). 创建一个空配置文件,让编辑器和其他工具知道你正在使用 Prettier:
echo {}> .prettierrc.json
复制代码
3). 创建一个.prettierignore[3]文件,让 Prettier CLI 和编辑器知道哪些文件不能格式化,example:
# Ignore artifacts:
dist
build
coverage
复制代码
4). 配置编辑器(VScode为例)
IDE中安装 Prettier-Code Formater[4] 插件:
image.png
找到IDE中设置模块,搜索 format On Save,勾上这个就可以了。
image.png
现在当我们 Ctrl + S 保存代码时,插件就会帮助我们自动格式化了。
这里有小伙伴要问了,要是有人将没有格式化的代码提交上去怎么办?
这时候就需要在 git commit 的阶段自动将提交的代码进行格式化,这里我们借助工具 husky[5],它主要可以帮助我们在 git 阶段检查提交消息、运行测试、检查代码
等。没接触过的小伙伴可以去官网[6]了解一下,配置如下:
npm install --save-dev husky lint-staged
npx husky install
npm set-script prepare "husky install"
npx husky add .husky/pre-commit "npx lint-staged"
// or
yarn add --dev husky lint-staged
npx husky install
npm set-script prepare "husky install"
npx husky add .husky/pre-commit "npx lint-staged"
复制代码
package.json
中:{
"lint-staged": {
"**/*": "prettier --write --ignore-unknown"
}
}
复制代码
这段配置的意思是:当执行 git commit 阶段前,先执行lint-staged
,lint-staged
中的内容就是对暂存区的文件
执行格式化的命令。
其他:若使用的是脚手架工具搭建的项目,会自带eslint配置(eslintConfig
)。prettier 和 eslint 会有一些配置上的冲突
,这个时候需要安装eslint-config-prettier[9] 以使 ESLint 和 Prettier 相互配合,安装完后在.eslintrc
中配置(以Create-React-App为例):
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest",
"prettier"
]
},
复制代码
这样就可以用"prettier"
的部分规则覆盖前面的规则,让它们都能正常工作。
更多详情见:Prettier[10]。
JS/TS主流的大致有这几种:
比较推荐使用 Airbnb JavaScript Style Guide,它在 Github 上足有 12万 star,几乎覆盖了 JavaScript 的每一项特性。
具体配置:
1). 安装依赖
npm install eslint --save-dev
// or
yarn add eslint --dev
复制代码
2). 生成配置文件
npm init @eslint/config
// or
yarn create @eslint/config
复制代码
跟着终端中的提示,按照自身需求一步步选择即可。
有了具体的规范后,我们同样需要使用工具去约束:还是通过在git commit
阶段校验,若不通过则取消提交。
配置(还是在 package.json
中的 lint-staged
):
"lint-staged": {
"**/*": "prettier --write --ignore-unknown", //格式化
"src/*": "eslint --ext .js,.ts,.tsx" //进行eslint校验
},
复制代码
注意:这里如果选用的Typescript,则会默认使用
@typescript-eslint/parser
解析器,官方为了追求更快的解析速度,并不会对.ts文件中的类型进行检查,只会做语法检测。如果需要对类型也进行检测,需要在extends中加上plugin:\@typescript-eslint/recommended-requiring-type-checking[16]。
但是在笔者的使用中发现效果并不好,一些基本的类型依然检测不出来,索性这里换了另一种方式:在
pre commit
中执行yarn run tsc
,这里的意思是对项目中ts文件进行类型检测,默认会读取根目录中的tsconfig.json
配置。这种方式并不完美,它的弊端就在于
全量检测
,如果项目不大还好,若是项目代码量够多,检测10-20s也是常有的事。
更多详情查看eslint官网:eslint.org/docs/user-g…[17]
CSS检查代码规范使用 stylelint
插件,规范则推荐使用 stylelint-config-standard[18]:
1). 安装
npm install --save-dev stylelint stylelint-config-standard
复制代码
2). 在项目的根目录中创建一个配置文件.stylelintrc.json
,内容如下:
{
"extends": "stylelint-config-standard"
}
复制代码
3). 解决与prettier
配置的冲突:
npm install --save-dev stylelint-config-prettier
复制代码
4). 将下面配置复制到.stylelintrc.json
中:
{
"extends": ["stylelint-config-standard", "stylelint-config-prettier"]
}
复制代码
5). 在 git commitv阶段进行检测:
"lint-staged": {
"**/*": "prettier --write --ignore-unknown", //格式化
"src/**.{js,jsx,ts,tsx}": "eslint --ext .js,.jsx,.ts,.tsx", //对js文件检测
"**/*.{less,css}": "stylelint --fix" //对css文件进行检测
},
复制代码
下面列一些团队内定的其他规范:
(1)命名规范
变量的命名中应尽量减少缩写的情况发生,做到见名知意。
// 自我感觉良好的缩写:
let rContent = 'willen';
// 无需对每个变量都写注释,从名字上就看懂
let firstName = 'jackie';
// 从命名无法知道返回值类型
function showFriendsList() {....} // // 无法辨别函数意图,返回的是一个数组,还是一个对象,还是true or false?
// 明确函数意图,对于返回true or false的函数,最好以should/is/can/has开头
function shouldShowFriendsList() {...}
function isEmpty() {...}
function canCreateDocuments() {...}
function hasLicense() {...}
function sendEmailToUser(user) {.... } //动词开头,函数意图就很明显
复制代码
(2)写注释
在每个文件的顶部明确说明该组件做什么,有没有业务理解难点等等,对业务特殊函数/变量也需要写注释
/**
* 导航页面-右边区域
*/
const Content=>()=>xxx
const MAX_INPUT_LENGTH = 8; //用于限制密码输入框
function Component(props) {
return (
<>
{/* 如果用户没有订阅则不展示广告 */}
{user.subscribed ? null : <SubscriptionPlans />}
</>
)
}
复制代码
(3)变量兜底
// 对于求值获取的变量,没有兜底
const { data } = getApiRequest();
data.map((s) => s.id); //没有考虑data异常的情况,代码一跑就爆炸
// 对于求值变量,做好兜底
const { data = [] } = getApiRequest();
data.map((s) => s?.id); //没有考虑data异常的情况,代码一跑就爆炸
复制代码
(4)辅助函数必须是纯函数
// 不要让功能函数的输出变化无常
function plusAbc(a, b, c) { // 这个函数的输出将变化无常,因为api返回的值一旦改变,同样输入函数的a,b,c的值,但函数返回的结果却不一定相同。
var c = fetch('../api');
return a+b+c;
}
// 功能函数使用纯函数,输入一致,输出结果永远唯一
function plusAbc(a, b, c) { // 同样输入函数的a,b,c的值,但函数返回的结果永远相同。
return a+b+c;
}
复制代码
(5)优先使用函数式编程
// 使用for循环编程
for(i = 1; i <= 10; i++) {
a[i] = a[i] +1;
}
// 使用函数式编程
let b = a.map(item => ++item)
复制代码
(6)优先使用函数式组件
除非需要用到错误边界,否则函数式组件应该是首选方法。
(7)组件复杂度
如果一个组件做的事情太多,应适当提取一些逻辑,将其拆分为更小的组件。
如果提取的组件很复杂,则需要依照一定的规则和条件一一提取它。
代码行数并不是一个客观的衡量标准,更多是需要考虑责任划分和抽象。
(8)用错误边界
当需要对大量数据进行渲染处理时,需要通过错误边界组件对其进行降级处理。
更多详情见之前写的专题文章:浅析前端异常及降级处理[19] 。
function Component() {
return (
<Layout>
<ErrorBoundary>
<CardWidget />
</ErrorBoundary>
<ErrorBoundary>
<FiltersWidget />
</ErrorBoundary>
<div>
<ErrorBoundary>
<ProductList />
</ErrorBoundary>
</div>
</Layout>
)
}
复制代码
(9)props参数传递
props一层层传递一直是我们很头疼的一个问题,最核心的问题是不清楚props是从哪个初始组件传来的,以及props中到底有哪些东西,上下文是什么?
因此对于传递较深的场景我推荐直接使用 context,对于 props 中的内容和上下文通过 TS 来解决。
// A.tsx
interface AProps {
param: string;
}
const A = ({ param }: AProps) => {
return <B param = {param} />;
};
// 上下文清晰
// B.tsx
const B = ({ param }: { param: AProps['param'] }) => {
return <div>hello world</div>;
};
复制代码
(10)props传参数量
如果超过 5 个props,就该考虑是否拆分该组件。在某些情况下,这是需要对组件进行重构的标志。
注意:组件使用的props越多,重新渲染的理由就越多。
(11)避免嵌套三元运算符
三元运算符在第一级之后变得难以阅读,虽然看起来节省了代码空间,但最好在代码中明确意图,保持良好的阅读性。
// 不够清晰,要是再嵌套一层两层呢
isSubscribed ? (
<ArticleRecommendations />
) : isRegistered ? (
<SubscribeCallToAction />
) : (
<RegisterCallToAction />
)
// 将判断逻辑进行拆分
function CallToActionWidget({ subscribed, registered }) {
if (subscribed) {
return <ArticleRecommendations />
}
if (registered) {
return <SubscribeCallToAction />
}
return <RegisterCallToAction />
}
function Component() {
return (
<CallToActionWidget
subscribed={subscribed}
registered={registered}
/>
)
}
复制代码
(12)将列表组件封装成独立组件
// 列表渲染和其他逻辑杂糅在一起
function Component({ topic, page, articles, onNextPage }) {
return (
<div>
<h1>{topic}</h1>
{articles.map(article => (
<div>
<h3>{article.title}</h3>
<p>{article.teaser}</p>
<img src={article.image} />
</div>
))}
<div>You are on page {page}</div>
<button onClick={onNextPage}>Next</button>
</div>
)
}
// 将列表组件提取出来,一目了然
function Component({ topic, page, articles, onNextPage }) {
return (
<div>
<h1>{topic}</h1>
<ArticlesList articles={articles} />
<div>You are on page {page}</div>
<button onClick={onNextPage}>Next</button>
</div>
)
}
复制代码
(13)避免嵌套渲染函数
// 不要将其定义在渲染函数组件中
function Component() {
function renderHeader() {
return <header>...</header>
}
return <div>{renderHeader()}</div>
}
// 将其抽离到独立的组件中去
import Header from '@modules/common/components/Header'
function Component() {
return (
<div>
<Header />
</div>
)
}
复制代码
(14)组件/函数导入导出
// 在文件头部导入,顺序依次为: 第三方库 > 公共组件/方法 > 非公共部分组件/方法
import React from 'react'
import _ from 'loadsh'
import Header from '@components/header'
import Content from './Content'
// 在底部导出
export { Content, Header }
export default Component
复制代码
...
不得不说,在这件事上我们团队曾经花费了大量的经历去思考和实践,也是信了React官方的鬼话:不要花超过五分钟在选择项目文件组织结构上[20]。
在项目初期若不重视,到了后期就是到处天马行空,你很难在期望的目录下找到你想要的文件。
文件夹名称全部采用小写 + "-" 来隔开,index.ts更多是用来做导出作用,要不然最后编辑器中满屏的index.tsx,很难区分。
- src 开发目录
- pages 视图
- module-a 模块A
- components 私有组件
- ComA.tsx
- ComB.tsx
- index.module.less
- index.tsx
- Content.tsx
- module-b 模块B
- components 公共组件
- index.ts 导出所有组件
- header
- index.tsx
- index.module.less
- User.tsx
- useGetBaseInfo.hooks.ts
- routers 路由文件
- store redux中的数据
- utils 这里是以utils为后缀
- index.ts
- a.utils.ts
- b.utils.ts
- hooks 这里是以hooks为后缀
- index.ts
- a.hooks.ts
- b.hooks.ts
- styles 静态资源文件
- service api请求,这里是以api为后缀
- a.api.ts 按照后端微服务进行划分
- b.api.ts
- constans 常量
复制代码
通过对工具函数、hooks、api 等加上后缀,更加容易区分引入的文件。
git commit 规范主要可以帮助开发人员在 code review 期间更容易理解提交的内容,现在大部分主流 commit 规范都是基于Angular 团队的规范[21]而衍生出来的,它的 message 格式如下:
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
复制代码
每个提交消息都包含一个subject、一个body和一个footer (中间使用空行分割),提交信息的任何一行不能超过 100 个字符。
type主要有以下几种类型:
scope:可以是影响范围的任何内容。
subject:包含对更改的简洁描述,规则:
body:commit 具体修改内容, 可以分为多行,应该包括改变的动机,并与以前的行为进行对比。
footer: 一些备注, 通常是修复的 bug 的链接。
截取一张开源库的 commit,example[22]:
image.png
有了规范后,我们需要通过工具去约束:commitlint[23]。它要做的就是在我们每次提交 git commit
的时候,都会帮我们检查 commit message
是否符合一定的规范,如果不符合,就让这次提交失败。
具体配置:
# 安装 commitlint cli 和 conventional config
npm install --save-dev @commitlint/{config-conventional,cli}
# Windows:
npm install --save-dev @commitlint/config-conventional @commitlint/cli
配置要使用的 commitlint 规则
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
加入到husky中:
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
or
yarn husky add .husky/commit-msg 'yarn commitlint --edit $1'
复制代码
更多详情见官网:github.com/conventiona…[24]
优秀的开发者应该反向推动UI的规范,并且能够支撑UI规范的落地。
UI 规范的最大好处就是能够提质提效:
那到底应该怎么去推动UI规范?我的做法是先让设计师去给出一系列的规范,没有相关规范就拉上产品经理一起制定规范。然后前端建立一套自己的组件库,再将组件库提供给UI设计师,以此来相互监督是否达成了规范协议。
具体参考 Antd设计规范[25]。
统一规范的最根本目的是为了保证团队成员的一致性,从而减少沟通成本,提高开发效率。
学会热爱标准,但要确保它们不是一成不变的。如果制定的规范不适合您的团队,请确保可以适应和重写所需要的任何规则。它并不是要强制执行一种工作方式,而是为了帮助促进团队之间的互动 。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/E0aJhEuo0dgNqCQ3E-B7CQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。