在没有框架的情况下设计和构建渐进式 Web 应用程序(第 3 部分)

已发表: 2022-03-10
快速总结 ↬本文总结了一个由三部分组成的系列,关于使用 vanilla JavaScript 设计和编写基本 Web 应用程序的试验和磨难。 在第一部分中,我们介绍了原因,第二部分主要讨论了如何进行,这部分最后通过研究项目是如何结束的以及从经验中学到了什么来结束。

回到本系列的第一部分,我们解释了为什么会出现这个项目。 即渴望了解如何使用原生 JavaScript 制作小型 Web 应用程序,并希望让非设计开发人员稍微从事他的设计工作。

在第二部分中,我们采用了一些基本的初始设计,并通过一些工具和技术选择来启动和运行。 我们介绍了设计的某些部分如何以及为何发生变化,以及这些变化的后果。

在这最后一部分中,我们将介绍将基本 Web 应用程序转变为渐进式 Web 应用程序 (PWA) 并“交付”该应用程序,然后再查看通过使简单的 Web 应用程序 In/Out 获得的最有价值的经验教训:

  • JavaScript 数组方法的巨大价值;
  • 调试;
  • 当你是唯一的开发者时,你就是另一个开发者;
  • 设计就是开发;
  • 持续的维护和安全问题;
  • 在不失去理智、动力或两者兼而有之的情况下从事业余项目;
  • 运送一些产品胜过不运送任何产品。

因此,在学习经验教训之前,让我们看看如何将用 HTML、CSS 和 JavaScript 编写的基本 Web 应用程序转变为渐进式 Web 应用程序 (PWA)。

就制作这个小型 Web 应用程序所花费的总时间而言,我估计大概需要两到三周。 然而,由于它是在晚上以 30-60 分钟的时间完成的,从第一次提交到我在 2018 年 8 月上传我认为的“1.0”版本实际上花了大约一年的时间。因为我得到了应用程序'功能完成”,或者更简单地说,在我满意的阶段,我预计会有一个大的最后推动。 你看,我没有做任何事情来将应用程序变成一个渐进式 Web 应用程序。 事实证明,这实际上是整个过程中最简单的部分。

跳跃后更多! 继续往下看↓

制作渐进式 Web 应用程序

好消息是,在将一个基于 JavaScript 的小应用程序转变为“渐进式 Web 应用程序”时,有很多工具可以让生活变得轻松。 如果您回想本系列的第一部分,您会记得成为 Progressive Web App 意味着满足一组标准。

要了解您的 Web 应用程序如何衡量,您的第一站可能应该是 Google Chrome 的 Lighthouse 工具。 您可以在“审核”选项卡下找到 Progressive Web App 审核。

这是我第一次通过它运行时 Lighthouse 告诉我的。

Chrome 开发工具显示 Progressive Web App 结果为 55/100
Progressive Web App 的初始分数并不高。 (大预览)

一开始,In/Out 的 Progressive Web App 的得分仅为55100 。 然而,我在不到一个小时的时间里就把它从那里带到了100100

提高分数的权宜之计与我的能力无关。 只是因为 Lighthouse 准确地告诉了我需要做什么!

必要步骤的一些示例:包括一个manifest.json文件(本质上是一个提供有关应用程序元数据的 JSON 文件),在头部添加一大堆元标记,将 CSS 中内联的图像切换为标准 URL 引用图像,并添加一堆主屏幕图像。

制作许多主屏幕图像、创建清单文件和添加一堆元标记可能在一小时内完成很多工作,但有一些很棒的 Web 应用程序可以帮助您构建 Web 应用程序。 这多好啊! 我使用了 https://app-manifest.firebaseapp.com。 向它提供有关您的应用程序和徽标的一些数据,点击提交,它会为您提供一个包含您需要的所有内容的 zip 文件! 从那里开始,这只是复制和粘贴的时间。

由于缺乏知识,我推迟了一段时间的事情,比如服务工作者,也很容易添加,这要归功于大量的博客文章和专门针对服务工作者的网站,比如 https://serviceworke.rs。 有了 Service Worker,这意味着应用程序可以离线工作,这是渐进式 Web 应用程序的必要功能。

