前言
什么是规范?
规范,名词意义上:即明文规定或约定俗成的标准,如:道德规范、技术规范等。 动词意义上:是指按照既定标准、规范的要求进行操作,使某一行为或活动达到或超越规定的标准,如:规范管理、规范操作。
为什么需要规范?
- 降低新成员融入团队的成本, 同时也一定程度避免挖坑
- 提高开发效率、团队协作效率, 降低沟通成本
- 实现高度统一的代码风格,方便review, 另外一方面可以提高项目的可维护性
- 规范是实现自动化的基础
- 规范是一个团队知识沉淀的直接输出
技术栈规范
三大框架跟编程语言一样都有自己的设计哲学,这跟库是不一样, 一个库的替换成本很低;而框架的背后是一个架构、一个生态。每个框架背后牵涉着开发思维、生态系统、配套工具、最佳实践、性能调优。要精通和熟练一个框架需要付出的成本是很高。
编程语言
Javascript
前端基础框架
Vue.js
风格直接参照官方的Vue特有代码的风格指南,这是为了在工程中使用Vue,可以回避错误,小纠结和反模式 Vue.js风格指南
生态
- 路由,vue-router
- 状态管理 ,vuex
- 异步处理,axios
前端UI框架
ElementUI
首推饿了么技术支持的PC的后台管理系统UI框架,2020年3月10日在github的star上面44.1k,远远关注度超过9.5k的antDesignVue,加上组件的数量54个左右,功能的完善性是好于组件数量57个的antDesignVue,都要不停地维护这个组件库 组件库开发指南
开发工具
vscode
代码规范检查工具:Eslint
代码格式化工具:Prettier - 🔥 关于代码格式化的所有东西都交给它吧!
包管理工具
npm
工作流规范
开发版本号规范
例子:v3.2.1
3主要版本号:有API变更导致不兼容旧版本的时候使用;
2次要版本号:新增功能,但是向前兼容的情况不使用;
1补丁:修复向前兼容的bug时使用
版本控制规范
使用git作为版本管理工具,涉及多人并发协作,需要管理多个软件版本的情况下,定义良好的本本库管理规范,可以让大型项目更有组织性,也可以提高成员的协作效率
参照和直接使用🥑git-flow工作流的方式来制定自己的git工作流的规范
master 只能用来包括产品代码,你不能直接在工作在这个master分支上,而是在其他指定的,独立的特性分支中。不能直接提交改到master分支上也是很多工作流程的一个共同的规则
develop 是你任何新的开发的基础分支,当你开始一个新的功能分支时,它将是开发的基础。另外,该分支也汇集所有已经完成的功能,并等待被整合到master分支中
feature/XXX 做一个新功能的时候,创建一个名为”feature/XXX”分支(在做新功能开发时候使用一个独立的分支是版本控制中最重要的规则之一),等待功能完成时,整合到”develop”分支中去,并删除当前功能分支
release/3.2.1 当你认为现在”develop”分支的代码已经是一个成熟的release版本时,这意味着:第一,它包括所有新的功能和必要的修复;第二,它已经被彻底的测试过了。满足上述两点,那就可以生成一个新的release了(使用版本号命名的),完成后合并到“master”和“develop”
hotfix/XXX 当对 release 版本作做全面测试时,可能就会发现一些小错误。你要创建一个名为“hotfix/XXX”的分支,因为这是对产品的代码进行修复和你不应该在一个还不完全稳定的开发分支上对产品进行代码修复,所有要基于”master”分支去创建。完成后,合并到“master”和“develop”,删除该hotfix分支
提交信息规范
- 格式统一的提交信息有助于自动化生成CHANGELOG
- 版本库不只是存放代码的仓库,它记录项目的开发日志,也应该清晰表达这次提交的做了什么(这些记录可以帮助后来者快速学习和回顾代码,也应该方便其他协作者review你的代码)
- 规范化提交信息可以促进提交者有意义的,粒度合适的提交,提交者要想好怎么描述这个提交
commitizen - 🔥简单的提交规范和提交帮助工具,推荐
构建规范
采用vue-cli的脚手架去构建项目,🔥零配置、渐进增强的项目构建CLI
- 这类工具现在是推崇“约定大于配置”,按照他们的规范,可以实现开箱即用,快速开发业务,在团队协作中这点很重要,我们不推荐团队成员去关心又臭又长的webpack构建配置
- vue-cli3抽离了cli server层,可以独立更新工具链。也就是说,项目的构建脚本和配置在一个独立的service项目中维护,而不是像以前一样在每个项目目录下都有webpack配置和依赖,这样子做的好处是独立地,简单地升级整个构建链
- 灵活的插件机制,对于团队的定制化构建应该封装到插件中,这样也可以实现独立的更新
构建链应该还具有以下特点:
- 强约定,体现团队的规范,首先避免团队成员去关心或者更改构建的配置细节,暴露最小的配置接口,另外构建工具不仅仅是构建,通常它还会集成代码的检查,测试功能
- 方便升级,尤其是团队需要维护多个项目场景,这一点很有意义
发布工作流规范
发布工作流指的是将“软件成品”对外发布(如测试或生产)的一套流程,将这套流程规范化后,可以实现自动化 一个典型的发布工作流:
- 代码变更
- 提交代码变更到远程版本库
- 程序通过CI测试(例如Travis变绿)
- 提升package.json中的版本
- 生成CHANGELOG
- 提交package.json和CHANGELOG.md文件
- 打上tag
- 推送
如果遵循上面的规范,可利用社区上现有工具来自动化这个流程:
- conventional-changelog-cli
- conventional-github-releaser
- 实际上自己开发一个也不是特别难的事情
持续集成
将整套开发工作流确定下来之后,就可以使用> 持续集成服务> 来自动化执行整个流程
比如一个典型的CI流程
_
_![](https://cdn.nlark.com/yuque/0/2020/png/228922/1584066813078-5bc738d2-c5bf-4bfc-8a16-3775781e072f.png#align=left&display=inline&height=383&originHeight=383&originWidth=993&size=0&status=done&style=none&width=993)<br />**持续集成的好处:**
- 尽早发现错误,快速试错,越早发现错误,处理错误的成本越低
- 自动化工作流,减少人工干预,人类比机器容易犯错,而且机器擅长做重复的事情
对于持续集成规范一般会定义这些内容:
- 执行的环境;比如容器,node版本,操作系统等等
- 触发的条件;比如定时触发,在哪个分支触发,会触发什么任务等等
- 执行任务
- 划分持续集成的阶段,比如:
- 检查:包括单元测试和代码lint,所有push到版本库的代码都会跑这个阶段,一般可以在提交title中包含【ci skip】来跳过这个阶段
- 构建:对前端项目进行构建,只有打上版本tag的提交或release分支跑构建任务
- 发布:将前端的构建结果进行交付/发布,只有打上tag的提交或者release分支在构建成功后会跑发布任务
- 定义持续集成脚本模板
工具
Github:Travis CI 只要是 Github 上的开源项目全部免费,且支持在 OS X 运行
通用:Jenkins 🔥推荐,有很多插件可以支持,非常强大
任务管理
看板是目前最为流行的任务管理工具,它可以帮助我们了解项目的进度,资源的分配情况,还原开发现场
- 基于issue看板,可以基于GitHub或Gitlab的issue来做任务管理,它们都支持看板
- teambition - 挺好用,卡片式,支持导出数据,和统计人员的数据,但是免费版的话,只支持9个人的加入
- Trello - 颜值高.
项目组织规范
通用的项目组织规范
目录组织的风格
脚手架和项目模板
编码规范
Javascript
- Lint工具:ESLint - 🔥目前是社区最流行的、通用的Javascript Lint工具,Lint界的Babel。支持定制插件、preset。如果不想折腾可以选择它的一些预定义配置
- 规范:JavaScript Standard Style - 🔥 零配置的、‘标准’的Javascript编码规范. 底层基于Eslint
Html
- 标签注意语义化,规范化
- 规范:Code Guide
Css
- Lint工具:stylelint - 🔥 通用的CSS编码检查工具,支持最新的CSS语法、CSS-in-js、以及其他类CSS语法(如SCSS、Less). 它也有预定义配置,推荐使用
- 规范:Code Guide
- 命名格式:BEM - 🔥 BEM命名规范
Code Review
很多代码设计的‘最佳实践’是无法通过具象化的自动化工具或文档覆盖的, 这时候,’经验’或者’群体智慧’就派上用场了
- 编程原则、设计思想. 例如符合SOLID原则? 是否足够DRY?接口设计是否简洁易扩展、
- 模块耦合程度、代码重复
- 代码健壮性。是否存在内存泄露、是否线程安全、是否有潜在性能问题和异常、错误是否被处理
- 代码的性能和效率。
- 是否有没有考虑到的场景?
Code Review有两种方式: 一个提交时、一个是定时:
- 提交时, 大部分开源项目采用这种方式。通俗讲就是Pull Request。只有代码通过测试、和其他成员的Review才可以合进正式版本库。这种方式也称为‘阻塞式’代码检查,一般配合GitFlow使用。
- 定时. 在项目完结后、项目的某个里程碑、或者固定的时间(每天、每个星期..). 团队成员聚在一起,回顾自己写的代码, 让其他成员进行审查
测试规范
测试是保障代码质量的重要手段,但是很少有人愿意在这里花太多时间。
比如笔者,我很少会去给业务代码和组件写单元测试,除非自己对代码非常没有信心,按照我的理念写测试不如将代码写得更简单一点,比如把一个函数拆分为更小的函数,保持单一职责。
但是对于一些底层、共享的代码模块还是有测试的必要的。
前端开发者需要关注的主要有一下几种测试类型:
- 单元测试:对独立的团建模块进行测试
- UI组件测试:包括了快照(snapshot)测试
- 集成测试:在单元测试的基础上,将模块组合起来,测试它们的组合型
- E2E测试:在完整,真实的运行环境下模拟真实用户对应用进行测试,主要测试前端和后端的协调性
- 兼容性测试:上面提到了浏览器兼容规范,在将版本提交给测试/发布之前,需要确保能符合兼容性要求
- 性能测试: 测试和分析是否存在性能问题
- 其他:
- 安全测试
- SEO测试
测试的流程
首先要定义一个合适的软件测试流程, 合适的测试流程可以降低开发和测试团队之间的沟通协作成本、提高测试效率。例如我们可以参考测试流程:
_
_![](https://cdn.nlark.com/yuque/0/2020/png/228922/1584066813160-2e5f9315-40f1-4fbe-b58f-760fdee1a7e7.png#align=left&display=inline&height=337&originHeight=337&originWidth=940&size=0&status=done&style=none&width=940)
单元测试
单元测试有很多好处:
- 提高信心,适应变化和迭代,如果现有代码有较为完善的单元测试,在代码重构时,可以检验模块是否依然可以工作,一旦变更导致错误,单元测试也可以帮助我们快速定位并修复错误
- 单元测试是集成测试的基础
- 测试即文档。如果文档不能解决你的问题,在你打算看源码之前,可以查看单元测试。即通过这些测试用例,开发人员可以直观地理解程序单元的基础API
- 提升代码质量,易于测试的代码,一般都是好代码
业务代码或业务组件是比较难以实施单元测试的,一方面他们比较多变,另一方面很多团队很少有精力维护这部分单元测试,所以通常只要求一些基础/底层的组件,框架或者服务进行测试,视情况考虑是否要测试业务代码
测试准则:
- 推荐Petroware的Unit Testing Guidelines, 总结了27条单元测试准则,非常受用
- 另外<阿里巴巴的Java开发手册>中总结的单元测试准则, 也不错,虽然书名是Java,准则是通用的
单元测试指标:
一般使用测试覆盖率来量化,尽管对于覆盖率能不能衡量单元测试的有效性存在较多的争议
大部分情况下,还是推荐尽量提高覆盖率,比如要求,语句覆盖率达到70%,核心模块的语句覆盖率和分支覆盖率都要达到100%,视团队情况而定
相关工具
- Headless Browsers: 无头浏览器是网页自动化的重要运行环境。 常用于功能测试、单元测试、网络爬虫
- 测试框架
- 单元测试
- 断言库
- Mock/Stubs/Spies
- 代码覆盖率
- 基准测试
异常处理,监控和调试规范
很多开发者常常误用或者轻视异常的处理,合理有效的异常处理可以提高应用的健壮性和可用性,另外还可以帮助开发者快速定位异常 异常处理
<阿里巴巴的Java开发手册>中总结的异常处理规范对JavaScript的异常处理也很有参考意义,比如:
- 异常不要用来做流程控制,条件控制。
- 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
- catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理。不要对大段代码进行try-catch
- …
然后在根据Javascript本身的异常处理特点总结一些规范行为,例如:
- 不要throw非Error对象
- 不要忽略异步异常
- 全局监控Javascript异常
- …
资源
日志
对于前端来说,日志也不是毫无意义(很多框架性能优化建议在生产环境移除console),尤其是在生产现场调试代码时,这时候可贵的控制日志可以帮助你快速找到异常的线索
通常我们只要保留必要的,有意义的日志输出,比如你不应该将console.log()放到一个Vue渲染函数中,或者放到一个循环中,DDos式的日志信息并不能帮助我们定位问题,反而会影响运行的性能,所以需要一个规范来约束日志输出行为,比如:
- 避免重复打印日志
- 谨慎地记录日志,划分日志级别。比如生产环境禁止输出debug日志,有选择地输出info日志
- 使用前缀对日志进行分类,例如:[User] xxxx
- 只记录关键信息,这些信息可以帮助你诊断问题
- …
扩展资源
异常监控
因为程序跑在不受控的环境,所以对于客户端应用来说,异常监控在生产环境是非常重要的,它可以收集各种意料之外生产环境问题,帮助开发者快速定位异常
异常监控通常会通过三种方式来收集异常数据
- 全局捕获。例如使用window.onerror,或者unhandledrejection
- 主动上报。在try/catch中主送上报
- 用户反馈。比如弹窗让用户填写反馈信息
和日志一样,不是所有异常都应该上报给异常监控系统,譬如一些预料之内的异常,比如用户输入错误,鉴权失败,网络错误等等;异常监控主要用来上报一些意料之外的,或者致命性的异常
要做好前端的异常监控其实并不容易,它需要处理这些东西:
- 浏览器兼容性
- 碎片收集(breadcrumbs)。收集灾难现场的一些线索,这些线索对问题诊断很重要,例如当前用户信息,版本,运行环境,打印的日志,函数调用栈等等
- 调用栈的转换。通常在浏览器运行的压缩优化过的代码,这种调用栈基本没什么可读性,通用需要通过SourceMap映射到原始代码,可以使用这个库: source-map
- 数据的聚合。后端监控系统需要对前端上报的信息进行分析和聚合
对于小团队未必有能力开发这一套系统,所以推荐使用一些第三方工具。例如:
扩展
前后端协作规范
前端是web的一个细分领域,往往不能脱离后端而存在,所以和后端协作的时间是最长的 协作流程规范 前后端团队经过长期的合作,一般可以总结出一条对于双方开发效率最优的协作流程,将这个落实为规范,后面的团队成员都遵循这个步调进行协作
一个典型的前后端协作流程如下:
- 需求分析。参与者一般有前后端,测试,以及产品,由产品主持,对需求进行宣贯,接受开发和测试的反馈,确保大家对需求有一致的认知
- 前后端开发讨论。讨论应用的一些开发设计,沟通技术点,难点,以及分工问题;
- 设计接口文档。可以由前后端一起设计;或者由后端设计,前端确认是否符合要求
- 并行开发。前后端并行开发,在这个阶段,前端可以先实现静态页面;或者根据接口文档对接进行Mock,来模拟对接后端接口
- 在联调之前,要求后端做好接口测试
- 真实环境联调。前端将接口请求代理到后端服务,进行真实环境联调
接口规范
首先应该确定下来的是接口规范。其实使用哪种接口标准是其次,重要的是统一,且要满足前后端的开发效率要求
不建议去定义自己的接口标准,而应该去选择一些通用的,有标准定义接口形式
- RESTful: RSETful是目前使用最为广泛的API设计规范,基于HTTP本身的机制来实现
个人是比较喜欢这个API规范,但是我发现很多开发者并不能真正(或者说没心思)理解它,设计出来的接口,跟我想象的相差甚远。换句话说,对于RESTful,开发者之间很难达成一致的理解,容易产生分歧
因为是使用最广泛的API形式,所以社区上有很多工具对RESTful接口进行文档化,测试和模拟
- JSONRPC 这是一种非常简单、容易理解的接口规范。相对于RESTful我更推荐这个,简单则不容易产生分歧,新手也可以很快接受
- GraphQL 🔥更为先进、更有前景的API规范。但是你要说服后端配合你使用这种标准可能很有难度
接口设计需要注意的点:
- 明确区分是正常还是异常,严格遵循接口的异常原语,上述接口形式都有明确的异常原语,比如JSONRPC,当出现异常时应该返回错误对象响应,而不是正常的相应体重返回错误代码,另外要规范化的错误码,HTTP响应码就是一个不错的学习对象
- 明确数据类型。很多后端写的接口都是string和number不分的,如果妥协的话,前端就需要针对这个属性做特殊处理,这也可能是潜在的bug
- 明确空值的意义,比如在做更新操作是,空值是表示重置,还是忽略更新?
- 响应避免冗余的嵌套
- 接口版本化,保持向下兼容。就像我们上文的“语义化版本规范”说的,对于后端来说,API就是公共的接口,公共暴露的接口应该有一个版本号,来说明当前描述的接口做了什么变动,是否向下兼容。现在前端代码可能会在客户端被缓存,例如小程序,如果后端做了break change,就会影响这部分用户
接口文档规范
后端通过接口文档向前端暴露接口相关的信息。通常需要包含这些信息:
- 版本号
- 文档描述
- 服务的入口,例如基本路径
- 测试服务器(可选)
- 简单使用示例
- 安全和认证
- 具体接口定义
- 方法名称或者URL
- 方法描述
- 请求参数及其描述,必须说明类型(数据类型,是否可选等)
- 响应参数及其描述,必选说明类型(数据类型,是否可选等)
- 可能的异常情况,错误代码,以及描述
- 请求示例(可选)
人工维护导致的问题:
上文“代码即文档”就提到了人工维护接口文档可能导致和文档不同步问题
如果可以从代码或者规范文档(例如OpenAPI这类描述规范)中生成接口文档,可以解决时限和文档不一致问题,同时也可以减少文档编写和维护的投入
接口测试与模拟
为了做到高效率的前后端并行开发,接口的测试与模拟是必要的
- 前端要求后端在联调之前,需要测试验证好自己的接口是否可以正常工作。而不是在联调间,把前端当“接口测试员”,阻塞接口联调进度
- 另外前端需要在后端接口未准备好之前,通过接口模拟的方式,来编写业务逻辑代码
针对接口测试与模拟,存在下图这样一个理想模型:
一切从定义良好的接口文档出发,生成Mock Server和Mock Client, Mock Server给前端提供模拟数据,而Mock Client则辅助后端对它们的接口进行测试.
资源:
RESTful
- Swagger 这是最为接近上面理想模型的一个解决方案
- JSON Server 快速生成JSON mock服务器
- Easy Mock 可视化的、在线的接口mock服务
GraphQl
模拟数据生成
培训/知识管理/技术沉淀
我觉得一个团队的知识管理是非常重要的,你要问一个刚入行的新手加入团队希望得到什么?很多人回答是“学习”,希望自己的技术可以更加精进,钱倒还是其次
然而现实是目前很多公司的氛围并不是这样的,一天到晚写业务代码,工作量大,每天做重复的事情,而且还加班,工作多年技术也没有感觉有多少进步,确实会让人非常沮丧(说的就是自己)
所以为了改善这种情况,我来聊聊最近“小团队”做的一些尝试
新人培训
如果团队有规范的新成员培训手册,可以节省很多培训的时间,避免每次重复口述一样的内容。
培训手册包含以下内容:
- 产品架构与组织架构,介绍公司背景和产品,一般组织的团队结构和产品的架构是相关联的
- 产品研发流程,介绍产品开发和迭代会涉及到的流程,以及团队之间的协作协调
- 工作范围:团队成员的职责范围
- 建立资源索引:开发需要设计到的资源,比如各种文档地址,研发系统入口(例如gitlab,bug跟踪系统,文件共享,发布平台,开发/测试环境,监控系统),协作规范等等。将这些资源整理好可以减少不必要的沟通成本
- 规范:即本文的主体“前端协作规范”。有规范可循,可以让成员以较快的速度入手开发,同时也减少培训成本投入
培训手册将可以文档具象化的内容整理为文档,和上文说的Code Review一样,一些东西无法通过文档来说明,所以我们一般会搭配一个“培训导师”,在试用期间,一对一辅导
营造技术氛围
- 鼓励成员写博客,或者建立自己的团队专栏,写一篇好的文章不容易
- 鼓励参与开源项目
- 建立面试题库,组织一起解一些面试题或算法题,加深对知识点的理解
定期的专题分享,鼓励团队成员定期进行专题学习和研究,编写技术博客,并将学习的成果分享给其他成员,这是一种抱团取暖的学习方式,旨在帮助团队成员一起学习和成长
比如开发老手可以分享自己的经验,研究更深层次的技术;新手则可以研究某些开发技巧,新技术,例如css grid,SVG动画等等。推荐团队成员有个明确的研究领域,这样子分工合作可以学习到更多东西 专题怎么来
- 专题请求,可以请求其他成员完成专题,比如比较深的知识,可以要求团队比较有经验的进行学习分享
- 学习总结
- 项目回顾
- 难点攻克
- 项目规范
- 工具使用
落实和完善开发规范,规范本身就是团队知识沉淀的一种直接输出
- 图书分享,和离散的文章和教程相比,图书的知识会比较系统,另外很多经典的图书是要冷静下来好好欣赏的
- 鼓励重构和持续优化代码
- 抽象一套基础库或框架,减少重复工作,提高工作效率。不加班先从提高工作效率开始
目需要交流问题
- 目前已完成的基础组件及业务组件
- 常用工具类库封装
- 组件/快速原型开发的方式
- 项目目录结构和编码规范
- 开发流程及维护(文档)
- 单元测试
- 前后端联调方式
- 项目入口多时热重载更新慢的问题(webpack优化)
- 因为多项目(公文,事务,信息发布,流程,表单设计器),新启项目慢 响应业务的速度慢
- 工具函数散落 没有版本管理,我们会封装调用网关的请求工具,而各个项目都有自己的实现,一旦出问题,不仅排查起来没有规律,也无法集中解决,维护性很差
- 基础环境升级困难,比如早前 webpack2 的 tree shaking,webpack3 的 common chunks 策略更改,那么每个项目都需要升级一遍,就会陷入重复开发的泥淖中
- 多环境打包自动化部署
- 代码无分层,例如按照关注点分离的原则,UI 层和数据层应当分离,只有这样,UI 测试和数据层的测试才能独立