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

553 阅读4分钟

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

开头

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

开始

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

简单来说

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

改进一下

  1. step-1

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

  const Form = () => {
    const handleChange = () => {}
    return (
      <input onBlur={handleChange} />
    )
  }

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

  1. step-2

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

  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包,毕竟本身它并有依赖于大屏的任何东西。

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

const requestParams = {
  type: 'update', // add delete merge...
  value: {
    id: 'xxxx',
    path: 'xxxx',
    data: {
      path: {
        to: {
          updateData: 'xxxx'
        }
      }
    }
  }
}

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

完善一下

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

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

  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为空则停止。

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

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

结束

结束🔚。

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

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