建立前端开发准则,让团队能够有效率的开发好维护的程序码(by 均一前端工程师宜陞)

https://ithelp.ithome.com.tw/upload/images/20210903/201256323AEAaxri0Z.jpg

【前言】均一的程序码基础 junyiacademy 从 2013 年 fork Khan Academy 原始码,一直发展到现在,程序码的复杂度不可同日而语,开发和维护的难度越来越高,很根本而且不好偿还的技术债逐渐浮上台面,有些甚至成为新功能开发的巨大阻力。

於是,我们在 2020 六月决定制定出前端的开发准则,具体行动有停止混用多个框架,使用一致的目录结构,并为选用的框架及相对应的目录、档案制定 style guide。最终希望前端能在有规范情况下,有效率的开发出可维护的程序码。

行动 1:订出 Client 端与 Server 端的状态管理规范

状态管理一直是复杂的前端专案需要面临的问题,除了选一个好用的状态管理工具,也需要为 UI 相关的 Client 端状态与 API 资料相关的 Server 端状态设计不同的管理方式。均一在此行动中分为两阶段进行,在阶段一我们先选了 Redux(注 1)来统一我们的管理工具,并在阶段二中持续优化 Client 端与 Server 端的状态管理方式:

阶段一:使用 Redux 统一状态管理工具

其实均一的 codebase 中原本就有用到 Redux,但前端的状态管理方式一直没有统一的规范,常见的问题像是直接在 React 的 UI 元件中呼叫 API,导致 server 端的状态逻辑混杂在应该只用来处理 UI 逻辑的元件之中。此外,在 React 推出了 Context API(注 2)之後,到底要用 Redux 还是 Context API 在前端团队中一直是个悬而未决的大哉问。

因此在订定状态管理规范初期,最先开始处理的就是「Redux 有的人喜欢,有的人不喜欢」的问题。在经历一段时间的研究与一场技术分享之後,考量到许多状态管理机制在 Redux 中早已实作并有一套明确的 design pattern 可以遵循,对前端团队来说,与其选择 Context API 并自行订定、维护一套状态管理机制,不如先以设计优良的 Redux 出发,让均一的团队成员熟悉这个做法,之後要换其他套件都是可以讨论的。

但 Redux 常为人诟病的一个问题就是,只为了处理一些状态管理需求就需要写一堆样板程序,为了优化这个问题,我们采用了 Redux 官方推荐的 Redux Toolkit。使用 Redux Toolkit 中的 Slice 可以大幅简化需要撰写的样板程序。此外,善用 Toolkit 中的 Selector 也让均一的程序码更解耦,在叠加新功能时可感受到新架构有更高的开发弹性。

在前面文章提到「直接在 UI 元件中呼叫 API」的非同步问题,则是藉由把呼叫 API 以及相关的 side effect 都搬移到 Redux 的 middleware,让 UI 元件的逻辑更乾净。而 Redux 的 middleware 种类繁多,常见的如:Redux-Saga、Redux-Thunk、基於 RxJS 的 Redux-Observable,考量到 RxJS(注 3)的泛用性较高,且在各个语言几乎都有对应的套件,即使未来不再使用 Redux-Observable 还是可以持续使用 Rx 的概念进行开发,尽管 Redux-Observable 的学习成本较高,最後均一仍选择了 Redux-Observable。

阶段二:使用 React Query 管理 server 端状态;Redux 只用来管理「复杂」的 client 端状态

由於观察到越来越多人推荐以 React Query、SWR(注 4)等套件来处理 data fetching,在订定 Redux 规范时就持续在关注这些套件的发展,经过前端团队进行相关的研究与试用後,决定以 React Query 取代 Redux-Observable。此决定的原因一部分是使用 React Query 确实可以少写非常多样板程序,让程序码变得更简洁,另一部分则是考量到未来均一打算 migrate 到 GraphQL,而目前 React Query 对 GraphQL 的支援度较佳。

使用 React Query 的另一个好处是可以将 server 端的状态以及 cache 机制统一交给 React Query 管理,如此一来就能明确的分离 server 端与 client 端的状态。在大部分的专案中,当把 server 端状态都交给 React Query 处理後,通常只会剩下少量的 client 端状态,而 client 端的状态变少,其实就不太需要使用 Redux 这个专门用来处理庞杂 client 端状态的管理工具。在理想的情况下,均一的前端只需要 React + React Query 即可进行完善的状态管理,只有在 client 端状态较复杂的情况,才会请出 Redux 来帮忙管理。

行动 2:在新专案导入 TypeScript

均一前端团队长期使用弱型别的 JavaScript 进行开发,TypeError 一直是工程师心中的痛,且在型别未明的情况下,常需要耗费大量的脑中记忆体来记住程序码中的型别。考量到 TypeScript 的安全性明显比 JavaScript 来得高,且各大套件也开始 migration 到 TypeScript 或甚至以 TypeScript 为主,均一团队也开始在 codebase 中导入 TypeScript。

不过均一并没有在原本的 codebase 导入 TypeScript,而是在新的前端 repo 导入,因此没有经历到太多 migration 的痛苦,否则就需要一步一步地将 strict mode 打开,慢慢解 error。而原本使用 JavaScript 的前端 repo,则使用 JSDoc 来进行基本的型别撰写。

行动 3:制定 style guide

为了让团队中的前端工程师有统一的标准能够遵循,我们开始为选用的框架、技术以及相对应的目录结构制定 style guide,同时也期待实习生与其他的开发者能够参考这些规范来上手均一的前端开发,减少因为不同人撰写的程序码差异过大导致维护困难的情况发生。

