粉碎训练回顾

2020-12-12 00:58:26

Smash Training是一个间隔重复训练的网络应用程序,我创建该应用程序是为了帮助我与Super Smash Bros. Ultimate进行比赛。我于2020年5月16日在Reddit上发布了它,受到热烈欢迎。截至2020年12月,它每月接收150-200个用户。我将其列为我最成功的项目!

在本文中,我将讨论为该项目所做的选择。可从https://github.com/arxanas/smashtraining获得源代码。

我决定我想构建一个间隔重复训练应用程序,而不是重复使用Anki之类的通用间隔重复抽认卡系统,因为该项目将从特定领域的知识中受益。例如:

练习具有大量变体,例如“短跳”与“全跳”,或“朝左”与“朝右”,应分别进行跟踪。

许多练习天生就依赖于其他练习:除非已掌握某些基本技能,否则不要尝试这些练习。

训练一个角色的练习不一定会赋予其他角色相同的技能。一些练习可能仅适用于某些角色。

我决定制作一个应用程序来自动执行我尝试手动执行的重复间隔方案,然后与他人共享。

应该设计为支持设备之间的同步,尽管同步本身并不是第一次迭代的要求。

应该托管在不需要监控的稳定平台上(例如,不是我的家用计算机)。

最后,我使用了ssb.fit,因为1)smash.training被接受了(!),2)我想对其进行优化以在移动设备上键入它,尽管该名称不太容易记住。不幸的是,这种令人难忘的缺乏体现在Reddit主题为“ WTF的那个粉碎培训网站叫?”上。但是,另一位评论者写道“ ssb.fit更好,是移动设备的缩写”,也许证明了最初的选择。

不幸的是,域名和网站标题不完全匹配。因此,许多人似乎将其称为“ ssb.fit”,因此也许也应该将该项目称为“ ssb.fit”(而不是“ Smash Training”)。

我与朋友和家人进行了一些用户研究,其中包括一些以前玩过Smash的人和一些没有玩过Smash的人。

我迭代的第一件事是运动追踪器小部件的设计。我最初基于Stronglifts应用程序:

Stronglifts是否已记下您成功重复练习的次数(五分之一)。但是,“粉碎训练”范式有所不同,您需要重复练习一段时间并评估准确性。

我在Stronglifts中尝试了“笑脸”用户界面,而不是代表次数用户界面以及其他一些选项。在收到朋友的大量反馈后,我来到了一个基于滑块的小部件,如下所示:

这使用滑块方法(具有五个可能的凹口),并给出每个凹口对应的描述,即“所有或几乎所有代表均正确”。

第二个主要问题是“学习练习”页面中元素的顺序。每个练习都逐步说明如何进行练习,必须执行哪些控制器输入,技术背景及其重要性,视频教程等。

我的假设是,大多数人很少阅读它,所以我应该把最重要的内容放在首位。但是,许多用户不同意哪个项目是最重要的。没有强烈的共识,但最终结果是这样的:

这些步骤也有一些提示,例如如何进入训练阶段进行练习。一些用户完全错过了这些步骤,并对如何执行练习感到困惑。不幸的是,我无法设计一个缓解此问题的UI。

我选择编写一个Web应用程序,因为它们是跨平台的,并且我已经对该领域有所了解。特别是,我不想花钱购买iOS开发人员许可证,但也不想排除iOS用户。 (事后分析表明,Android用户与iOS用户的比例约为2:1,这构成了iOS的重要队列。)

所有Javascript Web应用程序捆绑解决方案从根本上来说都是糟糕的,Webpack也不例外。但这有效。

在开发过程中,我在Babel中遇到了一个神秘的错误,无法隔离。通过仅针对较新的浏览器来解决此问题,此后问题消失了。

按照Vue的建议,我使用vue-cli-service作为构建,测试和棉绒操作的包装器。但是我发现很难进行配置和调试。当我遇到测试无法正确编译导入模块的问题时,我放弃并重新实现了自己需要的功能。

我还使用了TypeScript,因为我发现它的静态键入系统对于维护很有用。

对Vue的TypeScript支持并不理想。许多Vue模式在TypeScript中不容易表达。存在诸如vuex-typescript之类的库,但需要大量样板程序才能获得静态类型支持。 Direct-Vuex库的样板较少,但我不知道如何进行测试。

尽管在项目中,我将TypeScript推到了极限,但它却跟不上进度,因此TypeScript的使用通常很愉快。就我而言,它无法充分跟踪关联/映射的类型。 @ ts-ignore注释可以说明这一点:

出口类型TechVariantOf< T扩展了TechId> = {// @ ts-ignore"键入' x'不能用于为类型' AllTechVariants'编制索引。 //奇怪的是,无论如何这里都会计算出正确的类型,并且//可在以后进行详尽的检查。 [x AllTechMetadata的键[T] ["变体" ]]:AllTechVariants [x]; };

考虑到这是相当高级的类型级别的黑客,我对TypeScript能够描述数据域的能力通常感到满意。

我选择使用Vue作为前端Web框架,因为我从Hacker News听到了有关它的好消息。特别是,我想要一个自以为是的框架,以便减少自己配置事情的时间。

当我使用它时,Vue推广了“单个文件组件”系统,其中将HTML,CSS和Javascript混合到同一文件中。这不是一个很棒的经历:

这使构建过程变得复杂,因为必须将这些单文件组件转换为浏览器可消耗的资产。

心理模型是间接的额外一层,因为这些单文件组件本身已被编译为Javascript类,但在文件的脚本部分中也包含Javascript类。

工具支持差。例如,尽管HTML组件最终会由Javascript类支持,但转到定义却无法使用。

我宁愿使用JSX解决方案,因为它消除了一些间接性并具有更好的工具支持。

我希望Vue做事的方式更少。例如,可以使用normal =语法设置HTML元素上的属性,但是为了简洁起见,也可以使用前导:(表达式求值)或前导@(回调)。相比之下,针对所有这些情况,React with JSX仅具有=。

Vuetify是一个为Vue提供Material Design UI的库。坚实的,多合一的Material Design库的存在是我选择使用Vue的另一个原因。库和文档都非常好,而且我能够(从UI角度)有效地对我的应用程序进行原型设计。如果您使用的是Vue,强烈建议您使用。

我打开了一个文档请求请求,该请求被迅速合并,并为其中一个文档问题+1了,该问题可以解决,但很遗憾仍未解决。

我使用Netlify使用其免费层来托管网站的前端,并为用户本地存储数据。 Netlify知道如何构建和部署我的Vue项目,并且具有良好的Github集成,因此效果很好。

另一个选择是Github Pages,它将使项目依赖于更少的基础服务,但是还需要我编写自己的构建步骤。

我使用localStorage API将数据本地存储在客户端上。我小心地设计了数据模式,使其仅允许追加,并且每个记录都具有唯一的ID,其思想是使合并来自多个客户端的更改变得容易。但是,仅此一项就很难不加思索地删除记录。

后来,我以与我设计应用程序完全相同的方式发现了CouchDB作为分布式文档存储,但是包括同步和删除功能。我还发现了PouchDB库,该库公开了CouchDB接口,并允许您在本地存储数据或远程同步数据。它还支持更多的后端,而不仅仅是localStorage。

我希望我从一开始就使用过PouchDB!现在,我受困于效率低下,缺少功能的实现,这需要一些迁移工作才能移至PouchDB。

我抓取了一个称为Elite GSP的公共服务来累积历史排名数据(称为“ Global Smash Power”或GSP),以便用户可以跟踪他们与其他人相比随时间的排名进度。

为了避免在某个地方托管数据库,我决定将记录直接检入源代码管理中,然后由Netlify分发。为此,我在个人网络服务器上设置了工作。在最坏的情况下,如果Web服务器宕机并停止更新Git存储库,则过时的数据仍然会相当有用,并且可以随时重新启动该作业。当前,该作业每天运行一次。

我希望我已经知道可以使用Github Actions来提交有问题的仓库,如Git抓取文章中所述:通过抓取到Git仓库跟踪随时间的变化。我肯定会这样做,而不是依靠我自己的网络服务器的可用性。

所有这些自动提交导致提交历史变得相当污染。然后,如果它是数据库更新的提交,则我决定修改最近的提交,而不是进行新的提交。

这正在重写公共历史,并且在技术上不赞成这样做,因为如果最近进行了抓取工作,则所有下游开发人员都必须将其更改基于母版。但是,重新设置基准通常是无冲突的,因为重写的提交只会更改计算机生成的文件,因此实际上这不是问题。

零托管Web应用程序对于本地优先Web应用程序是完全可行的。我想如果您的应用程序要求用户能够彼此交互,并因此支持对数据访问的身份验证/授权,会更加困难。

使用自动挂钩代替我自己的Web服务器来运行部署服务是一种更加方便和声明式的方法,因为我不必自己管理任何计算机。

这个项目对我来说是成功的,因为1)我解决了一个问题; 2)我解决了别人遇到的一个问题; 并且3)我将维护负担降低到最低限度—自启动以来,我没有任何部署问题。