火星链 火星链
Ctrl+D收藏火星链

HTM:不用任何框架开发 Web 应用程序:可能吗

作者:

时间:1900/1/1 0:00:00

过去流行的是 Angular,然后是 React,现在是 Vue

</ul>。

V神:现在不用对与其价值观有悖的事情妥协:V神今晚在王峰十问上表示:“对我个人来说,财富增加对我的生活没有太多变化,只是我不需要为了花费两美元乘巴士这些琐事担心。现在不用把时间浪费在赚钱上,而是可以专注于创造我认为有价值的东西。而且,对于那些和我价值观有悖的事情,我也用不着妥协。”[2018/6/22]

模板中的条件或循环语句该怎么办?且不说这可能从来都不是一个好主意(UI 中不应该包含逻辑),你可以(也应该)只用 JS 来实现逻辑,然后使用上面的技术将结果插入到模板中。

现在,我们有了基本的模板,那么该如何将事件绑定到 DOM 节点呢?这里也有几种选择:

HTML事件处理器代码(<button onclick="myClickHandler(event)">)可以被插入到HTML源代码中,但这并非最好的办法,因为指定的处理器只在指定的范围内可用。

事件处理器API(button.addEventListener("click", myClickHandler))可用于所有通过DOM API或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,尽管可能不太常见。在 Web 组件方面,webcomponents.org 列出了 2000 多个元素。甚至还有普通的 Web 组件,只是它们与我们要讨论的不太相关(更多的是关注轻量级实现,而不是互操作性)。

在 SPA 中管理路由需要使用 Web History API。虽然这并不复杂,但你仍然可能希望将其委托给简单的路由器库,如 Navigo。

你所要做的就是在路由时用一个 DOM 元素替换另一个 DOM 元素(使用 replaceChildren() 或 replaceWith() 方法)。

按需加载 JavaScript 代码是任何一个 Web 应用程序都需要考虑的问题。你一定不希望为了显示一个登录界面而加载全部的应用程序代码。

早在 2009 年,在 Web 框架出现之前,James Burke(Dojo 开发者)就发布了 RequireJS(最开始叫“RunJS”)来解决这个问题。从那时起,随着模块化的出现,出现了更多的技术。从 ES6(2015)开始,我们可以动态加载代码。在 Node 中可以,在浏览器中也可以:

那么如何将模块分拆到单独的文件中?打包器(如 Webpack)可以为你做这些工作。

需要注意的是,在导入路径里你应该只使用常量,否则打包器就无法猜到你想要加载什么,就会将所有可能的文件都打包在一个文件中。例如,await import(./welcome/${moduleName}) 将把所有东西都打包到指定的目录中,因为打包器不知道变量 moduleName 在运行时会是什么。

越来越多的框架为原生平台(如 React Native)提供了运行、迁移或编译应用程序的方法,以便将它们作为独立应用程序部署到 Android 或 iOS 移动系统上。

除了考虑开发真正的原生应用程序之外,更普遍的解决方案是将 Web 应用程序嵌入到原生容器中,比如之前的 PhoneGap(现已停止维护)或 Apache Cordova,现在的 NativeScript(它支持框架,如 Angular,也支持普通的应用程序),或者像 Electron 这样的原生 Web 应用程序包装器,或者 Electron 的轻量级后继者 Tauri。

很多框架在前端和后端运行的代码是相似的,这样更容易实现对 SEO 友好的服务器端渲染(SSR)。

这可能是一个又酷又便利的特性,但需要注意的是,它也可能导致服务器锁定。因此,在向应用程序引入框架锁定之前,你需要考虑它对项目、基础设施、客户端技术等方面的影响。

所幸的是,你也可以在不使用框架的情况下实现这个特性。

从服务器端渲染

采用普通的实现方案在一开始看起来很简单:不就是返回 HTML 吗?是的,你已经有现成的组件了,但是:

你还需要一个服务器端 DOM API,因为默认情况下,服务器端不提供 DOM API(由 Domenic Denicola 负责维护的 JSDOM 或经过优化的 Happy DOM 就是很好的选择)。

