[译]2022年前端最佳开发策略

1,506 阅读15分钟

原文链接:The best frontend development strategies in 2022

原文作者:Tobias Uhlig

译者:giriawsh (Giria)

个人翻译,如有错误,欢迎指正。文章仅代表原文作者观点。

这篇文章具有一定挑战性、观点倾向性和发人深省性。它涵盖了许多您很可能不知道的新鲜内容和想法。

内容

  1. 介绍
  2. 计算机或智能手机上有多少个内核?
  3. 浏览器使用多少个内核?
  4. Web Workers API
  5. “应用程序worker是主要参与者”范式
  6. worker可以访问 DOM 吗?
  7. 是否有workers可以访问 DOM 的例外?
  8. 如何以聪明的方式创建虚拟 DOM?
  9. UI 开发可以直接在浏览器中进行吗?
  10. TypeScript 有未来吗?
  11. React 有什么问题?
  12. 多窗口应用
  13. 我们需要自己实现worker 设置吗?
  14. 最后的想法
  15. [更新] 如何开始?

1. 介绍

我将尽我最大的努力创建一系列相互关联的条目,您可以按照这些来了解前端开发应该如何工作。 我也会尽量保持这篇博文尽可能简单,以便“非开发人员”也可以读懂绝大部分。

2. 计算机或智能手机上有多少个内核?

你可能看到的所有CPU都长这样:

img

举个例子:如果你在使用一台Mac,你可以点击左上角的苹果图表,然后选择关于本机,它将会向你展示如下的类似内容:

Processor 3,2 GHz 8-Core Intel Xeon W

一台iPhone手机有6个内核。

太长不看:每台计算机或智能手机都有多个可用内核。

这意味着您可以并行运行多个线程。

你会造一辆汽车只用一个发动机气缸吗?

如果你的回答是:“当然不是!这车会慢很多!”,那么你应该仔细阅读这篇文章。

3. 浏览器使用多少个内核?

就其本身而言,浏览器每个选项卡/窗口仅使用一个内核。

这意味着:你的Angular或者React应用长这样:

img

在你的应用程序中运行的 JavaScript 任务越多,它就会变得越慢。最糟糕的情况是 UI 完全冻结,您的一个内核处于 100% 并且所有其他内核完全空闲。

这根本无法扩展。

[旁注] 如果您要创建简单、小型且相当静态的网站或应用程序,此设置就足够了。

4. Web Workers API

有了Web Workers ,我们便可以在后台线程中运行脚本操作,这个线程是与 Web 应用程序的主执行线程分离的。

这样做的好处是可以在单独的线程中进行繁重的处理,从而允许主线程(通常是 UI)运行而不会被阻塞/减慢。

--Web Workers API - Web APIs | MDNWeb Workers makes it possible to run a script operation in a background thread separate from the main execution thread

W3C 和 WHATWG 将Web Worker设想为可以长时间运行的脚本,同时不会被响应点击或其他用户交互的脚本中断。

防止这些workers被用户活动打断,在他们在后台运行长时间任务的同时,应该允许Web页面保持响应。

Worker 的最简单用途是在不中断用户界面的情况下执行计算量大的任务。

--Web worker - WikipediaEdit descriptionen.wikipedia.org

因此,使用 worker 我们实际上可以并行使用多个内核并结束单内核无法扩展的噩梦。

为了让下面的名言真正深入人心:

Worker 的最简单用途是在不中断用户界面的情况下执行计算量大的任务。

我们需要思考一个问题:“最昂贵的任务是什么?”

答案很简单:UI 框架或库本身以及我们用它构建的应用程序。

这导致了这样一个想法:让我们把所有能做的都移出主线程,这样主线程就可以完全专注于它想要做的事情:操作 DOM

如果您的应用程序不再在主线程中运行,则没有任何东西可以减慢或阻塞您的 UI 或造成内存泄漏。

这种想法引出了以下概念:

5. 应用程序worker成为主要参与者

为了解决这个性能瓶颈,我们希望主线程尽可能空闲,以便它们可以完全专注于渲染/动态操作 DOM:

img

现在可能发生的最坏情况是您的App Worker会变慢,并且此内核以 100% 的速度运行。但是,这不会影响您的 UI渲染(渲染线程 → 主线程)。

单页应用程序 (SPA) 的最佳解决方案可能如下所示:

img

为了防止app worker处理过多的逻辑,我们可以选择使用虚拟 DOM 工作线程(图中的VDOM Worker)并在那里计算状态转换(state transitions)之间的增量更新。

对于具有非常空闲的app worker的应用程序,您也可以直接在应用程序工作器内部运行虚拟 DOM 引擎。

我们也可以使用data worker。如果我们有一个远程数据存储并且想要在本地对我们的数据进行排序/分组/过滤,这些计算可能会在那里进行。

这篇博文介绍了如何在保持相同 API 的同时使用可选的 worker:

JavaScript Development: Making a Web Worker optionalIn case you have a lot of JavaScript related logic running inside a main thread or a Web Worker

6. worker可以访问 DOM 吗?

WorkerGlobalScope 中,windowwindow.document 是未定义的。

意味着:您根本无法直接访问真正的 DOM。

所以,我们在这里基本上有 2 个选择。

选择 1 :在 worker 中重新写一整个 DOM API。在我看来,这是一个坏主意。workers不知道 DOM 是有原因的,而且在worker里有大量的逻辑经常变化。 如果您按顺序触发许多操作,DOM OP 将变得异步,它将导致大量worker触发 postMessage。唯一的好处是你可以像以前一样继续编写你的应用程序,但这是有问题的。稍后我将介绍如何做得更好。

实际上有一个项目就是这样做的:

GitHub - ampproject/worker-dom: The same DOM API and Frameworks you know, but in a Web Worker.

更聪明的方法是选项 2:坚持worker不应该知道真实 DOM 。

这使得使用虚拟 DOM 成为绝对必要的。

我经常在社交媒体上阅读,看到诸如“vdom 很糟糕!”之类的帖子。这是不正确的。这在很大程度上取决于它是如何实现的。

Angular 和 React 中的主要表现是基于 xml 或 JSX 的模板。这些模板需要被转换成我们可以使用的数据结构。

JavaScript 既不快速也适合用来解析字符串。解析模板的代价如此昂贵,以至于服务器端渲染 (SSR) 再次流行起来。我 20 年前就做过这个,创建了一个基于 PHP 的 CMS,它生成了 html 输出文件。

您可能会争辩说,今天有可以处理更多客户端的云服务,但是rich/fat/thick客户端的概念仍然完全有意义。

7. 是否有workers可以访问 DOM 的例外?

确实有一个:

OffscreenCanvas - Web APIs | MDNThe OffscreenCanvas interface provides a canvas that can be rendered off screen.

Workers可以接管canvas DOM 节点的所有权。这在 Chromium 中已经得到有效应用,Safari (Webkit) 和 Firefox也 正在积极实施它。可能还需要 6 个月,所以这是 2022 年的主题。

您可能注意到最终workers设置中的黄色框部分 → Canvas worker。如果你想深入研究这个可以看这里:

Rendering 3d offscreen: Getting max performance using canvas workers

8. 如何以聪明的方式创建虚拟 DOM?

虽然 JavaScript 不擅长解析字符串,但它擅长处理嵌套的对象/数组结构。这种格式有一个您最熟悉的名称:JSON

如果我们坚持使用基于 JSON 的 vdom 语法,则无需在 UI 中一遍又一遍地解析代价昂贵的模板,甚至无需将此部分移至构建步骤。

如果运用得当,虚拟 DOM 中将没有变量、if/else 语句、绑定、方法、循环或任何类型的逻辑。你永远不会看到超过 1000 行代码的模板(看看 Angular)。

通过编程方法,您将在它所属的地方使用逻辑:在 JavaScript 内部。例如。创建列表时,您可以先创建一个 vdom 骨架,一旦加载了数据存储,就遍历记录并动态创建新的虚拟 DOM 节点。