前端的 style guide 以 Google JS style guide 为基础,在此基础上针对以下几个大项目撰写实作规范:

  1. Naming,如:React handler functions 要怎麽命名
  2. Import/Export module statements,如:import module 的排列顺序
  3. File’s implementation,例如:component 要怎麽写
  4. Framework/Package usage,例如:Material-UI 的 styles API 要怎麽用

此外,我们也订定了目录结构的规范,希望能做到当想要改一个功能时,工程师不需要打开编辑器就已经大概知道要去哪个资料夹中的哪个档案修改。

行动 4:将准则导入开发环境

要让工程师快速的将 style guide 导入日常开发,我们想到最快的方法就是将这些原则直接透过自动化工具引入团队成员的 IDE、Git flow 之中。

使用 Prettier 自动排版程序码

均一长期以来都是透过 ESLint 在进行语法检查,虽然 ESLint 确实提升了程序码的一致性,但许多语法错误仍须透过肉眼辨识来进行手动修改,为了加速日常的排版工作,我们开始使用 Prettier(注 5)在 IDE 进行自动排版。

过去均一的 codebase 还没有使用过类似的自动排版工具,因此在全面套用 Prettier 前我们先取一个 package 用 Prettier 进行排版,确定没问题後,再一次性的将全部的 JS files 都进行排版。此後,前端写 code 的体验大升级,无时无刻都受益於 Prettier 搭配 VSCode 的 Format On Save 功能,在过去需要人工判断的排版问题,现在只要按下 save 即瞬间完成。

使用 code snippet 省去复制贴上程序码的时间

通常在写 code 前会去复制贴上其他已符合 coding style 的程序码,而 code snippet 其实就只是将程序码的模板做成 snippet。snippet 比较有趣的地方在於,它能够根据档名自动将某些程序码的 naming 设定为对应的文字,例如:component 要叫什麽名字,这也让 snippet 除了节省复制、贴上、找模板的时间外,也省去了更新 naming 的时间。

在 commit 前检查排版、语法的正确性

除了前面提到的在 IDE 中使用自动排版工具,在 Git 流程中,我们也设定了 commit 前的自动检查机制(pre-commit)。最後均一是透过 husky(注 6)这个套件在 pre-commit 阶段检查 code change 是否有通过 Prettier 和 ESLint 检查。多了这个工具在 commit 前督促工程师做好排版工作,省去不少在 code review 时沟通排版问题的时间。

附带一提,由於团队成员的 commit message 也长期没有统一的规范,我们也为此导入了 Commitizen 来让大家照着一定的格式撰写 commit message。

总结与未来方向

建立准则後的差别

订定了以上准则後,在均一开发前端明显比过去容易得多。在一致的准则下开发,有更高的确定感,不会再苦於找不到档案、不知道档案要放哪、不知道该用哪个套件。此外,遵循设计优良的架构进行开发,明显感受到模组间的耦合度降低,side effect 减少,在开发时有更高的弹性,对新进成员来说整体的开发环境也比过去友善多了。

未来的规划

下一阶段,均一的前端会使用 Nx(注 7)帮助我们管理 monorepo 中的多个 apps,并将均一常用的元件提取为共用元件,整理到 monorepo 中的 Storybook(注 8)。

另外,我们相信 GraphQL 是未来的趋势,前端预计会在下一代的架构中使用 GraphQL,选择技术时也会以该技术是否支援 GraphQL 为考量点。

注解

  1. Redux 是一个善於集中管理前端状态的套件。
  2. Context API 是 React 负责传递资料到各个前端元件的 API,透过 Context API 可实作出类似 Redux 的状态集中管理功能。
  3. RxJS 是透过 JavaScript 实作的 ReactiveX API。ReactiveX API 集成了 design pattern 中的 Observer pattern, Iterator pattern 以及 functional programming 的概念。
  4. React Query、SWR 都是用来管理前端非同步资料的套件,这些套件善於处理资料的取得、更新与快取。
  5. Prettier 是一套 code formatter,可透过 Prettier 改善程序码的可读性与排版的一致性
  6. husky 是一个可在 git commit, git push 阶段执行 linter, tests 的开发工具
  7. Nx 是一套用来管理 full-stack monorepo 的开发工具
  8. Storybook 是一套用来管理 UI 元件与页面的工具

均一招募中!

如果你对前端软件开发充满热忱,而且跟宜陞一样期待用科技来影响教育,我们正在寻找软件工程师,马上到招募页面查看职位详情!


作者|江宜陞(均一平台教育基金会前端工程师)
编辑|江芳瑜(均一平台教育基金会实习生)


<<:  资料库:什麽是 unsigned integer

>>:  DAY03 环境建构(Anaconda + Jupyter Notebook)及套件工具安装

[Day30]颁发和注销访问token

安装passport套件 安装套件cmd执行以下 composer require laravel/...

自动化测试,让你上班拥有一杯咖啡的时间 | Day 22 - 与 JS-alert, confirm, prompt 如何互动

此系列文章会同步发文到个人部落格,有兴趣的读者可以前往观看喔。 今天要跟大家分享当网站有用到Jav...

123大家好~

大家好~ 在接下来30天的文章中 希望可以帮自己的经历做个笔记之外 也可以透过这次机会在更加的成长茁...

如果要架设一个HTTPs File Server, 推荐用什麽软件呢?

如果要架设一个HTTPs File Server, 个人使用, 所以不会有certificate, ...

#16 No-code 之旅 — Project Setup

嗨嗨~ 今天比较晚下班,还没写文章也还没写专案XD 今天先建立专案好了~ 周末再赶进度! Setup...