在没有框架的情况下设计和构建渐进式 Web 应用程序(第 1 部分)
已发表: 2022-03-10Web 应用程序实际上是如何工作的? 我的意思不是从最终用户的角度来看。 我的意思是在技术意义上。 Web 应用程序实际上是如何运行的? 什么开始了? 没有任何样板代码,构建应用程序的正确方法是什么? 特别是所有逻辑都在最终用户设备上运行的客户端应用程序。 如何管理和操纵数据? 你如何让界面对数据的变化做出反应?
这些问题很容易被框架回避或完全忽略。 开发人员使用 React、Vue、Ember 或 Angular 之类的东西,按照文档启动和运行,然后离开。 这些问题由框架的技巧箱处理。
这可能正是你想要的东西。 可以说,如果您想按照专业标准构建某些东西,这是明智之举。 然而,随着魔法被抽象出来,你永远无法了解这些技巧是如何实际执行的。
你不想知道这些技巧是怎么做的吗?
我做到了。 因此,我决定尝试构建一个基本的客户端应用程序 sans-framework,以自己了解这些问题。
但是,我有点超前了。 先介绍一点背景。
在开始这段旅程之前,我认为自己非常精通 HTML 和 CSS,但不精通 JavaScript。 当我觉得我已经解决了我对 CSS 的最大问题时,我感到很满意,我给自己设定的下一个挑战是理解一门编程语言。
事实是,我对 JavaScript 还比较初级。 而且,除了破解 Wordpress 的 PHP 之外,我也没有接触过或接受过任何其他编程语言的培训。
让我限定“初学者级别”的断言。 当然,我可以在页面上获得交互性。 切换类、创建 DOM 节点、追加和移动它们等等。但是当涉及到组织代码以用于除此之外的任何内容时,我一无所知。 我没有信心构建任何接近应用程序的东西。 我不知道如何在 JavaScipt 中定义一组数据,更不用说用函数来操作它了。
我不了解 JavaScript 的“设计模式”——解决经常遇到的代码问题的既定方法。 我当然不知道如何处理基本的应用程序设计决策。
你玩过“顶级王牌”吗? 好吧,在 web 开发者版中,我的卡片看起来像这样(满分 100 分):
- CSS:95
- 复制粘贴:90
- 发际线:4
- HTML: 90
- JavaSript:13
除了想在技术层面挑战自己之外,我还缺乏设计能力。
在过去的十年里,我几乎只为其他人的设计编码,我的视觉设计技能自上世纪末期以来就没有遇到过任何真正的挑战。 反思这一事实和我微不足道的 JavaScript 技能,培养了一种日益增长的专业不足感。 是时候解决我的缺点了。
我的脑海中出现了一个个人挑战:设计和构建客户端 JavaScript Web 应用程序。
关于学习
学习计算语言的资源从未如此丰富。 特别是 JavaScript。 但是,我花了一段时间才找到以点击方式解释事物的资源。 对我来说,Kyle Simpson 的 'You Don't Know JS' 和 Marijn Haverbeke 的 'Eloquent JavaScript' 帮助很大。
如果您开始学习 JavaScript,那么您肯定需要找到自己的大师; 解释方法适合你的人。
我学到的第一件关键事情是,尝试向无法以您理解的方式解释事物的老师/资源学习是毫无意义的。 有些人看带有foo
和bar
in 的函数示例并立即理解其含义。 我不是那些人中的一员。 如果你不是,不要假设编程语言不适合你。 只需尝试不同的资源并继续尝试应用您正在学习的技能。
你也不一定会享受任何突然“咔哒”一声的灵光乍现的时刻。 就像一见钟情的编码一样。 更有可能的是,您需要大量的毅力和大量的学习应用才能感到自信。
一旦你觉得自己有一点能力,尝试应用你的学习会教给你更多。
以下是我在此过程中发现有用的一些资源:
- 趣味趣味功能 YouTube 频道
- Kyle Simpson Plural Sight 课程
- Wes Bos 的 JavaScript30.com
- Marijn Haverbeke 的 Eloquent JavaScript
是的,这几乎就是你需要知道的关于我为什么会到达这一点的全部内容。 现在房间里的大象是,为什么不使用框架?
为什么不 React、Ember、Angular、Vue 等
虽然一开始就提到了答案,但我认为为什么不使用框架的主题需要扩展。
有大量高质量、支持良好的 JavaScript 框架。 每个都是专门为构建客户端 Web 应用程序而设计的。 正是我想要建造的那种东西。 我原谅你想知道显而易见的事情:比如,呃,为什么不使用一个?
这是我对此的立场。 当你学习使用抽象时,这主要是你正在学习的东西——抽象。 我想学习事物,而不是事物的抽象。
我记得那天学习了一些 jQuery。 虽然可爱的 API 让我比以往任何时候都更轻松地进行 DOM 操作,但在没有它之前我变得无能为力。 我什至不能在不需要 jQuery 的情况下切换元素上的类。 在没有 jQuery 的页面上给我一些基本的交互性任务,我在我的编辑器中偶然发现,就像一个被剪掉的 Samson。
最近,当我试图提高对 JavaScript 的理解时,我试图将我的头脑围绕在 Vue 和 React 上。 但最终,我不确定标准 JavaScript 的结束位置以及 React 或 Vue 的开始位置。 我的观点是,当您了解这些抽象为您做什么时,它们会更有价值。
因此,如果我要学习一些东西,我想了解该语言的核心部分。 这样,我就有了一些可转移的技能。 当当前的月份框架风格被抛弃以用于下一个“热门新事物”时,我想保留一些东西。
好的。 现在,我们正在了解为什么要制作这个应用程序,以及,不管喜欢与否,它是如何制作的。
让我们继续讨论这件事将会是什么。
应用理念
我需要一个应用创意。 没有什么太大的野心; 我没有任何关于创建创业公司或出现在 Dragon's Den 的幻想——学习 JavaScript 和应用程序基础是我的主要目标。
该应用程序必须是我有机会在技术上实现并完成一个半体面的设计工作的东西。
切线时间。
工作之余,我会尽可能组织和踢室内足球。 作为组织者,在心里记下谁给我发消息说他们在玩,谁没有玩是一件很痛苦的事。 一个游戏通常需要 10 人,一次推 8 人。 有一个大约 20 人的名单,他们可能会也可能不会玩每场比赛。
我确定的应用程序想法是能够从名单中挑选球员,让我计算有多少球员确认他们可以玩。
当我想得更多时,我觉得我可以再扩大一点范围,这样它就可以用来组织任何简单的基于团队的活动。
诚然,我几乎没有想到 Google 地球。 然而,它确实面临所有基本挑战:设计、数据管理、交互性、数据存储、代码组织。
在设计方面,除了可以在手机视口上运行和运行良好的版本之外,我不会关心任何其他东西。 我会将设计挑战限制为仅解决小屏幕上的问题。
核心理念当然倾向于“待办事项”风格的应用程序,其中有大量现有示例可以寻找灵感,同时也有足够的差异来提供一些独特的设计和编码挑战。
预期功能
我打算设计和编码的功能的初始要点列表如下所示:
- 用于将人员添加到名册的输入框;
- 将每个人设置为“进”或“出”的能力;
- 将人员分成团队的工具,默认为 2 个团队;
- 从名册中删除一个人的能力;
- “工具”的一些界面。 除了拆分之外,可用的工具还应该包括将输入的数据下载为文件、上传之前保存的数据以及一次性删除所有播放器的能力;
- 该应用程序应显示当前“加入”人数;
- 如果游戏没有人被选中,则应隐藏团队拆分器;
- 付费模式。 设置中的切换允许“进入”用户有一个额外的切换来显示他们是否已付款。
一开始,这就是我认为最小可行产品的功能。

设计
设计始于纸片。 发现有多少在我脑海中令人难以置信的想法在受到铅笔画所提供的微不足道的审查时变得可笑,这很有启发性(阅读:粉碎)。
因此,许多想法很快就被排除在外,但另一方面,通过勾勒出一些想法,它总是会导致其他我永远不会考虑的想法。
现在,阅读这篇文章的设计师可能会说,“当然了”,但这对我来说是一个真正的启示。 开发人员习惯于看到后期设计,很少看到在那之前的所有废弃步骤。
一旦对铅笔画之类的东西感到满意,我就会尝试在设计包 Sketch 中重新创建它。 正如想法在纸和铅笔阶段消失一样,同样数量的人未能通过 Sketch 的下一个保真阶段。 然后选择那些在 Sketch 中似乎可以作为画板的画板作为候选编码。
反过来,我会发现,当这些候选人是内置代码时,也有一部分人因各种原因而无法工作。 每个保真度步骤都为设计带来了新的挑战,要么通过,要么失败。 失败会让我从字面上和比喻上回到绘图板上。
因此,最终,我最终得到的设计与我最初在 Sketch 中的设计有很大不同。 这是第一个 Sketch 模型:


即便如此,我也没有妄想; 这是一个基本的设计。 然而,在这一点上,我有一些我相对有信心可以工作的东西,我正在努力尝试构建它。
技术要求
有了一些初始功能要求和基本的视觉方向,是时候考虑使用代码应该实现什么了。
尽管普遍认为为 iOS 或 Android 设备制作应用程序的方式是使用本机代码,但我们已经确定我的意图是使用 JavaScript 构建应用程序。
我还热衷于确保应用程序勾选了所有必要的框,以符合渐进式 Web 应用程序或 PWA 的资格,因为它们更常见。
如果您不知道渐进式 Web 应用程序是什么,这里是“电梯间距”。 从概念上讲,想象一个标准的 Web 应用程序,但它满足一些特定的标准。 遵守这组特殊要求意味着支持设备(想想手机)授予 Web 应用程序特殊权限,使 Web 应用程序大于其各部分的总和。
特别是在 Android 上,几乎不可能将仅使用 HTML、CSS 和 JavaScript 构建的 PWA 与使用原生代码构建的应用程序区分开来。
以下是被视为渐进式 Web 应用程序的应用程序要求的 Google 清单:
- 站点通过 HTTPS 提供;
- 页面可在平板电脑和移动设备上响应;
- 所有应用程序 URL 在离线时加载;
- 为添加到主屏幕提供的元数据;
- 即使在 3G 上也能快速加载;
- 站点跨浏览器工作;
- 页面转换不会让人觉得它们在网络上被阻塞;
- 每个页面都有一个 URL。
另外,如果你真的想成为老师的宠儿,让你的应用程序被认为是一个“示例渐进式 Web 应用程序”,那么它还应该满足以下要求:
- 网站的内容被谷歌索引;
- 适当时提供 Schema.org 元数据;
- 酌情提供社交元数据;
- 必要时提供规范 URL;
- 页面使用 History API;
- 页面加载时内容不会跳转;
- 从详细信息页面按返回会保留上一个列表页面上的滚动位置;
- 轻按时,输入不会被屏幕键盘遮挡;
- 内容可以从独立或全屏模式轻松共享;
- 网站在手机、平板电脑和桌面屏幕尺寸上都具有响应性;
- 任何应用安装提示都没有过度使用;
- 添加到主屏幕提示被拦截;
- 即使在 3G 上,首次加载也非常快;
- 站点使用缓存优先网络;
- 当用户离线时,网站会适当地通知用户;
- 向用户提供有关如何使用通知的上下文;
- 鼓励用户打开推送通知的 UI 不能过于激进;
- 显示权限请求时,站点使屏幕变暗;
- 推送通知必须及时、准确和相关;
- 提供启用和禁用通知的控件;
- 用户通过凭据管理 API 跨设备登录;
- 用户可以通过支付请求 API 的原生 UI 轻松支付。
冷酷的! 我不了解你,但是对于一个基本的应用程序来说,第二组东西似乎需要做很多工作! 碰巧有很多项目与我的计划无关。 尽管如此,我并不羞于说我放低了视线,只通过了最初的测试。
对于整个应用程序类型,我认为 PWA 是比原生应用程序更适用的解决方案。 在游戏和 SaaS 可以说在应用商店中更有意义的地方,较小的实用程序可以作为渐进式 Web 应用程序在网络上生活得非常愉快和成功。
虽然关于我逃避努力工作的主题,但早期做出的另一个选择是尝试将应用程序的所有数据存储在用户自己的设备上。 这样就不需要连接数据服务和服务器并处理登录和身份验证。 就我的技能而言,弄清楚身份验证和存储用户数据似乎几乎肯定会比我能咀嚼的更多,并且为应用程序的职权范围过度杀伤力!
技术选择
对目标是什么有了相当清晰的认识后,注意力转向了可以用来构建它的工具。
我很早就决定使用 TypeScript,它在其网站上被描述为“……一个类型化的 JavaScript 超集,可以编译为纯 JavaScript。” 我对我喜欢的语言的所见所闻,尤其是它对静态分析的了解如此之好。
静态分析只是意味着程序可以在运行之前查看您的代码(例如,当它是静态的时)并突出显示问题。 它不一定能指出逻辑问题,但可以指出不符合一组规则的代码。
任何可以指出我(肯定有很多)错误的东西都必须是一件好事,对吧?
如果您不熟悉 TypeScript,请考虑使用 vanilla JavaScript 中的以下代码:
console.log(`${count} players`); let count = 0;
运行此代码,您将收到类似以下的错误:
ReferenceError: Cannot access uninitialized variable.
对于那些有一点 JavaScript 能力的人来说,对于这个基本示例,他们不需要一个工具来告诉他们事情不会有好的结局。
但是,如果您在 TypeScript 中编写相同的代码,则会在编辑器中发生这种情况:

我什至在运行代码之前就收到了一些关于我的白痴的反馈! 这就是静态分析的美妙之处。 这种反馈通常就像有一个更有经验的开发人员坐在我身边,在我去的时候发现错误。
顾名思义,TypeScript 主要是让您为代码中的每个事物指定预期的“类型”。 这可以防止您无意中将一种类型“强制”为另一种类型。 或者尝试在不适用的数据上运行方法——例如对象上的数组方法。 这不是在代码运行时必然导致错误的事情,但它肯定会引入难以跟踪的错误。 多亏了 TypeScript,您甚至可以在尝试运行代码之前在编辑器中获得反馈。
TypeScript 在这个发现之旅中当然不是必不可少的,除非有明显的好处,否则我永远不会鼓励任何人使用这种性质的工具。 首先设置工具和配置工具可能会耗费时间,因此在深入研究之前一定要考虑它们的适用性。
TypeScript 还提供了其他好处,我们将在本系列的下一篇文章中介绍,但仅静态分析功能就足以让我想采用 TypeScript。
我做出的选择有一些连锁反应。 选择将应用程序构建为渐进式 Web 应用程序意味着我需要在一定程度上了解 Service Worker。 使用 TypeScript 意味着引入某种构建工具。 我将如何管理这些工具? 从历史上看,我曾使用 NPM 作为包管理器,但 Yarn 呢? 值得用 Yarn 代替吗? 以性能为中心意味着考虑一些缩小或捆绑工具; 像 webpack 这样的工具变得越来越流行,需要评估。
概括
我意识到有必要开始这个探索。 我的 JavaScript 能力很弱,没有什么比试图将理论付诸实践更能令人振奋的了。 决定使用 vanilla JavaScript 构建一个 Web 应用程序是我的烈火洗礼。
我花了一些时间研究和考虑制作应用程序的选项,并决定将应用程序制作为渐进式 Web 应用程序对我的技能和相对简单的想法最有意义。
我需要构建工具、包管理器,以及随后的大量耐心。
最终,在这一点上,基本问题仍然存在:这真的是我能做到的吗? 还是我会因为自己的无能而感到谦卑?
我希望你能和我一起阅读第二部分,了解构建工具、JavaScript 设计模式以及如何制作更“类似于应用程序”的东西。