那么我们就可以很容易实现无限滚动或其他高级功能了。

你可以在这里找到更多的示例分析:

Your benefits of working with JSON based virtual DOM

New formatting concept for JSON based virtual DOM

虽然通过这样的编程方法对于低级 vdom OPs 很有意义,但我们绝对更喜欢声明式方法来创建我们的应用程序。

为了实现这两者,我们唯一需要做的就是在 vdom 之上添加一个声明性抽象层:一个组件树。

意思是:您只能在创建组件类时使用 vdom。对于创建应用程序,您可以坚持使用组件树。

9. UI 开发可以直接在浏览器中进行吗?

当 React 在 5 到 8 年前正当流行时,浏览器在支持最新的 ECMAScript 功能方面真的很糟糕。例如。不支持类 (ES6) 或 JS 模块。

在这一点上,将 UI 开发转移到 node.js 是完全有意义的。

意思是:您可以使用最新的语言功能,并以繁琐的构建步骤为代价将您的代码编译/转换为浏览器可以理解的 Javascript。

浏览器供应商在追赶方面做得很好。今天,许多闪亮的新功能可以直接使用,并且大多数 stage3 提案确实可以立即实施。

在 worker 范围内,JS 模块在 Chromium 中工作正常。 Webkit (Safari) 也完成了实现,但它仍然仅限于 Safari Technology Preview 版本。 Mozilla (Firefox) 正在积极推动它。

时至今日的对比来看,构建步骤成本高昂,UI 库或框架的开发模式不再需要这些步骤。

优点很明显:

  1. JavaScript 旨在成为浏览器引擎理解的唯一编程语言。
  2. 以浏览器无法理解的方式编写 JS 是错误的。
  3. 通过将 UI 开发带回浏览器,我们可以实时调试我们的真实代码,而无需任何构建/转换或使用源映射。
  4. 我们不需要热模块更新。

特别是创建和调试代码将再次变得有趣,因为我们可以确保没有外部因素导致错误。

未来肯定仍然需要像 webpack 这样的工具来创建 dist/production 输出。但是,它们将是构建工具,而不是运行时环境。

从 node 到 deno 的切换将进一步推动这一点。 CommonJS 迟早会消亡。一旦 deno 有了包管理器,越来越多的包将使用可以在浏览器中运行的语法(例如,不使用bare module specifier(译者注:就是我们常用的import moment from "moment") → 使用无效路径和没有文件扩展名的import)。

10. TypeScript 有未来吗?

这可能是本文中最具争议的部分。 JS 社区分成俩拨人:一些人喜欢使用 TS,而另一些人拒绝接触它。这值得讨论。

我的观点是:

现在,在 node 中开发 UI 并且无论如何都需要构建/转换步骤时,使用 TS 很好。

一旦 UI 开发重新进入浏览器,这将发生根本性的变化。

你会为了使用 TS 而设置一个完整的构建步骤吗?

就目前来讲,这太昂贵了。

现在的问题是:TS 不是 Web 标准。而且还没有计划将其实施到浏览器中。

历史已经多次告诉我们,基于 Web 的技术,而不是基于 Web 标准会发生什么:它们在某个时候就消失了。 MS Silverlight 就是一个完美的例子。

一般来说,类型检查是一件好事。主要问题是 Angular 和 React 不使用基于 JSDoc 的注释,这使得 IDE 在编写代码时就可以给你警告。

实际上我们甚至可以使用基于 JSDoc 的注释来“伪造”TS:

How to Write TypeScript Interfaces in JSDoc Comments

这绝对是一个我们可以讨论的有价值选项。

如果你真的想直接在你的编程语言中进行类型检查并且不关心构建步骤代价,Dart2 不是更合适的方式吗?

Dart2 完全支持 worker,所以我们也可以让 worker setup在那里运行。它在移动设备的优势还包括 AOT 编译。

11. React 有什么问题?

