在软件开发中至关重要的是要弄清楚您的软件需要做什么,并确保您用来构建的技术支持此功能。这是主要的资源浪费,更不用说对开发团队感到巨大的挫败感,必须回到绘图板上。
即使有最仔细的计划,事情并不总是像您想要的那样进行。一些图书馆和框架对功能过高,使团队在建立方面构成,只是发现他们的最终产品无法满足期望。此外,从事踩新地面的产品的开发人员可能会遇到意外的兼容性或性能问题。
本文探讨了我们在实施新功能的同时遇到的一种情况。如果您不熟悉我们,您可以查看使用Appsmith到build a CRUD app的容易,并且您可以通过为企业提供更好的内部工具来了解我们如何帮助企业improve their internal processes。
。为什么我们要谈论Appsmith中的JS对象变量突变?
可变突变(最简单的定义)正在改变编程变量的值。在Appsmith的上下文中,JS Objects将变量和函数封装在页面级范围上,以重复使用与JavaScript objects的形式和函数相似,后者封装了属性和方法。
。从历史上看,存储在appsmith js对象中的变量已充当常量 - 它们是在应用程序代码中定义的,并且从那里不可变。应用程序运行时无法更改它们。这不是很有用,并且总是我们知道我们需要解决的问题以使JS对象功能功能完整。
令我们高兴的是waiting patiently for this feature到达的用户,现在完全支持Appsmith的JS对象中的可变突变。
用户如何解决这个问题?
Appsmith是一个多功能的平台,因此缺乏可变突变并没有减慢使用该平台构建其应用程序的速度。
大多数人都喜欢的解决方法是使用Appsmith的storeValue()函数从JS对象函数中保存current session的数据。由于这并不是storeValue()
打算使用的目的,因此既不方便也不直观。以这种方式存储来自JS对象的数据过于冗长,需要使用每个对象独特的名称进行设置和检索它们。它也很慢,因为它使用了浏览器的sessionStorage属性。
除了用于管理JS对象变量时的详细信息外,使用storeValue()
需要额外的脚手架才能工作。使用此方法存储的键/值对必须在页面加载函数中初始化,然后才能读取它们。每个对象都需要使用storeValue()
为每个变量存储在会话中的唯一命名键/值对,每个变量都在页面加载中初始化。要跟踪很多,而且尚不清楚这种初始化过程是必要的,导致了很多挫败感。
我们的用户很清楚,他们期望可变的JS对象变量会更快而不是迟到。 Appsmith的JS对象是用JavaScript编写的,它们称为JS对象,因此您希望它们的行为能像JavaScript对象一样,让您更新其变量!
最初的实施...和绩效问题
因此,这就是我们构建的,我们以最明显的方式构建了它 - 使用JavaScript Proxy对象跟踪突变并反映了Appsmith框架中的这些变化。最初,事情看起来不错 - 除了一些黑客以使某些数据类型与map和set起作用外,我们还遵循other projects that had similar requirements的例子。 如果对他们来说足够好,那么对我们来说应该足够好,对吗?
但是,一旦我们充分实施了该功能,我们就会遇到由我们实施的缓解措施引起的性能问题。 Appsmith依靠JavaScript workers从JS对象执行用户的代码,并且在使用Proxy
对象时,我们无法将数据直接从工人回到主线程(该数据可以处理绑定到JS对象中变量的UI窗口小组之类的内容)。这是因为用于在Web工作人员之间发送消息的postMessage API和内部线程使用structuredClone()函数,does not support Proxy
对象。为了解决这个问题,我们使用了自己的嵌套克隆算法,该算法在将其传递到主线程之前删除了工作线程中的Proxy
对象,这正在损害性能。
每当我们必须将数据从工人带回主线程时,这种克隆算法就必须运行。无论何时出现JS对象中的数据,都会触发该算法。当应用仅使用几个简单对象时,这是可以的,但是添加了更可变变量的对象,性能就会变得更糟。我们的用户构建复杂的应用程序,因此性能的这种比例降解是不可接受的。
该图显示了我们最初从工人传递数据到主线程的方法的方法 - 在何处发生了速度。您还可以查看我们使用的代码here。
架构上,此功能也很难维护。克隆数据的算法必须出现到任何地方使用数据。但是,如果它是表演者,并为我们所有可用选项的用户提供了最佳结果,我们将坚持下去。
现实是,使用Proxy
对象以这种方式实现可变突变非常慢,我们无法证明释放它是合理的。我们评估了从用户那里退缩此功能的潜在影响(,并仔细考虑了我们拥有多少商誉!),并决定找到一种更好的方法。
我们如何做得更好 - 将现有功能与出色的性能匹配
因此,我们有一个问题要解决 - 我们必须模仿初始解决方案提供的功能,但没有缺点。我们必须发挥创造力。从计划的角度来看,决定重建已经投资资源的功能并不是有利的,但是从程序员的角度来看,这是很棒的 - 我们 Love 问题解决。
我们想要一种解决克隆算法引起的性能和发展问题的解决方案。它必须是有效的,因此用户界面不会变得缓慢或不响应,并且DRY,因此我们不必在Appsmith JS Object的每个外观时都调用该算法。
。我们的最终解决方案非常简单。我们没有使用Proxy
对象拦截和跟踪更改JS对象的变量,而是一个自定义类,该类从JS对象作为输入中获取变量,并添加Setter/Getter方法,将跟踪读取和写入操作跟踪到变量本身。调用设置器表示变量可能已更新,然后我们可以在其上运行一些差异逻辑,以确认是否有实际更新。一旦确认更改,就可以将其传播并从工人传播到主线程。您可以看到此here背后的代码。
该图显示了我们更新的解决方案,以及我们如何使用代理对象跟踪更改的方式。
由于我们不再具有任何Proxy
对象,因此在将其从工人传递到主线程之前,我们不需要任何嵌套的克隆逻辑就将其从JS对象变量中删除。这消除了性能问题,因为与运行克隆算法相比,称为getter或setter要便宜得多。
我敢肯定,对于某些程序员而言,与Proxy
实施相比,这似乎不是跟踪更新的最佳方法,但是当涉及工人时,我们发现这种新方法非常实用。最重要的是,它起作用,并且是 fast 。我们可以通过一种解决方案返回用户,这将真正改善他们对产品的体验。
在不损害Appsmith体验的情况下向用户兑现承诺
我们本可以在其初始状态下传递JS对象突变,并添加了Proviso hey,它可以很好地工作,如果您使用太多变量,它就很慢!如果您遇到了性能问题,请重组您的应用程序!许多产品会导致对用户实施不佳的后果,并将其作为他们解决的问题。 这对我们来说还不够好。我们想以一种可以为我们的用户提供我们所承诺的东西而不损害我们的平台性能的方式。
。现在,我们已经实现了这一目标,我们正在研究如何更快地确定计划的实施中的潜在问题,从而可以减少发生这种情况时造成的延迟。我们还确保我们与那些与我们联系的人保持联系的人以及他们可能遇到的错误,并保持进步的速度,以便他们可以与我们的开发时间表保持一致。
这种经历向我强调了与您的社区互动,确保您倾听他们的重要性,并让他们了解影响他们的任何发展。通过聆听用户,您将知道要优先考虑哪些功能,并且可以确保为他们设定现实期望并验证他们的反馈。这样,如果有延误,您更有可能与理解而不是沮丧相遇。
您还可以破坏用户体验的技术,从而解决技术,但可以降低整体用户体验,以解决一个问题。重要的是,如果不够好,即使它似乎是唯一的问题,也不要解决问题的第一个解决方案。编程是一项创造性的努力,而且(几乎)总是一个解决方法。
如果您有兴趣阅读有关我们如何构建Appsmith,check out our blog的更多信息。如果您想查看我们的努力工作的结果,Appsmith提供了free cloud-hosted version,或者可以使用Docker在自己的基础架构上托管。