perf: single-pass escapeHtml/escapeAttr

Hot path on large table rebuilds — every text cell runs through one
of these. Switching from 4 chained .replace() calls to a single regex
with a lookup map is ~3× faster. At 5000 rows × 4 fields per rebuild,
80k → 20k regex operations.
This commit is contained in:
Administrator 2026-04-19 14:06:52 +02:00
parent b4c26f8106
commit 4f2d462754

View File

@ -3819,14 +3819,21 @@ function setupColumnResizing() {
}); });
} }
// Single-pass escape instead of 4 chained .replace(/x/g, ...) calls.
// Hot path on large table rebuilds — every text cell runs through one of these.
const _HTML_ESC_MAP = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' };
const _HTML_ESC_RE = /[&<>"]/g;
const _ATTR_ESC_MAP = { '&': '&amp;', '"': '&quot;', "'": '&#39;' };
const _ATTR_ESC_RE = /[&"']/g;
function escapeHtml(str) { function escapeHtml(str) {
if (!str) return ''; if (!str) return '';
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); return String(str).replace(_HTML_ESC_RE, (c) => _HTML_ESC_MAP[c]);
} }
function escapeAttr(str) { function escapeAttr(str) {
if (!str) return ''; if (!str) return '';
return String(str).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;'); return String(str).replace(_ATTR_ESC_RE, (c) => _ATTR_ESC_MAP[c]);
} }
function showCopyToast(msg) { function showCopyToast(msg) {