数据可视化大屏设计器开发-实时保存

数据可视化大屏设计器开发-实时保存

开头

本文是数据可视化开始的开发细节第二章。关于大屏设计时,数据的实时保存问题。
在我们使用在线excel时就会发现,每一次你的操作都是会被保存的,这样做的好处就是,避免了在修改大量数据之后,不小心关闭页面或者断网的情况,导致修改内容消失的情况发生。

开始

实时保存也有很多的方案。

简单来说

简单来说,开启一个定时器,定时将前端的数据保存到后台,但是这样会造成资源的浪费,有时候可能我们完全没有去操作,只是单纯的在页面中停留,这样还去频繁的调用保存接口完全没有必要。

改进一下

  1. step-1

修改一下上面的方法,我们只在数据发生变化的时候才调用保存接口。
那么怎么才能做到上面的方法呢?比如我们输入一个文本框的内容,不可能每输入一个字就调一次保存,这样反而更加频繁调用接口。
这时候就不应该去监听表单的onChange事件,而是去监听它的onBlur事件,这样就解决了上面的问题。

1
2
3
4
5
6
const Form = () => {
const handleChange = () => {}
return (
<input onBlur={handleChange} />
)
}

如上解决了调用频繁的问题。

  1. step-2

但是又存在了新的问题,因为我们只监听了onBlur,所以无法使用外部受控的方法来设置value,导致数据回填会出现问题。
所以选择在组件内部控制一份状态,同时监听外部状态的改变,如果改变了则将内部状态与外部状态进行同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const Form = (props) => {
const { value, onChange, defaultValue } = props
const [ internalState, setInternalState ] = useState(value || defaultValue)

const handleChange = () => {
onChange(internalState)
}

const onInternalChange = (e) => {
setInternalState(e.target.value)
}

useEffect(() => {
setInternalState(value)
}, [value])

return (
<input value={internalState} onChange={onInternalChange} onBlur={handleChange} />
)

}

优化一下

上面完成了接口调用频繁的问题,还可以继续优化。
因为设计器所设计的大屏包含大量的配置,如果每一次都是将所有数据一起同步到后端,肯定会让接口的调用速度变慢。
而且设计阶段,对于保存的操作又是非常的频繁,这样肯定会影响用户体验。

解决的办法就是只将修改的数据保存到后端,这样接口请求速度一定不会慢,而如何做到这一点呢?
我们可以预先定义一系列的操作类型,对每一个操作类型设置不同的操作方法,因为后端和前端的数据格式都是一个json,我们完全可以做到前后端同步。
只是如果前端和后端分开两套代码实现同一个逻辑,那有点得不偿失。
当然后端我们可以选择使用node来进行开发,完成相关逻辑,同时对大屏操作的方法单独抽离成一个npm包,毕竟本身它并有依赖于大屏的任何东西。

比如像下面这样的保存参数例子🌰:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const requestParams = {
type: 'update', // add delete merge...
value: {
id: 'xxxx',
path: 'xxxx',
data: {
path: {
to: {
updateData: 'xxxx'
}
}
}
}
}

当拿到这份数据时,我们只需简单的与大屏数据进行合并即可。

完善一下

上面的方法基本达到了实时保存的要求,但是还可以进行完善。
因为设计大屏的时候,经常发生频繁修改配置的情况,虽然做了onBlur的处理,不过有时候调用的频率还是会很高,如果又在网络状况不好的环境下,那经常就会发生错误。
所以我们可以将上面的请求进行合并

本身其实也只是一个个修改对象,我们可以将数据请求格式更改为数组,后端按顺序遍历数组,同样可以达到修改的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const requestParamsList = [
{
type: 'update', // add delete merge...
value: {
id: 'xxxx',
path: 'xxxx',
data: {
path: {
to: {
updateData: 'xxxx'
}
}
}
}
},
// ...
]

接着我们在前端开启一个请求池,这里命名成REQUEST_POOL,一个等待池,命名为PENDING_POOL
当发生新的修改时。
先判断PENDING_POOL是否为空,如果为空,则直接将请求放入REQUEST_POOL,否则放入PENDING_POOL
如果上述放入的REQUEST_POOL,并且在短时间内没有新进入的请求或者REQUEST_POOL已满,则将REQUEST_POOL内的请求参数合并发送。
REQUEST_POOL完成请求后,继续将PENDING_POOL中的请求加入到其中,PENDING_POOL为空则停止。

提示
这一步骤的功能目前未实现到大屏当中,还只是一个概念。

下一个提示
关于上面实时保存,还有一个最重要的点:发送请求后,后端应给予前端正确且明确的反馈,告知此次更新已经完成,否则会发生前后端存储数据不同步的情况。

结束

结束🔚。

顺便在下面附上相关的链接。

试用地址
试用账号
静态版试用地址
操作文档
代码地址


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!