Nextjs: отображаемые сервером блоки кода, выделенные Prismjs, не соответствуют друг другу и вызывают повторную визуализацию из-за ведущих пробелов в атрибуте класса

Я пытаюсь реализовать рендеринг на стороне сервера выделенных (токенизированных) блоков кода с помощью Prismjs (примечание: я знаю, как это сделать, используя рендеринг на стороне клиента через useEffect и refs, и я получил эту работу, используя prism-react-renderer. Я специально ищу решение для голых Prismjs и SSR).

// codeExamples = array of some code strings

const Code = ({ code, language }) => {
  const html = Prism.highlight(code, Prism.languages[language], language);
  return (
    <pre
      data-language={language}
      className={`language-${language}`}
    >
      <code
        dangerouslySetInnerHTML={{
          __html: html,
        }}
      />
    </pre>
  );
};

export default function Home() {
  return codeExamples.map((example, i) => (
    <Code
      language="javascript"
      code={example}
      key={i}
    ></Code>
  ));
}

В некоторой степени это работает, но я столкнулся с проблемой: блок кода ненадолго перерисовывается, вероятно, из-за ведущего пробела в атрибуте class:

  • 1-й рендер: class="language-javascript"
  • 2-й рендер: class=" language-javascript

введите описание изображения здесь

Это вызывает (помимо бессмысленного дорогостоящего повторного рендеринга) неприятный сдвиг макета, временно исправленный путем добавления жестко запрограммированных font-size в пикселях к элементу <pre>.

Иногда я получаю предупреждения либо о несоответствии свойств сервера и клиента (невозможно воспроизвести прямо сейчас), либо о Extra attributes from the server: class - но только при запуске next dev, а не при запуске next build && next start.

Проверено на последних версиях Nextjs и Prism.


person HynekS    schedule 08.03.2021    source источник


Ответы (1)


Я получил ответ, погрузившись в Prismjs исходный код:

if (parent && parent.nodeName.toLowerCase() === 'pre') {
    parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
}

Вот откуда берутся пробелы. Совершенно очевидно, почему он здесь (в качестве отступа для конкатенации строк).

Временное исправление состоит в том, чтобы добавить начальный пробел к элементу className на <pre>.

<pre
   data-language={language}
   className={` language-${language}`}
>

Но совершенно очевидно, что это не хороший способ использовать Prismjs для SSR.

person HynekS    schedule 08.03.2021
comment
Проблема не только в SSR, но и в SSG (Static Site Generation). Вероятно, что угодно с регидратацией на клиенте получит это. У меня также был случай, когда имена классов были поменяны местами ... Я бы добавил, что Prismjs использует довольно плохой способ управления классами. Либо вы устанавливаете его с помощью className, либо добавляете / удаляете с помощью classList. - person Jonny; 16.06.2021