工作必备工具——2020 年我如何编写前端应用程序
介绍
现在是程序员的黄金时代,尤其是 JavaScript 程序员。不仅市场广阔、薪资丰厚,而且我们的工具也从未如此强大,让人们能够以前所未有的速度构建应用程序和系统。我十几岁时费力地用 PHP、Dreamweaver 和一些用 FTP 上传到网上的漏洞百出的 JavaScript 代码片段搭建网站的日子已经一去不复返了。随着前后端的彻底解耦、主流框架的引入(允许声明式代码和单页应用程序)、源代码控制和轻松的部署流程,我们正处于 Web 编程的黄金时代。在 JavaScript 领域尤其如此,无论是服务器端还是客户端:我想不出还有哪个生态系统能像这个生态系统一样,充满着各种创意和变化。
代价就是被广泛讨论的“JS 疲劳”,那种发现自己无法再依赖五年前学到的东西的痛苦。在 JS 的世界里,你必须时刻关注新的库、好的实践和趋势。这源于社区的广泛参与,他们试图为普遍存在的问题找到更好的解决方案。在这个蓬勃发展的环境中,世界某个角落的变化就能迅速改变整个行业。两年前领先的一些解决方案如今已被视为过时之作。有时这只是一种趋势,有时是范式的转变,变得更好了。评估 JS 库的可靠性本身就是 JavaScript 开发者必须学习的一项技能。因此,开发 JavaScript 就像是在投入一场不断质疑和实验的人生(这或许也是自学者在这种环境下表现如此出色的原因,因为他们适应能力很强),我相信这正是JavaScript 让我如此兴奋的原因。
所以在这篇文章中,我只是想分享我在启动前端项目时发现的最新实用设置。这里没有什么革命性的东西,这也不是新鲜事,有时你可能会不同意我的观点。但另一方面,这可能会让你对一些你从未听说过的东西感到好奇,或者找到最后的动力去尝试一下大家都在谈论的东西。
语言
我这里谈论的是前端开发,这是一个完全由 JavaScript 主导的领域,因为它是唯一能够在浏览器中执行的动态编程语言。或者直到最近 WebAssembly 的兴起。但即使我们能找到一些其他语言的类似 React 或 Vue 的框架的早期优秀成果(例如Vugu),JS 很可能仍将长期主导大部分前端应用,并且只有在计算量大(例如视频编辑或 3D)的情况下才会让位于原生实现。因此,JavaScript 是前端应用的首选语言。
但对于新项目,我现在总是使用 Typescript——它在 JS 类型之争中无疑是赢家,而且是一个非常好用的子集。它非常好用,而且实际上很容易上手,以至于我很少不用它写代码,即使是在技术面试,或者编写一个追踪我刚出生女儿尿布的微型应用程序时也是如此。它好到我开始拒绝那些不使用 Typescript 的工作,因为我不想再回到重构的地狱。对于一个三年多前还说“不相信 Typescript”的人来说,这真是一个相当强硬的举动。
说这些话的人可能根本没用过它,或者只是勉强用过。但只要真正尝试一下,你就会发现它解决了多少问题。它不仅强制执行了良好的标准实践,取代了转译的链条,还赋予了你出色的 IDE 智能,让你的效率提升十倍,并增强了你对代码的信心。这并非完美的灵丹妙药,你仍然需要测试你的代码。但我再也不用为修改一个函数的签名而绞尽脑汁了:我的 IDE 会直接告诉我,它需要在十个不同的文件中进行重构。
智力和时间的投入很少 - 或者至少是开始使用基本类型和推理 - 但在日常生活中感受到之前,回报是不可估量的。
所以底线是:我使用TypeScript进行前端项目,并且我坚信你也应该这样做。
框架
Javascript 是一种可以在浏览器中运行的语言(但也可以不在浏览器中运行,比如 node.js)。在这个特定的位置,它可以访问 DOM,即页面上所有元素的列表,并可以对其进行操作。Javascript 是命令式的:你告诉你的div
idtitle
将其内容更改为你从 XHR 请求中获得的标题。但是当你有 50 个这样的 div 和一个非常复杂的路由策略时,事情就会变得难以管理。这就是 javascript 前端框架如此流行的原因:因为它们转向了声明式范式。将一些变量链接到 50 个 div。更改 js 变量的内容,这 50 个 div 将立即更改,而你无需担心是否会发生这种情况。此外,它有助于将您的应用程序解耦为可重用的组件,将代码分成可管理的块。
如今,只有三个框架被广泛使用,其中一个的使用率远高于其他两个,我相信这是有充分理由的。我不会对它们进行比较,这取决于你的情况、契约、能力等等……对我来说,在尝试了所有这些框架之后,我最终选择了Reactnpx create-react-app myApp --typescript
。如果你从未尝试过,或者仍然觉得它晦涩难懂,我建议你在终端里输入代码,体验一下启动一个新的 React 项目是多么有趣。事实上,我所有的项目(非服务端渲染,见下文)都是用create-react-app
它启动的,它完美地融合了观点和自由。我从未感到有任何放弃的必要。
React 正在推动新的理念和实践。我推荐关注这些理念和实践,因为它们源于对程序员反复遇到的痛点的简单而强大的理解。React 的本质是优雅的。所以,没有理由不去使用最新的功能,比如 Hooks 和 context,并随着它们的发布不断更新。
说实话,我已经有一年没有编写过类组件了——这是最好的!
最后,typescript 与 React 配合得非常好,是一种输入 props 和 state 的优雅方式。
所以底线是:我使用具有最新功能的React 。
API
你是不是觉得我这么做毫无风险,只是在跟风炒作?好吧,我又要这么做了!
你并不总是对后端团队选择的 API 有发言权。但如果时机足够成熟(或者当我也参与后端团队的工作时),我总是会尝试推进 GraphQL 方向的发展。
API 是服务器在另一台机器向其提出问题时能够理解的一种语言。构建 API 的规范有很多,但就浏览器 JavaScript 应用程序与服务器之间的通信而言,我们主要看到的是 REST(或类似 REST 的实现),以及最近的Graphql。
就所提供的服务而言,GraphQL 堪称 API 中的 Typescript。它改变了我作为 React 前端程序员的工作方式,使其更加出色,以至于我再也不想回到 REST 了。对于那些只听说过它名字的人来说,我可以这样描述它:如果你构建了一个特别复杂的查询系统来选择你想要返回的每个字段——以及任何关系的每个字段,无论嵌套级别如何,你的 REST 端点会是什么样子。而且它还能自文档化、自验证,生成一个测试环境来测试它,并允许你在单个 CLI 命令中加载任何查询的 Typescript 类型。所以,是的,非常棒。
GraphQL 在各行各业都大放异彩,尤其是在 JavaScript 领域。JavaScript 拥有众多优秀的工具(我稍后会再次提到),像Apollo和Prisma这样的公司每年都在将这项技术推向新的高度。一些主流公司已经转向 GraphQL,而且这种趋势只会持续下去。
对于 Graphql 的使用(就像对待其他任何事情一样)总是持“嗯,这取决于你的项目,是否应该选择它”这种态度。但就我的前端经验而言,我还没有遇到过任何一种情况,无论多么小,Graphql 都不适合。
底线:如果可能的话,选择带有Apollo 客户端的graphQL,如果不行,我会有点抱怨。
路由
一旦你理解了这一点,就应该将数据管理(后端)与 UI 生成(前端)分开,而且既然你拥有一门强大的浏览器编程语言,那么让它管理整个网站或应用就很有意义了。单页应用就此诞生。每个 React/Vue/Angular/Whatever 项目都需要一些路由来(记住,是声明式的)将 URL 映射到这个或这个组件/页面。
对于这项任务,React Router 是最稳妥的选择。它成熟、维护良好,而且规模庞大,几乎不可能失败。现在有了合适的 Hook API,它比以往任何时候都更加强大。
但我想推荐另一个强大的库(希望它能持续更新):Hook Router。它的 API 非常优雅,易于理解,而且比我之前提到的那个领先库简洁得多。我绝对会推荐它,除非它还有一些小问题需要解决(比如,尾部斜杠的管理,这个小细节就说明它可能还不够成熟)。
总结:我很想用 Hook Router,但在专业项目中还是会用React Router。未完待续。
样式
CSS 很麻烦。因为它们依赖于未经类型检查的任意命名;因为它们依赖于全局作用域,而且你可以随意多次声明某个类——这很容易导致某些规则重载,并且难以调试。还因为它们涉及不同的专业人员,他们有着不同的关注点和技术思维(从设计师到集成商再到程序员)。
由于主流 JavaScript 框架已将 HTML 融入 JS 代码,因此它们也能在 JavaScript 中得到更好的处理,这样我们构建的元素和组件就能与其样式一起打包,而不会干扰应用程序的任何其他部分。这就是所谓的 CSS-in-js,正如我在这里提到的其他东西一样,它们将改变游戏规则,你一旦尝试过就会深深怀念它。
这里有很多选择,CSS-in-js 刚刚走出繁荣时期,有些东西似乎开始逐渐淡出人们的视野,而另一些则逐渐成为主流。最近几年,我尝试了不少 CSS-in-js,从基本的 CSS 模块到 Jss、Styletron 和 Radium。
但对我和其他许多人来说,API 的赢家是Styled-Components。它优雅、快速,让你可以编写真正的 CSS,同时以字符串模板的形式从 js 中注入任何内容。组件化和复用堪称完美。与使用原子命名约定的大型样式表相比,它略有不同,因此集成人员必须适应并开始在代码库中工作——不过,由于它仍然是常规的(sa|le|c)css,因此这种转变并不大。
虽然我很喜欢Styled-Components,但我认为Emotion更胜一筹。它们提供的 API 与 Styled-Components 相同,但增加了一些其他特性,比如CSS
prop,而且根据我的经验,它们与 SSR 的配合效果更好。
UI工具包
在构建前端应用程序时,编写 UI 元素是工作的重要组成部分。由于程序员并非设计师(他们可能认为自己是设计师,但实际上并非如此),而且您可能想将时间花在更有趣的问题上,因此使用 UI 工具包始终是一大优势——无论是为了快速完成 POC,还是在产品较为通用的生产环境中使用。
市面上有太多了,你不可能全部都看完。有些看起来成熟美观,有些则有些乏味。对我来说,关键在于:良好的组件属性 API、美观的样式、丰富的组件种类以及合适的样式设置能力,这样我就可以根据自己的设计或客户需求调整套件,从而为大家节省大量时间和金钱。
我尝试过Material UI(该领域最大的 UI 设计平台之一)、Semantic UI、Evergreen、Blueprint、Atlaskit、Ant Design、Uber 的 One,甚至React-Bootstrap(嗯,很久以前就尝试过了)。我必须承认,我对这些 UI 设计非常痴迷,并且总是在寻找新的最佳解决方案。
我非常不喜欢Material UI 。他们的主题系统——在我看来——既痛苦又奇怪。我对Ant Design 的评价更好一些,但同样,他们的 sass 主题系统远非理想(参见上一节),而且用 SSR 设置起来也有点问题。
但今年早些时候,我偶然发现了Chakra Ui,到目前为止,它满足了所有要求。它制作精良,外观精美,功能多样,最重要的是:它基于Emotion构建,并遵循Styled System Theme 规范进行主题设置,使用起来非常方便。每个组件都导出了所有有用的 CSS 属性,因此您可以在此处或此处添加边距,而无需使用style
prop 或添加 CSS。
除此之外,还有人做了一个可视化编辑器https://openchakra.app/,它基于 Chakra Ui,可以生成 React 代码。我一般不太相信这些可视化编辑器,但还是值得一试。
底线:使用任何让你开心的东西,但我将继续使用Chakra Ui启动我的项目,如果你还没有检查过,你应该检查一下。
状态管理
现在是时候引入状态管理了。一旦你的应用组件化、解耦良好,你就会开始思考如何注入、更新和响应一些全局变量。例如,在许多不显眼的地方重复出现的用户数据——或者帖子列表、星星数量、UI 状态、菜单、顶部栏按钮等等……
自从 React 引入 context API 以来,你可以在组件树的任意层级注入状态,并让你的组件对其做出响应。然而,这种简单的状态共享可能会变得非常混乱:你很快就会发现,调试这种共享状态通常非常困难。另一个缺少的关键点是选择器context+state or reducer hook solution
的概念:当你的共享状态发生变化时,所有监听此状态的组件都会重新渲染——如果此状态是一个对象,你就无法将组件链接到它的特定键。因此,你的组件每次在任何键发生变化时都会重新渲染,即使它没有使用这些键。当然,你可以使用记忆化来解决这个问题,但这会变得非常混乱。
全局状态管理的黄金标准当然是Redux 。它由Facebook 的 Dan 开发,将 Flux 设计、不可变性和最佳实践与出色的调试体验相结合,甚至还提供了一个 Chrome 扩展程序来跟踪状态变化的每一步。对我来说,当许多不同的开发人员在同一个应用上工作时,Redux 会在大型项目中大放异彩。如果你使用 React,你应该了解 Redux,因为你在职业生涯中总有一天会用到它。
然而,Redux 并非完美无缺。最主要的缺点在于开发者体验。Redux 理解和设置起来并不难,但需要大量的样板代码。它极其冗长——这种冗长有时也是一种优势——但反复使用会让人感到乏味。添加异步操作(异步操作总是需要的)需要在 Redux 设置中添加 thunk 或 sagas——这会增加更多代码编写。
现在,还记得我说过 GraphQL 拥有出色的 JavaScript 工具吗?Apollo 在其 GraphQL 客户端中提供了许多不错的功能,其中之一就是非常强大的缓存系统。您执行的每个查询都会将从服务器返回的所有内容保存在内存中,这些内容经过反序列化并按类型和 ID 存储。这样,即使查询不同(或者对象嵌套很深),它也会更新其本地版本。然后,每个依赖于包含已更改对象的查询数据的组件都会在缓存更新时进行更新。同样,这非常非常强大。在发生突变时,您可以轻松地自行更新缓存以实现乐观更改 - 或者请求更新的数据作为响应,Apollo 会为您完成此操作 - 只要您查询每个缓存对象的 ID。
因此,在使用 Apollo 构建应用时,您无需将数据存储在全局状态中(这占用了 Redux 的大部分资源),只需依赖 Apollo 查询,即可让奇迹发生。这是 Graphql 的一大优势,也是它对前端程序员如此有利的原因。我是否应该补充一下,有一个非常棒的 Chrome 扩展程序可以监视和调试缓存?Apollo 还提供许多其他功能,但这些功能超出了本文的范围。
但是那些不是来自 API 的数据怎么办呢?比如 UI 状态?这些数据可能很少。然而,即使是这样,我也不太愿意使用简单的上下文状态,或者完整的 Redux 机制。
Apollo 提供了一种使用缓存存储任何所需数据(甚至是本地数据)的方法,这看起来似乎很适合这项任务。但是,对于简单的状态更新,声明 graphQL 类型、突变和查询感觉很奇怪。我尝试过,但最终还是选择了其他地方。
对我来说,解决方案来自一个非常友好(而且很纯素)的库 Easy-Peasy。它在底层使用了 Redux 和 Immer,但利用了 React Hooks 和 context API 来提供一个非常直观的系统。你用你的数据和操作构建一个对象(并用 TS 定义其类型),然后在一边获取一个 Provider,在另一边获取一些 Hooks,这些 Hooks 是操作或值的选择器。所以,它集所有优点于一身:简单的 API、支持 Hooks、支持 TypeScript、支持多个全局状态、获得真正的选择器,而且——最重要的是:你可以使用 Redux 调试工具来实现完美的调试工作流程。
所以底线是:我使用 Apollo 缓存来存储服务器发送的数据,使用Easy-Peasy来存储其余数据 - 或者几乎所有其余数据,请参阅下一部分。
表格
表单。有时候,管理useState
页面上每个字段都很难。然后还有验证,包括干净/脏值检测、错误消息、规则等等……一旦你处理过一个表单,你就会明白正确处理它的底层复杂性。
所以我们需要用一个库来实现这一点。而且这个库要简洁、不臃肿,并且支持 Hook。嗯,这里就有一个:React Hook Form。它优雅、强大、简洁。更棒的是,Chakra Ui 文档里有一页介绍如何使用它来实现 Hook Form。是不是感觉一切都完美契合?
Hook Form 是我为状态管理三角设计的最后一部分。我在每个创建/编辑页面上都使用它,并将其直接与 Apollo 的查询/修改功能连接起来。
SSR 和预渲染
与所有 JS 框架一样,在客户端构建页面有一个缺点:机器人无法抓取它的元标签,而 Google 机器人虽然应该能够执行 JavaScript,但执行方式并不一致(例如超时等等)。所以最好不要依赖客户端进行 SEO,而且共享预览也是不行的。
为此,您需要为机器人提供完整构建的网站版本。众所周知,有两种方法可以实现这一点。一种是,在将网站发送给任何客户端(包括机器人)之前,在服务器上构建整个网站,然后让 JS 从浏览器进行管理——这就是SSR(服务器端渲染);另一种是,仅在机器人请求时才在云端渲染网站,使用一些无头 Chrome 实例来完成这项工作——这被称为预渲染。
那么该使用哪一个呢?
这要看项目具体情况。但实现完整的服务器端渲染 (SSR) 需要很多技巧,而且修改现有代码库来启用它非常麻烦。根据我的经验,预渲染通常更容易实现,主要是因为它将渲染问题从 React 代码库中抽象出来。所以这不是前端的问题,而是架构/后端的问题。如果团队需要,有一些 Docker 镜像可以开箱即用地实现预渲染。
说到完整的 SSR,有一个主流框架做得很好,那就是Next.js。我对它的抱怨只在于路由系统:它们会遵循文件系统的规则,而我并没有抛弃 PHP 回到这个约定地狱。除此之外,结合 Apollo 的使用,它非常高效,而且它们开箱即用,拥有良好的代码拆分功能。
上次构建 SSR 时,我使用了另一个名为Razzle的工具,当时感觉它更优雅。虽然 Razzle 很有前景,但它的维护并不好,因为没有任何公司支持,而且支持也有点滞后。值得一试,但对于专业的、不承担风险的项目,建议使用 Next。
底线:如果只是为了 SEO 和机器人,我建议使用预渲染。对于最终用户来说,SSR 意味着网站首次渲染时体验会略有提升。这需要付出一些努力,但收获不大。
静态站点渲染
如果您的网站规模不大或更新频率不高,您可能会对静态渲染感兴趣。这意味着一次性对网站包含的所有页面进行 SSR 渲染,然后通过静态主机提供所有内容。无需后端或 API——至少对于您的最终用户而言——因为您需要的所有数据在渲染时都已包含在网站中。
顺便说一句,这不仅限于前端。我静态渲染了一个庞大的法语同义词 API(超过 35000 个 JSON 文档),但可能永远不会再渲染了。
我不是这方面的专家,但我非常讨厌这个领域的领导者 Gatsby,因为他们的数据加载 API 很奇怪。就我的需求而言,我倾向于使用 Next(这个 SSR 框架有一个非常简洁的静态渲染功能)或React Static,后者功能非常丰富。
总结:对于博客或简单的演示网站(数据变化不大)来说,静态渲染非常合理。你可以看看React Static,它提供了我目前找到的最自然的 DX。
最后的话
还有一些事情我现在没精力去着手。比如,我建议你尽早将Storybook集成到任何代码库中,除了一些零散的业余项目,尤其是涉及到一些 UI 编码的时候——这样可以省去很多麻烦。
我们可以讨论测试,或者项目的文件组织。不过,那留到下次再说吧。
在结束之前,我想强调一下,学习新工具是多么令人疲惫,在亲身体验之前,回报是多么渺茫。这是一种很自然的态度。我们曾经学会了适应,学会了围绕着曾经遇到的问题不断成长,直到问题消失殆尽。但这些问题依然存在。当有人告诉我们“这个库太棒了,它解决了这个问题”时,我们会想“我已经有解决方案了”——也许我们应该尝试一下。还记得吗?JQuery 曾经一度被认为是我们构建任何东西所需的一切,而现在我们用上了 JS 框架,再也不想回到它了。
JavaScript 要求我们保持敏锐的思维,永不停止探索。每天都有新的解决方案被发现,而当它们最终被大众接受时,通常是因为它们解决了你可能也遇到的实际问题——即使你看起来并没有遇到。花一个小时去尝试一下永远是个好主意。
Lachlan Donald 在 Unsplash 上拍摄的照片
文章来源:https://dev.to/olup/the-tools-for-the-job-how-i-code-frontend-apps-in-2020-e03