你的渲染组件不能假设是 DOM 是在客户端或服务器端,也就是说,不要使用全局 DOM,因为在服务器端,每个请求都需要一个 DOM。要做到这一点,你需要从(客户端或服务器)应用程序上下文中选择 DOM 对象(windowdocument 和类型,如 Node、HTMLElement、NodeFilter),而不是直接获取。

在客户端和服务器应用程序之间共享渲染组件有多种办法,比如将其发布在包存储库中,但最灵活的应该是让应用程序包引用 monorepo 中的模块。

添加交互性

然而,一旦 HTML 元素被转换成字符串,在这些元素上设置的所有事件处理器都丢失了。为了恢复交互性,你需要一些“补水”步骤,也就是注入脚本,让它们在客户端执行。框架因其普适性很难做到这一点。就拿影子 DOM 来说,它们不断尝试改进算法,希望能够以最聪明的方式做到这一点,但如果我们把问题缩小到应用程序层面,就会变得简单很多。

当然,在普通的服务器应用程序中做到这一点也意味着需要将 JS 脚本注入到响应消息中(通过引用或内联,具体取决于你想要怎样的“渐进”程度,比如将 Web 组件所需的代码嵌入到 HTML 响应中,让它们在客户端执行)。

普通的解决方案让你可以控制在哪里、什么时候以及附加哪些东西:你可以先只发送 HTML,再加载基本的交互性 JavaScript,然后加载更多(取决于用户的操作),等等。

这比本文中提到的任何一个东西都简单,因为它们是应用程序代码,而不是通用的框架代码。

多年来,国际化问题都是通过库来处理的(最终也被集成到框架中)。要自己集成这些库也很容易,但你也可以选择自己实现一个,因为与通用库相比,自己的实现可以支持更简单、更有效的消息类型。

这里为你提供了:

类型检查:每个消息都有一个静态类型(和几个翻译实现),所以 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 来支持新特性)。

标签:WEBDOMHTMAPIweb3域名交易Modulus Domains ServicePHTMAll Coins Yield Capital

POL币最新价格热门资讯
DAO:快速进阶NFT话术手册

世界都在为加密而沸腾, DeFi热潮退去后,更多的关注使NFT市场逐步走向成熟,作为一名nerds或者未来可能成为一名NFT爱好者的你,随着NFT项目的普及,是不是越是深入接触了解.

1900/1/1 0:00:00
NULS:如何将区块链与制造业结合

人们正在积极探索区块链在各个行业的应用。除了金融之外,区块链在汽车行业、农业以及制造业等也都有应用。此前01区块链写了一篇《四大汽车制造商如何应用区块链》.

1900/1/1 0:00:00
COIN:从“政府”视角来解读链游生态发展:以stepn、ilv、Axie和星鲨为例

本文以目前GameFi领域最常见的双代币模型为例,将探索两个思路的可行性:保持打金代币在某个区间稳定尽可能的让治理代币实现价格增长用一个简单好懂的比喻来举例,如果我们将游戏比作一个小国家.

1900/1/1 0:00:00
DEF:对话 Dragonfly 合伙人:如何成为优秀的Web3 VC?

以下为GuildFi分析师 Cloud 针对Bankless播客《How to be a Web3 VC with Haseeb Qureshi 》所做播客笔记.

1900/1/1 0:00:00
ETA:元宇宙:陷阱or风口?

什么是元宇宙?1992年美国科幻大师尼尔·斯蒂芬森在小说《雪崩》中这样描述元宇宙(Metaverse):“戴上耳机和目镜,找到连接终端.

1900/1/1 0:00:00
TOK:?欧易研究院:简单分析NFT流动性方案的优势与劣势

对于加密资产领域的大多数人来说,资金的流动性十分重要。在Defi项目中有流动性挖矿,为交易对提供流动性就可以获得Token奖励,在欧易这类中心化交易所中有赚币服务,如同定期存款把资产锁定一定时期.

1900/1/1 0:00:00