虽然与使应用程序成为 PWA 没有严格的关系,但 Chrome 开发工具的“覆盖”选项卡也非常有用。 在几个月来对设计和代码进行了如此多的零星迭代之后,清楚地表明哪里有冗余代码是很有用的。 我发现一些旧函数在代码库中乱扔垃圾,我只是忘记了!

简而言之,在完成了 Lighthouse 审计建议后,我感觉自己就像老师的宠物:

在 Lighthouse Progressive Web App 审核中获得 100/100
Lighthouse 通过准确告诉您要更改的内容来轻松获得高分。 (大预览)

现实情况是,将应用程序变成一个渐进式 Web 应用程序实际上非常简单。

随着最后的开发工作结束,我将这个小应用程序上传到我网站的一个子域,就是这样。

回顾展

自从停止开发我的小 Web 应用程序以来已经过去了几个月。

从那以后的几个月里,我一直在随意使用该应用程序。 现实情况是,我所做的大部分团队运动组织仍然通过短信进行。 然而,这个应用程序绝对比写下谁进出谁比每场比赛晚上找一张纸更容易。

因此,事实是,它几乎不是一项不可或缺的服务。 它也没有为开发或设计设置任何标准。 我也不能告诉你我对此感到 100% 满意。 我刚到了一个我很乐意放弃它的地步。

但这从来都不是练习的重点。 我从经验中学到了很多。 以下是我认为最重要的要点。

设计就是发展

一开始,我并没有足够重视设计。 我开始这个项目时认为,我用垫子和笔或在 Sketch 应用程序中绘制草图的时间,是可以更好地用于编码的时间。 然而,当我直接开始编写代码时,我常常只是一个忙碌的傻瓜。 首先以尽可能低的保真度探索概念,从长远来看可以节省更多时间。

一开始有很多情况下,他们花了几个小时在代码中工作,却发现从用户体验的角度来看它存在根本性的缺陷。

我现在的看法是,纸和铅笔是最好的规划、设计和编码工具。 面临的每一个重大问题主要是用纸和铅笔解决的; 文本编辑器只是执行解决方案的一种手段。 如果没有在纸上有意义的东西,它就没有机会在代码中工作。

接下来我学会了欣赏,我不知道为什么花了这么长时间才弄清楚,设计是迭代的。 我下意识地接受了一个大写字母“D”的设计师的神话。 有人四处游荡,笔直地举起自动铅笔,在字体上打蜡,啜饮一口纯白色(显然是豆浆),然后随意地将完全形成的视觉完美呈现给世界。

这与“天才”程序员的概念不同,是一个神话。 如果您是设计新手,但正在尝试自己的手,我建议您不要沉迷于第一个激起您兴奋的想法。 尝试变化是如此便宜,所以拥抱这种可能性。 我喜欢 In/Out 设计的所有东西在最初的设计中都不存在。

我相信是小说家迈克尔·克莱顿(Michael Crichton)创造了格言:“书不是写出来的——它们是被改写的”。 接受每个创作过程本质上是相同的。 请注意,相信这个过程会减轻焦虑,而练习会改善您的审美理解和判断。

你是你项目中的另一个开发者

我不确定这是否特别适用于偶尔进行的项目,但我做了以下鲁莽的假设:

“我不需要记录任何这些,因为它只是我,显然我会理解它,因为我写了它。”

没有东西会离事实很远!

有时晚上,在我必须在项目上工作的 30 分钟内,我除了试图理解六个月前编写的函数外,什么也没做。 代码重新定位花了这么长时间的主要原因是缺乏高质量的注释以及命名不当的变量和函数参数。

在我的日常工作中,我非常勤奋地评论代码,总是认真地担心其他人可能需要理解我正在写的东西。 然而,在这种情况下,我是另一个人。 你真的认为你会记得你在六个月内编写的代码块是如何工作的吗? 你不会的。 相信我,花点时间评论一下吧!

