当前位置: 首页 > news >正文

网站建设 软件开发的公司免费发布广告

网站建设 软件开发的公司,免费发布广告,成都最近疫情,北京住房建设委员会网站1、场景描述 在项目开发中,遇到在表格中一次性加载完的需求,且加载数量不少,有几百几千条,并且每条都可能有自己的下拉框,输入框来做编辑功能,此时普通的el-table肯定会导致浏览器卡死,那么怎么…

1、场景描述

在项目开发中,遇到在表格中一次性加载完的需求,且加载数量不少,有几百几千条,并且每条都可能有自己的下拉框,输入框来做编辑功能,此时普通的el-table肯定会导致浏览器卡死,那么怎么办呢?

2、解决方案

当然很多童鞋肯定会想到利用插件,其实我本人是不咋喜欢插件的,能自己写就自己写,毕竟插件可能也有bug或者啥的,万一出现了,作者不去改咋办,所以我总结了下面几个解决方法

提示:本篇博客基本都用的tsx + vue3 composition-api体验版 来写的噢,用vue2或者vue3模板语法写会更简单噢,照葫芦画瓢逻辑都是一样的,我这里就不写了,想了解相关的vue3知识我另一篇博客上有噢~

Vue3知识点学习

1、滚动触底分页(良)

当纵向滚动条触底的时候,加载新的数据到当前表格中,逻辑如下:

table.scrollTop + table.clientHeight === table.scrollHeight 

当上述成立时候触发加载,table为表格dom, 但是如果数据很多的话,每次滚动都会将新的tr加入到表格中,那表格tr dom总数还是会依次递增,dom一多照样卡死

2、滚动区间分页(良+)

如果你没有表格编辑功能,全是展示数据的话,那么这个解决方案已经完全可以了

比如你当前表格可显示区域能够展示十条数据,那么首次进来时,显示数据的区间为 [0, 9],每次表格滚动时,都动态的去获取当前展示的区间,其实这种方式是比较好的,为啥呢?因为不管你几千几万条数据,我同一时刻就只有10行,完全不会因为tr数量过多导致浏览器渲染卡顿,当然这可视区域能展示多少条数据,是有你表格可视高度和单行tr高度一起决定的

const selectWrap = table.querySelector('.el-table__body-wrapper');const selectRow = table.querySelector('table tr');展示tr数量 = Math.ceil(selectWrap.clientHeight / selectRow.clientHeight)

tsx:页面

