请选择 进入手机版 | 继续访问电脑版
 找回密码
 立即注册

QQ登录

只需一步,快速开始

Lynn.Dou 讲师达人认证 悬赏达人认证 SpreadJS 开发认证
超级版主   /  发表于:2025-5-30 14:46  /   查看:81  /  回复:3
本帖最后由 Lynn.Dou 于 2025-5-30 17:38 编辑

易变单元格、循环引用单元格、脏单元格

易变单元格
易变单元格 指的是包含 易变函数的单元格,这类函数在 Excel 每次计算时(即使其依赖项未变化)都会强制重新计算,导致性能损耗。
常见的易变函数有:RAND()、RANDARRAY()、RANDBETWEEN()、NOW()、TODAY()等
循环引用单元格
单元格公式直接或间接的引用了自身。
脏单元格(suspendDirty和resumeDirty,即暂停标脏和恢复标脏
单元格值被修改后会被标记为脏,其状态为脏状态;
如果公式引用的单元格为脏,那么公式单元格也会为脏。即通过公式链递归性的将依赖于脏单元格的单元格也标记为脏
脏可以理解为一个状态,在计算模式中,处于脏状态的单元格才会参与公式重算。

自动模式和手动模式
SpreadJS支持计算模式 CalculationMode,可以设置为手动 manual或自动 automatic。
  • 自动计算 (默认) :SpreadJS 会在依赖值(公式中引用的单元格、值或名称)发生变化时自动更新公式(计算所有脏单元格)。
  • 手动计算: 只在输入或更新公式时计算该公式,并保持其依赖项为脏单元格。在剪切/复制粘贴时,该模式将设置公式和单元格的值,但不会重新计算任何公式。

在有一个包含许多复杂公式的大型工作表中,如果使用默认的自动模式,可能会由于频繁的重新计算影响性能和响应速度。此时开启手动计算会非常有用。例如,在剪切或粘贴值时,SpreadJS会重置公式和单元格的值,但不会重新计算任何公式。

一旦切换到手动计算模式,你需要使用calculate()方法手动更新工作表中的公式,该方法接受CalculationType枚举值作为参数。

强制计算:

calculate
脏单元格(Dirty Cell) 是数据变更状态的核心标识,直接影响计算引擎的执行逻辑和性能优化策略。
调用calculate API来强制进行计算,强制计算的执行逻辑为:重新构建、标记脏单元格 ——> 广播脏单元格 ——> 计算脏单元格。
1、使用CalculationType重新构建并标记公式引用的单元格为脏,即定义了脏单元格范围,这决定了哪些公式会被强制计算。
2、递归性的将依赖于脏单元格的公式单元格也标记为脏
3、强制计算。其中自动计算模式下,所有脏单元格会被重算。在手动模式下,只计算公式单元格,其余脏单元格保持脏状态。
CalculationType
  • all - 默认的计算类型,将范围内的所有单元格标记为脏单元格以进行计算。
  • rebuild - 首先重新构建范围内的所有公式模型,然后将它们标记为脏单元格以进行计算。
相较于all,rebuild下公式引擎计算负担更重,因为要重新构建公式模型:将公式首先解析为 formula string,之后在单元格中重新设置formula。
  • minimal - 保持当前单元格的计算脏状态。将公式标记为需要进行计算,但不会标记易变单元格和循环引用单元格。
除当前已被标脏单元格外,不会标记新脏。
  • regular - 将易变单元格和循环引用单元格标记为脏单元格以进行计算。

通过一张表来总结下:
            
自动计算 (默认)
手动计算
描述
每次相关单元格数据发生变化时自动更新公式
仅在用户请求重算时才计算公式
示例:
在B1输入公式” =A1+1“,然后将A1值修改为2
B1先显示为 1,
在A1值修改后变为3
B1先显示为 1
在A1值修改后保持不变
示例:
在A1输入公式”=ROW()“ ,然后复制A1到A10
A1 显示为1,
A10 显示为 10
A1 和 A10 都显示为 1
suspendCalcService ()
挂起计算引擎,不会计算任何单元格
resumeCalcService(false)
恢复计算引擎,计算易变单元格、循环引用单元格、脏单元格。
恢复计算引擎,将易变单元格标记为脏,但不计算脏单元格。
resumeCalcService()
resumeCalcService(true)
恢复计算引擎,并计算所有单元格
恢复计算引擎,将所有单元格标记为脏,但不计算脏单元格
sheet.setFormula
计算该公式单元格 和 依赖于该公式单元格的其他公式单元格。
计算该公式单元格。
spread.calculate()
默认 all。计算所有公式。
spread.calculate(GC.Spread.Sheets.CalculationType.regular)
计算易变公式和循环引用公式。
spread.calculate(GC.Spread.Sheets.CalculationType.rebuild)
重构所有公式模型然后计算。
spread.calculate(null, “Sheet1“)
计算所有公式
计算 Sheet1 中的所有公式
        

结合具体的示例来理解下这四个枚举的区别:
  1. //手动计算
  2. spread.options.calculationMode = GC.Spread.Sheets.CalculationMode.manual;

  3. var sheet = spread.getActiveSheet();
  4. sheet.setValue(0, 0, 1);
  5. sheet.setValue(1, 1, "普通公式");
  6. sheet.setValue(2, 1, "易变公式");
  7. sheet.setValue(3, 1, "自定义公式");
  8. sheet.setFormula(1, 0, "=A1");
  9. sheet.setFormula(2, 0, "=Rand()");
  10. sheet.setFormula(3, 0, "=factorial(5)");

  11. document.getElementById("all").addEventListener("click", function () {
  12.     // 手动计算模式下,修改A1单元格为2,A2单元格不会被计算,但是会保持脏状态。
  13.     //执行calculate all后,A2和A3将被计算。
  14.     spread.calculate(GC.Spread.Sheets.CalculationType.all, "Sheet1");
  15. })

  16. //自定义函数还未注册至spread时,就已经被使用了(setFormula),此时公式计算显示为#NAME!。
  17. // 之后addCustomFunction注册自定义函数后,需要执行CalculationType.rebuild公式才可计算出来。
  18. document.getElementById("rebuild").addEventListener("click", function () {
  19.     // 注册自定义函数
  20.     sheet.addCustomFunction(factorial);
  21.     //执行calculate rebuild后,会重新构建所有公式模型,A4 自定义公式 和 其他自定义公式将被计算
  22.     spread.calculate(GC.Spread.Sheets.CalculationType.rebuild, "Sheet1");
  23. })

  24. document.getElementById("minimal").addEventListener("click", function () {
  25.     // 手动计算模式下,修改A1单元格为2,A2单元格不会被计算,但是会保持脏状态。
  26.     //执行calculate minimal后,A2将被计算,因为它是脏的。A3(易变单元格)不会被计算,且未标记为脏
  27.     spread.calculate(GC.Spread.Sheets.CalculationType.minimal, "Sheet1");
  28. })

  29. document.getElementById("regular").addEventListener("click", function () {
  30.     //手动计算模式下,修改A1单元格为2,A2单元格不会被计算,但是会保持脏状态。
  31.     // 与minimal区别,会将易变单元格和循环引用单元格也标记为脏
  32.     //执行calculate regular后,A3作为易变单元格也会被标记为脏,A2和A3都会被计算
  33.     spread.calculate(GC.Spread.Sheets.CalculationType.regular, "Sheet1");
  34. })
复制代码




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

3 个回复

倒序浏览
不吐葡萄皮
初级会员   /  发表于:2025-5-30 17:27:56
沙发
回复 使用道具 举报
不吐葡萄皮
初级会员   /  发表于:2025-5-30 17:31:43
板凳
易变函数有:RAND()、RANDARRAY()、RANDBETWEEN()、NOW()、TODAY()

OFFSET() INDEX() 和 INDIRECT()  是动态引用函数,这三个可以不是易变函数
回复 使用道具 举报
Lynn.Dou讲师达人认证 悬赏达人认证 SpreadJS 开发认证
超级版主   /  发表于:2025-5-30 17:37:19
地板
不吐葡萄皮 发表于 2025-5-30 17:31
易变函数有:RAND()、RANDARRAY()、RANDBETWEEN()、NOW()、TODAY()

OFFSET() INDEX() 和 INDIRECT()  是 ...

感谢大佬指出,已修正
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 立即注册
返回顶部