Внедрение highlight.js в приложение Express для сверхбыстрой подсветки кода с предварительным рендерингом.

Что мы строим

Простой серверный код Node/Express для преобразования содержимого Markdown в полностью отформатированный HTML с выделенными блоками кода.

Технологии, которые мы используем

Markdown — это облегченный язык разметки с простым синтаксисом форматирования текста. Его конструкция позволяет преобразовывать его во многие выходные форматы.

Highlight.js — это подсветка синтаксиса, написанная на JavaScript. Работает как в браузере, так и на сервере. Он работает практически с любой разметкой, не зависит ни от какой платформы и имеет автоматическое определение языка.

Унифицированный — это дружественный интерфейс, поддерживаемый экосистемой плагинов, созданных для создания контента и управления им.

Унифицированные плагины: remark-parse, remark-rehype, rehype-stringify, rehype-highlight

Введение — зачем использовать подсветку кода на стороне сервера

В Regbrain мы решили внедрить подсветку кода на стороне сервера, чтобы ускорить загрузку нашего основного сайта. Мы постоянно сравниваем наш веб-сайт с Lighthouse и стремимся к максимальной производительности.

Загрузка JavaScript для выделения кода в браузере занимала слишком много времени. Сначала нужно было получить файлы JavaScript, а затем браузер перерисовывал содержимое, что приводило к замедлению работы веб-сайта. Для повышения скорости мы решили реализовать подсветку кода на сервере и теперь полностью отформатированный текст отправляем в браузер.

На этом этапе у вас может возникнуть вопрос: как подсветка кода работает на стороне сервера? Мы рассмотрим это более подробно позже, но сначала давайте рассмотрим наше техническое решение.

Подсветка кода на стороне сервера

Наши статьи написаны в уценке, поэтому наш рабочий процесс должен принимать необработанную уценку в качестве входных данных и обслуживать полностью отформатированный HTML. Мы делаем это в следующих шагах:

  1. Получить уцененный контент
  2. Преобразование уценки в синтаксическое дерево уценки с помощью remark-parse
  3. Преобразование синтаксического дерева уценки в синтаксическое дерево html с помощью remark-rehype
  4. Просмотрите синтаксическое дерево html, чтобы применить подсветку кода к содержимому внутри тегов <code> с помощью rehype-highlight.
  5. Преобразование синтаксического дерева html в строку для отправки клиенту с помощью rehype-stringify

Мы достигаем всего вышеперечисленного с помощью унифицированной структуры и подключаемых модулей следующим образом:

Импортировать необходимые библиотеки

Берем единый фреймворк и необходимые плагины

let unified = require('unified')
let markdown = require('remark-parse')
let remark2rehype = require('remark-rehype')
let highlight = require('rehype-highlight')
let html = require('rehype-stringify')

Создайте единый процессор

Мы создаем процессор, который объединяет все вышеперечисленные плагины для достижения нашей цепочки преобразований от уценки до полностью выделенного html:

let processor = unified()
  .use(markdown)
  .use(remark2rehype)
  .use(highlight)
  .use(html)

Преображение!

Теперь у нас есть процессор, который может анализировать любой ввод уценки следующим образом:

let input = some markdown content 
let output = await processor.process(input)

Пример реализации маршрутизатора Express js

Мы реализуем вышеуказанные шаги в нашем приложении Экспресс следующим образом:

let express = require('express') 
let router = express.Router() 
let unified = require('unified') 
let markdown = require('remark-parse') 
let remark2rehype = require('remark-rehype') 
let html = require('rehype-stringify') 
let highlight = require('rehype-highlight') 
router.get('/:slug', async {
 let input = await article.from.database.in.markdown()
 let processor = unified()
   .use(markdown)
   .use(remark2rehype)
   .use(highlight)
   .use(html)
 let output = await processor.process(input)
 res.render('article', output) })
module.exports = router

Не забывайте про CSS

Последнее, что нам нужно сделать, это включить выделение стилей css на наших страницах. Проще всего было бы просто связать их как внешние стили, но это ухудшит скорость загрузки нашего веб-сайта, поскольку получение внешних стилей блокирует отрисовку страницы. Чтобы избежать потери производительности, мы включаем все css в качестве внутреннего стиля на страницу.

Вопрос о производительности снова

Как сделать рендеринг на стороне сервера производительным? Несмотря на то, что приведенная выше подсветка кода немного замедляет наш сервер по сравнению с отправкой «чистого» html, мы реализуем ряд дополнительных слоев ниже, которые позволяют нам добиться отличной скорости загрузки страницы:

AMP — по умолчанию мы используем наш основной контент в качестве AMP-страниц. Это означает, что Google и Bing могут кэшировать наши страницы и очень быстро обслуживать их на мобильных устройствах.

Никаких внешних стилей или JavaScript (кроме асинхронного AMP) — мы не используем какие-либо блокирующие внешние ресурсы, такие как стили, изображения или файлы JavaScript. Это уже реализовано в соответствии со спецификацией AMP, но даже если бы мы не реализовали AMP, это было бы хорошим подходом для повышения скорости загрузки страницы. Весь наш css является внутренним. Мы подготавливаем серверную часть css и делаем ее специфичной для типа контента, который мы обслуживаем, чтобы избежать включения неиспользуемых стилей (… в разумных пределах…).

Минификация — мы используем минимизацию css и html для дальнейшего уменьшения размера наших страниц.

CDN — мы используем глобальную сеть распространения контента и настраиваем наши HTTP-заголовки, чтобы получить преимущества кэширования CDN, мы также настраиваем сжатие активов для нашей CDN.

С описанной выше настройкой мы можем обслуживать даже десять Экспресс-приложений на самом маленьком экземпляре AWS EC2, что оказывается действительно привлекательным с точки зрения затрат по сравнению с различными вариантами хостинга отдельных приложений в качестве услуги.

Первоначально опубликовано на https://regbrain.com.