web应用程序(web应用系统开发)
用一个框架不就等于反复造轮子吗?
本文由原作者授权,InfoQ翻译分享。转载请注明作者、译者信息及出处。
以前流行的是Angular,然后React,现在Vue.js……其他的像Ember,Backbone或者Knockout都快消失了。有些标准,比如Web组件,很少使用。好像每年都会发布一些新的框架,比如Svelte和Aurelia,每个框架在服务器端都有对应的对象(NestJS,NextJS或者Nuxt对应开头那些,Sapper对应Svelte等等。).更不用说非Javascript的Web框架(如Django、Spring、Laravel、Rails等。).甚至还有框架之上的框架(Quasar,SolidJS),为框架生成组件代码的框架(Stencil,Mitosis),NCDP(无代码开发平台)。
这种多样性让开发者和技术选择决策者感到困惑,他们想知道哪种技术值得学习。
网上经常出现一些比较这些框架的文章,好像是为了帮助我们解决这个困惑。但大多数作者通常是有偏见的,因为他们可能“使用了这个框架”,但“只尝试了一些其他框架”。低偏见的作者总会得出“视具体情况而定”的结论(取决于性能、工具、支持、社区等。),这其实是一个非结论性的结论。
即使有些基准测试是基于同一个应用比较不同的框架,也很难得到真实的结果,因为这个基准测试是受被测应用(比如待办应用)限制的。
框架看起来像宗教(或政治):每个框架都假装为开发人员提供解决方案,但每个框架都不一样。它们都声称为应用程序提供了最好的前景,但对于哪一个才是真正名副其实的,存在着无休止的争论。每个框架都要求你遵循特定的规则。它们之间可能有相似之处,但是从一个框架切换到另一个框架总是很困难的。
现在,让我们来看看“无神论”的框架方法:不使用框架。
从哪里讲起?
我有超过25年的专业软件开发经验。此外,本文将基于构建真正的纯JS Web应用程序(前端和后端)的经验。
为什么不使用框架?
其实这个想法挺新的。回到2017年,Django Web framework的联合创始人Adrian Holovaty谈到了他的框架“疲劳”以及他为什么离开Django建立自己的纯JS项目。
有人可能会问,为什么会有人想不用框架开发Web应用呢?为什么不在别人花费了多年时间和精力的成果基础上发展呢?还是因为NIH(不是这里发明的)综合症,大家都想建立一个定制的框架?
开发商并不比一般人更倾向于自找麻烦。事实上,他们可能比任何人都懒:他们只想写更少的代码(这样他们可以犯更少的错误)和自动化(以避免人为错误)...
但他们也希望敏捷,即能够轻松快速地解决问题。
虽然“快速”似乎是框架所承诺的(为您搭建脚手架,增加可靠性),但它并不是免费的:他们希望您签署一份合同,同意支付“税”费,并将您的代码放入“孤井”(术语“税和孤井”来自Akira Sud,IBM Carbon系统设计团队负责人< https://github . com/Carbon-design-system/Carbon-we b-components # readme & gt;)。
框架税
使用该框架是有成本的:
遵循它们的 API 规则,这样它们就可以向你提供服务。这就是框架的工作方式:你的代码必须遵守某些规则,包括或多或少的样板代码。你每天要想的不是“如何做这件事”,而是“如何让框架做(或不做)这件事”。如果你规避了这些约束,风险就由你自己承担:如果你通过直接调用底层 API 来绕过框架,就不要指望它们能理解你的意图,也不要指望它们的行为能保持一致。所以,框架会让你“专注于业务”是一个虚假的承诺:实际上,框架的事情你也没少操心。如果你想要以下这些东西,就不得不强制进行升级:
1)想要一个新功能(即使不需要所有功能,也要全部升级);
2)想修复一个bug;
3)不想失去框架的支持(随着新版本的发布,你的应用所依赖的版本将被抛弃)。
如果框架出现了bug,但没有明确的计划修复日期,会让你感到非常沮丧(也可能让项目面临风险)。第三方提供的框架库(比如小部件)或者插件也不例外。如果你继续使用旧版本,它们与你的应用程序的兼容性会越来越差。对于框架维护者来说,维护向后兼容已经成为一件非常麻烦的事情。他们发现开发自动升级代码的工具(Angular的ng-update,React的原生升级助手,脸书的jscodesshift等)会更有利可图。).
需要学习如何使用它们(它们能做或不能做什么、它们的概念、API、生态系统、工具),包括了解在新版本中可能发生的变化。如果你选择的是当前最流行的框架,这可能会容易些,但你不可能了解一个框架的方方面面。此外,炒作也从来不会消停:如果你决定在一个新应用程序中使用另一个框架(或者更糟的是,从一个框架迁移到另一个框架),那么你在旧框架上所有的投入都将归零。这就是为什么很多企业项目会缺乏活力,即使每个项目都可能与前一个项目不一样。已故的 David Wheeler 曾经说过:“保持兼容性意味着有意重复别人的错误”。将控制权委托给框架,这是对框架缺陷的妥协:你可能无法做任何你想做的事(或防止框架做你不希望它们做的事情)或者你也许不能获得你想要的性能(因为额外的分层、普适性、更大的代码体积或向后兼容性需求)。技能零散化。很多开发人员要么不太了解底层 API(因为他们总是使用框架提供的东西),要么活在过去(只知道过时的知识,不知道最新的改进和功能)。“工具法则”常常导致过度设计,为简单的问题构建复杂的解决方案,而构建简单解决方案的知识逐渐零散化。在指南的指导下,我们失去了(或者没有获得)好的软件设计(原则、模式)文化,并失去(或者没有获得)构建重要工程的经验。就像 CSS 框架(Bootstrap、Tailwind 等)的用户缺乏 CSS 技能一样,Web 框架的用户也注定缺乏现代 Web API 和软件设计经验。
一旦你把钱放进相框,就很难再拿出来了。
框架孤井
除了必须支付“税收”费用才能获得框架的好处之外,如果框架不标准化,还会带来另一个问题。
因为它们强迫你遵循框架规则——而每一条规则都是不同的——这意味着你的应用将被绑定到一个专有的生态系统,也就是用专有的API(及其升级过程)锁定你的应用代码。这对您的项目来说是一个高风险的赌注,正如它们所暗示的:
没有可移植性:将代码迁移到另一个框架(或者一个有重大变化的新版本,甚至是不使用框架)将是非常昂贵的,包括可能需要进行重新培训的成本;你的代码与其他框架运行时或你想要使用的其他框架组件库没有互操作性:由于规则不同,大多数框架彼此之间很难实现互操作。
当然,在项目初期,你可以选择最流行的框架。这对于短期项目来说可能是可以接受的,但对于长期项目来说就不是了。
画框来来去去。从2018年开始,每年会有一到三个新框架替换旧框架。
然而,在标准框架中没有隔离井。在Web平台(即浏览器框架)上,使用标准的Web API可以降低你的投资风险,因为它们可以在大多数浏览器上运行。即使不是所有浏览器都支持,也还是可以用polyfill来补偿。
例如,今天的Web组件是可移植的(可以在几乎所有浏览器中使用)和可互操作的(可以被任何代码使用,包括专有框架),因为它们可以被封装到任意的HTML元素中。它们不仅具有更好的性能,而且它们的运行时(自定义元素、shadow DOM、HTML模板)也作为浏览器的一部分运行,因此它们已经存在(不需要下载)并且是本机的。
很少有开发人员试图逃离框架的孤立井。
那么框架本质上就是不好的吗?
如果你为实现应用程序逻辑创建了自己的框架,你不能说框架不好:任何应用程序都需要实现自己的业务规则。
如果满足以下条件,则框架是好的:
是应用程序特有的:任何应用程序最终都会设计自己的“业务”框架。成为标准,例如,Web 平台就是一个标准的 Web 框架,而 Web 组件框架(lit、stencil、skatejs 等)最终构建的组件都符合这个标准。添加一些其他解决方案(包括其他框架)所缺少的独特价值。对于这种情况,你几乎没有选择,这些附加价值证明了隐含的锁定成本是合理的。例如,一个特定于操作系统的框架遵循了操作系统的标准,除此之外没有其他方式可以获得能够满足需求的应用程序或扩展。用于构建非关键(短期、低质量预期,并且可以接受“税费”和“孤井”)应用程序。例如,使用 Bootstrap 构建原型、MVP 或内部工具。
去框架化的目标
简单地说,避免使用框架构建应用程序的目标是:
通过避免框架的“一刀切”约束来最大化灵活性。此外,去掉规则的约束,提升应用程序的创造力。大多数使用 Bootstrap 开发的 Web 应用程序都属于此类,因为它们很难摆脱预定义组件和样式,最终将很难从其他角度思考问题。尽量减少对炒作过度的框架的依赖。不被框架锁定,才能够避免可移植性和互操作性方面的问题。只在需要时进行最细粒度的操作(例如,不依赖框架的刷新周期),并减少依赖项,只使用一些必需的轻量级库,以此来最大化性能。
当然,我们的目标不可能是“推倒重来”。让我们看看该怎么办。
框架之外的选择
那么,如何在没有框架的情况下开发一个应用呢?
首先要明确一个反目标:不要把“构建没有框架的应用”和“替换框架”混为一谈。框架是托管任何应用程序的通用技术解决方案,因此它们的目标不是您的应用程序,而是所有应用程序。相反,脱离框架可以让你更专注于你的应用。
不使用框架开发应用程序并不意味着重新实现框架。
要评估在不使用框架的情况下构建一个应用程序的难度,我们需要理解:它没有构建框架那么难,因为以下不是我们的目标:
构建专有的组件模型(实现特定组件生命周期的容器);构建专有的插件或扩展系统;构建一个奇特的模板语法(JSX、Angular HTML 等);实现通用的优化(变更检测、虚拟 DOM);特定于框架的工具(调试扩展、UI 构建器、版本迁移工具)。
所以,构建一个普通的应用并不是一个“推倒重来”的艰巨任务,因为这个“轮子”主要是关于API/契约、实现、通用引擎以及相关的优化调试能力等。放弃通用目标,专注于应用的目标,意味着你可以摆脱大部分目标,这才是真正的“专注于你的应用”。
那么,我们如何设计和实现一个普通的应用程序呢?因为大多数应用程序都是使用框架构建的,所以如果没有这些熟悉的工具,设计一种方法来实现类似的结果确实很困难。您必须:
改变你的想法:不要使用特定于框架的服务。对于一个普通的应用程序来说,你可能不需要这些服务。不需要变更检测,直接更新 DOM 即可……用其他技术替代方案来执行原先使用框架执行的常见任务(更新 DOM、延迟加载等)。
一些作者,如杰里米·利克内斯(Jeremy Likness)或克里斯·费迪南迪(Chris Ferdinandi)(被称为“JS极客”)也提到过这个话题。然而,根据定义,任何普通的应用程序都可以选择(或不选择)使用这些技术中的一种,这取决于需求。例如,MeetSpace的作者只需要使用标准API。
接下来,我们来看看一些常见的“解决方法”。
标准
API是一个“好框架”,因为它们:
具备可移植性:它们在任何地方都可用,如果不可用,可以通过 polyfill 的方式实现。具备互操作性:它们可以与其他标准交互,并被用在专有代码中。长期存在:由多个行业参与者设计,而不只是一个。它们被设计得很好,一旦发布就会一直存在,使用它们的风险较小。在大多数情况下在浏览器中都是立即可用的,避免了下载过程。在某些情况下,你可能需要下载 polyfill。但是,与专有框架(注定会越来越不流行)不一样的是,它们的可用性会越来越高(逐渐降低下载的必要性)。
在选择编程语言时,我们应该关注标准。经过多年的发展,Javascript现在包括了其他编程语言中出现的特性,比如class关键字和通过JSDoc注释(比如@type)提供的有限类型检查支持。
很多编程语言都可以编译成Javascript:Typescript、Coffeescript、Elm、Kotlin、Scala.js、Haxe、Dart、Rust、Flow等。它们都给你的代码增加了不同的价值(类型检查、额外的抽象、语法糖)。普通应用出现的时候应该用吗?为了回答这个问题,让我们看看它们是否暗示了与框架相同的缺点:
遵循语法:大多数编程语言都强制要求这么做(Coffeescript、Elm、Kotlin 等)。但需要注意的是,它们是 Javascript 的超集(Typescript、Flow),你仍然可以用纯 Javascript 编写你选择的某些部分。如果你使用的是非常旧的编程语言(包括 Javascript)版本,就需要升级,但升级频率比框架低很多。需要学习它们的语法。不过,你可以循序渐进地学习超集编程语言,因为你的代码的某些部分可以继续使用传统 JS。对于非超集编程语言来说,离散化技能确实是一个风险。因为它们的编译具有普适性,可能不是最优的,而你可能没有意识到这一点。也许你可以使用更简单和高效的 JS 代码来完成同样的操作。需要对缺点做出妥协,因为我们无法改变转译成 JS(或者使用 tsconfig.json 做一点定制)或编译成 WebAssembly 的过程。有些语言可能还会忽略 JS 的一些概念。具备可移植性,因为通常代码可以转译到 ES5(但有时你不得不妥协,即使你想要转译到 ES6)。WebAssembly 很新,所有现代浏览器都支持它。提供与其他 JS 代码的互操作性。例如,Typescript 可以被配置为支持 JS。
在普通应用中,我们应该谨慎使用非超集语言,因为它们或多或少都隐含着一些约束。超集语言(Typescript、Flow)通过避免“全有或全无”来最小化这些约束。我们应该把它们用在能带来价值的地方。
需要注意的是,建立在Javascript之上的语言层意味着我们工具链中的另一层复杂性,这可能会因为某些原因导致失败(见下文)。此外,在编译或翻译之后,开发阶段的好处也将消失(类型或可见性约束检查通常不会在运行时强制执行)。
开发库
基于不“重写框架”的假设,会得出普通JS应用不应该使用开发库的结论。这是完全错误的。“重新发明轮子”,即从头重写一切,并不是一个明智的目标。我们的目标是消除框架(而不是开发库)中的隐含约束。请不要把它和“什么都自己写”的教条混为一谈。
所以,如果你自己不会写一些代码(可能是因为没有时间,也可能是因为需要太多的专业知识),用开发库也没什么不好。你只需要关心:
模块化:如果你只需要一小部分功能,就要避免依赖整个大开发库;避免冗余:在没有标准的情况下才使用开发库,并优先选择实现了标准的开发库;避免锁定:不要直接使用开发库的 API,而是把它们包装在应用程序 API 中。
需要注意的是,不要被声称自己不是框架的文档或文章所迷惑(因为它们“没有明确定义”为框架,或者一个“完整的应用”没有定义):只要隐含了约束,它们就是框架。
模式
Holovaty说,仅仅应用模式(不使用框架)来构建软件是不够的。
模式是众所周知的东西,并不特定于某个开发过程。它们是自文档化的,因为有经验的开发人员可以很快识别它们。
以下是几个例子:
模型、视图和控制器模式(MVC);根据配置创建对象的工厂模式;简化反应式编程的观察者模式;用于遍历集合的迭代器模式;用于延迟加载、安全检查的代理模式;用于封装操作(可能基于上下文被触发)的命令模式。
有许多这样的模式:你可以自由地使用它们来满足你的需要。如果一个模式为你的应用程序的一个典型问题提供了一个典型的解决方案,你必须使用它。从广义上讲,任何符合SOLID原则,具有良好内聚性的东西,都有利于应用的灵活性和可维护性。
更新视图
在采访开发人员时,当被问及在构建一个普通的应用程序时,他们主要会担心什么时,大多数人都会回答:实现复杂的模型变化检测和后续的“视图”更新。这是典型的“工具法则”效应。它会让你按照框架去思考,但实际上你的一些简单需求根本不需要使用框架:
“视图”只是 DOM 元素。你当然可以对它们进行抽象(你也应该这样做),但最终它们也只是抽象而已。更新它们只是调用 viewElement.replaceChild(newContent)的问题,不需要更新更大范围的 DOM,也不需要重画或滚动。更新 DOM 的方法有好多种,可以插入文本,也可以操作实际的 DOM 对象,只要选一个适合你的就行了。在普通应用程序中,“检测”什么时候需要更新视图通常是没有必要的。因为在大多数情况下,你只知道在一个事件之后需要更新什么,然后你直接执行这个命令就可以了。当然,在某些情况下,你可能需要通过反转依赖和通知观察者(见下文)来进行一般性的更新。
模板
开发人员不想错过的另一个特性是用动态部分或侦听器编写HTML片段。
首先,DOM API(比如document . createelement(& # 34;按钮& # 34;))没那么难,而且实际上比任何模板语言都强大,因为你完全可以访问这些API。编写长的HTML片段可能会很乏味。如果它们真的很长,可以被分成更细粒度的组件。
然而,将这些元素视为模板确实可以提高可读性。那么如何管理它们呢?这里有几种方法:
现在可以在浏览器中使用 HTML 模板了(实际上从 2017 年就可以了)。它们提供了构建可重用的 HTML 片段的能力。这实际上是 Web 组件的一部分。Javascript 从 ES6(2015)开始支持模板字面量,你可以很轻松地将值嵌入到字符串中。你可以嵌入原始类型(数字、字符串,包括其他 HTML 代码等),但不能嵌入更复杂的元素,例如注册了监听器的 DOM 元素。我们可以借助标记模板字面量函数将复杂的值(如 DOM 节点)嵌入到模板中。ObservableHQ 已经设计了一个非常方便的工具,可以用它编写 html`${items.map(item => `
。
模板中的条件或循环语句该怎么办?且不说这可能永远都不是一个好主意(UI中不应该包含逻辑)。您可以(也应该)只使用JS实现逻辑,然后使用上述技术将结果插入模板。
事件
现在我们有了基本的模板,如何将事件绑定到DOM节点呢?这里还有几个选项:
HTML 事件处理器代码(
那么定制或者业务事件呢?如果我需要对应用程序组件触发的一些事件做出反应,该怎么办?也有很多方法可以处理:
自定义事件:你可以通过扩展 EventTarget 来创建自己的事件类,并派发或监听它们,就像“标准”事件一样。理论上说,使用 EventEmitter 也是一种办法(存在于 Node 中,在浏览器中作为库存在),但它很少被使用。观察者模式:你可以构建自己的观察者,也可以考虑使用 RxJs,它是这方面的标准。你只需要构建一个 Subject,并在发生事件时通知所有订阅者,让订阅者对事件做出反应。
组件
虽然开发普通的应用程序不同于开发复杂的基础设施(即托管组件的容器),但是如果某个东西会在系统中多次出现,那么将其设计为可重用的组件(不考虑上下文)仍然是一个好主意。无论您使用什么技术,无论是业务还是技术,一定程度的粒度抽象仍然是有用的:将与同一业务概念相关的数据和规则封装到一个可重用的对象中,或者构建可以在应用程序的多个位置实例化的小部件,这总是一个好主意。
创建组件的方法有很多种,取决于你自己的需求。早在2017年,Mev-Rael就提出了很多处理Javascript组件的状态、自定义属性和视图的技术。当然也不要局限于别人推荐的技术,先考虑自己的需求,再选择合适的技术。
除了标准的小部件组件(通常是标准的Web组件),任何组件都应该能够:
将逻辑和视图拆分开(通常会使用 MVC 模式)。把它们混合在一起通常会导致代码不易于维护,还会降低灵活性(例如,如果你想同时以详情或表格的形式显示一条记录,你的 RecordComponent 只需要使用 DetailRecordView 或 RowRecordView)。参数化组件的行为或视图。通过触发事件的形式通知订阅者组件中发生了某些事件(通常是在发生用户交互之后)。同步:如果发生一些事件,组件应该能够进行重绘。这个使用反应式开发库(如 RxJS)可以很容易实现。
在任何情况下,无论您选择什么设计策略,您的组件(或者更具体地说,它的相关“视图”)必须能够提供一些HTML呈现结果。可以使用包含HTML代码的字符串,但HTMLElement(或Element)通常是更好的选择(可读性高,直接更新,可以绑定到事件处理程序),性能更好(不需要解析)。
此外,您可能希望使用第三方的外部组件。由于专有框架的高度流行,他们可以更好地利用社区开发的库和组。大部分其实和纯JS实现(比如JQuery)的特性差别不大,但问题是缺乏互操作性,所以最后你会发现你需要的其实是纯JS或者Web组件。
幸运的是,这样的库确实存在,比如Vanilla JS Toolkit,尽管它们可能不太常见。就网页组件而言,webcomponents.org列出了2000多个元素。甚至还有普通的Web组件,但它们与我们将要讨论的内容无关(与互操作性相比,它们更关心轻量级实现)。
路由
在SPA中管理路由需要使用Web历史API。虽然这并不复杂,但你可能还是想把它委托给一个简单的路由器库,比如Navigo。
您所要做的就是在路由时用另一个DOM元素替换一个DOM元素(使用replaceChildren()或replaceWith()方法)。
延迟加载
按需加载Javascript代码是任何Web应用都需要考虑的问题。您不希望为了显示一个登录界面而加载所有的应用程序代码。
早在Web框架出现之前的2009年,詹姆斯·伯克(Dojo开发者)就发布了RequireJS(起初称为“RunJS”)来解决这个问题。此后,随着模块化的出现,出现了更多的技术。从ES6(2015)开始,我们可以动态加载代码。您可以在节点或浏览器中执行此操作:
{WelcomeModule} =等待导入(& # 34;。/welcome/ModuleImpl & # 34;)module = new WelcomeModule()复制代码
那么,如何将模块分割成单独的文件呢?包装商(如Webpack)可以为你做这些工作。
需要注意的是,在导入路径中只能使用常量,否则打包程序无法猜到你要加载的内容,会把所有可能的文件打包在一个文件中。例如,等待导入(`。/welcome/$ {moduleName } `)会将所有内容打包到指定的目录中,因为打包程序不知道运行时变量moduleName会是什么。
原生应用程序
越来越多的框架为原生平台(如React Native)提供了运行、迁移或编译应用程序的方式,以便它们可以作为独立的应用程序部署在Android或iOS移动系统上。
除了考虑真正的原生应用程序的开发,更常见的解决方案是将Web应用程序嵌入原生容器,如以前的PhoneGap(现已停止维护)或Apache Cordova,当前的Nativescript(支持Angular和普通应用程序等框架),或像Electron这样的原生Web应用程序包装器,或Tauri,Electron的轻量级继任者。
服务器端渲染
许多框架在前端和后端运行类似的代码,这更容易实现SEO友好的服务器端呈现(SSR)。
这可能是一个很酷很方便的功能,但是需要注意的是,它也可能导致服务器锁定。因此,在将框架锁定引入应用程序之前,您需要考虑它对项目、基础设施、客户端技术等的影响。
幸运的是,不使用框架也可以实现这个特性。
从服务器端渲染
一开始,采用一个通用的实现方案似乎很简单:不就是返回HTML吗?是的,您已经有现成的组件,但是:
你还需要一个服务器端 DOM API,因为默认情况下,服务器端不提供 DOM API(由 Domenic Denicola 负责维护的 JSDOM 或经过优化的 Happy DOM 就是很好的选择)。你的渲染组件不能假设是 DOM 是在客户端或服务器端,也就是说,不要使用全局 DOM,因为在服务器端,每个请求都需要一个 DOM。要做到这一点,你需要从(客户端或服务器)应用程序上下文中选择 DOM 对象(window document 和类型,如 Node、HTMLElement、NodeFilter),而不是直接获取。在客户端和服务器应用程序之间共享渲染组件有多种办法,比如将其发布在包存储库中,但最灵活的应该是让应用程序包引用 monorepo 中的模块。
添加交互性
然而,一旦HTML元素被转换成字符串,在这些元素上设置的所有事件处理程序都将丢失。为了恢复交互性,你需要一些“补水”的步骤,也就是注入脚本,让它们在客户端执行。框架很难做到这一点,因为它的普遍性。以影子DOM为例,他们在不断尝试改进自己的算法,希望用最聪明的方式来做这件事,但如果我们把问题缩小到应用层面,就会变得简单很多。
当然,在普通的服务器应用程序中这样做也意味着需要将JS脚本注入到响应消息中(通过引用或内联,这取决于您想要的“渐进”程度,比如将Web组件所需的代码嵌入到HTML响应中,并使其在客户端执行)。
常见的解决方案可以让你控制在哪里,什么时候,附加什么:你只能先发送HTML,然后加载基本的交互式Javascript,再加载更多(取决于用户的操作)等等。
这比本文中提到的任何东西都简单,因为它们是应用程序代码,而不是通用框架代码。
国际化
多年来,国际化问题一直通过库来处理(并最终集成到框架中)。自己集成这些库也很容易,但是也可以选择自己实现一个,因为自己的实现相比一般的库可以支持更简单有效的消息类型。
就这么简单:
接口welcome messages { title:string greetings(user:string,un read count:number):string }类WelcomeMessage_en实现WelcomeMessage { title = & # 34欢迎光临!",greetings = (user,un read count)= & gt;`欢迎${user},您有${unreadCount}封未读邮件。`}类WelcomeMessage_fr实现WelcomeMessage { title = & # 34欢迎光临。",greetings = (user,un read count)= & gt;` Bienvenue $ {user},vous avez $ {unread count}封新邮件。`}复制代码
这里为您提供:
类型检查:每个消息都有一个静态类型(和几个翻译实现),所以 IDE 可以检查你是否使用了有效的消息属性,并为你提供自动补全功能。翻译完整性检查:在为所有消息键提供所有语言的翻译之前,无法通过编译。
您需要做的就是(加载并)实例化与用户的地区相关的消息类。通用库不会提供这种特定于业务的消息类型。
工具
如果你想摆脱对强绑定软件技术栈的依赖,那么你很可能也想摆脱对工具的依赖:你不想只靠工具前进(它们的局限性、性能、错误、版本)。你不希望被一个你无法解决的构建问题所困扰(或者需要几个小时或几天才能解决)(特别是如果你使用的是最近构建的版本,而且它们还没有经过实战的充分测试)。
话虽如此,你还是很难避免使用这些工具。大多数时候,你的产品代码必须以某种方式打包,包括精简、混淆、代码拆分、树摇优化、延迟加载、包含样式等。毫无疑问,现有的打包工具如Webpack、Parcel、ESBuild或Vite会比你做得更好。
你能做的是:
尽可能少用转译。例如,使用 Typescript 可能是件好事,但它会带来额外的复杂性,你的工具链中必须有相应的工具来处理这种复杂性。CSS 也一样,特别是最新版本,不值得你用预处理器(如 Sass)来处理它们。尽可能少用工具。你用的工具越多,就越有可能出问题或无法满足你的需求。如果确实需要使用工具,请选择最流行的工具,因为它们经过实战测试,更有可能满足你的需求(这样你就不会陷入“改变需求或更换工具”的困境)。过早使用最新的打包工具可能会为你节省几秒钟的构建时间,但这些时间很可能都不够用来理解工具文档、处理 bug 或处理因缺乏支持而导致的问题。
最大的挑战
归根结底,最大的挑战不是技术,而是人:
你要走出舒适区。希望你终将能够明白,使用普通的解决方案并不是那么困难,框架的复杂性比它们带来的好处要大得多。此外,你可能会看到更多新的 API(WebComponents、ES6 模块、代理、MutationObserver……),而且 Web 比你想象的更现代、更强大。至于其他人,你可以尝试说服他们。他们可能不愿意这么做,因为任何人都不愿意开启自己从未尝试过的旅程。
其他人可能会告诉你:
“你要开发自己的框架”:不,我们要开发的是应用程序,而不是框架。“你要写更多的代码”:也许,但也许不会太多(取决于用了多少开发库),因为这需要与框架的样板代码进行比较。但不管怎样,需要加载的代码都会更少。“你将不断地重新发明轮子”:当然不是。不使用框架是为了不遵循它们预定义的规则(配置、生命周期管理、刷新机制等),但我们并没有忘记 DRY 原则,我们仍然可以(并且应该)使用经过实战测试的第三方库。“你需要为每一个功能写更多的代码”:不,你可以遵循自己的规则,而不是使用框架样板代码。“没有文档可看”:肯定不会有框架文档(因为根本就没有框架),但你仍然需要写应用程序文档。值得一提的是,使用模式有助于你自动文档化你的软件设计。你只需要关心应用程序的代码文档,而如果你多使用一个框架,就需要多看一份文档。“不会有约束或模式来指导开发人员”:不,如果你确实需要约束,没有什么能阻止你(你只需要定义契约就行了)。“你会错过性能提升”,比如曾经被大肆炒作的虚拟 Dom(如今受到了挑战,包括来自 Svelte 或 Aurelia 框架的挑战):不,因为需要这些“性能提升”的是框架本身(为了通用性),而不是应用程序。相反,通用框架更有可能错过一些可以通过自定义代码实现的性能提升。你遇到这个问题是因为你没有使用框架。每一个问题(包括漏洞、延迟、招募等)都会被归咎于因为没有使用框架。因为大多数开发人员的经验是,所有正常运行的东西都使用了框架,默认情况下,不使用它们将被认为是有风险的。一旦出现问题,无论是否与不使用框架有关,这个假设都会被认为是正确的。他们忘记了在使用框架时也会遇到类似的问题。“我们找不到开发者”:他们会说很难找到能够写纯 JS 代码的开发者。这句话是对的,也是错的。因为很多开发者(且不说管理者)会发现自己更习惯于使用框架。如果他们从来没有使用过或不了解基本的 Web API,那么他们可能会对从零开始构建一个 Web 应用程序感到害怕。但是,如果你想要开发高质量的应用程序,就不应该去找这种类型的开发者。当然,现在找 React 开发者很容易,但你需要的不只是 React 开发者,而是优秀的开发者。“你无法获得与框架相同的代码质量”。当然,框架或开发库通常是由行业里有经验的开发者编写的。但是,框架的代码主要与框架特定的活动相关(组件生命周期、通用的刷新机制和优化、工具,等等),与你的应用程序无关。此外,即使使用了框架,你仍然可能做出糟糕的设计,写出糟糕的代码。应用程序的质量总是更多地取决于团队的质量,而不是因为缺少框架。“你无法获得与框架相同的性能”:不,我们可以获得更好的性能。行业里关于框架采用了可以“提升性能”的复杂技术的说法就不在这里讨论了,因为它们可能主要被用来解决框架通用解决方案的性能缺陷(比如虚拟 DOM)。
毫无疑问,性能最好的框架是那些在普通代码之上添加较少层的框架。框架的“优化”更多的是为了弥补框架本身的开销。
结论
构建一个没有框架的Web应用并不意味着自己构建一个框架,而是不使用通用引擎开发一个应用。目的是:
避免散失控制和被隐含约束(锁定、升级成本等);可以进行优化(性能、体积、设计)。
也就是只写应用特定的代码(业务和技术),包括开发库的使用。你真正应该关注的框架是你自己的,也就是应用特定的框架。这才是真正的“以业务为中心”,也是最有效的。
没有你想象的那么难,尤其是有了现代标准的加持(主流浏览器在必要的时候可以通过polyfill支持新的特性)。
原文链接:https://javarome.medium.com/design-noframework-bbc00a02d9b3
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。系信息发布平台,仅提供信息存储空间服务。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。
本文来自网络,若有侵权,请联系删除,作者:丁阳,如若转载,请注明出处: