前言
隨着公司的發展和進步,數據大屏的業務日益增長,公司目前的做法大致可以分爲兩種 人工標配 or 第三方工具 .
人工:很簡單,一個前端配備,熟練的 html + css + js 技能就完事了 ;
優點:開發靈活多變
不足:效率低,頁面複用度不高(幾乎爲0複用度),大量重複性工作,佔用前端開發時間 等
當然,也有人會說,目前比較主流的前端框架,像 Vue ,React 都是組件化,模塊兒化的工程,組件複用性很高,也對,這點毋庸置疑,但也改變不了大量重複性的工作和消耗前端資源的劣勢 ;甚至還有人會說,當組件封裝的夠多,質量夠好,工程架構的得體,就可以解決上述的缺點,很對 !這就是我要分享給大家的《可視化數據平臺》稍安勿躁 emmm ~ ~
第三方工具 :如:阿里雲的dataV , 百度的 Sugar 都是很優秀的工具平臺 ;
優點:組件豐富,功能強大,基本滿足大中小型企業對數據大屏的需求
不足:有沒有不足請大家去體驗一下 , 這裏我就不做過多的評價 重點是要RMB
OKey,綜上所述,隨着公司業務的增多 ,市場未來對數據可視化的需求,爲了降低開發成本,提高開發效率,降低開發難度,我們的 NTDVP《數據可視化平臺》就這樣誕生了,在這裏,我作爲主要開發人員來給大家分享一下平臺的具體實現 。
NTDVP - 簡單認識一下吧
NTDVP - 技術棧
Vue 2.6 + Webpack 3.6 + Node + eCharts + D3
這裏簡單講一下 Node
在項目裏的應用情況
1,通過 node
啓動 dev-server.js
文件,然後運行如下代碼(dev-server.js
);
webpack-dev-server
主要是啓動了一個基於 express
的 Http
服務器 ;這個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,就比較明瞭了,主要寫一些接口,來實現平臺目錄和文件的保存,讀取,複製,刪除,修改等功能,後期有導入導出…目的是可以滿足本地部署和雲部署
NTDVP - 設計思路
整個工程結構在這裏不做分享,涉及到公司的產品代碼,我簡單說一下產品實現的思路和原理 ;
其實,以拖拉拽的方式做可視化頁面的產品有很多,原理也大同小異,無非就是擁有一個舞臺,一定數量的組件,然後組件通過配置二次加工後放進舞臺的過程,最後集成的舞臺就是我們想要的成品 ;
首先分析一下舞臺和組件的實現方式
舞臺
舞臺很簡單,就是一個容器 , 用來盛放拖拽出來的組件 , 當然這個舞臺可能有 寬,高,背景
等配置屬性,適用於已知寬高的大屏 ;這其中也會有未知寬高的大屏,終端設備等,所以採用了自適應模式 ;那麼舞臺如何渲染組件的呢 ? 這裏使用到了 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 的雙向綁定原理 ;
樣式則是通過配置的數組對象遍歷生成對應的屬性欄 ; 而屬性欄風格很多,輸入框,下拉框,單選,多選,開關,顏色,表格,自定義 等等分別是一個獨立的組件 ,提供單獨的事件監聽與廣播 ;
[{
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
}
}]
}]
NTDVP - 編輯(核心)
上圖可以看出來,整個編輯頁可以拆分爲四部分
- 頂部 -
包含logo + 組件分類列表 + 舞臺操作
- 左欄 -
當前舞臺組件管理 ,目前有刪除 ,後期考慮提供功能鍵對組件進行復制,層管理等
- 右欄 -
核心位置之一,操作選中組件的所有配置項和動態數據(api , sql , ws 等方式 )以及體驗交互(聯動和下鑽), 支持自動刷新 等
- 舞臺 -
中間的展示區,支持編輯佈局,拖拽組件,整體拖動,實時刷新,局部刷新,所見即所得
NTDVP - 遇到的問題及解決方案
整個初級階段,開發遇到的問題其實還是蠻多的,都在日常積累中一一擊破,這裏撿幾個記憶深刻的分享一下吧 ;
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,待整理 ,
NTDVP - 特色
- 近百種豐富組件,拖拽式圖形化編輯,所見即所得
- 自定義組件滿足私有定製化服務
- 響應式,自適應,支持移動端
- 多數據源支持
MySQL、SQL Server、Oracle 等數據源,本地 Excel 文件 或者 api 接口 ,ws 長連接
- 靈活部署和發佈
支持雲部署,私有化部署, 本地部署
當然了,NTDVP 目前處於初級階段 ,零碎的功能點就不一一列出了,還有許多功能需要完善和添加 , 我們正在全力以赴 ,爭取做出來一款市場認可的可視化產品 。
NTDVP - 後續計劃
- 豐富組件是必不可少的
- 添加快速構建模板
- 添加舞臺組件複製,粘貼
- 權限管理,角色管理,想要做的太多,太多了,就不一一列舉了,emmm~~
總結
整個項目截至到現在,收貨滿滿,不論是對技術的提升還是場景的應用,甚至到對一個Web項目的架構理念都有了新的認識和理解。在這裏不做實質技術點的踩坑與分享,後續慢慢記錄。
結束語
未來是數字化時代,可視化會越來越重要,相信在未來的場景中,必將佔領一席之地 。
志同道合的朋友們,相信你們有更好的構建方案,分享出來的時候,希望我們可以再見面 ,謝謝閱讀。