性能优化基础
性能影响因素
数据特征
- • JSON文件大小
- • 嵌套层级深度
- • 数组长度
- • 字符串长度
- • 数据类型复杂度
环境因素
- • 可用内存大小
- • CPU处理能力
- • 网络带宽
- • 存储I/O性能
- • 解析器选择
优化目标
- • 减少解析时间
- • 降低内存使用
- • 提高吞吐量
- • 改善用户体验
性能瓶颈
- • 大文件解析
- • 深度嵌套结构
- • 频繁序列化
- • 内存碎片化
优化原则
- • 测量优先
- • 针对性优化
- • 权衡取舍
- • 持续监控
解析性能优化
选择高性能解析器
JavaScript解析器对比
JSON.parse() (原生)推荐
• 最快的解析速度
• 浏览器优化
simdjson-js
• SIMD优化
• 大文件处理
JSONStream
• 流式解析
• 内存友好
性能对比 (相对速度)
JSON.parse
100%
simdjson-js
95%
JSONStream
80%
* 基于1MB标准JSON数据的基准测试
解析优化技巧
推荐做法
// 使用原生JSON.parse
const data = JSON.parse(jsonString);
// 避免重复解析
const cache = new Map();
function parseJSON(jsonString) {
if (cache.has(jsonString)) {
return cache.get(jsonString);
}
const result = JSON.parse(jsonString);
cache.set(jsonString, result);
return result;
}
// 延迟解析
function lazyParse(jsonString) {
let parsed = null;
return {
get data() {
if (!parsed) {
parsed = JSON.parse(jsonString);
}
return parsed;
}
};
}避免的做法
// 避免eval()解析
const data = eval('(' + jsonString + ')');
// 避免频繁的小块解析
for (let i = 0; i < items.length; i++) {
const item = JSON.parse(items[i]);
process(item);
}
// 避免在循环中解析
function processData() {
const data = JSON.parse(largeJSON); // 每次调用都解析
return data.items.map(item => transform(item));
}内存管理策略
内存使用模式
1x
JSON字符串
原始数据大小
2-3x
解析后对象
JavaScript对象
4-5x
处理过程
临时对象+GC
内存规划建议
处理大型JSON时,预留5-8倍于文件大小的可用内存,以应对解析和处理过程中的内存峰值。
内存优化技巧
1. 分批处理
✅ 推荐方式
function processBatch(items, batchSize = 1000) {
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
processBatchItems(batch);
// 让出控制权,允许GC运行
if (i % (batchSize * 10) === 0) {
await new Promise(resolve =>
setTimeout(resolve, 0)
);
}
}
}❌ 避免方式
function processAll(items) {
// 一次性处理所有数据
return items.map(item => {
return expensiveTransform(item);
});
}2. 及时释放引用
function processLargeJSON(jsonString) {
let data = JSON.parse(jsonString);
// 处理后立即清除引用
const result = processData(data);
data = null; // 帮助GC回收
return result;
}
// 使用WeakMap避免内存泄漏
const cache = new WeakMap();
function cacheResult(object, result) {
cache.set(object, result);
}3. 对象池模式
class ObjectPool {
constructor(createFn, resetFn, maxSize = 100) {
this.createFn = createFn;
this.resetFn = resetFn;
this.pool = [];
this.maxSize = maxSize;
}
acquire() {
return this.pool.length > 0
? this.pool.pop()
: this.createFn();
}
release(obj) {
if (this.pool.length < this.maxSize) {
this.resetFn(obj);
this.pool.push(obj);
}
}
}
const processorPool = new ObjectPool(
() => new DataProcessor(),
(processor) => processor.reset()
);大数据处理
数据分块策略
按大小分块
function chunkBySize(data, maxChunkSize) {
const chunks = [];
let currentChunk = [];
let currentSize = 0;
for (const item of data) {
const itemSize = JSON.stringify(item).length;
if (currentSize + itemSize > maxChunkSize && currentChunk.length > 0) {
chunks.push(currentChunk);
currentChunk = [];
currentSize = 0;
}
currentChunk.push(item);
currentSize += itemSize;
}
if (currentChunk.length > 0) {
chunks.push(currentChunk);
}
return chunks;
}按数量分块
function chunkByCount(array, chunkSize) {
const chunks = [];
for (let i = 0; i < array.length; i += chunkSize) {
chunks.push(array.slice(i, i + chunkSize));
}
return chunks;
}
// 使用示例
const data = largeArray;
const chunks = chunkByCount(data, 1000);
for (const chunk of chunks) {
await processChunk(chunk);
}Virtual Scrolling
对于需要在UI中显示大量JSON数据的场景,虚拟滚动可以显著提升性能。
class VirtualJSONList {
constructor(data, itemHeight = 50, containerHeight = 400) {
this.data = data;
this.itemHeight = itemHeight;
this.containerHeight = containerHeight;
this.visibleItems = Math.ceil(containerHeight / itemHeight);
this.startIndex = 0;
}
getVisibleData() {
const endIndex = Math.min(
this.startIndex + this.visibleItems + 5, // 预加载5个
this.data.length
);
return {
items: this.data.slice(this.startIndex, endIndex),
offsetY: this.startIndex * this.itemHeight,
totalHeight: this.data.length * this.itemHeight
};
}
updateScrollPosition(scrollTop) {
this.startIndex = Math.floor(scrollTop / this.itemHeight);
}
}数据预处理
索引构建
function buildIndex(data, keyField) {
const index = new Map();
data.forEach((item, idx) => {
index.set(item[keyField], idx);
});
return index;
}
// 快速查找
const userIndex = buildIndex(users, 'id');
const user = users[userIndex.get(userId)];</数据压缩
// 字段缩写
const compressed = data.map(item => ({
i: item.id,
n: item.name,
e: item.email,
a: item.age
}));
// 数组格式 (适用于同构数据)
const headers = ['id', 'name', 'email', 'age'];
const compactData = data.map(item =>
headers.map(field => item[field])
);</压缩策略
压缩算法对比
| 算法 | 压缩率 | 速度 | CPU消耗 | 适用场景 |
|---|---|---|---|---|
| gzip | 高 (70-80%) | 快 | 低 | HTTP传输 |
| brotli | 很高 (75-85%) | 中等 | 中等 | 现代浏览器 |
| LZ4 | 中等 (60-70%) | 非常快 | 很低 | 实时数据 |
| zstd | 高 (75-80%) | 快 | 低 | 存储/传输 |
JavaScript压缩示例
// 使用pako库进行gzip压缩
import pako from 'pako';
function compressJSON(data) {
const jsonString = JSON.stringify(data);
const compressed = pako.gzip(jsonString);
return compressed;
}
function decompressJSON(compressed) {
const decompressed = pako.ungzip(compressed, { to: 'string' });
return JSON.parse(decompressed);
}
// 使用CompressionStream API (现代浏览器)
async function compressWithStream(data) {
const jsonString = JSON.stringify(data);
const stream = new CompressionStream('gzip');
const writer = stream.writable.getWriter();
const reader = stream.readable.getReader();
writer.write(new TextEncoder().encode(jsonString));
writer.close();
const chunks = [];
let done = false;
while (!done) {
const { value, done: streamDone } = await reader.read();
done = streamDone;
if (value) chunks.push(value);
}
return new Uint8Array(chunks.reduce((acc, chunk) => [...acc, ...chunk], []));
}流式处理
JSONLines处理
// 流式处理JSONLines格式
async function processJSONLines(stream) {
const reader = stream.getReader();
const decoder = new TextDecoder();
let buffer = '';
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
// 保留最后一行(可能不完整)
buffer = lines.pop() || '';
// 处理完整的行
for (const line of lines) {
if (line.trim()) {
try {
const data = JSON.parse(line);
await processRecord(data);
} catch (e) {
console.error('解析错误:', line, e);
}
}
}
}
// 处理剩余的buffer
if (buffer.trim()) {
const data = JSON.parse(buffer);
await processRecord(data);
}
} finally {
reader.releaseLock();
}
}流式处理优势
- • 内存使用恒定
- • 支持无限大数据
- • 早期数据处理
- • 更好的用户体验
- • 容错性强
适用场景
- • 日志文件处理
- • 大型数据集导入
- • 实时数据分析
- • 网络数据传输
- • ETL数据处理
性能监控
性能测量工具
class PerformanceProfiler {
constructor() {
this.metrics = new Map();
}
startTiming(label) {
this.metrics.set(label, {
start: performance.now(),
memoryStart: performance.memory?.usedJSHeapSize || 0
});
}
endTiming(label) {
const metric = this.metrics.get(label);
if (!metric) return;
const duration = performance.now() - metric.start;
const memoryEnd = performance.memory?.usedJSHeapSize || 0;
const memoryUsed = memoryEnd - metric.memoryStart;
return {
duration: Math.round(duration * 100) / 100,
memoryUsed: Math.round(memoryUsed / 1024 / 1024 * 100) / 100 // MB
};
}
profile(fn, label) {
this.startTiming(label);
const result = fn();
const stats = this.endTiming(label);
console.log(`${label}: ${stats.duration}ms, ${stats.memoryUsed}MB`);
return result;
}
}
// 使用示例
const profiler = new PerformanceProfiler();
const data = profiler.profile(() => JSON.parse(largeJSON), 'JSON解析');基准测试
简单基准测试
function benchmark(fn, iterations = 1000) {
const times = [];
for (let i = 0; i < iterations; i++) {
const start = performance.now();
fn();
times.push(performance.now() - start);
}
const avg = times.reduce((a, b) => a + b) / times.length;
const min = Math.min(...times);
const max = Math.max(...times);
return { avg, min, max, iterations };
}结果分析
平均时间:2.34ms
最小时间:1.87ms
最大时间:4.12ms
测试次数:1000
性能优化检查清单
解析优化
- ☐ 使用原生JSON.parse
- ☐ 避免重复解析
- ☐ 实现解析缓存
- ☐ 考虑流式解析
内存管理
- ☐ 分批处理数据
- ☐ 及时释放引用
- ☐ 使用对象池
- ☐ 监控内存使用