前言

前世今生

  1. 随着业务需求的复杂度不断提升,前端开发对性能的要求也越来越高,需要在保持流畅交互的同时支持更广泛繁杂的场景。比如在网页端运行 Unity 游戏引擎、运行 C/C++/C#/Rust 等代码等等,此时 webAssembly 应运而生,使得以多种语言编写的代码都可以接近原生的速度在 Web 中运行的途径,使得以前无法在 Web 上运行的客户端应用程序得以在 Web 上运行

  2. js 性能瓶颈:js 是一种解释型、弱类型语言,没有静态变量类型,整个 js 代码在引擎中经历以下阶段:Parser —> AST —> Bytecode —> Machine Code —> Optimize Machine Code。但是由于没有静态变量类型,变量类型可能时刻在变,导致隐藏类变化,导致之前做的优化失去作用,重新优化,耗费时间

asm.js

webAssembly 的前身,一种更快的 js。是 js 语言的一个严格子集,通过 减少动态决议 来帮助浏览器提升 js 优化空间

1
2
3
4
5
6
function asmJs() {
"use asm";

let myInt = 0 | 0; // 强制 int 类型
let myDouble = +1.1;
}
  1. 编译目标:一般来说开发人员不直接编写 asm.js 文件。对于 C++ 文件,一般使用 Emscripten 进行转换

  2. asm.js 运行速度快于原生 js:虽然运行速度取决于不同的测试用例、硬件条件、浏览器引擎优化程度等等,不过一般来说其运行速度能达到原生 C/C++ 运行速度的 50% 甚至更高

    • 减少动态决议:弱类型语言变成强类型语言。从而运行时不需要额外的类型推到
    • 从而直接跳过了很多机制:自动 GC 机制、模板编程、JIT 优化等等,编译过程能完成更多事情,生成的机器码运行周期越短,代码运行地越快
  3. asm 不足:asm 输出的还是 js 代码,即使优化的再好,也始终无法跳过 Parser 和 ByteCode Compiler,这两步在 js 代码执行过程中耗时较长

webAssembly

为解决 asm 的局限性,衍生出 webAssembly,它可以将 C/C++/.. 代码 绕过 js 直接生成机器码,一种可移植、体积小、加载快并且兼容 Web 的全新格式
.wat 文件为 .wasm 的文本格式,.wasm 为最终执行的二进制文件

  1. wasm 可理解为一种新的编程语言,和 html、css、js 并列为 web 领域的第四类编程语言
  2. wasm 为汇编语言,开发人员不直接编写,而由对应的编译器编译而来,二进制格式
  3. 主流浏览器可以直接读取并执行 wasm

编译过程

以 AssemblyScript 工具为例,展示 TS 源码编译成 wasm 的过程

AssemblyScript 是 TypeScript 的子集,映射到 WebAssembly 的低级类型体系:i32/i64/f32/f64、usize、静态数组、TypedArray;无 JS 对象/DOM。
编译器是 asc(AssemblyScript Compiler)。它用 TS 前端解析源码,做类型检查与优化,生成 WebAssembly IR,最终产出 .wasm(二进制)与可选 .wat(文本)

  1. 语法解析与类型检查:
    • ts 代码解析成 AST
    • 按 AssemblyScript 的受限类型系统(i32/i64/f32/f64/usize、静态数组、TypedArray 等)做类型检查与常量折叠;剔除 JS/DOM 不可达的部分
  2. 语义降级与中间表示:
    • 泛型单态化:对每个具体类型实例生成专门版本(避免运行时装箱/反射)
    • 类/对象布局确定:把类视为 线性内存中的记录结构” ,计算每个字段的定址偏移
    • 闭包/箭头函数:捕获环境转为堆对象(环境块 + 目标函数引用),通过函数表或静态调用链接
    • 控制流结构(if/for/while/try)映射为 Wasm 的结构化块和分支(block/loop/if/br_if)
  3. 低级指令选择与地址计算:
    • 所有对象与数组都落在一块 线性内存 里,读写用 load<T>/store<T>,地址用指针 + 字节偏移。例如 f64 数组第 i 个元素地址:ptr + (i << 3)(8 字节对齐)
  4. 优化与代码生成:
    • 使用 Binaryen/自带优化通道执行常量折叠、DCE、inlining、边界检查消除、循环简化等;不同 -O 等级决定强度。
    • 生成 Wasm 二进制(同时可选 .wat 以便审阅)

