摘要:自动化接入和升级方案通过命令行工具提供一键接入升级能力,同时集成到团队脚手架中,大大降低了工程接入和维护的成本。原始代码经过解析器的解析,在管道中逐一经过所有规则的检查,最终检测出所有不符合规范的代码,并输出为报告。
引言
代码规范是软件开发领域经久不衰的话题,几乎所有工程师在开发过程中都会遇到,并或多或少会思考过这一问题。随着前端应用的大型化和复杂化,越来越多的前端工程师和团队开始重视 JavaScript代码规范。得益于前端开源社区的繁盛,当下已经有几种较为成熟的 JavaScript 代码规范检查工具,包括 JSLint、JSHint、ESLint、FECS 等等。本文主要介绍目前较为通用的方案——ESLint,它是一款插件化的 JavaScript 代码静态检查工具,其核心是通过对代码解析得到的 AST(Abstract Syntax Tree,抽象语法树)进行模式匹配,定位不符合约定规范的代码。
ESLint 的使用并不复杂。依照 ESLint 的文档安装相关依赖,可以根据个人/团队的代码风格进行配置,即可通过命令行工具或借助编辑器集成的 ESLint 功能对工程代码进行静态检查,发现和修复不符合规范的代码。如果想降低配置成本,也可以直接使用开源配置方案,例如eslint-config-airbnb或eslint-config-standard。
对于独立开发者,或者执行力较强、技术场景较为单一的小型团队而言,直接使用 ESLint 及其生态提供的一些标准方案,可以用较低成本来实现 JavaScript 代码规范的落地。如果再搭配一些辅助工具(例如 husky 和 lint-staged),整个流程会更加顺畅。但对于数十人的大型前端团队来说,面向数百个前端工程,规模化地应用统一的 JavaScript 代码规范,问题就会变得较为复杂。如果直接利用现有的开源配置方案,可能会使工作事倍功半。
问题分析规模化应用统一的 ESLint 代码规范,会涌现各类问题,根源在于大型团队和小团队(或独立开发者)的差异性:
技术层面上:
技术场景更加广泛:对于大型团队,其开发场景一般不会局限在传统 Web 领域内,往往还会涉及 Node.js、React Native、小程序、桌面应用(例如 Electron)等更广泛的技术场景。
技术选型更加分散:团队内工程技术选型往往并不统一,如 React/Vue、JavaScript/TypeScript 等。
工程数量的增加和工程方案离散化导致 ESLint 方案的复杂度提升:这样会进一步增加工程接入成本、升级成本和方案维护成本。
在团队层面,随着人员的增加和组织结构的复杂化:
人员风格差异性更大、沟通协调成本更高。
方案宣导更难触达,难以保证规范执行的落实。
执行状况和效果难以统计和分析。
因为存在诸多差异,我们在设计具体方案时,需要考虑和解决更多问题,以保证规范的落实。针对上述分析,我们梳理了以下需要解决的问题:
如何制定统一的代码规范和对应的 ESLint 配置?
场景支撑:如何实现对场景差异的支持?如何保证不同场景间一致部分(例如 JavaScript 基础语法)的规范一致性?
技术选型支撑:如何在支撑不同技术选型的前提下,保证基础规则(例如缩进)的一致性?
可维护性:具体到规则配置上,能否提升可复用性?在方案升级迭代时成本是否可控?
如何保证代码规范的执行?
人员的增加和组织结构的复杂化,会导致基于管理的执行把控失效,这种情况应该如何保证代码规范的执行质量?
如何降低应用成本?
在工程数量增加、工程方案离散化的情况,降低方案的接入、升级和执行成本能节约大量的人力,同时也有利于方案落地推进。
如何及时了解规范应用状况和效果?
解决方案为了能在团队内实现 JavaScript 代码规范的统一,在分析和思考团队规模化应用存在的问题后,我们设计了一套完整的技术解决方案。该方案包括多场景统一的 ESLint 规则配置、代码集成交付检查、自动化接入工具、执行状况监测分析等四个模块。通过各个模块协调配合,共同解决上文提出的问题,在降低维护成本、提升执行效率的同时,也保障了代码规范的统一。
整体方案的设计如下图所示:
多场景统一的 JavaScript 规范:该模块是整个方案的核心,借助 ESLint 的特性,通过分层分类的结构设计,在保证基础规则一致性的同时,实现了对不同场景、技术选型的支撑。
代码集成交付检查:该模块是方案落地执行的保障,将代码静态检查集成到持续交付工作流中。具体设计实现上,在保证交付质量的同时,也通过定制集成检查工具降低了开发者的应用执行成本。
自动化接入和升级方案:通过命令行工具提供“一键”接入/升级能力,同时集成到团队脚手架中,大大降低了工程接入和维护的成本。
执行状况监测分析:通过对工具运行和代码集成交付检查过程进行埋点、检查结果收集和分析,了解方案的应用状态和效果。
方案实现上文中提出的问题,通过各模块的协调配合能够得到有效地解决,但具体到各个模块的实现,仍然需要进一步深入思考,以设计出更加合理的实现方案。本章将对方案的四个核心模块进行详细介绍。
通用 ESLint 配置方案这一模块主要借助 ESLint 的基础特性,采用分层分类的结构设计,提供多场景、多技术方案的通用配置方案,并使方案具备易维护、易扩展的特性。
ESLint 特性简介在进行 ESLint 配置方案设计前,我们先看一下 ESLint 的一些特点。
1.插件化
下图简单地描述了 ESLint 的工作过程:
ESLint 的能力更像一个引擎,通过提供的基础检测能力和模式约束,推动代码检测流程的运转。原始代码经过解析器的解析,在管道中逐一经过所有规则的检查,最终检测出所有不符合规范的代码,并输出为报告。借助插件化的设计,不但可以对所有的规则进行独立的控制,还可以定制和引入新的规则。ESLint 本身并未和解析器强绑定,我们可以使用不同的解析器进行原始代码解析,例如可以使用 babel-eslint 支持更新版本、不同阶段的 ES 语法,支持 JSX 等特殊语法,甚至可以借助 @typescript-eslint/parser 支持 TypeScript 语言的检查。
2.配置能力全面、可层叠、可共享
ESLint 提供了全面、灵活的配置能力,可以对解析器、规则、环境、全局变量等进行配置;可以快速引入另一份配置,和当前配置层叠组合为新的配置;还可以将配置好的规则集发布为 npm 包,在工程内快速应用。
3.社区生态较为成熟
开源社区中基于 ESLint 的项目非常多,既有针对各种场景、框架的插件,也有各种 ESLint 规则配置方案,基本可以涵盖前端开发的所有场景。
规范配置方案设计基于 ESLint 的插件化、可层叠配置特性,以及面向各种场景、框架的开源方案,我们设计了如下图所示的 ESLint 配置架构:
该配置架构采用了分层、分类的结构,其中:
基础层:制定统一的基础语法和格式规范,提供通用的代码风格和语法规则配置,例如缩进、尾逗号等等。
框架支撑层(可选):提供对通用的一些技术场景、框架的支持,包括 Node.js、React、Vue、React Native等;这一层借助开源社区的各种插件进行配置,并对各种框架的规则都进行了一定的调整。
TypeScript 层(可选):这一层借助 typescript-eslint,提供对 TypeScript 的支持。
适配层(可选):提供对特殊场景的定制化支持,例如 MRN(美团内部的 React Native 定制化方案)、配合 prettier 使用、或者某些团队的特殊规则诉求。
具体的实际项目中,可以灵活的选择各层级、各类型的搭配,获得和项目匹配的 ESLint 规则集。例如,对于使用 TypeScript 语言的 React 项目,可以将基础层、框架层的 React 分支、以及 TypeScript 支撑层的 React 分支层叠到一起,最终形成适用于该项目的 ESLint 配置。如果项目不再使用 TypeScript 语言,只需要将 ts-react 这一层去掉即可。
最终,形成了如下所示的 ESLint 配置集:
考虑到维护、升级和应用成本,我们最终选择将所有配置放到一个 npm 包中,而不是每种类型分别设置。仍以使用 TypeScript 语言的 React 项目为例,只需在工程中进行如下配置:
// 需要安装 typescript、eslint-plugin-react、@typescript-eslint 等插件 module.exports = { root: true, extends: [ // 因为基础层是必备的,所以框架层默认引入了对应的基础层,不需再多带带引入 eslintrc.base.js "eslint-config-xxx/eslintrc.react.js", "eslint-config-xxx/eslintrc.typescript-react.js" ] }
这种通过分层、分类的结构设计,还有利于后期的维护:
对基础层的修改,只需修改一处即会全局生效。
对非基础层某一部分的调整不会产生关联性的影响。
如需扩展对某一类型的支持,只需关注这一类型的特殊规则配置。
众所周知,TypeScript 类型的项目使用 TSLint 进行代码检查,也是一种简单、便捷的方案。但在本方案中我们依旧选择了:eslint + @typescript-eslint/parser + @typescript-eslint/eslint-plugin 的组合方案。主要有以下几点原因:
ESLint 的规则配置更加详细全面,覆盖更加广泛。
采用了分层分类的架构,能够保证即使框架或语言不同,也能在基本语法、风格层面保持规则的一致性,这样有利于团队内不同技术选型项目的风格统一。
@typescript-eslint 方案持续迭代,问题响应非常迅速,对 TSLint 相关的规则基本提供了对等的实现。
根据最新消息,TypeScript在 2019 路线图 中明确表明后续对 Lint 工具的支持和建设会以对 ESLint 进行适配的方式为主。
代码集成检查基于团队对工程化基础设施的建设,将代码规范静态检查与开发工作流集成,保证代码规范的落实。
通常而言,工程接入 ESLint 后,可以在开发的同时借助编辑器集成的 ESLint 检查提示能力(例如 VSCode 的 ESLint 插件),实时发现和修改不符合规范的语法错误和风格问题。但这仍不能避免因一些主观因素或疏漏造成的规范执行不到位,所以我们考虑在开发工作流的特定节点自动执行代码静态检查,阻断不合规范代码的提交或交付。
集成静态检查的开发工作流节点有很多,我们主要参考以下两种方案:
代码提交检查:在代码 Commit 时,通过 githook 触发 ESLint 检查。其优点在于能实时响应开发者的动作,给出反馈,快速定位和修复问题;缺陷在于开发者可以主动跳过检查。
代码交付检查:在代码交付(借助 CI 系统的交付流程功能)时,在代码检测平台中对代码进行 ESLint 检查,检测不通过则阻断交付。其优点在于能够强制执行,可在线追踪检测报告;缺陷在于离开发者的开发环境太“远”,开发者响应处理成本较高。
如果将两者进行结合,可能会事半功倍,效果如下图所示:
常用的代码提交检查方法一般是 husky 与 lint-staged 结合,在代码 Commit 时,通过 githook 触发对 git 暂存区文件的检查。但考虑到团队现有工程数量庞大、存在大量行数较多的文件,虽然 lint-staged 策略能够降低部分成本,但仍稍显不足。为此,我们对该方法进行优化,定制了本地代码提交检查工具 precommit-eslint,其核心特点是:
将增量检查执行到代码行这一粒度,支持 Warn 和 Error 两个检查级别。
只需将工具安装为工程的依赖,无需任何配置。
减少了 pre-commit hook 中植入脚本的侵入性。
进行了执行状况埋点和采集。
使用效果如下图所示:
在美团,我们使用自主开发的 CI 系统,并在独立部署的 Sonar 系统上定制化实现了相应规则,基本可以满足诉求,这里就不再赘述。对于独立的团队,基于 ESLint 提供的工具,可以很容易的实现使用 Node 快速搭建一个代码检测服务或平台,大家有兴趣不妨一试。
自动化接入工具这个模块主要通过 CLI 工具提供方案自动化接入的能力,降低工程接入和升级的成本。如果不借助自动化工具,在工程中接入上述方案还是有一定的工作量和复杂度的,大致步骤如下:
安装 Eslint。
根据项目类型安装对应的 ESLint 规则配置 npm 包。
根据项目类型安装相关的插件、解析器等。
根据项目类型配置 .eslintrc 文件。
安装代码提交检查工具。
配置 package.json。
测试及修复问题。
在这个过程中,特别需要注意依赖的版本问题:依赖之间的版本兼容性,例如 typescript 和 @typescript-eslint/parser 之间的兼容性;依赖对规则的支持性,比如某个版本的插件中去除了对某个规则的支持,但规则配置中仍然配置了该规则,此时配置就会失效。对于 ESLint 不熟悉的开发者而言,在配置的过程中都会或多或少遇到兼容性、解析异常、规则无效等问题,反复排查和定位问题会浪费大量的精力。
因此,在设计开发自动化接入工具时,我们综合考虑了操作步骤、依赖版本、规则集和工程方案的兼容性,设计了如下的工作流程:
该工具流程简单,不管什么开发场景和框架选型,繁琐的接入流程都可以简化为一条命令,需要配合工程方案升级时同样如此。如下图所示,执行该命令后项目就完成了 ESLint 的接入,使用统一的规则规范编码,同是在代码提交时自动进行增量检查:
埋点与统计分析统计分析的主要目的是掌握方案应用执行状况和效果,理论上应当支持工程和大盘两个视角,如下图所示:
执行情况分析其实并不复杂,核心是信息采集和分析。在本方案中,信息采集通过 precommit-eslint 工具实现:在 git commit 触发本地代码检查后,脚本会把检查结果(包括检查是否通过、错误或警告信息的数量级别等)上报;信息的统计分析借助日志上报分析平台实现,美团使用的是 CAT 平台(如果团队或公司没有专门的平台,可以在上文提到的代码检测服务平台中实现这部分功能)。为了便于数据的聚合分析,我们将一次代码提交检查中出现的问题数量进行了分级:
检查通过:检查无代码规范错误。
错误 1 级:检查出代码规范错误数量小于 10 个。
错误 2 级:检查出代码规范错误数量在 10 - 100 个之间。
错误 3 级:检查出代码规范错误数量在 100 - 1000 个之间。
错误 4 级:检查出代码规范错误数量大于 1000 个。
比如下图中,201903 第一周的代码提交检查结果统计(综合采样率 0.2),很明显,所有检查失败的提交中,错误数量在 10 个以内的占比最大,修复成本不高。
1.提交检查异常分布(仅筛选检查未通过信息)
2.提交检查警告信息分析
除此之外,还可以对单一工程,在更细的时间粒度上去观察提交检查的执行情况。
效果质量主要分析工程质量的变化:一方面可以通过代码检查执行通过率变化趋势、检查结果分布去看持续的生产流程中,代码质量是否有所提升;另一方面,由于代码检查采用增量模式,需要对工程代码进行整体分析,得到工程整体的不规范代码占比及变化趋势,从而从工程维度分析判断质量效果(涉及到权限相关问题,目前团队中未采用工程分析的方法)。具体的分析会在方案应用效果中一并进行介绍。
方案应用除了上述整体方案外,为保证开发者使用更方便,我们还进行了一些配套工作:
持续维护升级:以每月一版的方式持续迭代升级,解决应用中的问题、规则争议,以及支持新的规则或方案。
工程化集成:整套方案可以无缝接入到各个团队的脚手架工具中,自动成为团队的默认方案,在工程初始化阶段即可完成接入。
官网建设:提供详细的使用文档,包括规则信息、接入方法,并且对每个版本提供规则、环境依赖、changeLog 等详细说明。
常见使用问题:更新维护FAQ,帮助后续接入者快速查找并解决问题。
目前,该套方案已经接入美团外卖、餐饮平台、闪购、榛果、金融等多个团队,基于埋点统计分析,我们(基于2019年2月份最后一周统计数据分析,综合采样率0.2)得到了如下数据:
截止到2019年2月底,该方案已接入超过 200 个前端工程。
集成检查(增量)每天执行接近 1000 次。
集成检查(增量)平均每天检查出错误约 20000-25000 处。
集成检查代码质量:平均通过率为 75.562%,错误 1 级的比率为 15.644%,在所有未通过检查中占比 64.015%。
同时,我们持续统计上述数据的变化趋势,跟踪代码质量提升效果,以2018年12月到2019年3月的数据为例(截止2019年3月第一周,以周为时间统计尺度):
从图中可以看出,最近三个月检查通过率整体呈上升趋势,但 2019 年 1 月的第 2 周和第 3 周集成检查通过率有明显下降。分析项目信息发现,在 2019 年 1 月的第 2 周有一批新项目接入,代码检查规范检查出几十个错误。但整体来看,目前集成检查通过率基本稳定在 75% - 80%,从变化趋势看仍有上升空间。
方案实施之后,我们做了一个用户调研,发现整体方案的运营正在发挥着正向的作用。一方面,在一定程度上提升了多人协作的效率,无论是共同维护一个工程还是在多个工程间切换,避免了代码风格不一致带来的可读性成本和格式化风险;另一方面,会帮助大家发现和避免一些简单的语法错误。
规划和思考该方案已经稳定应用,除了现有功能,我们还在思考是否可以更进一步的优化,提供更丰富的能力。由此规划了一些仍未落地的方向:
扩展支持 HTML 和 CSS 的代码风格检查:虽然近几年前端框架、组件库的建设一定程度上减少了业务开发中(尤其是中后台业务)对 HTML 和 CSS 的需求,但是规范 HTML 和 CSS 的代码风格仍是必要的。基于此,可以用同样的思路将 HTML 和 CSS 的代码静态检查方案集成到当前的方案中,不再局限于 JavaScript(或 TypeScript)。
进一步的封装:目前整体方案会将所有依赖和配置暴露在工程内,如果将其完全封装在一个工具内会更便于应用,但难点在于兼顾灵活性、对编辑器的支持等问题。
增加工程维度的代码质量趋势分析:目前代码检查策略是增量检查,可以对接入的工程定期全量检查,基于时间线分析工程的代码质量变化趋势。
进一步深入分析检查结果和统计数据,发现一些潜在问题,为推动开发质量提升提供辅助,如:
统计开发者在工程中关闭或调整的规则,分析占比较高的规则被关闭的原因,进而调整规则或推动规则的执行。
统计分布检查出错误的规则分布,梳理出最常出问题的代码规则,发布对应的最佳实践或手册。
以上是美团外卖团队在 ESLint 方案规模化应用过程中的一些实践,欢迎大家提出建议,一起沟通交流。
作者简介宋鹏,美团外卖事业部终端研发工程师。
团队介绍美团外卖事业部终端团队,负责的多个终端和平台直接连接亿万用户、数百万商家和几万名运营与销售,目标是在保障业务高稳定、高可用的同时,持续提升用户体验和研发效率。
在用户方向上,构建了全链路的高可用体系,客户端、Web前端和小程序等多终端的可用性在99%左右;跨多端高复用的局部动态化框架在首页、广告、营销等核心路径的落地,提升了30%的研发效率;
在商家方向上,从提高进程优先级、VoIP Push拉活、doze等方面进行保活定制,并提供了Shark、短链和Push等多条触达通道,订单到达率提升至98%以上;
在运营方向上,通过标准化研发流程、建设组件库和Node服务以及前端应用的管理与页面配置等提升10%的研发效率。
团队有多个岗位正在招聘,欢迎加入我们,联系邮箱 tech@meituan.com ,注明 “外卖终端团队”。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/106397.html
摘要:热门文章我在淘宝做前端的这三年红了樱桃,绿了芭蕉。文章将在淘宝的三年时光折射为入职职业规划招聘晋升离职等与我们息息相关的经验分享,值得品读。 showImg(https://segmentfault.com/img/remote/1460000018739018?w=1790&h=886); 【Alibaba-TXD 前端小报】- 热门前端技术快报,聚焦业界新视界;不知不觉 2019 ...
摘要:热门文章我在淘宝做前端的这三年红了樱桃,绿了芭蕉。文章将在淘宝的三年时光折射为入职职业规划招聘晋升离职等与我们息息相关的经验分享,值得品读。 showImg(https://segmentfault.com/img/remote/1460000018739018?w=1790&h=886); 【Alibaba-TXD 前端小报】- 热门前端技术快报,聚焦业界新视界;不知不觉 2019 ...
摘要:最佳实践一个文件一个组件。,这是包含的是无副作用的纯函数式计算状态操作的函数。,的启动脚本,启动开发模式,项目打包,运行单元测试等等。每次代码推送到之前也会执行所有单元测试用例,全部通过才可以继续推送。,首次安装依赖包之后生成的文件。 前段时间 React license 的问题闹的沸沸扬扬,搞得 React 社区人心惶惶,好在最终 React 团队听取了社区意见把 license 换...
摘要:近期在按照业务划分项目时,我们组被分了好多的项目过来,大量的是基于的,也是我们组持续在使用的语言。部署环境强依赖本地,因为需要在本地建立仓库的临时目录,并经过多次的方式完成部署上线的操作。 近期在按照业务划分项目时,我们组被分了好多的项目过来,大量的是基于 Node.js 的,也是我们组持续在使用的语言。 现有流程中的一些问题 在维护多个项目的时候,会暴露出一些问题: 如何有效的使用...
阅读 2752·2021-11-25 09:43
阅读 2112·2021-11-18 13:25
阅读 4577·2021-09-22 15:52
阅读 1872·2021-09-22 15:49
阅读 2218·2019-08-30 15:54
阅读 3012·2019-08-29 17:13
阅读 2319·2019-08-29 16:54
阅读 2260·2019-08-29 12:58