场景:项目中需要加载上万条表格数据,服务端查询耗时几分钟,再加上浏览器渲染几分钟,对于一般配置的电脑来说完全扛不住,不是崩溃就是卡死。
表格使用的是elementUI 的table组件,对于一般需求还是没什么大问题的,但是这次不行。
解决方案:
①分页,分批次加载,(首先得服务端支持分页,另外得根据实际项目需求。都不满足,PASS)
②伪分页,监听table的滚动,滚动到底部在进行加载渲染。(数据是一次获取的,el-table监听比较麻烦,伪分页意义不大,PASS)
③在查询issue时,发现有个道友说道,希望elementUI团队引入slickGrid设计原理,来处理大数据渲染卡顿问题。
发现新大陆, SlickGrid
(请认准 6pac 是这哥们的https://github.com/6pac/SlickGrid)
测试上万条数据渲染速率,还可以

如何在vue项目中使用slickgrid???
以下进入正题,项目下载下来,example中例子挺多,基本可以满足需求。那就引入项目用用
如何在自己的vue项目中运行,首先必须先引入jquery,因为他的好多方法都依赖jquery。如何引入?网上一大堆教程可以参考。
- cnpm install jquery --save
- webpack.base,conf,js中修改如下:
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'jquery': path.resolve(__dirname, '../node_modules/jquery/src/jquery') // 增加这一行
}
},
- main.js 中添加
import 'jquery'
ok 引入成功
如何使用slick,引入js就行了,
demo.vue中添加
<template>
<div class="dc-collectquery">
<el-button @click="search" size="mini"><i class="el-icon-search"></i>查询</el-button>
<div style="width:100%;">
<div id="myGrid" style="width:100%;height:500px;"></div>
</div>
</div>
</template>
<script>
import '../assets/slick/jquery-3.1.0'
import '../assets/slick/jquery-ui-1.9.2'
import '../assets/slick/jquery.event.drag-2.3.0.js'
import '../assets/slick/slick.core.js'
import '../assets/slick/slick.dataview.js'
import '../assets/slick/slick.grid.js'
import Mock from 'mockjs'
export default {
name: "demo",
data() {
return {
}
},
methods: {
// 查询数据
search() {
this.handleTable()
},
// 处理表数据
handleTable() {
var grid;
var data = [];
var options = {
enableCellNavigation: true,
enableColumnReorder: false
};
var columns = [
{id: "index", name: "序号", field: "index"},
{id: "bizCode", name: "编号", field: "bizCode"},
{id: "bizName", name: "名称", field: "bizName" ,width:260},
];
// 引入mock 创建伪数据
var Random = Mock.Random
let list = Mock.mock({
'data|20-50': [
{
'bizCode|+1': Random.integer(1000000, 9999999),
bizName: Random.city(),
},{
'bizCode|+1': Random.integer(1000000, 9999999),
bizName: Random.city(),
},{
'bizCode|+1': Random.integer(1000000, 9999999),
bizName: Random.city(),
},
]
})
data = list.data
data.forEach((item, index) => {
item.index = index + 1
item.id = item.bizCode
})
grid = new Slick.Grid("#myGrid", data, columns, options);
},
}
}
</script>
<style lang="less">
@import "../assets/slick/jquery-ui-1.11.3.custom.css";
@import "../assets/slick/slick.grid.css";
@import "../assets/slick/examples.css";
.slick-preheader-panel {
.ui-state-default {
width: 100%;
overflow: hidden;
border-left: 0px !important;
border-bottom: 0px !important;
}
}
.slick-header-columns {
border-bottom: 0px !important;
text-align: center; // 实现标题居中
}
</style>