文件结构

  1. assembly/*.ts:仅供 Wasm 的 TS 源码(不要依赖项目的 Web/Node 代码)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    // 一份通用最小实现模板 assembly ts文件
    // 与业务无关,只提供数值钳制和 Float64 列批处理两类基础能力,任何表格/数值场景都可复用
    // 若要处理对象结构,保持“指针+长度”的列式传参,在 JS 侧序列化为 TypedArray 后再交给 Wasm
    // 如需真正的批处理统计或结果结构,完善 batchProcess(当前是占位)和统计写回

    export let processedRows: i32 = 0; // 已处理行数
    export let totalTimeMs: f64 = 0; // 总耗时(ms)

    // 钳制单个数值到区间 [min, max]。最基础的数值保护函数
    export function optimizeNumber(value: f64, min: f64, max: f64): f64 {
    if (value < min) return min;
    if (value > max) return max;
    return value;
    }

    // 对线性内存里从 ptr 开始、长度为 len 的 Float64Array 原地钳制。第 i 个元素地址为 ptr + (i<<3)
    export function clampF64Array(
    ptr: usize,
    len: i32,
    min: f64,
    max: f64
    ): void {
    for (let i = 0; i < len; i++) {
    let off = ptr + ((<usize>i) << 3); // i * 8
    let v = load<f64>(off);
    if (v < min) v = min;
    else if (v > max) v = max;
    store<f64>(off, v);
    }
    }

    // 先钳制再按精度四舍五入的批处理
    // multiplier = 10^precision;multiplier <= 0 时跳过四舍五入
    export function clampRoundF64Array(
    ptr: usize,
    len: i32,
    min: f64,
    max: f64,
    multiplier: f64
    ): void {
    const doRound: bool = multiplier > 0.0;
    for (let i = 0; i < len; i++) {
    let off = ptr + ((<usize>i) << 3);
    let v = load<f64>(off);
    if (v < min) v = min;
    else if (v > max) v = max;
    if (doRound) {
    v = Math.round(v * multiplier) / multiplier;
    }
    store<f64>(off, v);
    }
    }

    // 批处理占位实现,与 JS 包装器签名对齐
    // 记录 processedRows = dataLen,返回占位指针 1024。耗时统计由 JS 侧负责
    export function batchProcess(
    dataLen: i32,
    columnsLen: i32,
    _cfgPtr: i32,
    _cfgLen: i32
    ): i32 {
    processedRows = dataLen;
    totalTimeMs = 0.0; // timing handled in JS
    // Return a placeholder pointer where a result structure could be written if needed
    return 1024;
    }

    export function getProcessedRows(): i32 {
    return processedRows;
    }
    export function getTotalTimeMs(): f64 {
    return totalTimeMs;
    }

    export function cleanup(): void {
    processedRows = 0;
    totalTimeMs = 0.0;
    }
  2. asconfig.json:多目标构建配置(debug/release)

    • outFile/textFile/sourceMap:输出 .wasm、.wat、sourcemap
    • optimizeLevel/shrinkLevel:优化与瘦身等级(release 用 O3)
    • bindings: “esm”:生成 ESM 入口(便于现代前端工具)
    • importMemory/exportMemory/initialMemory/maximumMemory:线性内存策略
    • exportRuntime/exportTable/exportStart:是否导出运行时/表/start
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {
    "targets": {
    "release": {
    "outFile": "public/wasm/table-optimizer.wasm",
    "optimizeLevel": 3,
    "shrinkLevel": 1,
    "noAssert": true
    },
    "debug": {
    "outFile": "public/wasm/table-optimizer-debug.wasm",
    "debug": true
    }
    },
    "options": {
    "runtime": "stub",
    "exportTable": false,
    "exportRuntime": false
    }
    }
  3. 产物与加载

    • 产物:.wasm(运行)、可选 .wat(查看)、.map(调试)
    • 前端加载(Vite/浏览器):把 .wasm 放静态目录(如 public/wasm/),作为静态资源 fetch
    • Vite 建议:在开发服务器允许加载 wasm(如 assetsInclude: [‘*/.wasm’],必要时 COEP/COOP 头避免跨源隔离问题)
    1
    2
    3
    4
    5
    6
    7
    8
    const resp = await fetch("/wasm/table-optimizer.wasm");
    const { instance } = await WebAssembly.instantiate(
    await resp.arrayBuffer(),
    {
    js: { log: () => {}, yield: () => new Promise((r) => setTimeout(r, 0)) },
    }
    );
    const { memory, clampRoundF64Array } = instance.exports;
  4. 与 TypeScript 的差异/限制

    • 仅支持与 Wasm 对齐的类型/标准库;不支持任意 JS/DOM/Node API
    • 类/数组/字符串可用,但跨边界需 loader 或手动内存管理;性能敏感建议 TypedArray 指针传参
    • 异常/垃圾回收与 JS 不同;长生命周期对象需注意 pin/unpin(若使用 loader API)

