15+人团队的前端体系架构应该如何管理?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何在大型组织中管理前端体系架构,相关文章不多, 写得也不好。本文所说的大型组织,是指公司前端工程师超过 15 人,有多个前端项目。我不想讨论管理问题或业务问题,这些问题在大公司中很常见,我们只关注前端架构。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前前端架构涉及的领域太多,建议分几个部分:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/98\/98e1ef7a1b60cc4b965b2b5cc647ee32.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"前端架构方案"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1. 视觉代码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从最简单的话题开始,我认为,它是应用程序的视觉代码。(译者注:前端是最接近用户的软件开发环节,通过创建 Web 页面或 App 应用,提供可视化信息给用户,需要更加关注视觉效果和用户交互)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同一家公司开发了多个前端应用程序,希望他们有:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"明确的标识"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"统一的界面和用户体验"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了实现这一点,需要一个设计系统,由设计团队提供,并在所有未来的产品设计中遵循这些设计准则。这项任务非常复杂,需要设计团队、工程师和产品经理之间进行大量的讨论和协调。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从前端角度来看,我们需要提供一个工具来实现这个设计系统,同时在工程师之间共享。一个很好的解决方案是创建 npm 包,由 storybook 或类似的工具来直观的展现。我认为,有一个专门的网络资源(带 URL)以及关于如何使用这个 npm 包的文档非常重要。这个网络资源将被前端工程师和设计师使用,并且可以成为他们之间很好的纽带。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/d6\/d68c396b02ad71ffd4743a81dc524452.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"视觉代码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"总结:"},{"type":"text","text":" 此时,我们有一个设计系统,表现为一个 npm 包和交互式文档,在所有前端应用程序共享并使用。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. 代码结构"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们来谈谈工程师的日常工作,我们要实现新特性、修复 bug、甚至重构代码。我们关注自己的代码,写的漂亮、易于理解。但是,当公司里不是 1 个,2 个,而是几十个小的或大的项目时,会发生什么呢?通常,通过不同项目对开发者进行分组,他们只处理对应的项目。由于体力和时间有限,通常一段时间内我们只能处理几个项目。自然地,我们开始受到这些分组的影响。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是会出现越来越多的情况,当人们进行跨团队协作时,需要检查彼此的代码和解决方案,甚至修复其他应用程序中的一些错误,或者在一些外部应用程序中添加他们需要的东西(对他们的分组来说影响是外部的)。坏消息是,这一过程正在大公司发生,几乎无法控制。好消息是,我们可以帮助改进它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"怎么办?是的,针对公司所有项目,提供相同的代码结构、编码,以及结构规范。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"让我们更深入地了解代码结构的含义:"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"项目中的目录结构"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当工程师第一次加入新项目时,新项目与他们已有项目中的目录结构相同,在那里他们可以知道相关的所有东西,这对链路和搜索有很大帮助。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"配置或辅助文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在每个项目中,它们应该总是在同一个地方。如果需要,也可以类推到测试配置文件或 CI 文件(CI 是持续集成,从代码提交到软件交付到自动化过程)。常见的有:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"README.md(项目说明文件,Github.com 或 gitlab 托管时,会自动生成项目首页)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"package.json(前端项目的入口文件,包含了依赖库和执行命令等,yarn.lock 或 package-lock.json 是对应等缓存文件)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":".gitignore(Git 代码管理时忽略的文件)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":".editorconfig(用来协同团队开发人员之间的代码的风格及样式规范)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"webpack.config.js(Webpack 打包配置文件)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":".eslintrc.js(Eslint 的配置文件,用来管理和检测 js 代码风格)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":".travis.yml(CI 配置文件,用来描述持续构建步骤,编译语言,系统环境等)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"postcss.config.js"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":".babelrc 或 babel.config.js"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":".prettierrc.js"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":".npmrc"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":".browserslistrc"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"根据文件类型约定的固定位置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果相同文件类型的位置总是遵循相同的结构,也会非常方便。例如,组件目录始终具有 style.css 文件:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"null"},"content":[{"type":"text","text":"\/Component\n--\/Component.tsx\n--\/style.css\n--\/index.ts\n"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"组件内部结构"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件内部的结构应该是相同的:导入、导出的顺序、公共函数的位置、类型等。在每种类型的文件中,你应该知道导出了什么。(译者注:导入 import、导出 export 是 ES6 的关键词,用于模块的引用和对外暴露)"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"命名约定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"包括目录、文件、变量、函数、类、类型的名称等。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"编码约定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在一般情况下,我会说,编码约定是一个非常广泛的部分,这里我只想说一些后面几个章节不会再讲的内容,例如是否以分号结尾(译者注,前端 js 代码中,结尾的分号在大部分情况下是可选的,但是团队协作时,最好能达成一致,知乎上也有"},{"type":"link","attrs":{"href":"https:\/\/www.zhihu.com\/question\/20298345","title":"xxx","type":null},"content":[{"type":"text","text":"相关话题"}]},{"type":"text","text":")等类似的问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相同的代码结构和项目工具集在实践中紧密结合在一起,相互帮助共存。我所说的工具集是指 CLI 工具(项目脚手架、语法和代码风格检查 linting、测试等)、IDE 扩展等。我们将在下一节讨论工具集。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/93\/938371dd9b43eec4a694bc57f3394f53.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"代码结构"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"总结:"},{"type":"text","text":" 在掌握并使用本节所讲的之后,我们应该让组织中的所有项目都用相同的目录结构、命名准则、文件结构等。理想情况下,每个开发人员都可以轻松转到任何其他项目中,而不会完全迷失在那里。这个“完全迷失”也与项目中使用的技术栈密切相关,我们来讨论一下。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3. 技术栈"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与上一节类似,我们希望在整个组织的项目中有统一的技术栈。原因非常相似——希望我们的工程师能适应公司的所有项目。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在前端项目中,技术栈的组件可以是:框架、基于该项目的构建、主编程语言、样式处理器、数据层(如 Apollo)、状态管理、测试、linting、构建系统等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当然,所有规则都有例外。我还想说一句,有些技术非常适合某些特定项目,即使这些技术不属于全公司范围的技术栈。但是,每当这种想法偏离公司技术栈时,你都应该三思而后行,因为支持这些东西的成本非常高,所以收益必须是巨大的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我在这里提一些通用的技术栈,它适合现在大多数项目(当然它只涵盖了真正技术栈的一部分,但是对有些人来说是一个很好的起点):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"React(用于构建用户界面的 JS 库)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Typescript(是 JavaScript 的一个超集,支持 ES6 标准,由微软公司开发)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Apollo(是强类型查询语言 GraphQL 的一种具体实现,包含 Apollo Client、Apollo Server 和 Apollo Engine)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Styled Components(样式化组件,可以编写 CSS 代码来设计组件样式,不需要组件和样式之间的映射,即创建后就是一个 React 组件,并附加样式到当前组件)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"React Router(React 官方路由,可以基于 URL 渲染不同的组件)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jest(前端测试框架,它注重简单性,集成了断言、JSDom、覆盖率报告等开发者所需要的所有测试工具,可用于 Babel, TypeScript, Node, React, Angular, Vue 等项目)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Cypress(端到端功能测试框架,基于 Node.js,jQuery。开箱即用,不仅支持本地浏览器模拟测试,也支持终端测试。还有测试录屏功能,方便在测试失败的时候,查看当时的失败场景)】Create React App(React 脚手架,用于快速搭建 React 项目)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们定义了公司的技术栈并达成一致后,还有两点非常重要。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们需要把技术资料写在文档上。这个文档应该很方便在工程师之间共享,这样他们就可以随时给彼此一个链接来参照。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们应该再次写下并分享文档,说明如何使用指定的技术栈来启动和引导新项目。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/50\/50a76744b9320a335ca8e740daf6e00c.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"技术栈"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"总结:"},{"type":"text","text":" 在我们实现并采纳了上面提到的所有内容之后,你应该让组织中的所有项目共享相同的技术栈。理想情况下,所有项目没有任何区别,即使有一点小的差异也没关系。如果再结合上一节,项目中的代码、目录、文件结构也应该几乎相同。因此,任何项目中的链路都应该非常简单明。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"4. 工具化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个话题很重要,我们现在到处都在使用辅助工具(开发新的工具,也叫“造轮子”),如 linting、应用程序构建、CI、组件生成器等等。所以,是否能为项目选择正确的工具至关重要,好的工具和差的工具(或者根本没有工具)就像自动化和手动测试。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在前面的章节中,我们刚刚讨论了技术栈和代码结构,并提到我们需要编写大量的文档,让开发人员关注它们。但正确的工具集可以让你自动遵循公司的指导原则。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果有指定的编码样式,你可以为人们提供 linting 工具,它会默认遵循这些规则。如果你已经定义了技术栈,那么好的 CLI 工具将为你提供一个方法,从现有的技术栈转到具有这些特定技术的新项目。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"让我们试着讨论一下,工具可以覆盖前端体系结构的哪些部分:"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"代码样式和结构"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们前面讨论过,可以很容易地通过工具实现自动化。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"项目脚手架"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你不再需要一个新的项目结构,不再手动安装所有依赖包等。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"组件生成"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多数情况下,应用程序中的某些组件甚至不只包含单个文件,因此创建、链接 \/ 导入文件可能需要一些时间,因此可以自动化。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"启动和构建"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当然,最明显的是要自动化——如何构建或启动应用程序。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"测试"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为测试构建应用程序并实际运行所有类型的测试(单元测试、集成测试等)的过程。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"依赖关系管理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们知道,现在大约 80% 的代码都是依赖关系。因此,我们需要不断更新,在一家大公司里管理这一点并非易事。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"跨项目依赖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们的项目很可能不是孤立的,可能依赖于其他项目,或者被其他项目依赖,因此我们可能需要一些工具来简化关联它们的过程,在多个项目的组合中进行开发(例如 Bit)等。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"CI"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CI 是我们日常基础工具集的重要组成部分,它的自动化和统一对团队来说是非常有用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你不想开发自己的工具集,我推荐 NX 工具集,它是最有趣的工具之一。另外,Babel 的作者也有类似的解决方案,Rome 了解一下。这些工具没有涵盖全部内容,而是我们所讨论内容的一大部分,可以是很好的切入点。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/5a\/5aa6575c6549f29c74fc65379e708002.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"总结"},{"type":"text","text":":想象一下,在我们完成并采用了所有章节所讲的之后,我们的架构会变得多么厉害"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每个项目都有相同的结构,由统一的工具集进行维护和管理。每个项目都以相同的方式启动和构建。组件都放在相应的目录,并使用相同的命名准则。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但这是优点还是有缺点?任何解决方案都有缺点。其中之一是,需要时间来招聘新的工程师。但是,如果一切都是以一种非常常见的方式(人们已经习惯了其他现有工具)并且有文档记录,那么当你将它和开发效率的好处、工程师轻松使用任何代码库的机会等进行比较时,就是小事情。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"5. 生产环境"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于前端架构的这一部分,工程师通常最不关心。可能是它大部分都与编码本身无关,也许没那么令人兴奋,但也很最重要。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在生产中,我们通常需要注意以下事项:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"各种不同的事件跟踪,如 Google Analytics(网站流量,用户行为,页面链路等分析平台),Segment(收集、清理和处理客户数据平台),HotJar(行为分析和用户反馈服务,了解网站用户的行为并通过热图,会话记录和调查等工具获得他们的反馈)等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"状态监视"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"类似健康检查,甚至是生产环境上运行的测试,错误报告(比如:Sentry)等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"性能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这有点类似于上一项,但更注重性能。这包括响应时间估算、首屏渲染等(相关工具:Lighthouse)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"A\/B 测试"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"各种 A\/B 测试解决方案(A\/B 测试是一种产品优化方法,为同一个优化目标制定两个方案,让一部分用户使用 A 方案,另一部分用户使用 B 方案,统计并对比不同方案的转化率、点击量、留存率等指标,以判断不同方案的优劣并进行决策)或功能标记(feature flag 控制线上功能开启或者关闭,通常采取配置文件的方式。feature flag 允许关闭未完成的功能,在主干上进行迭代开发,新功能即便未开发完成也不会影响发布,因为它对用户是关闭的,也可以修改配置让功能对特定的用户可见。当功能开发完成之后,修改配置便可以让功能发布)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"缓存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Varnish,Cloudflare 之类的工具。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有这些都可以在公司的前端应用程序中统一,这将简化工程师的工作。例如,通过添加具有预定义初始配置的包,并将这些包添加到新项目中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"发布生产的另一部分是代码预处理,例如:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"压缩:只是代码压缩"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"源代码映射:一些其他工具也可能需要源代码映射,如 Sentry。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"资源预处理:不同屏幕分辨率的处理,图像裁剪等。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这些都可以添加到前端应用程序的工具集中,在工具化一节讨论过。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/1a\/1a268ca39963c1f4910aa65e7800a30b.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"生产环境"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"总结:"},{"type":"text","text":" 理想情况下,所有这些都应该在初始化阶段自动添加到每个前端项目中。工程师只需要添加配置到工具集相应的地方。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"6. 开发环境"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"CLI 工具"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当我们谈到前端 CLI 工具时,已经在工具化一节讨论过开发经验,统一的工具是工程师日常工作的一大部分,但不是全部。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"API"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们要做的第二件有意义的事情,是本地使用便捷的 API,它可以提高开发人员体验和开发速度。通常,在本地为前端工程师提供 API 并不简单:这可能包括安装他们不熟悉的工具或框架。即使安装程序已经使用容器化部署,这也会占用开发人员机器上的大量计算资源(否则,可以考虑做一个安装程序)。在这种情况下,解决方案是什么?——外部服务 API。这可以是一个供所有工程师使用的单一服务器,也可以是一些简单的设置,指向你自己的专用服务器进行调用。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"CI"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CI 是第三大部分。假设你的公司已经为前端选择了一些 CI 工具,并使用此唯一工具(例如 Circle CI、Concourse CI,或任何其他工具)。如果没有,你应该把它统一起来。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我看来,特定项目的 CI 配置应该是团队工作的一部分,而团队正致力于这个项目。这就要用到 CI,每天都要使用它。有人对 CI 感兴趣,使它保持稳定,并且拥有修复、配置和改进它的能力和技能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,并不是所有的工作都应该由团队来完成。对于前端应用程序,存在一组非常特定的工作,这些工作可能是需要的。特别是,如果我们能设法统一目录 \/ 代码结构、技术栈等,那么在贵公司工作一段时间后,你可能会在 CI 工具的各个阶段接触到这些模式,将这些阶段作为某种“构建块”来实现,并为你的工程师提供从这些统一的“构建块”到 CI 流水线构建的可能。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"演示环境"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后我们要对开发好的功能进行了验证。在工程师完成并实现了所有的任务之后,他们几乎总是需要检查它的外观和功能,并与其他工程师、设计师或其他利益相关者分享。对于这样的需求,为特定 PR(合并请求)提供应用程序的临时部署版本,同时提供一个 URL,会非常方便。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/15\/155664913581bb0bd326c7de314cdeea.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"应用程序临时部署"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个解决方案大大加快了不同团队和人员之间的沟通,我认为这只是基础必备的。但是,这个临时部署的版本应该尽可能接近生产环境,因为它也可以检查一些明显的错误或 bug。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果前端应用程序的构建和部署过程的流水线是统一的,那么可以很容易地添加到项目中并实现自动化。此外,类似 Kubernetes(容器集群管理工具,简称 K8S)和 Helm(K8S 包管理器)这样的工具在自动化实现中也会有很大帮助。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/12\/122e0c024751881c523e85bf9346da40.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"开发环境"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"总结:"},{"type":"text","text":" 舒适高效的开发流程,使用单一的 CI 工具和构建块,使用统一的 CLI 前端工具和演示环境创建各种 CI 流水线。所有这些都应该使我们的开发过程尽可能顺利。把这个乘以你在公司的工程师人数,你就会明白这是多么有益。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"7. 模块化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个主题非常大,可能需要单独的一篇文章来讨论,但我会尝试简要地介绍一下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在大型组织中,庞大的代码库并不少见,伴随着所有已知的问题,比如缓慢的 CI 流水线、协作工作的问题、缓慢的测试等。因此前端架构的一个重要部分是决定我们希望看到独立的前端应用程序 \/ 模块的粒度。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们现在有三种主要类型:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Monolith"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大型仓库只有一个项目,包含所有代码,所有团队同时在这个仓库工作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Monorepo"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有许多项目,但仍然是一个大的仓库(维基百科词条)。所有团队仍在同一个仓库中工作,但使用不同的项目。现在已经有机会解决一些问题,我们采用的是 monolith 方法,只为特定项目运行流水线,项目有更小的测试套件等等。如果你选择这种方法,像 Lerna 这样的工具可以让你变得更轻松。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"每个项目一个仓库"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每个项目有自己的仓库和全部辅助文件,比如 CI 流水线、部署等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在所有这些模型中,项目可能意味着一个独立的前端应用程序、页面、独立的前端模块等。这取决于你想要决定拆分前端应用程序的粒度。在大多数情况下,这种划分应该与所需的组织结构和人员管理同步。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在你决定拆分应用程序之后,第二个大的话题就是如何将他们连在一起,我们有如下几个方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"构建时组合:你的项目可以只是 npm 包,在构建时安装和组合。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服务器端合成:这种方法通常包括服务器端呈现和服务器上发生的合成。像 Hypernova 这样的工具可以帮助更好地组织它。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客户端合成:浏览器内项目的合成。在 Martin Fowler 的博客 中,对这些方法有很好的解释。另外,要重点提一下 Module Federation,这是 Webpack 5 中引入的新方法。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"路由合成:每个项目都有自己的 URL,在“Nginx 层面”我们决定将用户重定向到哪里。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如我之前所说的,用这么小的篇幅来报道这个话题非常困难,但我希望你有一些自己感兴趣的想法,并且去挖掘这些话题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/9f\/9ff436fde24d0c03fafb961cc04aef5e.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"模块化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"总结:"},{"type":"text","text":" 我们找到了一种方法,通过组织仓库,将项目分成更小的部分(尽可能),使团队彼此更加独立,让我们的团队成员感到快乐和高效。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"8. 测试"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有很多可用到前端应用程序测试资源,可以很容易地找到,所以我尽量不谈细节,而是更多地关注大型组织的问题以及我们如何解决这些问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,每个工程师对测试技术的理解各不相同,在哪种情况下应用哪种技术,如何编写“好的”测试等等。因此,准确地记录公司使用的测试层面的所有细微差别和指导方针,以及每个测试层面的指导方针是非常有意义的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从测试策略方面看,你希望有哪些可能的测试类别:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"单元测试"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"集成测试"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"E2E 测试"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其他"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作为第二步,我们需要将它们统一到公司的不同前端应用程序中,这样人们就不会对迁移到不同项目时如何测试和测试什么产生疑问。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你成功地统一了测试级别和方法,那么你可以自动帮助解决第二个问题——测试基础结构设置。每个项目本身都需要在本地和一些测试基础设施上进行设置和配置。例如,我们决定使用 Cypress,它需要在 Docker 容器中运行。这需要花一些时间在本地和 CI 上进行设置。如果我们把它乘以我们拥有的项目的数量——这是一个巨大的时间量。所以,对应的解决方案是再次统一,并为项目提供一些工具。听起来很简单,但需要大量的时间来实现。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"非开发时间测试"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我还想谈谈另一种方法,在已经实现和部署了特性之后测试应用程序。监控当然是其中的一部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在前面的章节中,我们已经提到了前端应用程序的错误和性能监控、正常运行时间监控以及来自不同地区的响应。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在你的网站上运行 Lighthouse 测试也很好(可以包含在 CI 流水线中)。更容易找到性能瓶颈、可用性问题,并整体上改进网页质量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对最重要的业务流进行最后的生产测试。如果你直接在生产环境中运行这些测试,它们很可能工作得最好,但如果我们可以在非常接近生产环境甚至镜像生产环境的暂存 \/ 测试环境中运行此类测试。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/08\/08d5aa5d98c8119b52b17fc8b886ce16.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"测试"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"总结:"},{"type":"text","text":"我们有了明确的测试规范,为每个前端应用程序定义必要的测试级别。每个应用程序都有一个单一的 CI 流水线,以及一个 CLI 工具,它可以帮助你在本地进行测试。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文链接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/medium.com\/swlh\/frontend-architecture-in-scale-for-large-organizations-593930ed10cd","title":"","type":null},"content":[{"type":"text","text":"https:\/\/medium.com\/swlh\/frontend-architecture-in-scale-for-large-organizations-593930ed10cd"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章