我已经阅读了一篇题为“你的语法荧光笔在评论的重要性上是错误的”的博客文章。 基本前提是语法高亮不应该淡出注释,它们应该是最重要的。 我倾向于同意,如果我不能很快找到一个可以解决这个问题的代码编辑器主题,我可能不得不自己调整一个来达到这个目的!

调试

当您遇到错误并且编写了所有代码时,建议错误可能源自键盘和椅子之间是不公平的。 但是,在假设之前,我建议您测试您最基本的假设。 例如,我记得我花了两个多小时来解决我认为是由于我的代码引起的问题。 在 iOS 中,我只是无法让我的输入框接受文本输入。 我不记得为什么它以前没有阻止我,但我确实记得我对这个问题的挫败感。

原来这是由于 Safari 中的一个尚未修复的错误造成的。 事实证明,在 Safari 中,如果您有:

 * { user-select: none; }

在您的样式表中,输入框不会接受任何输入。 您可以使用以下方法解决此问题:

 * { user-select: none; } input[type] { user-select: text; }

这是我在“应用程序重置”CSS 重置中采用的方法。 然而,真正令人沮丧的是我已经学会了这一点,后来又忘记了。 当我终于在解决问题的同时检查 WebKit 错误跟踪时,我发现一年多前我已经在错误报告线程中编写了一个解决方法,并完成了缩减!

想用数据构建? 学习 JavaScript 数组方法

也许通过这个应用程序构建练习,我的 JavaScript 技能取得的最大进步就是熟悉了 JavaScript 数组方法。 我现在每天都使用它们来满足我所有的迭代和数据操作需求。 我不能足够强调map()filter()every()findIndex()find()reduce()等方法的有用性。 您几乎可以用它们解决任何数据问题。 如果您的武器库中还没有它们,请立即为 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array 添加书签,并尽快挖掘。 我自己对我喜欢的数组方法的总结记录在这里。

ES6 引入了其他用于操作数组的节省时间的方法,例如SetRestSpread 。 在我分享一个例子时,请尽情享受; 如果你想从一个简单的平面数组中删除重复项,曾经有很多麻烦。 不再。

考虑这个带有重复条目“Mr Pink”的数组的简单示例:

 let myArray = [ "Mr Orange", "Mr Pink", "Mr Brown", "Mr White", "Mr Blue", "Mr Pink" ];

要使用 ES6 JavaScript 消除重复项,您现在只需执行以下操作:

 let deDuped = [...new Set(myArray)]; // deDuped logs ["Mr Orange", "Mr Pink", "Mr Brown", "Mr White", "Mr Blue"]

过去需要手动解决方案或访问库的东西现在已融入该语言。 诚然,在诸如短数组上听起来可能没什么大不了的,但想象一下在查看具有数百个条目和重复项的数组时可以节省多少时间。

维护和安全

您构建的任何使用 NPM 的东西,即使只是用于构建工具,也可能容易受到安全问题的影响。 GitHub 在让您了解潜在问题方面做得很好,但仍然存在一些维护负担。

对于仅仅是一个副项目的东西,在积极开发之后的几个月和几年里,这可能会有点痛苦。

现实情况是,每次更新依赖项以修复安全问题时,都会引入破坏构建的可能性。

几个月来,我的package.json看起来像这样:

 { "dependencies": { "gulp": "^3.9.1", "postcss": "^6.0.22", "postcss-assets": "^5.0.0" }, "name": "In Out", "version": "1.0.0", "description": "simple utility to see who's in and who's out", "main": "index.js", "author": "Ben Frain", "license": "MIT", "devDependencies": { "autoprefixer": "^8.5.1", "browser-sync": "^2.24.6", "cssnano": "^4.0.4", "del": "^3.0.0", "gulp-htmlmin": "^4.0.0", "gulp-postcss": "^7.0.1", "gulp-sourcemaps": "^2.6.4", "gulp-typescript": "^4.0.2", "gulp-uglify": "^3.0.1", "postcss-color-function": "^4.0.1", "postcss-import": "^11.1.0", "postcss-mixins": "^6.2.0", "postcss-nested": "^3.0.0", "postcss-simple-vars": "^4.1.0", "typescript": "^2.8.3" } }

