Библиотека Marked в JavaScript предоставляет мощные возможности для парсинга Markdown в HTML. Одним из важных аспектов оптимизации производительности при работе с большими объёмами Markdown является кэширование результатов преобразования. Кэширование позволяет избежать повторного парсинга одного и того же текста, что особенно важно при динамически обновляемых страницах и серверных приложениях.
Markdown-документы могут содержать сотни или тысячи строк. Парсинг каждого документа с нуля при каждом рендере может быть затратным по времени. Кэширование сокращает нагрузку на CPU и ускоряет рендеринг, сохраняя уже сгенерированные HTML-фрагменты для повторного использования.
В памяти Простое кэширование в объекте JavaScript. Подходит для приложений с ограниченным количеством документов или короткой сессией:
const cache = new Map();
function renderMarkdown(markdown) {
if (cache.has(markdown)) {
return cache.get(markdown);
}
const html = marked.parse(markdown);
cache.set(markdown, html);
return html;
}
В этом примере ключом служит сам Markdown-текст, а значением — результат его преобразования в HTML. Такой подход исключает повторный парсинг идентичных строк.
С использованием хэширования Для больших документов прямое хранение текста в качестве ключа может быть неэффективным. Решение — использовать хэш-функции, например SHA-256, чтобы получить компактный ключ:
import crypto from 'crypto';
import { marked } from 'marked';
const cache = new Map();
function hashMarkdown(markdown) {
return crypto.createHash('sha256').update(markdown).digest('hex');
}
function renderMarkdown(markdown) {
const key = hashMarkdown(markdown);
if (cache.has(key)) {
return cache.get(key);
}
const html = marked.parse(markdown);
cache.set(key, html);
return html;
}
Такой подход сокращает потребление памяти и делает кэширование масштабируемым для больших текстов.
Кэш не должен бесконечно храниться без контроля. Возможны следующие стратегии:
Пример реализации простого LRU-кэша:
class LRUCache {
constructor(maxSize) {
this.maxSize = maxSize;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) return null;
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.maxSize) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
}
Использование LRU с Marked:
const cache = new LRUCache(100); // максимум 100 элементов
function renderMarkdown(markdown) {
const key = hashMarkdown(markdown);
const cached = cache.get(key);
if (cached) return cached;
const html = marked.parse(markdown);
cache.set(key, html);
return html;
}
Если Markdown содержит асинхронные элементы, например динамическое подгружение данных, кэширование должно учитывать состояние этих данных. В таких случаях можно кэшировать промисы, чтобы несколько запросов не инициировали повторные вычисления:
const cache = new Map();
async function renderMarkdownAsync(markdown) {
if (cache.has(markdown)) {
return cache.get(markdown);
}
const htmlPromise = marked.parseAsync(markdown);
cache.set(markdown, htmlPromise);
return htmlPromise;
}
Кэширование в Marked позволяет повысить производительность, снизить время отклика и сделать работу с Markdown более эффективной, особенно при больших объёмах данных или частых обновлениях контента.