本帖最后由 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 中的所有公式
|
结合具体的示例来理解下这四个枚举的区别:
- //手动计算
- spread.options.calculationMode = GC.Spread.Sheets.CalculationMode.manual;
- var sheet = spread.getActiveSheet();
- sheet.setValue(0, 0, 1);
- sheet.setValue(1, 1, "普通公式");
- sheet.setValue(2, 1, "易变公式");
- sheet.setValue(3, 1, "自定义公式");
- sheet.setFormula(1, 0, "=A1");
- sheet.setFormula(2, 0, "=Rand()");
- sheet.setFormula(3, 0, "=factorial(5)");
- document.getElementById("all").addEventListener("click", function () {
- // 手动计算模式下,修改A1单元格为2,A2单元格不会被计算,但是会保持脏状态。
- //执行calculate all后,A2和A3将被计算。
- spread.calculate(GC.Spread.Sheets.CalculationType.all, "Sheet1");
- })
- //自定义函数还未注册至spread时,就已经被使用了(setFormula),此时公式计算显示为#NAME!。
- // 之后addCustomFunction注册自定义函数后,需要执行CalculationType.rebuild公式才可计算出来。
- document.getElementById("rebuild").addEventListener("click", function () {
- // 注册自定义函数
- sheet.addCustomFunction(factorial);
- //执行calculate rebuild后,会重新构建所有公式模型,A4 自定义公式 和 其他自定义公式将被计算
- spread.calculate(GC.Spread.Sheets.CalculationType.rebuild, "Sheet1");
- })
- document.getElementById("minimal").addEventListener("click", function () {
- // 手动计算模式下,修改A1单元格为2,A2单元格不会被计算,但是会保持脏状态。
- //执行calculate minimal后,A2将被计算,因为它是脏的。A3(易变单元格)不会被计算,且未标记为脏
- spread.calculate(GC.Spread.Sheets.CalculationType.minimal, "Sheet1");
- })
- document.getElementById("regular").addEventListener("click", function () {
- //手动计算模式下,修改A1单元格为2,A2单元格不会被计算,但是会保持脏状态。
- // 与minimal区别,会将易变单元格和循环引用单元格也标记为脏
- //执行calculate regular后,A3作为易变单元格也会被标记为脏,A2和A3都会被计算
- spread.calculate(GC.Spread.Sheets.CalculationType.regular, "Sheet1");
- })
复制代码
|