前言

文章写于 2020年3月,后边只有简单的补充更新历程 ~

随着公司的发展和进步,数据大屏的业务日益增长,公司目前的做法大致可以分为两种 人工标配 or 第三方工具 .

人工:很简单,一个前端配备,熟练的 html + css + js 技能就完事了 ;
优点:开发灵活多变
不足:效率低,页面复用度不高(几乎为0复用度),大量重复性工作,占用前端开发时间 等

当然,也有人会说,目前比较主流的前端框架,像 Vue ,React 都是组件化,模块儿化的工程,组件复用性很高,也对,这点毋庸置疑,但也改变不了大量重复性的工作和消耗前端资源的劣势 ;甚至还有人会说,当组件封装的够多,质量够好,工程架构的得体,就可以解决上述的缺点,很对 !这就是我要分享给大家的《可视化数据平台》稍安勿躁 emmm ~ ~

第三方工具 :如:阿里云的dataV , 百度的 Sugar 都是很优秀的工具平台
优点:组件丰富,功能强大,基本满足大中小型企业对数据大屏的需求
不足:有没有不足请大家去体验一下 , 这里我就不做过多的评价 重点是要RMB

OKey,综上所述,随着公司业务的增多 ,市场未来对数据可视化的需求,为了降低开发成本,提高开发效率,降低开发难度,我们的《数据可视化平台》 就这样诞生了,在这里,我作为主要开发人员来给大家分享一下平台的具体实现 。

简单认识一下吧

在这里插入图片描述
在这里插入图片描述

技术栈

Vue 2.6 + Webpack 3.6 + Node + eCharts + D3

这里简单讲一下 Node 在项目里的应用情况

