Angular中如何用虚拟化优化pre标签innerHTML的大JSON渲染
解决Angular中大JSON渲染卡顿的虚拟滚动方案
直接把整个美化后的JSON一次性渲染到pre标签的innerHTML里,大数据量时会生成上万DOM节点,必然导致渲染和滚动卡顿。利用Angular CDK的ScrollingModule实现虚拟滚动,只渲染可见区域的行,就能解决这个问题。核心思路是把JSON拆分成单独的行,用虚拟滚动逐行渲染可见部分。
步骤1:改造JSON高亮逻辑,生成行数组
原来的syntaxHighlight返回整个HTML字符串,现在我们需要把JSON拆分成每行,给每行生成对应的高亮HTML:
首先在组件中定义存储高亮行的数组:
highlightedJsonLines: string[] = [];
然后修改处理逻辑,在JSON数据变化时生成行数组(可以放在ngOnChanges或数据加载完成的方法里):
import { SimpleChanges, OnChanges } from '@angular/core'; // 组件要实现OnChanges接口 export class YourComponent implements OnChanges { jsonToBeRendered: any; // 你的原始JSON数据 highlightedJsonLines: string[] = []; ngOnChanges(changes: SimpleChanges) { // 当JSON数据变化时重新生成高亮行 if (changes.jsonToBeRendered && this.jsonToBeRendered) { this.generateHighlightedLines(); } } private generateHighlightedLines() { // 格式化JSON为带缩进的字符串 let json = JSON.stringify(this.jsonToBeRendered, undefined, 2); // 转义HTML特殊字符 json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); // 给JSON元素添加高亮样式 const highlightedFull = json.replace( /("(\u[a-zA-Z0-9]{4}|\[^u]|[^\"])*"(\s*)?:?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) => { let cls = 'number'; if (/^"/.test(match)) { cls = /:$/.test(match) ? 'key' : 'string'; } else if (/true|false/.test(match)) { cls = 'boolean'; } else if (/null/.test(match)) { cls = 'null'; } return `<span class="${cls}">${match}</span>`; } ); // 按换行符拆分成单独的行 this.highlightedJsonLines = highlightedFull.split('\n'); } }
步骤2:用CDK虚拟滚动替换原pre标签
修改HTML模板,使用cdk-virtual-scroll-viewport和*cdkVirtualFor渲染可见行:
<!-- 虚拟滚动容器,设置固定高度 --> <div class="json-scroll-container"> <cdk-virtual-scroll-viewport itemSize="18" class="viewport"> <!-- 逐行渲染高亮后的JSON行 --> <pre *cdkVirtualFor="let line of highlightedJsonLines" class="json-line"> <span [innerHTML]="line"></span> </pre> </cdk-virtual-scroll-viewport> </div>
步骤3:添加必要的样式
设置容器高度、虚拟视口样式,同时保留JSON的排版和高亮样式:
/* 虚拟滚动容器,固定高度让滚动生效 */ .json-scroll-container { height: 600px; border: 1px solid #e0e0e0; border-radius: 4px; } /* 虚拟视口样式 */ .viewport { height: 100%; width: 100%; } /* 每行的pre样式,保持JSON的空格和缩进 */ .json-line { margin: 0; padding: 0 8px; white-space: pre; font-family: 'Consolas', 'Monaco', monospace; line-height: 1.2; /* 要和itemSize对应,这里line-height=18px,所以itemSize设18 */ font-size: 14px; } /* JSON高亮样式 */ .json-line .key { color: #6f42c1; } .json-line .string { color: #28a745; } .json-line .number { color: #dc3545; } .json-line .boolean { color: #007bff; } .json-line .null { color: #ffc107; }
步骤4:导入ScrollingModule
确保在你的Angular模块中导入ScrollingModule:
import { ScrollingModule } from '@angular/cdk/scrolling'; @NgModule({ imports: [ // ...其他模块 ScrollingModule ] }) export class YourModule { }
注意事项
itemSize必须和每行的实际高度匹配(比如line-height是18px,就设18),否则虚拟滚动的计算会出错。- 如果JSON数据量极大(比如十万行以上),
generateHighlightedLines的处理过程可能阻塞主线程,可以考虑将JSON格式化和高亮逻辑放到Web Worker中执行。 - 保持
white-space: pre样式,确保JSON的缩进和空格不会丢失。
内容的提问来源于stack exchange,提问作者Atin Singh
相关产品推荐
相关产品推荐