wasm 优势

  1. 性能优势

    • 执行速度快:贴近硬件的指令集,并且执行前已经被编译成字节码,无需额外的解析、编译步骤
    • 内存管理强:提供了更细粒度的内存管理能力,使用线性内存模型,所有的内存分配都是在一块连续的内存区域中进行,减少了 GC 的耗时
  2. 多语言支持

    • 打破了 js 在 web 开发中的局限,支持多种编程语言,在 web 中充分发挥其他语言的优势
    • 为跨平台共享代码提供了便利
  3. 安全性

    • 沙盒环境:借助 web 开发的沙盒理念,有效避免了其他低级语言存在的额外攻击不好处理的问题
    • 内存安全:特有的结构化的堆栈结构确保内存访问都是安全的,与其他一些低级语言不同,wasm 可以有效防止缓冲区溢出和其他常见的内存错误,避免了这些错误在传统编程中可能引发的安全漏洞
    • 验证编译:预编译避免了在运行时进行潜在不安全的即时编译

应用场景

  1. 浏览器内应用

    • 游戏开发:将 C、C++、Rust、Unity、Unreal Engine 等语言或引擎编写的游戏直接在 web 端运行,破除了以前只能在客户端运行的限制
    • 多媒体处理:支持在 web 端处理视频编辑、3D 渲染、音频处理等需要大量计算的任务
  2. 跨平台应用

    • 将原本只能在桌面端运行的应用程序,借助 wasm 轻松移植到 web 端

场景示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
// 通用表格优化器 - WebAssembly 实现(静默版)
// 支持任意表格数据的性能优化