需求一:合并单元格,标题居中
修改如下
// 处理表数据
handleTable() {
var dataView;
var grid;
var data = [];
var options = {
enableCellNavigation: true,
enableColumnReorder: false,
createPreHeaderPanel: true,
showPreHeaderPanel: true,
preHeaderPanelHeight: 23,
explicitInitialization: true
};
var columns = [
{id: "index", name: "序号", field: "index"},
{id: "bizCode", name: "编号", field: "bizCode"},
{id: "bizName", name: "名称", field: "bizName" ,width:260},
{id: 'beginQty', field: 'beginQty', name: '数量', columnGroup: "库存"},
{id: 'beginUnTaxAmount', field: 'beginUnTaxAmount', name: '无税金额', columnGroup: "库存"},
{id: 'beginTaxAmount', field: 'beginTaxAmount', name: '含税金额', columnGroup: "库存"},
];
var Random = Mock.Random
let list = Mock.mock({
'data|20-50': [
{
'bizCode|+1': Random.integer(1000000, 9999999),
bizName: Random.city(),
beginQty: Random.integer(5, 10),
beginUnTaxAmount: Random.float(1000, 9999).toFixed(2),
beginTaxAmount: Random.float(1000, 9999).toFixed(2),
},{
'bizCode|+1': Random.integer(1000000, 9999999),
bizName: Random.city(),
beginQty: Random.integer(5, 10),
beginUnTaxAmount: Random.float(1000, 9999).toFixed(2),
beginTaxAmount: Random.float(1000, 9999).toFixed(2),
},{
'bizCode|+1': Random.integer(1000000, 9999999),
bizName: Random.city(),
beginQty: Random.integer(5, 10),
beginUnTaxAmount: Random.float(1000, 9999).toFixed(2),
beginTaxAmount: Random.float(1000, 9999).toFixed(2),
},
]
})
data = list.data
data.forEach((item, index) => {
item.index = index + 1
item.id = item.bizCode
})
function CreateAddlHeaderRow() {
var $preHeaderPanel = $(grid.getPreHeaderPanel())
.empty()
.addClass("slick-header-columns")
.css('left', '-1000px')
.width(grid.getHeadersWidth());
$preHeaderPanel.parent().addClass("slick-header");
var headerColumnWidthDiff = grid.getHeaderColumnWidthDiff();
var m, header, lastColumnGroup = '', widthTotal = 0;
for (var i = 0; i < columns.length; i++) {
m = columns[i];
if (lastColumnGroup === m.columnGroup && i > 0) {
widthTotal += m.width;
header.width(widthTotal - headerColumnWidthDiff)
} else {
widthTotal = m.width;
header = $("<div class='ui-state-default slick-header-column' />")
.html("<span class='slick-column-name'>" + (m.columnGroup || '') + "</span>")
.width(m.width - headerColumnWidthDiff)
.appendTo($preHeaderPanel);
}
lastColumnGroup = m.columnGroup;
}
}
dataView = new Slick.Data.DataView();
grid = new Slick.Grid("#myGrid", dataView, columns, options);
dataView.onRowCountChanged.subscribe(function (e, args) {
grid.updateRowCount();
grid.render();
});
dataView.onRowsChanged.subscribe(function (e, args) {
grid.invalidateRows(args.rows);
grid.render();
});
grid.init();
grid.onColumnsResized.subscribe(function (e, args) {
CreateAddlHeaderRow();
});
CreateAddlHeaderRow();
// Initialise data
dataView.beginUpdate();
dataView.setItems(data);
dataView.endUpdate();
},
......
// 这个可以控制 合并的单元格居中,或者是下面的标题都居中
.slick-preheader-panel { // 注掉这行 实现单元格居中
.slick-header-columns {
border-bottom: 0px !important;
text-align: center;
}
}

需求二:添加合计,修改合计样式
......
function CreateAddlHeaderRow() {
.....
}
//------------------------添加如下-----------------------
function TotalsDataProvider(data, columns) {
var totals = {};
var totalsMetadata = {
// Style the totals row differently.
cssClasses: "totals",
columns: {}
};
// Make the totals not editable.
for (var i = 0; i < columns.length; i++) {
totalsMetadata.columns[i] = {editor: null};
}
this.getLength = function () {
return data.length + 1;
};
this.getItem = function (index) {
return (index < data.length) ? data[index] : totals;
};
this.updateTotals = function () {
let self = this
var columnIdx = columns.length;
while (columnIdx--) {
var columnId = columns[columnIdx].id;
var total = 0;
var i = data.length;
while (i--) {
total += (parseFloat(data[i][columnId]) || 0);
}
totals[columnId] = "" + total.toFixed(2);
}
totals['index'] = ''
totals['bizCode'] = ''
totals['bizName'] = '合计'
console.log(totals)
localStorage.setItem('dcTotals', JSON.stringify(totals))
};
this.getItemMetadata = function (index) {
return (index != data.length) ? null : totalsMetadata;
};
this.updateTotals();
}
let dataProvider = new TotalsDataProvider(data, columns);
dataView = new Slick.Data.DataView();
grid = new Slick.Grid("#myGrid", dataProvider, columns, options);
//-----------------------------------------------
dataView.onRowCountChanged.subscribe(function (e, args) {
......
});
......
.totals { // 修改合计样式
font-weight: 700;
color: blue;
}