到 2019 年 6 月,我从 GitHub 收到了以下警告:

GitHub 界面突出显示构建工具依赖项的安全问题
将依赖项列在 GitHub 上意味着很少出现安全警告。 (大预览)

没有一个与我直接使用的插件相关,它们都是我使用的构建工具的子依赖项。 这就是 JavaScript 包的双刃剑。 就应用本身而言,In/Out 没有问题; 那没有使用任何项目依赖项。 但由于代码在 GitHub 上,我觉得有责任尝试解决问题。

可以手动更新包,只需对 package.json 进行一些选择更改。 然而,Yarn 和 NPM 都有自己的更新命令。 我选择运行yarn upgrade-interactive ,它为您提供了一种从终端更新内容的简单方法。

使用 Yarn 升级软件包的 CLI 界面
Yarn 使升级项目依赖项更加可预测。 (大预览)

看起来很简单,甚至还有一个小彩色键可以告诉您哪些更新是最重要的。

您可以添加--latest标志以更新到依赖项的最新主要版本,而不仅仅是最新的修补版本。 一分钱……

麻烦的是,在 JavaScript 包世界中事情发展得很快,所以将一些包更新到最新版本然后尝试构建会导致以下结果:

Gulp 构建错误
Gulp 构建错误(大预览)

因此,我回滚了我的package.json文件并在这次没有--latest标志的情况下再次尝试。 这解决了我的安全问题。 说实话,这不是我周一晚上玩得最开心的一次。

这涉及到任何副项目的重要部分。 对您的期望保持现实。

附带项目

我不知道你是不是也一样,但我发现一种头晕目眩的乐观和兴奋让我开始项目,如果有的话,尴尬和内疚让我完成它们。

如果说我在业余时间制作这个小应用程序的经历充满乐趣,那将是一个谎言。 有时我希望我永远不会对任何人开口。 但现在它完成了,我 100% 相信投入时间是值得的。

也就是说,可以通过现实地了解理解和解决您面临的问题需要多长时间来减轻对此类副项目的挫败感。 每晚只有30分钟,一周几个晚上? 你仍然可以完成一个业余项目; 如果您的步伐缓慢,请不要感到不满。 如果事情不能让您全神贯注,请准备好比您可能习惯的更慢和更稳定的步伐。 确实如此,无论是编码、完成课程、学习杂耍还是写一系列文章来说明为什么要花这么长时间才能编写一个小型 Web 应用程序!

简单的目标设定

您不需要花哨的过程来设定目标。 但这可能有助于将事情分解为小/短任务。 像“为下拉菜单编写 CSS”这样简单的事情在有限的时间内完全可以实现。 而“研究和实现状态管理的设计模式”可能不是。 把事情分解。 然后,就像乐高一样,小块拼凑在一起。

考虑到这个过程是为了解决更大的问题,我想起了著名的比尔盖茨名言:

“大多数人都高估了自己一年能做的事,却低估了自己十年内能做的事。”

这是来自一个帮助根除脊髓灰质炎的人。 比尔知道他的东西。 听听比尔。

运送东西总比什么都不运送好

在“发布”这个 Web 应用程序之前,我查看了代码并且非常沮丧。

尽管我从完全幼稚和缺乏经验的角度开始了这段旅程,但在我如何构建代码(如果你能原谅如此宏大的术语)方面,我做出了一些体面的选择。 我研究并实现了一种设计模式,并享受了该模式所提供的一切。 可悲的是,当我越来越不顾一切地想要完成这个项目时,我没能保持纪律。 目前的代码是一个真正的大杂烩,而且效率低下。

几个月以来,我开始意识到这些缺点并不重要。 并不真地。

我很喜欢 Helmuth von Moltke 的这句话。

“除了与主要敌对力量的第一次接触之外,没有任何行动计划可以确定地延伸。”

