我正在为一个新的web应用程序使用urql和Svelte。
我遇到了一个逻辑问题,我不知道如何最好地解决。
假设我有很多todo,并且这些todo的列表已经缓存在我的手机浏览器上。
当我在地铁上(非常慢的网络(时,我打开一个来编辑它(然后应用程序从缓存加载带有todo的编辑表单(;CCD_ 1";在我妻子更改它之前不久,todo重新更新了自己,撤销了我在文本区写的所有更改。
你认为怎样才能解决这种情况?
-
我应该等待
cache-and-network
完成才能编辑表单吗?(这对于离线用户体验来说并不好(; -
我应该警告用户有更新的todo吗?
-
我是否应该警告用户,只有当我将todo保存在后端的DB中时,才有更新的todo?
-
其他建议?
您可以使用navigator.onLine
来检查您的用户是在线还是离线。如果他处于脱机状态,请将他的更改保存到localStorage
。当状态再次改变时;您可以将当前状态与localStorage
进行比较,并相应地更新表单。
我所说的大部分逻辑都在这里解释
如果您需要在localStorage
中保存表单的帮助,可以使用以下模板:
[].forEach.call(document.querySelector('#form-id').elements, function(el) {
localStorage.setItem(el.name, el.value);
});
// then refresh the page and run this to restore your form values:
[].forEach.call(document.querySelector('#form-id').elements, function(el) {
el.value = localStorage.getItem(el.name);
});
您描述了一个经典的race condition
,其中两个用户在同一资源上创建不同的操作(在我们的用例中是db中的记录(
简短回答:record versioning
-为每条记录管理一个唯一的版本号。只有在使用相同版本值交付时才能执行更改
长答案:假设我们使用no-sql db
,其中每个列表都保存在cache-and-network
0集合中,其中每个记录看起来像这样:
{
id: 'uuid-4',
items: [
{ name: 'item #1', checked: false },
{ name: 'item #2', checked: true },
{ name: 'item #3', checked: false }
],
version: 1648727895753
}
在上面的示例中,我使用了timestamp
作为记录版本(使用new Date().getTime()
方法(。每次更新此记录时,版本也会发生变化。
现在,让我们再次描述您的场景:
2个用户从服务器下载相同的CCD_ 13。此时,两个用户都具有相同的版本值(比如版本A
(。现在,让我们假设一个用户进行了更新,而另一个用户处于脱机状态(在您的示例中,由于旅行(。为了使这种模式发挥作用,我们在每次成功更新后都会更改版本值,这样现在记录就有了一个新的版本值(比如版本B
(。当另一个用户尝试进行更改时,它将被阻止,因为他仍然使用旧版本号(在我们的示例A
中(。为了进行更改-应首先下载最新版本(B
(,然后用户将更新自己的编辑并将其更改写入数据库(此过程将产生C
版本的更新记录(
因此,为了实现这一点,应该创建以下机制:
服务器端(数据库逻辑(
- 记录更新
- 只有当请求版本值等于记录版本值时才进行更新
- 获取记录时
- 返回当前版本值的记录
客户端(应用程序逻辑(
- 应用程序加载
- 获取当前待办事项列表记录(通过
id
字段(
- 获取当前待办事项列表记录(通过
- 记录更新
- 使用当前版本值向服务器发送更新
- 如果更新失败:
- 将当前待办事项列表记录保存为
old version
- 获取当前待办事项列表记录(通过
id
字段( - 合并更改(您将决定如何将其反映给用户(
- 将更新发送到具有当前版本值的服务器(它现在是更新最多的一个(
- 将当前待办事项列表记录保存为
技术说明
- 两个记录版本之间的合并(例如,介于到基于JSON的对象之间(本身就是一个完整的宇宙。您可以使用第三方库来发现差异(例如json-diff(,或者决定应用默认行为(例如:如果在数据库中删除了列表中的项目,则会应用不同的样式,以便用户理解该项目已被删除(
- 即使是现在,我们仍然有竞争条件的风险(嗯……在使用简单的
todo list
时极不可能,但在高负载的应用程序中可能会发生(。对于生产准备就绪的设置,请考虑使用消息队列,以便按特定顺序应用所有操作(先进先出(。再说一遍,这对你的用例来说是一种过度的处理,但由于我们在这里解决了一个更普遍的问题,我认为值得一提