class TableWasmOptimizer {
constructor() {
this.instance = null;
this.memory = null;
this.isInitialized = false;
this.processedRows = 0;
this.totalProcessingTime = 0;
}

// 初始化 WebAssembly 模块
async init() {
try {
const response = await fetch("/wasm/table-optimizer.wasm");
const wasmBuffer = await response.arrayBuffer();

const wasmModule = await WebAssembly.instantiate(wasmBuffer, {
js: {
log: () => {},
yield: () => new Promise((resolve) => setTimeout(resolve, 0)),
},
});

this.instance = wasmModule.instance;
this.memory = this.instance.exports.memory;
this.isInitialized = true;
return true;
} catch (_error) {
this.isInitialized = false;
return false;
}
}

// 将一列数值批量下沉到 wasm 处理(钳制 + 四舍五入)
optimizeNumericColumnBatch(
rows,
numericField,
{ min = 0, max = 1000000, precision = 2 } = {}
) {
if (!this.isInitialized || !rows?.length) return rows;
const { clampRoundF64Array } = this.instance.exports;
if (!clampRoundF64Array) return rows;

const values = new Float64Array(rows.length);
for (let i = 0; i < rows.length; i++) {
const v = rows[i]?.[numericField];
values[i] = typeof v === "number" && Number.isFinite(v) ? v : 0;
}

const ptr = 4096;
const memView = new Float64Array(this.memory.buffer, ptr, values.length);
memView.set(values);

const multiplier = precision > 0 ? Math.pow(10, precision) : 0;
clampRoundF64Array(ptr, values.length, min, max, multiplier);

const out = new Float64Array(this.memory.buffer, ptr, values.length);
for (let i = 0; i < rows.length; i++) rows[i][numericField] = out[i];
return rows;
}

// 批量处理多个数值列
optimizeNumericColumnsBatch(
rows,
fields,
{ min = 0, max = 1000000, precision = 2, batchSize = 3, delay = 0 } = {}
) {
if (!this.isInitialized || !rows?.length || !fields?.length) return rows;
let index = 0;
const runBatch = () => {
const end = Math.min(index + batchSize, fields.length);
for (let i = index; i < end; i++) {
this.optimizeNumericColumnBatch(rows, fields[i], {
min,
max,
precision,
});
}
index = end;
if (index < fields.length) setTimeout(runBatch, delay);
};
runBatch();
return rows;
}

// 通用数据优化函数(保留:字段级轻量处理)
optimizeData(data, config = {}) {
if (!this.isInitialized) return data;
try {
const startTime = performance.now();
const optimizedData = this.processDataWithWasm(data, config);
this.totalProcessingTime = performance.now() - startTime;
return optimizedData;
} catch (_error) {
return data;
}
}

// 使用 WebAssembly 处理数据(轻量:字符串截断 + 数值钳制)
processDataWithWasm(data, config) {
const {
stringMaxLength = 50,
numberPrecision = 2,
numberMin = 0,
numberMax = 1000000,
batchSize = 100,
} = config;

return data.map((row, index) => {
const optimizedRow = { ...row };

Object.keys(optimizedRow).forEach((key) => {
const value = optimizedRow[key];
if (typeof value === "string" && value.length > stringMaxLength) {
optimizedRow[key] = value.substring(0, stringMaxLength - 3) + "...";
}
});

Object.keys(optimizedRow).forEach((key) => {
const value = optimizedRow[key];
if (typeof value === "number") {
const optimizedValue = this.instance.exports.optimizeNumber(
value,
numberMin,
numberMax
);
if (numberPrecision > 0) {
const multiplier = Math.pow(10, numberPrecision);
optimizedRow[key] =
Math.round(optimizedValue * multiplier) / multiplier;
} else {
optimizedRow[key] = optimizedValue;
}
}
});

if ((index + 1) % batchSize === 0) this.instance.exports.yield?.();
this.processedRows++;
return optimizedRow;
});
}

// 列配置优化(保持不变)
optimizeColumns(columns, config = {}) {
if (!this.isInitialized) return columns;
try {
const {
widthMin = 50,
widthMax = 500,
titleMaxLength = 30,
enableFixedColumns = true,
} = config;

return columns.map((column) => {
const optimizedColumn = { ...column };

if (optimizedColumn.width) {
optimizedColumn.width = this.instance.exports.optimizeNumber(
optimizedColumn.width,
widthMin,
widthMax
);
}

if (
optimizedColumn.title &&
optimizedColumn.title.length > titleMaxLength
) {
optimizedColumn.title =
optimizedColumn.title.substring(0, titleMaxLength - 3) + "...";
}

if (enableFixedColumns && optimizedColumn.fixed) {
optimizedColumn.fixed = "left";
}

if (
optimizedColumn.children &&
Array.isArray(optimizedColumn.children)
) {
optimizedColumn.children = this.optimizeColumns(
optimizedColumn.children,
config
);
}

return optimizedColumn;
});
} catch (_error) {
return columns;
}
}

// 批量处理(数据 + 列配置)
batchProcess(tableData, columns, config = {}) {
if (!this.isInitialized) return { tableData, columns };
try {
const startTime = performance.now();
const optimizedData = this.optimizeData(tableData, config);
const optimizedColumns = this.optimizeColumns(columns, config);

if (config?.numericFields?.length) {
this.optimizeNumericColumnsBatch(optimizedData, config.numericFields, {
min: config.numberMin ?? 0,
max: config.numberMax ?? 1000000,
precision: config.numberPrecision ?? 2,
batchSize: config.numericBatchSize ?? 3,
delay: config.numericBatchDelay ?? 0,
});
}

const processingTime = performance.now() - startTime;
return {
tableData: optimizedData,
columns: optimizedColumns,
processingTime,
};
} catch (_error) {
return { tableData, columns };
}
}

// vxe-table 渲染优化
optimizeVxeTableRendering(xGrid, config = {}) {
if (!this.isInitialized || !xGrid) return;
try {
const { batchSize = 5, delayBetweenBatches = 10 } = config;
const columns = xGrid.getColumns();
const fixedColumns = columns.filter((col) => col.fixed);
this.processFixedColumnsBatch(
xGrid,
fixedColumns,
batchSize,
delayBetweenBatches
);
} catch (_error) {}
}

processFixedColumnsBatch(
xGrid,
fixedColumns,
batchSize,
delayBetweenBatches
) {
for (let i = 0; i < fixedColumns.length; i += batchSize) {
const batch = fixedColumns.slice(i, i + batchSize);
setTimeout(() => {
batch.forEach((col) => {
try {
xGrid.setColumnFixed(col.field, col.fixed || "left");
} catch (_error) {}
});
}, i * delayBetweenBatches);
}
}

getStats() {
if (!this.isInitialized) return { initialized: false };
try {
const statsPtr = this.instance.exports.getStats?.();
let wasmProcessedRows, wasmTotalTime;
if (typeof statsPtr === "number") {
wasmProcessedRows = this.instance.exports.memory.buffer[statsPtr];
wasmTotalTime = new Float64Array(
this.instance.exports.memory.buffer,
statsPtr + 4,
1
)[0];
}
return {
initialized: true,
processedRows: this.processedRows,
totalProcessingTime: this.totalProcessingTime,
averageTimePerRow:
this.processedRows > 0
? this.totalProcessingTime / this.processedRows
: 0,
wasmProcessedRows,
wasmTotalTime,
timestamp: Date.now(),
};
} catch (_error) {
return { initialized: true };
}
}

cleanup() {
if (this.isInitialized) this.instance.exports.cleanup?.();
this.processedRows = 0;
this.totalProcessingTime = 0;
}
}