1,通过 node 启动 dev-server.js 文件,然后运行如下代码(dev-server.js

webpack-dev-server 主要是启动了一个基于 expressHttp 服务器 ;这个Http服务器和 client (客户机)使用了websocket通讯协议,原始文件作出改动后,webpack-dev-server会实时的编译,实时编译后的文件都保存到了内存当中 ;所以我们可以看到实时更新 。重点:基于启动的http服务实时编译工程

// dev-server.js 文件
var webpack = require('webpack')
var webpackDevServer = require('webpack-dev-server')
var webpackDevconfig = require("./webpack.dev.conf.js"); 
var opn = require('opn')
var port = process.env.PORT || config.dev.port

// api 
var apiServer = {
  setup: (app) => {
    // 具体接口就不做展示了
  }
}

webpackDevconfig.then(res => {
  var compiler = webpack(res);
  var server = new webpackDevServer(compiler, Object.assign(res.devServer, apiServer));

  server.listen(port, "0.0.0.0", function (error) {
    console.log(error);
  });
  opn("http://127.0.0.1:" + port) // 打开本地页面
})

2,就比较明了了,主要写一些接口,来实现平台目录和文件的保存,读取,复制,删除,修改,导入导出…等功能,目的是可以满足本地部署和云部署

设计思路

整个工程结构在这里不做分享,涉及到公司的产品代码,我在这里简单分享一下产品实现的思路和原理 .

其实,以拖拉拽的方式做可视化页面的产品有很多,原理也大同小异,无非就是拥有一个舞台,一定数量的组件,然后组件通过配置二次加工后放进舞台的过程,最后集成的舞台就是我们想要的成品 抛开数据来谈的话

首先分析一下舞台和组件的实现方式

舞台

舞台很简单,就是一个容器 , 用来盛放拖拽出来的组件 , 当然这个舞台可能有 宽,高,背景等配置属性,适用于已知宽高的大屏 ;这其中也会有未知宽高的大屏,Pc, 终端设备等,所以采用了自适应模式 ;那么舞台如何渲染组件的呢 ? 这里使用到了 Vue.extends() 构造器 + document.createDocumentFragment() 文档片段

1,对组件配置文件的信息读取通过 Vue.extends() 构造器来渲染一个新的组件
2,使用原生Api Document.createDocumentFragment 文档片段模拟虚拟dom做性能优化

# Vue-extends() 查看

//使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
//data 选项是特例,需要注意 - 在 Vue.extend() 中它必须是函数
<div id="mount-point"></div>
// 创建构造器
var Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')
//结果如下:
<p>Walter White aka Heisenberg</p>

# Document.createDocumentFragment() 查看

let fragment = document.createDocumentFragment();
//fragment 是一个指向空DocumentFragment对象的引用。 有兴趣的可以在控制台实践一下

DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。

因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。

组件

组件就相对比较复杂一点,由于我们的工程使用的 Vue 技术栈, 所以这里使用的是字符串模板来动态的创建组件,如何创建呢 ? 每一个组件分别有三个文件 暂不考虑公共提取部分

1,组件配置文件 - 相当于组件的父文件,通过对配置属性的修改来渲染对应组件

2,组件文件 - 图表及组件,根据父文件的属性配置生成对应的组件

3,属性配置文件 - 属性样式配置文件,也就是编辑页面图的右侧属性栏

看到这里也许有的童鞋已经联想到如何让它们联合工作了,其实就是采用简单的发布订阅模式,不管是组件的拖动位置,大小变化还是右侧栏的属性值变化,都会触发当前选中组件的实时更新,让我们进一步剖析一下

组件配置文件 - 样例代码 => 返回一个模板和最新属性键值对

var handle = function (attr, info) {
    let attributes = {
        name: "BarAlien",
        infoId: info.id,
        zIndex: 1,
        top: 1,
        left: 10,
        width: 300,
        height: 200,
        title: "柱状图",
        remark: '', //图表简介
        chartCustomStyle: false, //开启图表自定义背景和边框
        chartBackgroundColor: '', //图表背景颜色
        chartBorderRadius: false, //图表圆角
        borderWidth: 10, //图表边框宽度
        borderColor: '', //图表边框颜色
        shadowWidth: 0, //图表阴影宽度
        ...... // 更多省略
    }

    // 合并属性 
    Object.assign(attributes, attr)

    // 获取 attr 属性并传入组件,组件通过props获取    
    let stringAttr = getStringTypeAttr(attributes);

    //字符串模板操作
    let template = `<BarAlien ${stringAttr}/>`
    return { template, attributes }
}

export default handle;

组件 - 就简单了, 通过父文件传过来的参数, props 接收,渲染, 这个大家用过 vue 的应该都会

属性配置文件 - 样例文件

这里的右侧属性栏,相对来讲比较麻烦,采用的是 Vue 的双向绑定原理
样式则是通过配置的数组对象遍历生成对应的属性栏 ; 而属性栏风格很多,输入框,下拉框,单选,多选,开关,颜色,表格,自定义 等等分别是一个独立的组件 ,提供单独的事件监听与广播

[{
    label: "开启图表自定义配色",
    bind: "openCustomColor",
    tipLink: "/docs/Chart-Common#图表的自定义配色",
    tipText: "自定义配色说明",
    type: "switch"
}, {
    type: "color",
    bind: "textColor",
    format: "rgb",
    label: "文字颜色",
    visiblity: "openCustomColor",
    placement: "right-top-right-bottom"
}, {  
	type: "number",
    bind: "shadowBlur",
    visiblity: "openCustomShadow",
    label: "阴影模糊大小",
    min: 0
},{
    type: 'table',
    bind: "customColors",
    visiblity: "openCustomColor",
    label: "可增加多个配色项,依次对应各项颜色",
    addTipText: "新增配色项",
    controls: [{
        type: "color",
        label: "颜色",
        bind: "color",
        defaultValue: "#23b7e5",
        tdStyle: {
            minWidth: 100
        }
    }]
}]
编辑(核心)

在这里插入图片描述
上图可以看出来,整个编辑页可以拆分为四部分

  • 顶部 - 包含logo + 组件分类列表 + 舞台操作
  • 左栏 - 当前舞台组件管理 ,目前有删除 ,复制,层管理,等, 2020, 11 月已经修改为树形管理,可多组件拖拽,复制,删除
  • 右栏 - 核心位置之一,操作选中组件的所有配置项和动态数据(api , sql , ws 等方式 )以及体验交互(联动和下钻), 支持自动刷新 等
  • 舞台 - 中间的展示区,支持编辑布局,拖拽组件,整体拖动,实时刷新,局部刷新,鼠标右键等 所见即所得
遇到的问题及解决方案

整个初级阶段,开发遇到的问题其实还是蛮多的,都在日常积累中一一击破,这里捡几个记忆深刻的分享一下吧 (截至2020-05)

1,如何解析字符串 { key:function () {} , key2:function () {} } 实现组件自有函数
解决:acorn.js , 尝试了多种方法,最终迫不得已使用了 acorn.js javascript 解析器
目标:解析如上字符串,获取对应的函数的实体,进行二次编辑,保存回原函数,这里小弟不才,没能想到其它更好的方式,如有哪位大佬知道,评论告知,非常感谢

//项目中解析代码
var ast = acorn.parse(res.data).body[0];
var nodeBinds = {};
for (let nb of ast.declarations[0].init.properties) {
	nodeBinds[nb.key.name] = res.data.substring(nb.value.body.start, nb.value.body.end);
}
//结果 nodeBinds[key] 就是上述字符串的第一个函数 key

2,舞台跟预览如何减少重排与重绘 , 如何提高渲染速度 , 如何确定页面后处理执行时间
解决:
通过文档片段Document.createDocumentFragment()模拟虚拟Dom来整合当前舞台所有组件渲染 ,整合期间记录通知数量,然后统计通知数量等于当前页面所有组件length,则认为整合完毕,最后挂载Dom,执行后处理 。

3,发布订阅模式,修改右侧栏属性频率较高,组件渲染负担重 ,如何处理

起初呢,我也考虑过是否做成手动渲染,就是在所有配置参数被设置完后,点击按钮渲染组件,后来发现虽然渲染性能大大提升,但是看不到实时效果,因为大部分同事并不知道配置项的具体页面效果,造成重复性的修改与点击渲染 ,而且页违背了我们想做一款实时图形化编辑的平台初衷 ;(放弃)
解决:
①,右侧栏的广播事件,发布通知均使用防抖函数,组件响应以最后一次为准,这个时间可配置,不同人体验不同
②,组件渲染数据方面,采用必要条件同时满足策略,如:普通折线图,调试数据层,字段映射必须满足x轴,y轴,data数据,均有值方可渲染,否则不做渲染动作
③,这里的组件与右侧栏属性是订阅模式,相互通知更新,渲染 ;并且组件与之对应的属性有唯一infiId,不会影响舞台上其它组件渲染与重绘 。

4,2020-05 后,也遇到了一些棘手的问题,暂时就不分享了

特色
  • 近百种丰富组件,拖拽式图形化编辑,所见即所得
  • 自定义组件满足私有定制化服务
  • 响应式,自适应,支持移动端,PC端以及物理大屏
  • 多数据源支持 MySQL、SQL Server、Oracle 等数据源,本地 Excel 文件 或者 api 接口 ,ws 长连接
  • 灵活部署和发布 支持云部署,私有化部署, 本地部署

当然了,NTDVP 目前处于初级阶段 ,零碎的功能点就不一一列出了,还有许多功能需要完善和添加 , 我们正在全力以赴 ,争取做出来一款市场认可的可视化产品 。

总结

整个项目截至到现在,收货满满,不论是对技术的提升还是场景的应用,甚至到对一个Web项目的架构理念都有了新的认识和理解。在这里不做实质技术点的踩坑与分享,后续慢慢记录。

后续计划

我在这里不一一列举了,后期会写在更新历程里,一起见证吧

更新历程

组件

1.新增各种组件
2.原有组件配置项稳定性以及添加新的配置项
3.丰富原有功能

编辑

1.支持组件复制,图片复制粘贴,多组件复制,拖拽,删除
2.支持手机模式,配置标题栏,顶栏,Tab栏,可嵌入单个页面
3.多组件同时拖拽
4.自适应模块重叠,组件层级设置等
5.多人编辑,锁定,解锁,防勿操作
6.舞台编辑底色网格参考,鼠标十字线对照

数据

1.新增后处理API渲染方式,减少HTTP请求
2.新增封装前处理异步HTTP请求,当前页面组件共享返回数据方案
3.新增联动,下钻调试并应用

优化

1.首屏加载速度优化
2.打包压缩优化
3.代码级优化 (闭包,定时器,全局方法挂载与销毁)
4.Echarts 实例与内存销毁
5.不同页面过渡体验
6.编辑体验
7.平台样式优化

**首次发布于2020 - 3 月 , 更新截至 2020 -10 月 **

Logo

智屏生态联盟致力于大屏生态发展,利用大屏快应用技术降低开发者开发、发布大屏应用门槛

更多推荐