需求三:部分列 右对齐
// 增加过滤
columns.forEach(item => {
if (item.name == '数量' || item.name == '无税金额' || item.name == '含税金额') {
item.formatter = function cpuUtilizationFormatter(row, cell, value, columnDef, dataContext) {
return "<span class='cell-float'>" + value + "</span>";
}
}
})
......
.cell-float {
float: right;
}

需求四:实现单元格的复制(复制表格数据)
需要引入plugins,并实现方法
grid.setSelectionModel(new Slick.CellSelectionModel());
grid.registerPlugin(new Slick.CellExternalCopyManager({}));
实例如下
<template>
<div class="dc-collectquery">
<el-button @click="search" size="mini"><i class="el-icon-search"></i>查询</el-button>
<div style="width:100%;">
<div id="myGrid" style="width:100%;height:500px;"></div>
</div>
</div>
</template>
<script>
import '../assets/slick/jquery-3.1.0'
import '../assets/slick/jquery-ui-1.9.2'
import '../assets/slick/jquery.event.drag-2.3.0.js'
import '../assets/slick/slick.core.js'
import '../assets/slick/slick.dataview.js'
import '../assets/slick/slick.grid.js'
//需要下面四个plugins
import '../assets/plugins/slick.cellrangedecorator'
import '../assets/plugins/slick.cellrangeselector'
import '../assets/plugins/slick.cellexternalcopymanager.js'
import '../assets/plugins/slick.cellselectionmodel.js'
import Mock from 'mockjs'
export default {
name: "demo",
data() {
return {
}
},
methods: {
// 查询数据
search() {
this.handleTable()
},
// 处理表数据
handleTable() {
var grid;
var data = [];
var options = {
enableCellNavigation: true,
enableColumnReorder: false
};
var columns = [
{id: "index", name: "序号", field: "index"},
{id: "bizCode", name: "编号", field: "bizCode"},
{id: "bizName", name: "名称", field: "bizName" ,width:260},
];
// 引入mock 创建伪数据
var Random = Mock.Random
let list = Mock.mock({
'data|20-50': [
{
'bizCode|+1': Random.integer(1000000, 9999999),
bizName: Random.city(),
},{
'bizCode|+1': Random.integer(1000000, 9999999),
bizName: Random.city(),
},{
'bizCode|+1': Random.integer(1000000, 9999999),
bizName: Random.city(),
},
]
})
data = list.data
data.forEach((item, index) => {
item.index = index + 1
item.id = item.bizCode
})
grid = new Slick.Grid("#myGrid", data, columns, options);
// 要想实现复制,重要的是这两个方法
grid.setSelectionModel(new Slick.CellSelectionModel());
grid.registerPlugin(new Slick.CellExternalCopyManager({}));
},
}
}
</script>
<style lang="less">
@import "../assets/slick/jquery-ui-1.11.3.custom.css";
@import "../assets/slick/slick.grid.css";
@import "../assets/slick/examples.css";
.slick-preheader-panel {
.ui-state-default {
width: 100%;
overflow: hidden;
border-left: 0px !important;
border-bottom: 0px !important;
}
}
.slick-header-columns {
border-bottom: 0px !important;
text-align: center; // 实现标题居中
}
</style>
ok,暂时只用到这些东西。
番外篇
为了使用SlickGrid而引入jquery等众多依赖,虽然无需大的改动,就能达到解决问题的目的,但是这样并不是个睿智之举。
项目比较急,没去深入研究。至于如何剥离jquery,纯净版的vue☞SlickGrid 有待各位大神自己改造了。
项目中还涉及到导出excel,上万条数据的时候可能会慢点(导出过90M的excel)。如何导出excel,网上一大堆,可以参考
vue2 项目开发使用到的东东
前端导出excel☞js-xlsx实现合并单元格
Vue2.0---将页面中表格数据导出excel
网友评论