const tableWasmOptimizer = new TableWasmOptimizer();
export default tableWasmOptimizer;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
<script>
import tableWasmOptimizer from "common/utils/wasm/table-optimizer";

export default {
async created() {
// 初始化 WebAssembly
await this.initWasmOptimizer();
},
beforeUnmount() {
// 清理 WebAssembly
if (this.wasmOptimizer) {
this.wasmOptimizer.cleanup();
}
},
activated() {
this.search();
},
methods: {
// 初始化 WebAssembly 优化器
async initWasmOptimizer() {
try {
const success = await this.wasmOptimizer.init();
this.wasmReady = success;
} catch (error) {
this.wasmReady = false;
}
},

// 使用 WebAssembly 优化数据处理
optimizeWithWasm(tableData, columns) {
if (this.wasmReady && this.wasmOptimizer) {
try {
return this.wasmOptimizer.batchProcess(tableData, columns);
} catch (error) {
return { tableData, columns };
}
}
return { tableData, columns };
},

// 优化 vxe-table 渲染
optimizeVxeTableRendering() {
if (this.wasmReady && this.wasmOptimizer && this.$refs.xGrid) {
try {
this.wasmOptimizer.optimizeVxeTableRendering(this.$refs.xGrid);
} catch (error) {}
}
},

async search() {
this.table.loading = true;
this.cache_params = cloneDeepWith(
delEmptyProperty(this.tableOption.params)
);

try {
const res = await this.$axios.post(
BusinessModule.DpsSourceDataGetVersionData,
this.cache_params
);
let { beginDate, endDate } = res;

// 加载表头
let week_columns = await this.$axios.post(
BusinessModule.WorkCalendarGetWorkCalendarDetail,
{ beginDate, endDate }
);

// 处理数据
this.processDataWithOptimization(res, week_columns);
} catch (error) {
this.$message.error("数据加载失败");
} finally {
this.table.loading = false;
}
},

// 使用优化方案处理数据
processDataWithOptimization(res, week_columns) {
// 当 Wasm 未就绪时,走降级方案,避免主线程长时间阻塞
if (!this.wasmReady || !this.wasmOptimizer) {
this.processDataInMainThread(res, week_columns);
return;
}

// 生成列结构
let _dynm_columns = this.generateColumnsFallback(week_columns);

// 处理表格数据
let _table_data = this.processTableDataFallback(res);

// 使用 WebAssembly 优化
let optimized;
try {
optimized = this.optimizeWithWasm(_table_data, _dynm_columns);
} catch (e) {
// 出现异常则回退到降级方案
this.processDataInMainThread(res, week_columns);
return;
}

// 更新表格数据
this.tableOption.columns = Object.freeze(optimized.columns);
this.tableOption.data = Object.freeze(optimized.tableData);

// 优化 vxe-table 渲染
this.$nextTick(() => {
this.optimizeVxeTableRendering();
});
},

// 主线程数据处理(降级方案)
processDataInMainThread(res, week_columns) {
// 使用异步处理避免阻塞 UI
this.$nextTick(() => {
requestAnimationFrame(() => {
// 生成列结构
let _dynm_columns = this.generateColumnsFallback(week_columns);
this.tableOption.columns = Object.freeze(_dynm_columns);

// 延迟处理表格数据
setTimeout(() => {
let _table_data = this.processTableDataFallback(res);
this.tableOption.data = Object.freeze(_table_data);

// 优化渲染
this.$nextTick(() => {
this.optimizeTableRendering();
});
}, 50);
});
});
},

// 降级列生成逻辑
generateColumnsFallback(week_columns) {
let _dynm_columns = cloneDeepWith(this.fixedColumns);
week_columns.forEach((o, index) => {
_dynm_columns.push({
field: "_work_" + index,
title: o.productionTypeDesc,
children: [
{
field: "_month_" + index,
title: o.monthNumber,
children: [
{
field: "_weeks" + index,
title: o.weekNumber,
children: [
{
field: "_week_" + index,
title: o.weekDay,
children: [
{
field: o.dayIdentifier,
title: moment(o.calendarDate).format("M/D"),
width: 120,
},
],
},
],
},
],
},
],
});
});
return _dynm_columns;
},

// 降级数据处理逻辑
processTableDataFallback(res) {
let _table_data = [];
(res?.sourceDataVoList || []).forEach((o) => {
let _obj = { ...res };
delete _obj.sourceDataVoList;
for (const date in o.scheduleContentMap) {
o[date] = o.scheduleContentMap[date];
}
_table_data.push({ ..._obj, ...o });
});
return _table_data;
},

// 优化表格渲染
optimizeTableRendering() {
// 使用异步处理避免阻塞 UI
this.$nextTick(() => {
setTimeout(() => {
try {
// 获取当前列配置
const currentColumns = this.$refs.xGrid.getColumns();

// 处理固定列
const fixedColumns = currentColumns.filter((col) => col.fixed);
fixedColumns.forEach((col, index) => {
setTimeout(() => {
try {
this.$refs.xGrid.setColumnFixed(
col.field,
col.fixed || "left"
);
} catch (error) {}
}, index * 10); // 每10ms处理一个
});

// 处理隐藏列
const hideColumns = currentColumns.filter((col) => !col.visible);
hideColumns.forEach((col, index) => {
setTimeout(() => {
try {
this.$refs.xGrid.hideColumn(col.field);
} catch (error) {}
}, (fixedColumns.length + index) * 10);
});
} catch (error) {}
}, 50); // 延迟50ms执行
});
},
},
};
</script>