setup() {return () => (<el-tabledata={visibleResult.value} // 可视区域的数据></el-table>)
}

ts:逻辑

  /** 表格上展示的数据 */const visibleResult = computed(() => {return result.value.filter((_item, index) => {if (index < curStartIndex.value) {return false;} else if (index > curEndIndex.value) {return false;} else {return true;}});})

那么如何去控制这个滚动区间呢?这个方法很多,监听滚动条的scroll,当向下滚动或向上滚动,我们都可以监听到,然后改变curStartIndex和curEndIndex的值就可以改变啦,这样的话,光是只看数据倒是解决了,但是要是表格要实现编辑效果咋办?每行数据有十几个下拉框和输入框,你要知道,el-select dom层级很高,像这种el-select数量一多,就算你当前展示区域没有多少条数据,也会导致渲染卡顿的,所以就有下面的优化版方案

3、滚动区间分页 + 表格编辑(优)

当前表格可视区域有30条数据,每条数据有10个el-select和5个el-input(当然可能有童鞋会说,el-select dom层级高,那我就自己写select鸭~但是你自己写的未必有el-select好看且功能相当)。此时首次加载就很卡顿了,原因就是el-select多了,那么我首先想的是这些el-select又不是直接要用,为啥不在我点击这个单元格时候再弹出下拉呢?不点击单元格时候,就直接展示文本效果(el-select没有值时展示 '请选择', 有值时展示你选择的值)

例图如下:

 滚动区间分页

 不管你有多少条数据,我始终首次只加载这么十多个tr,tr数量不变,变化的是每次的区间取值,大家会发现下面有个tr id为 virtual-scroll,那这是干什么 的呢?其实核心思路是:

显示区的高度 + 已经滚动过的高度 + 虚拟滚动条高度 === 总的数据高度table-wrapper.clientHeight + table-wrapper.scrollTop + virtual-scroll.height === data.lentth * tr.clientHeight

 是不是发现和方案1类似,但是区别不同的是,虚拟条高度可是一来就会被计算出来的,因为开始时候scrollTop为0, 那么 虚拟条高度 就是 总的数据高度 - 显示区高度

tsx: 通过自定义指令来监听表格的滚动,返回值触发loadMore方法,来决定显示区展示数据的区间

setup() {return () => (<el-tabledata={visibleResult.value} // 可视区域的数据{...{ directives: [{ name: 'load-more', value: methods.loadMore }] }}></el-table>)
}

ts: 逻辑

  /** 表格上展示的数据 */const visibleResult = computed(() => {return result.value.filter((_item, index) => {if (index < curStartIndex.value) {return false;} else if (index > curEndIndex.value) {return false;} else {return true;}});})const methods = {/*** 懒加载回调* @param startIndex 区段位置开始索引* @param endIndex 区段位置结束索引*/loadMore(startIndex: number, endIndex: number) {curStartIndex.value = startIndexcurEndIndex.value = endIndex},
}

那上面是实现滚动区间的代码,那说的表格编辑相关在哪里呢?下面即是:

表格编辑

当我们表格中有很多下拉框和输入框时,其实拖垮性能的多半是el-select,那解决方法是将渲染input和select的逻辑提出来,input不做处理,select当前单元格row,column索引和focusCell的值一致时说明单元格被聚焦了,就显示el-select,否则直接展示文本

关键方法及属性讲解:

inputChange:输入框回调

selectChange:下拉框选择回调

selectPerofrmance:下拉框当前值渲染

domPropsInnerHTML: 等同于v-html,但是jsx中不能用v-html

decorateHeader:表格各列数据我都是动态配置的,后续我会出一期博客来讲解vue3+tsx下如何封装表格

然后效果大致如下,总结下就是同一时间最多只会存在一个el-select,既然el-select dom减少了,那么表格渲染速度就自然而然快了

 上面例图中被黄色圈中的就是聚焦,没有被聚焦的都展示文本,源码讲解如下:

const focusCell = ref<string>('0,0') // 表格数据第0行第0列

 你在el-table 里有 cell-click 这个事件,它会将当前row, column全都回调回去,那么你就会知道你当前点击单元格的索引值, 我们将索引值生成, 在同一时间,focusCell只会有一条数据,所以我们直接用字符串来存储就好

<el-tableon-cell-click={methods.cellClick}
>
</el-table>const methods = {cellClick(row, column) {if (focusCell.value !== `${row.index},${column.index}`) {focusCell.value = `${row.index},${column.index}`}}
}

那么关键来了,下拉框单元格聚焦的时候,我们才显示下拉框,其他时候展示文本,输入框类型单元格聚焦的时候不做处理,代码该如何写呢?

setup() {return () => {/** 输入框类型渲染 */const inputDomRender = (scope, item) => (<el-inputvalue={scope.row[item.prop]}on-input={e => methods.inputChange(e, scope, item)}/>)/** 下拉框类型渲染 */const selectDomRender = (scope, item) => ((focusCell.value === `${scope.row.autoIndex},${scope.column.index}` ? <el-selectvalue-key='id'value={scope.row[item.prop]}onChange={e => methods.selectChange(e, scope, item)}>{item.selects.map(item1 => {return <el-optionkey={item1.id}label={item1.label}value={item1.id}></el-option>})}</el-select> : <divdomPropsInnerHTML={methods.selectPerofrmance(scope.row[item.prop], item.prop)}></div>))return <el-tableon-cell-click={methods.cellClick}>{decorateHeader.map((item: TableLabel) => {return <el-table-columnwidth={item.width}label={item.label}align={item.align ? item.align : 'center'}prop={item.prop}scopedSlots={{default: scope => {return <div>{item.mode !== 'input' ? selectDomRender(scope, item) : inputDomRender(scope, item)}</div>}}}></el-table-column>})}</el-table>}
}

自此上述两步优化,其实就能使我们一次性加载几千条可编辑数据不会卡顿了~

3、v-load-more自定义指令

源码:

import {VNodeDirective
} from 'vue'
let timeout;
/** 设置表格滚动区间 */
const setRowScrollArea = (topNum, showRowNum, binding) => {if (timeout) {clearTimeout(timeout);}timeout = setTimeout(() => {binding.value.call(null, topNum, topNum + showRowNum);});
};
const loadMore= {bind(el: Element, _binding) {setTimeout(() => {// 创建虚拟滚动条const selectWrap = el.querySelector('.el-table__body-wrapper');const selectTbody = selectWrap.querySelector('table tbody');const createElementTR = document.createElement('tr');createElementTR.id = 'virtual-scroll'selectTbody.append(createElementTR); // 先行将虚拟滚动条加入进来})},componentUpdated(el: Element, binding: VNodeDirective, vnode, oldVnode) {setTimeout(() => {const dataSize = vnode.data.attrs['data-size'];const oldDataSize = oldVnode.data.attrs['data-size'];// 当数量相同时,表明当前未发生更新,减少后续操作if (dataSize === oldDataSize) {return;}const selectWrap = el.querySelector('.el-table__body-wrapper');const selectTbody = selectWrap.querySelector('table tbody');const selectRow = selectWrap.querySelector('table tr');// 当一行都没有,说明无数据渲染,但一般逻辑都不会进入这里if (!selectRow) {return;}const rowHeight = selectRow.clientHeight;// 能够在当前显示区的展示条数,本项目就是11条const showRowNum = Math.round(selectWrap.clientHeight / rowHeight);const createElementTRHeight = (dataSize - showRowNum) * rowHeight;const createElementTR = selectTbody.querySelector('#virtual-scroll')// 监听滚动后事件selectWrap.addEventListener('scroll', function() {let topPx = this.scrollTop;let topNum = Math.round(topPx / rowHeight);const minTopNum = dataSize - showRowNum;if (topNum > minTopNum) {topNum = minTopNum;}if (topNum < 0) {topNum = 0;topPx = 0;}selectTbody.setAttribute('style', `transform: translateY(${topPx}px)`);// 本来触底的话,应该设置为0,但是触底后 就没有滚动条了createElementTR.setAttribute('style', `height: ${createElementTRHeight - topPx > 0 ? createElementTRHeight - topPx : rowHeight}px;`);setRowScrollArea(topNum, showRowNum, binding);})});}
}export default loadMore

 不太了解自定义指令是啥的可以参考我另一篇博客

vue学习(6)自定义指令详解及常见自定义指令

4、有趣拓展

1、我想在上述表格中对指定列实现高亮搜索怎么做?

当有值时滚动到指定位置,无值时不动,那首先在加载数据时,要先写下面代码

 // 先让他触发滚动,才能让virtual-scroll高度生成
table.$el.querySelector('.el-table__body-wrapper').scrollTo({ top: 1, behavior: 'smooth' })

输入框执行逻辑如下,防抖肯定是要的,然后搜索的列是 originName,当发现有搜索到值时,找到第一个被匹配到的行索引,然后去计算表格应该滚动到哪个高度位置,然后滚动

    /*** 滚动定位到表格指定位置* @param flag 是否能执行滚动的标志* @returns*/scrollToTable: () => $debounce(function() {// 当没有值时,不进行搜索if (dialogSearchKey.value) {const vmEl = table.value.$el;const selectWrap = vmEl.querySelector('.el-table__body-wrapper')if (vmEl) {const autoIndex = result.value.find((item: TableDataItem) => {return item.originName.indexOf(dialogSearchKey.value) !== -1})?.autoIndex ?? -1if (autoIndex !== -1) {scrollToIndex(selectWrap, autoIndex)}}}}, 500, false),/*** 表格滚动到指定索引值行* @param selectWrap 表格dom* @param autoIndex 索引*/scrollToIndex(selectWrap, autoIndex) {const showNum = 12 // 当显示条数小于次数时,不进行滚动操作const topPx = autoIndex * columnHeight.valueif (autoIndex > showNum) {selectWrap.scrollTo({ top: topPx, behavior: 'smooth' })}}

 然后高亮被搜索文字源码如下:

setup() {return () => {return <el-tableon-cell-click={methods.cellClick}>{decorateHeader.map((item: TableLabel) => {return <el-table-columnwidth={item.width}label={item.label}align={item.align ? item.align : 'center'}prop={item.prop}scopedSlots={{default: scope => {return <div><divclass='multiline'domPropsInnerHTML={methods.textRender(scope.row[item.prop], item.prop)}/></div>}}}></el-table-column>})}</el-table>}
}

 props.heightLight  为输入框的搜索关键词

    /*** 文字渲染* @param word 被渲染的文字* @param prop 属性名* @returns 替换后的渲染文字*/textRender(word: string, prop: string): string {const reg = new RegExp(`${props.heightLight}`, 'ig')// 有搜索关键词 && 是否有该子字符串if (props.heightLight && word.indexOf(props.heightLight) !== -1) {return word.replace(reg, `<font color='red'>$&</font>`)// 正常返回的列} else {return word}}

 2、前端实现表格导出excel

需要依赖如下:

import * as XLSX2 from 'xlsx';
import XLSX from 'xlsx-style';

存在需要对依赖源码进行打补丁的地方:

1、/node_modules/xlsx-style/dist/cpexcel.js
错误源码:var cpt = require('./cpt' + 'able');
应修改为:var cpt = cptable;2、/node_modules/xlsx-style/dist/ods.js
错误源码:return require('../' + 'xlsx').utils;
应修改为:return require('./' + 'xlsx').utils;

变量名讲解:

// props.headerDisplay: 导出excel头部
{autoIndex: '序号',originName: '视频文件',
}// exportData: 导出文件数据格式
[{autoIndex: 1,originName: 'xxx'},{autoIndex: 2,originName: 'xxx'}
]// headProp: 导出excel头部属性名数组
['autoIndex', 'originName']

 导出excel源码: 

exportTransfer(exportData, header) {nextTick(() => {const newData = [props.headerDisplay, ...exportData] // 封装组合后的数据const headProp = header.map(item => item.prop) // 头部属性数组// 把json转为worksheet对象 后续用这种方法const wb = XLSX2.utils.json_to_sheet(newData, { header: headProp, skipHeader: true })// returnconst widthList = header.map(item => item.width ? Number(item.width) : 150)// 举例一共有14列(包括序号,不包括操作栏)wb['!cols'] = [] // 先初始化列数组配置for (let i = 0; i < 14; i++) {wb['!cols'][i] = {wpx: widthList[i]}}// 样式的文档地址// https://www.npmjs.com/package/xlsx-stylefor (const key in wb) {if (key.indexOf('!') === -1) { // 非excel配置类,带!都是配置类,这里对非配置类进行样式修改wb[key].s = {font: { // 字体设置sz: 13,bold: false,color: { // 只有DEFG第一行(标题行)才是红色,其余皆是黑色rgb: new RegExp(/^[DEFG]1{1}$/).test(key) ? 'D9001B' : '000000' // 十六进制,不带#, 除去文件类型, 视频来源, 视频版权, 视频分类 这四栏标题行标红外,其他都是黑色}},alignment: { // 文字居中horizontal: 'center',vertical: 'center',wrapText: 1 // 换行},border: { // 设置边框top: { style: 'thin' },bottom: { style: 'thin' },left: { style: 'thin' },right: { style: 'thin' }}}}}const filedata = sheet2blob(wb)openDownloadDialog(filedata, `xxx`)})},/*** 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载* @param sheet excel对象* @param sheetName* @returns*/sheet2blob(sheet, sheetName?: string) {const s2ab = (s) => {const buf = new ArrayBuffer(s.length);const view = new Uint8Array(buf);for (let i = 0; i !== s.length; ++i) {view[i] = s.charCodeAt(i) & 0xFF;}return buf;}sheetName = sheetName || 'sheet1';const workbook = {SheetNames: [sheetName],Sheets: {}};workbook.Sheets[sheetName] = sheet; // 生成excel的配置项const wopts = {bookType: 'xlsx', // 要生成的文件类型bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性type: 'binary'};const wbout = XLSX.write(workbook, wopts);const blob = new Blob([s2ab(wbout)], { // 字符串转ArrayBuffertype: 'application/octet-stream'});return blob;},/*** 创建excel地址并下载到本地* @param url 文件blob二进制路径* @param saveName 文件名*/openDownloadDialog(url, saveName) {if (typeof url === 'object' && url instanceof Blob) {url = URL.createObjectURL(url); // 创建blob地址}const aLink = document.createElement('a');aLink.href = url;aLink.download = `${saveName}.xlsx`; // HTML5新增的属性,指定保存文件名let event;if (window.MouseEvent) {event = new MouseEvent('click');} else {event = document.createEvent('MouseEvents'); // 事件抛出event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);}aLink.dispatchEvent(event); // 事件派发}

有不理解的或有更好想法的可以底下评论喔~


文章转载自:
http://asperges.rymd.cn
http://media.rymd.cn
http://weet.rymd.cn
http://dipolar.rymd.cn
http://discrepancy.rymd.cn
http://explorative.rymd.cn
http://multiflex.rymd.cn
http://nyctitropic.rymd.cn
http://vertebrate.rymd.cn
http://anthropometrist.rymd.cn
http://actinomorphic.rymd.cn
http://selectric.rymd.cn
http://concertina.rymd.cn
http://ouidah.rymd.cn
http://lengthways.rymd.cn
http://kickplate.rymd.cn
http://marzine.rymd.cn
http://hypermedia.rymd.cn
http://pensel.rymd.cn
http://buffalofish.rymd.cn
http://cattail.rymd.cn
http://barology.rymd.cn
http://rot.rymd.cn
http://showroom.rymd.cn
http://butylate.rymd.cn
http://ascap.rymd.cn
http://lei.rymd.cn
http://orach.rymd.cn
http://somatotrophic.rymd.cn
http://game.rymd.cn
http://marcando.rymd.cn
http://greenhouse.rymd.cn
http://subtersurface.rymd.cn
http://whipsaw.rymd.cn
http://myringitis.rymd.cn
http://graphite.rymd.cn
http://foliicolous.rymd.cn
http://crosslight.rymd.cn
http://clapper.rymd.cn
http://megapolis.rymd.cn
http://endoergic.rymd.cn
http://revile.rymd.cn
http://incubous.rymd.cn
http://stash.rymd.cn
http://octopodes.rymd.cn
http://tautosyllabic.rymd.cn
http://unpile.rymd.cn
http://canarian.rymd.cn
http://violate.rymd.cn
http://moistness.rymd.cn
http://titaniferous.rymd.cn
http://tbo.rymd.cn
http://geoponic.rymd.cn
http://bazar.rymd.cn
http://boiserie.rymd.cn
http://pentagonian.rymd.cn
http://prebind.rymd.cn
http://nj.rymd.cn
http://oscillator.rymd.cn
http://excitated.rymd.cn
http://byzantium.rymd.cn
http://reconnoissance.rymd.cn
http://amidships.rymd.cn
http://raddle.rymd.cn
http://disastrously.rymd.cn
http://prostatotomy.rymd.cn
http://shillong.rymd.cn
http://hiking.rymd.cn
http://enjoinder.rymd.cn
http://hereditarily.rymd.cn
http://wigmaker.rymd.cn
http://calla.rymd.cn
http://venomous.rymd.cn
http://ramiform.rymd.cn
http://pachytene.rymd.cn
http://ced.rymd.cn
http://aleph.rymd.cn
http://plumicorn.rymd.cn
http://reafforest.rymd.cn
http://condenser.rymd.cn
http://plantain.rymd.cn
http://tenaculum.rymd.cn
http://disembowel.rymd.cn
http://perceptional.rymd.cn
http://parseval.rymd.cn
http://bead.rymd.cn
http://culturable.rymd.cn
http://silvering.rymd.cn
http://oscillatory.rymd.cn
http://aerograph.rymd.cn
http://devastator.rymd.cn
http://jazz.rymd.cn
http://espionage.rymd.cn
http://simony.rymd.cn
http://avignon.rymd.cn
http://bissextile.rymd.cn
http://gametocide.rymd.cn
http://governessy.rymd.cn
http://sparable.rymd.cn
http://hither.rymd.cn
http://www.15wanjia.com/news/88112.html

相关文章:

  • 现在网站用什么软件做公司网站建设
  • 南昌网站建设电话bt搜索引擎最好用的
  • wordpress 分类目录代码seo课程多少钱
  • 网站模板建站最成功的网络营销案例
  • 南京网站建设流程网络营销策略实施的步骤
  • 免费用手机建立网站网络营销师证书含金量
  • 找做网站公司全网营销推广怎么做
  • 新手建站素材千锋教育靠谱吗
  • 杭州精品网站建设江苏seo团队
  • 新手学做免费网站软件免费源码网站
  • wordpress如何按更新排序seo文章
  • 男人女人晚上做那事网站沈阳网络关键词排名
  • 烟台网站建设科技公司企业推广公司
  • 做问卷赚钱的网站外贸seo网站建设
  • 网站建设社会效益搜索引擎优化规则
  • 企业信息系统查询系统官网江苏百度优化排名软件
  • 上海人才招聘官网2022seo专员是指什么意思
  • 用凡客建站做的网站有哪些长春网站制作
  • 武汉营销网站设计免费域名
  • 济南 论坛网站建设互联网销售
  • 中国外贸网百度seo刷排名软件
  • 做一家购物网站要多少钱网络热词作文
  • 微信微网站制作友情链接的作用
  • 游标卡尺 东莞网站建设如何开发软件app
  • 网站设计规划高中信息技术快速将网站seo
  • 代做设计的网站天津百度搜索网站排名
  • 网站 缓存方式bt kitty磁力猫
  • 如何自己学建设网站北京网站优化价格
  • 六盘水市诚信网站建设公司google 优化推广
  • 网站制作有哪些种类优化网站标题和描述的方法