这被解释为:

“没有计划能在与敌人的第一次接触中幸存下来”。

也许我们可以进一步归结为“狗屎发生”?

我可以通过以下类比推测我对所发布的内容的接受程度。

如果一个朋友宣布他们将尝试参加他们的第一次马拉松比赛,那么他们越过终点线将是最重要的——我不会因为他们的完成时间而责备他们。

我并没有打算编写最好的 Web 应用程序。 我给自己设定的职责只是设计和制造一个。

更具体地说,从开发的角度来看,我想了解如何构建 Web 应用程序的基础知识。 从设计的角度来看,我想为自己尝试解决一些(尽管很简单)的设计问题。 制作这个小应用程序遇到了这些挑战,然后是一些挑战。 整个应用程序的 JavaScript 文件只有 5KB(压缩后)。 我很难使用任何框架来处理一个小文件。 除了也许 Svelte。

如果您正在为自己设定这种性质的挑战,并期望在某个时候“交付”某些东西,请在一开始就写下您这样做的原因。 将这些原因放在您的脑海中,并以它们为指导。 一切最终都是某种妥协。 不要让远大的理想阻碍你完成你打算做的事情。

概括

总的来说,自从我从事 In/Out 工作已经一年了,我的感受大致分为三个方面:我后悔的事情、我想要改进/修复的事情以及未来的可能性。

我后悔的事

正如已经提到的,我很失望我没有坚持我认为更优雅的方法来更改应用程序的状态并将其呈现到 DOM。 正如本系列的第二部分所讨论的那样,观察者模式以可预测的方式解决了如此多的问题,最终被抛弃了,因为“交付”项目成为了优先事项。

起初我对我的代码感到尴尬,但在接下来的几个月里,我变得更加哲学化了。 如果我后来没有使用更多的步行技术,这个项目很有可能永远不会结束。 将一些需要改进的东西带到这个世界上仍然感觉比它根本没有被诞生到这个世界上要好。

改善输入/输出

除了选择语义标记之外,我没有为可访问性提供任何启示。 当我构建 In/Out 时,我对标准的网页可访问性很有信心,但对处理应用程序的知识还不够。 我现在在该领域做了更多的工作/研究,所以我很乐意花时间做体面的工作,使这个应用程序更易于访问。

“添加人员”功能的修订设计的实施很匆忙。 这不是一场灾难,只是比我想要的要粗暴一些。 让它更光滑会很好。

我也没有考虑更大的屏幕。 考虑让它在更大尺寸上工作的设计挑战会很有趣,而不仅仅是让它成为一个内容管。

可能性

使用 localStorage 可以满足我的简单需求,但是拥有一个“适当的”数据存储会很好,因此不必担心备份数据。 添加登录功能还将打开与另一个人共享游戏组织的可能性。 或者也许每个玩家都可以标记他们是否在玩自己? 令人惊讶的是,您可以从如此简单而卑微的开端设想有多少探索途径。

用于 iOS 应用程序开发的 SwiftUI 也很有趣。 对于只使用过网络语言的人来说,乍一看,SwiftUI 看起来像是我现在有勇气尝试的东西。 我可能会尝试使用 SwiftUI 重建 In/Out — 只是为了有一些特定的东西来构建和比较开发经验和结果。

所以,是时候总结一下,给你 TL;DR 版本的所有这一切。

如果您想了解某些东西在网络上的工作原理,我建议您跳过抽象。 抛弃框架,无论是 CSS 还是 JavaScript,直到你真正理解它们对你有什么好处。

设计是迭代的,拥抱这个过程。

在您可以使用的最低保真度介质中解决问题。 如果你可以在 Sketch 中测试这个想法,就不要去编码。 如果您可以使用笔和纸,请不要在 Sketch 中绘制它。 先写出逻辑。 然后写成代码。

现实一点,但永远不要沮丧。 养成每天只花 30 分钟做某事的习惯就能取得成果。 无论您的追求采取何种形式,这一事实都是真实的。