公平地说:在 React 之前只有 JQuery。当 React 流行时,这是一个相对来讲很大的改进,毕竟React 是第一个使用虚拟 DOM 流行的库。

那么,为什么我们不应该在 2022 年使用 React?

  1. React 在主线程内运行。
  2. React 代码库基于 CommonJS → 如果没有构建步骤,它无法在浏览器中运行。
  3. 没有 JSDoc comments。
  4. JSX 模板解析起来非常昂贵。甚至还有像 Svelte 这样的编译器可以将它移到服务器端。
  5. React 不暴露核心。一切都是扩展自 Component ,这完全没有意义。
  6. 无缘无故的状态管理太难了。
  7. render() 方法肯定是有问题的。

让我更深入地解释一下:防止状态更改触发render太复杂了。如果 React 组件包含子组件(render() 中的自定义标签),我们很容易创建新实例,如果您使用key不是那么小心。

如果你有很多state props,例如他们都会对该物体的位置发生影响,那么你就需要在 JSX 中添加相当多的对应逻辑。

React 只是一个库,而不是一个框架。意味着:组件几乎就是其中的所有内容。没有像这样的逻辑层次链:

core.Base -> component.Base -> button.Base -> tab.header.Button

一旦解决了render()的问题,您就可以为您想要创建的任何内容选择最合适的class。例如。 Container 有一个 vdom 对象,其中包含对自身子项的 vdom 的引用。然后我们可以直接更改那个子组件的 vdom,而无需重新创建子项基于 JS 的实例。

在这一点上,状态管理也就变得微不足道,我们甚至不需要hooks。这种情况下,同时更改许多配置并确保最多 1 次调用的 vdom 引擎便是关键。

12. 多窗口应用

将workers setup切换为使用 SharedWorkers 可以进一步增强该概念:

img

这使我们能够在不同的浏览器窗口中移动整个组件树,同时将它们的 JS 实例保持在原位。

这就是无需后端的多窗口状态管理。

也将使得跨窗口拖放成为可能的。

本博客中有几篇文章介绍了详细信息。

13. 我们需要自己实现worker 设置吗?

自己实施所有提到的想法可能需要数年时间。

你很幸运,我已经为你写好了。生态系统内超过 12,000 次提交,完全获得 MIT 许可:

GitHub - neomjs/neo: The application worker driven frontend framework

这包括一个远程方法访问 API,它使您能够通过Promises(消息顶部的抽象层)直接调用不同 worker 或 main 中的方法。

大量组件以及控制器、视图模型、应用程序和其他实用程序类已经就位。

您不需要任何第三方库来支持架构设计模式,如 MVVM、Observable 等。

特别是状态管理非常简单(提示:一个类配置系统 a class config system )。

许多演示应用程序和示例等待您探索,以及40 多篇博文:neomjs.github.io/pages/

CLI 也是高级的:您可以使用一行代码创建一个新的应用程序(工作区): npx neo-app 。我们甚至拥有跨应用程序拆分模块,因此将多个应用程序放在一个页面上时几乎没有开销。

14. 最后的想法

实际上,您不必等到 2022 年,现在就可以使用这些想法将前端开发提升到一个新的水平。一些公司和开发人员已经在这样做,并正在抢占先机将新技术转变为业务优势:

neo.mjs nominated for “The most exciting Use of Technology”

大多数开发人员仍然不知道neo.mjs 项目的存在,这是实在不公平。

我很想看到有人证明我在这些概念上是错的!

为此,您需要创建您的第一个基于 Neo 的 PoC 应用程序。在这种情况下,我很乐意查看您的代码。

对于运行时的动态 DOM 操作,neo 是目前最快的选择。特别是在涉及大型复杂应用程序时。

欢迎您加入项目 Slack 频道:

Join neo.mjs on Slack

最好的问候和希望您编码快乐,

Tobias

P.S.:今年九月会有一些大的变化,它也将很好地调整我的个人情况。

15. [更新] 如何开始?

我刚刚创建了一个关于如何使用这项技术进行实际应用程序构建的教程:

Define a web 4.0 app to be multi threaded