tgoop.com/super_oleg_dev/104
Last Update:
Почему я вообще поднял тему lazy hydration.
Сейчас исследую возможность оптимизировать тяжелый футер tinkoff.ru не только на клиенте, но и на сервере.
В теории, его можно кэшировать, и не рендерить для каждой страницы заново - а время его рендеринга занимает 20-30% от общего времени рендера большинства страниц, то есть оптимизация напрямую повлияет на максимальные нагрузки, которые могут обрабатывать приложения.
Отдельная история - это персонализация и динамика, которая пролезла в футер, и от которой надо избавиться перед кэшированием, но тут нас больше интересует техническая часть.
Если искать информацию по кэшированию React компонентов, можно найти несколько устаревших форков рендерера `ReactDOMServer`, например https://github.com/rookLab/react-component-caching
Этот подход сложный в реализации и поддержке, и не удивительно что актуальных решений кажется не существует.
Но оказалось что можно сделать очень простой локальный механизм, который отлично ложится на наш `LazyRender` компонент, настолько простой что до сих пор ищу подвох.
Сразу покажу обновленный код:
```const LazyRender = ({ children, cacheEnabled, cacheKey, serverCache }) => { const containerRef = useRef(null);
const isVisible = useObserver(containerRef);
if (cacheEnabled && typeof window === 'undefined') { let html: string; if (serverCache.has(cacheKey)) { html = serverCache.get(cacheKey); } else { const reactDomServer = require('react-dom/server'); html = reactDomServer.renderToString(children); serverCache.set(cacheKey, html); } return React.createElement('div', { dangerouslySetInnerHTML: { __html: html }, }); }
if (isVisible) { return React.createElement('div', {}, children);
} return React.createElement('div', {
ref: containerRef, suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: '' }, });
};
```
Где `cacheKey` - уникальный ключ для кэширования конкретной вариации children компонента, он должен иметь низкую кардинальность для эффективной работы кэша.
А `serverCache` - любой LRU кэш со стандартными методами has / get / set, и настроенным не слишком высоким временем жизни кэша.
Для этого кэша на сервере важно быть синглтоном, в принципе как и для любого другого.
В `LazyRender`, если кэш включен, мы вручную рендерим children в HTML строку, и дальше сохраняем результат в кэши и используем при рендере других страниц.
Основной минус подхода - React Context приложения будет недоступен в children компоненте. Мы планируем использовать это для микрофронта (а футер как раз такой микрофронт), где это кажется не проблема, и даже хорошая практика.
По итогу, механизм кэширования это самая простая часть, а больше времени заняла интеграция в модуль отвечающем за загрузку и рендер микрофронтов, и в модуль который добавляет микрофронт футер в лэйаут приложения.
И конечно же доработки самого футера.
Из интересного, закрыл включение кэширования за фича-тогглом - сам футер одновременно можно раскатить на все приложения, а код с кэшированием требует обновления фреймворка на приложениях, и этот рубильник даст возможность в будущем контроллируемо проверить работоспособность механизма.
Также, добавил серверную метрику - счетчик с лейблами hit / miss и уникальным ключем кэша, для возможности чекать hit rate и в целом эффективность кэширования.
Обязательно поделюсь результатами, как дело дойдет до интеграции и подключения на продакшене!
BY SuperOleg dev notes
Share with your friend now:
tgoop.com/super_oleg_dev/104
