硬核干货:葡萄城 SpreadJS 前端表格技术分享 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GrapeCityChina
V2EX    推广

硬核干货:葡萄城 SpreadJS 前端表格技术分享

  •  
  •   GrapeCityChina 2020-07-28 12:19:32 +08:00 18138 次点击
    这是一个创建于 1949 天前的主题,其中的信息可能已经有所发展或是发生改变。

    表格作为数据呈现的一种基本方式,在各类软件系统都发挥着重要的作用。在移动互联时代,即便再复杂的数据通过“表格”的整理,都可以清晰的呈现给用户,并支持用户从多个维度查看、筛选和修改。不论是应对文档、报告、凭证,还是票据,表格都能够附加存储更多的样式信息,尤其对离散式数据存储更加高效。

    如今,企业开始纷纷尝试信息化转型,前端、后端、中台、云,这些概念也逐渐变得家喻户晓,在几乎所有的 B 端产品中,表格都作为一种交互式组件受到广泛欢迎。

    不过即便如此,表格也经常因为样式复杂、操作繁琐等因素,在用户使用时造成诸多体验问题,尤其在 Web 项目中,表格技术时常受限于前端性能制约。

    那么,有没有一种办法,可以帮助开发者突破前端表格技术的困境,实现关键技术落地呢?

    2020 年 6 月 30 日,作为全球领先的软件开发技术提供商,葡萄城举办了“赋能智变”线上品牌战略发布会。本次会议上,葡萄城正式发布“赋能开发者”业务使命,并就前端表格开发等技术话题进行了分享。

    前端表格为开发者带来的困扰

    在 C/S 架构的应用程序中,表格类组件可以获取更多资源支持,内存控制也更加直接。但是当企业进行 B/S 转型时,前端表格却面临着诸多挑战。

    例如,多浏览器差异、浏览器沙箱机制、内存访问受限、客户端性能低下等。在驳杂且质量参差不齐的前端开发环境中,这些问题都会使我们在开发应用时,消耗大量的时间和重复编码,而无法专注于核心业务。不但耗时、耗力,加剧软件开发成本,更可能因为外部环境的变更,使系统的稳定性受到冲击,使后期维护成本飙升。

    总结来说,开发前端表格主要有三个技术难点:性能、内存消耗和可靠性。

    为了应对这些技术难点,葡萄城结合了多年来的表格开发技术经验,推出了一款可提升系统性能、可靠性,降低内存消耗的纯前端表格控件 SpreadJS

    葡萄城 SpreadJS 的表格控件技术

    针对前端表格开发的三大技术难点:性能、内存消耗和可靠性,SpreadJS 分别提出了应对措施:

    • 基于双缓存画布绘制引擎,SpreadJS 实现了极高的处理性能
    • 基于行模式的稀松矩阵存储策略,SpreadJS 可大幅节省内存消耗
    • 基于计算引擎技术,SpreadJS 可实现稳定可靠的应用系统

    1. 基于双缓存画布绘制引擎,实现性能提升

    在企业的应用中,数据是唯一的主干,而作为数据载体的表格,经常被“吐槽”卡顿,UI 界面“假死”,界面操作不流畅等。

    引起这些问题的症结在于浏览器渲染引擎的基础原理:当界面元素越多,浏览器的渲染时间会显著增长,内存消耗会越大。

    举例来说,现代应用程序为了追求更好的用户体验,需要对 UI 界面反复优化,而频繁的修改界面 UI 元素,将引发多次浏览器重绘。在这个过程中,UI 元素的创建及修改,会激活内部垃圾回收机制,影响数据处理效率。

    除此之外,前端开发环境的多样化、各类高 DPI 设备、手机、平板、4K 显示屏、企业大屏等,这些无不加重了企业应用系统的处理负担。

    对于经常用于展示大数据量的表格来说,性能至关重要。也就是说,任何基于表格开发的应用系统,必须满足以最低的资源消耗,实现高速渲染和刷新。

    为此,SpreadJS 纯前端表格控件引入了 Canvas 绘制模型和双缓存画布技术,具体实现方式如下。

    Canvas 绘制模型

    由于传统的表格组件使用 DOM 的方式展示表格数据,无论在 table,还是 div 中,复杂的 UI 都需要大量的 DOM 渲染。因此,在执行更新、滚动等操作时,需要不停的销毁、创建 DOM,这无形中增加了大量无效计算。

    为了解决这个问题,前端框架 React 和 Vue3 均采用了虚拟 DOM 的方式,而 SpreadJS 则采用了一种更为先进的方式HTML5 Canvas 绘制。

    使用 Canvas 绘制,SpreadJS 不仅无需重复创建和销毁 DOM 元素,在画布的绘制过程中,更是打破了 DOM 元素渲染对 UI 的诸多限制。SpreadJS 可以绘制种类更为丰富的 UI 元素,如线形、特殊图形等,通过画法逻辑,还可以实现更加精准的 UI 界面渲染,解决了浏览器差异造成的样式误差。

    但如果只使用一个画布进行渲染,那么每次绘制时,不论是主体图层还是装饰图层,都需要通过画法逻辑将所有元素进行绘制,这显然是低效的。

    双缓存画布技术

    为此,SpreadJS 又引入了双缓存画布的机制,将不易改变的主体图层绘制在缓存画布中,在发生渲染行为时,只需要将缓存画布中的主体图层直接通过图像克隆的方式绘制在主画布上,并附加绘制装饰图层元素,便可大大优化整个绘制性能。

    SpreadJS 双缓存画布的技术特点:

    • 类似油画的分层绘制

    SpreadJS 的绘制引擎基于油画的绘制原理,分为主体图层和装饰图层,主体图层渲染持久的、不易改变的元素,例如背景,单元格,表格线等。装饰图层则渲染常变性元素,如选择框、拖拽框、悬浮效果等。

    • 使用缓存画布

    当发生动态绘制,如表格滚动时,SpreadJS 会将主画布清空,从缓存画布中根据行为上下文进行画布偏移,将偏移后的图层直接绘制在主画布上,再在主画布上绘制剩余部分,使整个表格的滚动过程更加流畅。

    2. 基于行模式的稀松矩阵存储策略,大幅节省内存消耗

    虽然没有明文规定,但在业界的共同认知里,浏览器会对单一线程进行内存限制,例如 64 位的 chrome,每个 tab 页的内存消耗不允许超过 4G,在手持设备上,这个限制则更为明显,例如 iPhone 6s 为 1G,而 iPhone 7 为 2G 。

    这个限制,在单页面应用不成熟的十几年前,不会成为问题。因为,那时大家所关注的,还是如何提升后端的处理性能,前端只是一种静态的网页表达方式。

    随着前端工程化的高速发展,各种前端工程脚手架日渐成熟,WebComponent 标准被提上日程,企业开始由 C/S 向 B/S 应用转型。这就要求前端开发者,需要面对单线程处理复杂业务数据的挑战。这里的复杂,不仅仅是数据量大,更是数据状态的处理。如何高效的解决数据的前后端交互,如何快速响应数据变更及数据回滚呢?

    为此,SpreadJS 又提出了一个行之有效的解决方案稀松矩阵。

    对于表格,常规的存储方式是数组,如二维数组或对象数组。在类 Excel 的电子表格中,单元格内容是零散的,也就是说在 Sheet 中会出现大量空单元格,而这些空单元格,同样会占用内存空间。

    对于电子表格这种松散的文档结构,SpreadJS 采用了稀疏矩阵存储模型( Sparse Array)来保存数据。

    相较于传统的链式存储或数组存储,稀疏矩阵存储构建了基于行索引的数据字典,在松散布局的表格数据中,稀疏矩阵只会对非空数据进行存储,而不需要对空数据开辟额外的内存空间。

    这种特殊的存储策略,不仅节省了内存消耗,也使得数据片段化变得更加容易。利用 SpreadJS,可以随时框取整个数据层中的一片数据,进行序列化或反序列化。

    借助这个特性,开发者甚至可以随时替换或恢复整个存储结构中的任何一个级别的节点,实现高效的数据回滚和数据恢复。

    3. 支撑复杂逻辑运算的计算引擎,实现稳定可靠的应用系统

    公式,是类 Excel 电子表格中的重要功能,其广泛的应用于科学、财务、金融、制造等领域。SpreadJS 支持 450 多种公式函数,同时还提供自定义公式和异步公式函数。

    表面看似简单的 Excel 公式,却具备高阶编程语言的一切特性,如语法分析、解析、运算、执行等。

    当用户设置一个公式到表格中,计算引擎会将其解析为一个中缀表达式,如公式“SUM(A1:B1, 3/E1, C1) + 2*(D1 - 1)”,当通过计算引擎的解析后,会在内存中以树型结构进行存储,这个树型结构被我们称为表达式树。表达式树的生成,是后续构建计算依赖链的关键。

    当一个公式被解析为表达式树后,计算引擎将根据运算上下文为其构建运算依赖链。运算依赖链的目的是为了按需计算,当表格内容发生变化时,只有被影响的表达式树会进行运算,而运算的依据就是依赖链。

    如上图所示,这是 SpreadJS 的计算引擎在构建计算依赖链时的一个简单的流程图,表达式树从计算存储模型中找到对应的根节点以及根节点标识,随后遍历整个表达式树,找出其他依赖标识,构建依赖关系。

    当整个依赖链中的任意节点发生变化时,沿着这条依赖链,SpreadJS 会查找依赖节点并进行重算,这个过程中,没有在依赖链中的节点是不会发生重算计算的,也就是我们所说的脏值运算。

    利用这样的机制,SpreadJS 大大提升了整个表格的运算速度,给用户更好的使用体验和更加精准的运算结果。

    除了绘制引擎、存储策略和计算引擎外,SpreadJS 还实现了更多技术细节,例如触摸支持、富文本支持、前端 Excel 导入导出、JSON 存储等,而这些技术点,承载了葡萄城数年来在表格控件的开发技术和长期服务于开发者的经验积累。

    SpreadJS 广泛应用于各行业企业信息化系统开发

    目前,SpreadJS 已广泛应用于各行业的信息化系统开发中,满足表格文档协同编辑、 数据填报、 类 Excel 报表设计等业务场景,帮助华为、苏宁易购、天弘基金、远光软件等各领域龙头企业,搭建出功能和布局均高度类似于 Excel 的软件系统,加速这类信息系统的交付。

    以华为勘验设计平台的系统搭建为例:

    基于 SpreadJS 开发的勘验设计平台,承载了华为全球业务(基站)的规划勘察设计任务,借助 SpreadJS 跨平台应用嵌入的特性,华为将原先使用 Excel 作为设计模板的方式升级为在线填报,解决了模板不统一、用料数据不一致、文件难以管理的问题,还保留了 Excel 的数据计算能力,让所有模板和数据均可在服务器中存储并管理。

    关于 SpreadJS 的产品介绍及应用案例,欢迎访问葡萄城官网了解更多。

    总结

    葡萄城自 1980 年成立以来,一直专注于开发技术领域,透彻理解开发者所需,极致追求产品细节,经过数十年的打磨,推出了高度复刻 Excel 功能和使用体验的纯前端表格控件 SpreadJS,可完美匹配在线办公场景和前端表格系统开发需求。

    未来,葡萄城将继续秉承“赋能开发者”使命,支持和赋能一切具有创新精神的开发者,激发开发者的潜能,支持客户的成功,共创全新的未来。


    企业表格技术应用开发案例大赛正在进行中,欢迎各位大虾报名参加~

    8 条回复    2020-07-29 13:25:57 +08:00
    yhxx
        1
    yhxx  
       2020-07-29 09:46:58 +08:00
    很好奇整天这么买广告,能收回成本吗
    wangxiaoer
        2
    wangxiaoer  
       2020-07-29 10:01:09 08:00
    不知道为什么,看到葡萄城这几个字就没了兴趣。
    s609926202
        3
    s609926202  
       2020-07-29 10:36:54 +08:00 via iPhone
    手机端支持的不行
    banricho
        4
    banricho  
       2020-07-29 11:00:28 +08:00
    @wangxiaoer 我第一反应是个菠菜网站。。。
    jesson
        5
    jesson  
       2020-07-29 11:14:56 +08:00
    你们表格浮点计算溢出的问题修复了吗?
    tanranran
        6
    tanranran  
       2020-07-29 11:49:42 +08:00
    @banricho #4 #4 大多数人反映都是菠菜网站
    beastk
        7
    beastk  
       2020-07-29 12:36:50 +08:00 via iPhone
    在线发牌?
    summercom
        8
    summercom  
       2020-07-29 13:25:57 +08:00 via Android
    技术没有城墙,你们这个名字在软件技术领域很容易造成反感
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana   &bsp; 841 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 21:48 PVG 05:48 LAX 13:48 JFK 16:48
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86