Повышение производительности в XenForo 2.3

В сегодняшнем выпуске «Вы видели...?» запись для XenForo 2.3, мы рассмотрим, как мы сосредоточились на повышении производительности, чтобы обеспечить быстрое и бесперебойное взаимодействие вашего сообщества. Мы собираемся подробно изучить достигнутые нами достижения и то, как они соотносятся с различными показателями производительности.

Но прежде чем мы перейдем к отдельным изменениям, давайте быстро взглянем на базовый показатель — это наш текущий показатель производительности, рассчитанный Lighthouse для списка форумов сообщества XenForo:

1695798190235.png

Вот показатели производительности некоторых других программ с форума:


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

Следите за обновлениями! Вскоре мы опубликуем обновленную оценку XenForo 2.3.

Но прежде, давайте углубимся в изменения, которые привели нас сюда, не так ли? Если вы хотите перейти к определенному разделу, воспользуйтесь ссылками ниже.
В качестве альтернативы, если вы хотите пропустить большую часть чтения, ознакомьтесь без TL;DR ниже:

 

Улучшения Font Awesome​

Почти на каждой странице, которую мы обслуживаем в настоящее время, мы включаем до пяти различных вариантов библиотеки значков Font Awesome 5 Pro. Это до 8000 значков, разделенных на разные варианты, но на каждой странице мы используем… намного меньше. Размер файла каждого из этих шрифтов составляет до 200 КБ с дополнительными 40 КБ CSS.

Мы всегда хотели выпускать гораздо меньший набор значков, но когда администраторы, дизайнеры стилей и разработчики дополнений имеют в своем распоряжении полный набор значков, это может стать проблемой. В частности, непосредственная работа со шрифтами для создания подмножества может оказаться несколько сложной задачей.

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

В XenForo 2.3 мы разработали совершенно новый способ разделения значков Font Awesome, так что вашим пользователям будут доступны только те значки, которые действительно используются в вашей установке XenForo, и, что еще лучше, это происходит полностью автоматически!

Анализатор использования значков​

XF 2.3 теперь включает в себя анализатор использования значков, который может точно анализировать, какие значки используются в различных типах контента, в том числе в шаблонах Less, а также в шаблонах HTML, модификациях шаблонов, фразах и даже в JavaScript.

Способ использования значков существенно не отличается от того, что было в XF 2.2. Например, если вы знакомы с использованием: <xf:fa icon="fa-comments" /> в шаблонах или .m-faContent(@fa-var-comments) в Less шаблонов, тогда у вас все готово.
Тем не менее, некоторые подходы больше не будут работать. Например, использование <i class="fas fa-comments"></i> больше не будет работать. Такое использование не будет анализироваться и не приведет к отображению значка. По этой причине для использования во фразах используется совершенно новый синтаксис.
В таблице ниже приведены примеры:

Тип​
Код​
Шаблоны/модификации шаблонов
HTML:
<xf:fa icon="fas fa-comments" />
Шаблоны Less
Less:
.m-faContent(@fa-var-solid-comments);
JavaScript
JavaScript:
XF.Icon.getIcon('solid', 'fa-comments')
Фразы
Код:
{icon:fas::comments}

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

Рендеринг значков​

Итак, что же нам делать с проанализированными иконками? Что ж, вместо того, чтобы работать с несколько неуклюжим форматом файла шрифта, мы используем эту информацию для создания листа спрайтов SVG. На самом деле, не один, а по одному для каждого из стилей Font Awesome 5 Pro — light, solid, regular, и duotone и каждый из них содержит только те значки, которые мы анализируем в этих стилях.
Например, вот полный размер таблицы спрайтов, которую мы генерируем для solid стиля по умолчанию:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Font Awesome Pro by @fontawesome - https://fontawesome.com
License - https://fontawesome.com/license (Commercial License)
-->
<svg xmlns="http://www.w3.org/2000/svg">

    <symbol id="check-circle" viewBox="0 0 512 512">
        <path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"/>
    </symbol>
    <symbol id="exclamation-circle" viewBox="0 0 512 512">
        <path d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/>
    </symbol>
</svg>

Он загружается только на те страницы, которые на самом деле используют эти значки, но, что еще лучше, так это то, что, хотя раньше мы загружали полный файл шрифта размером 137 КБ, этот спрайт-лист весит всего 726 байт.
В целом общий размер файла, который мы загружали для значков по умолчанию на главной странице форума, составлял около 400 КБ, теперь он будет меньше 40 КБ.
 

Производительность JavaScript​

Теперь мы должны обратить внимание на JavaScript и посмотреть на потенциальную экономию производительности, которую мы заложили в него.

Отложенная загрузка (defer) для JavaScript​

Стандарты JavaScript существенно изменились за прошедшие годы. Еще не так давно лучшей практикой было переместить теги <script> в нижнюю часть HTML-кода, чтобы отложить выполнение необходимого кода на стороне клиента как можно позже, чтобы гарантировать, что как можно больше часть содержимого страницы может быть проанализирована в первую очередь, прежде чем все более сложный код JavaScript загрузится и заблокирует оставшийся анализ страницы.
К счастью, JavaScript и большинство современных браузеров поддерживают новую директиву defer для тегов <script>, что позволяет нам переместить их обратно в верхнюю часть DOM.
Это позволяет браузеру начать загрузку скриптов немного раньше, отложив их выполнение до загрузки остальной части DOM.

Ленивая загрузка обработчиков JS​

В предыдущих версиях XenForo огромный кусок нашего кода JavaScript считался «основным» JavaScript, скомпилированным в один файл, который загружался на каждой странице. В XenForo 2.3 мы создали систему отложенной загрузки часто используемых обработчиков, когда они необходимы. Они по-прежнему сгруппированы в обработчики form, action, structure и tooltip, но вместо того, чтобы все эти обработчики загружались на каждой странице, они теперь загружаются и регистрируются только при необходимости на текущей странице.
Это представляет собой значительную экономию при загрузке большинства страниц, гарантируя, что страницы загружаются как можно быстрее.
 

Прощай, jQuery​

После того как jQuery стал частью нашего продукта с самого начала, настало время попрощаться с ним.

jQuery - это JavaScript-библиотека, которая инкапсулирует большое количество собственных функций JavaScript в альтернативный набор функций, обильно посыпая их синтаксическим сахаром.

За время существования XenForo стандарты JavaScript и браузеров претерпели значительные изменения. Когда-то использование чего-либо другого было просто немыслимо. В недалеком прошлом jQuery был практически необходим даже для того, чтобы найти элемент с определенным именем класса, или для поддержки, казалось бы, бесконечных причуд в устаревших версиях Internet Explorer и других.

Да и сама по себе эта библиотека занимает более 30 КБ при каждой загрузке страницы, а сколько из этой библиотеки мы на самом деле использовали?

Ну, как разработчик, который лично просмотрел и переписал почти 40 000 строк кода, скажу, что это гораздо меньше, чем можно подумать. А из тех языковых возможностей jQuery, которые мы использовали, многие вещи являются простой прямой заменой на родную функцию JavaScript, которая давным-давно либо вообще не существовала, либо была слишком новой, чтобы получить достаточную поддержку браузера.

Мы понимаем, что для разработчиков, имеющих существующий код на основе jQuery, возникнут некоторые болевые точки, но, по правде говоря, если вы не поддерживаете и близко 40 000 строк кода, которые поддерживаем мы (разработчики XenForo), переход должен быть относительно плавным. Но если вы совсем застряли, то при желании всегда можно заново добавить jQuery, но мы бы рекомендовали избегать этого, если есть такая возможность. А удаление jQuery как зависимости можно начать уже сейчас, если вы планируете вносить изменения в существующий код до выхода XenForo 2.3. Мы настоятельно рекомендуем воздержаться от написания нового кода, напрямую использующего функциональность jQuery.


При необходимости мы можем более подробно рассказать о внесенных изменениях, но вот некоторые основные моменты.

Примечание: Следующий раздел посвящен специфике разработки, поэтому, если это вас не интересует, переходите к следующему разделу.

Переменные с привязкой к блоку​

Хотя это и не относится к jQuery, стоит отметить, что мы больше не используем var для определения переменных, а предпочитаем использовать let и const. Это делает определение переменных более понятным, а код менее подверженным ошибкам и более предсказуемым.

Выбор элемента(ов)​

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

jQuery / XF 2.2​

JavaScript:
var $element = $('.someClassName')
if ($element.length)
{
   // you have an object containing one or more elements; you can call various methods which will interact with this element or elements
}

JavaScript / XF 2.3​

JavaScript:
const element = document.querySelector('.someClassName')
if (element)
{
    // you have an instance of HTMLElement, if more elements exist with the same selector, you have the first element
}

// ... or

const elements = document.querySelectorAll('someClassName')
if (elements.length)
{
    // you have a NodeList object containing one or more HTMLElement objects
}

Стрелочные функции​

Опять же, хотя это и не связано с jQuery, в настоящее время используются как можно чаще. Помимо того, что они синтаксически более удобны, чем традиционные анонимные функции, они не создают новых привязок для ключевых слов, таких как this.

jQuery / XF 2.2​

JavaScript:
var self = this
var callback = function (foo)
{
    self.doSomething(foo)
}

JavaScript / XF 2.3​

JavaScript:
const callback = (foo) =>
{
    this.doSomething(foo)
}

// ...or

const callback = (foo) => this.doSomething(foo)

Обработка событий​

От некоторых функциональных возможностей, предоставляемых jQuery, было трудно отказаться, и большинство этих действительно полезных методов были переписаны и перенесены на ванильный JavaScript в качестве дополнительных методов нашего объекта XF. Не последнюю роль в этом играет управление событиями в jQuery, которое поддерживает события с именами и обеспечивает более интуитивный способ удаления слушателей событий из элемента, не требующий ссылки на исходный обратный вызов события.

jQuery / XF 2.2​

JavaScript:
var $element = $('.someClassName')
$element.on('namespace.click', function (e)
{
   e.preventDefault()
    $element.off('namespace.click')
});

JavaScript / XF 2.3​

JavaScript:
const element = document.querySelector('.someClassName')
if (element)
{
    XF.on(element, 'namespace.click', e =>
    {
        e.preventDefault()
        XF.off(element, 'namespace.click')
    })
}

AJAX​

По сравнению с XenForo 2.2 ситуация в основном не изменилась, поскольку мы по-прежнему используем обертку XF.ajax() в качестве вспомогательного метода, но за кулисами вместо использования метода $.ajax() из jQuery (который является оберткой вокруг XMLHttpRequest) мы перешли на использование более современного .

Главное, на что следует обратить внимание, - это то, что методы , доступные в результате вызова XF.ajax(), называются несколько иначе, чем в jQuery.

jQuery / XF 2.2​

JavaScript:
var t = this
XF.ajax('some-url', data, callback)
    .always(function ()
    {
        t.loading = false
    })

JavaScript / XF 2.3​

JavaScript:
XF.ajax('some-url', data, callback)
    .finally(() =>
    {
        this.loading = false
    })

Хранение произвольных данных для элемента​

Некоторые возможности jQuery, несмотря на свою мощь, иногда могут показаться непоследовательными или неоднозначными. Одной из таких возможностей является метод data, доступный для объектов jQuery. В зависимости от его использования он может вести себя по-разному. Рассмотрим следующий пример:

jQuery / XF 2.2​

JavaScript:
var $element = $('.someClassName').first() // <span class="someClassName" data-foo="1"></span>

var foo = $element.data('foo') // reads the data-foo attribute from the element in the DOM

var bar = $element.data('bar') // attempts to read the data-bar attribute from the element which doesn't exist, but the internal data store may have a value set

$element.data('bar', [1, 2, 3]) // sets the bar key in the internal data store to an array

$element.data('foo', '100') // the foo entry in the internal data store for this element now returns 100, ignoring the data-foo attribute which remains unchanged in the actual DOM
В XenForo по-прежнему существует необходимость хранить произвольные данные, особенно те, которые не всегда являются строкой. Однако наши современные подходы более предсказуемы и последовательны:

JavaScript / XF 2.3​

JavaScript:
const element = document.querySelector('.someClassName') // <span class="someClassName" data-foo="1"></span>

const foo = element.dataset.foo // reads the data-foo attribute from the DOM

element.dataset.foo = '100' // sets the data-foo attribute in the DOM

XF.DataStore.set(element, 'bar', [1, 2, 3]) // a new XF.DataStore class is introduced for reading / storing arbitrary, non-string data

const bar = XF.DataStore.get(element, 'bar') // returns an array: [1, 2, 3]

Цели обработчиков​

У нас есть специальная переменная, которую мы передаем во все обработчики элементов/событий, в настоящее время это this.$target. Обратите внимание, что в XF 2.3 она становится this.target, поскольку обычно префикс $ в именах переменных используется для обозначения объекта jQuery. В XF 2.3 this.target представляет собой объект HTMLElement.

Для поиска дочерних элементов целевого объекта это немного больше соответствует стандартному JavaScript, чем jQuery:

jQuery / XF 2.2​

JavaScript:
var $child = this.$target.find('.someChild').first() // returns the first child element which matches the someChild class

JavaScript / XF 2.3​

JavaScript:
const child = this.target.querySelector('.someChild') // returns the first child element which matches the someChild class; this will be a HTMLElement; note it uses the same querySelector method rather than a separately named method

Поддержка миграции​

Хотя, по понятным причинам, некоторые пользовательские методы в XF 2.3 будут недоступны для вас до релиза, мы рекомендуем вам уже сейчас начать переносить как можно больше кода, чтобы использовать ванильный JavaScript там, где это возможно.

Если вы считаете, что вам нужна поддержка в переводе кода на ванильный JavaScript, мы имеем около 40 000 строк опыта, и мы (разработчики XenForo) постараемся ответить на запросы в , где это возможно.
 

Улучшена производительность CSS с помощью HTTP/2+​


Одним из ключевых преимуществ веб-серверов, работающих по протоколу HTTP/2 и выше, является поддержка мультиплексирования. Мультиплексирование позволяет одновременно обрабатывать несколько запросов к ресурсам (например, JavaScript и CSS), что снижает накладные расходы и увеличивает скорость загрузки страниц.

В XenForo 2.3 мы оптимизировали эту функциональность, изменив способ выполнения CSS-запросов.
  • Основной CSS, единый для всех страниц, по-прежнему упаковывается в один запрос.
  • Теперь CSS, характерный для отдельных шаблонов или страниц, будет запрашиваться отдельно, а не в комплексе.
Для более наглядного представления этих изменений см. ниже.

До.​

HTML:
<link rel="stylesheet" href="css.php?css=public:normalize.css,public:fa.css,public:variations.less,public:core.less,public:app.less" />
<link rel="stylesheet" href="css.php?css=public:node_list.less,public:notices.less,public:share_controls.less,public:extra.less" />

После.​

HTML:
<link rel="stylesheet" href="css.php?css=public:normalize.css,public:fa.css,public:variations.less,public:core.less,public:app.less" />
<link rel="stylesheet" href="css.php?css=public:node_list.less" />
<link rel="stylesheet" href="css.php?css=public:notices.less" />
<link rel="stylesheet" href="css.php?css=public:share_controls.less" />
<link rel="stylesheet" href="css.php?css=public:extra.less" />

Выполняя эти запросы по отдельности и отделяя CSS от других, можно кэшировать отдельные шаблоны и более эффективно использовать их на разных страницах.
 

Барабанная дробь, пожалуйста...​

Ранее мы обсуждали несколько оптимизаций, внесенных в XenForo 2.3, каждая из которых направлена на повышение показателя производительности Lighthouse:
  • Переключение на листы спрайтов SVG для Font Awesome, что снижает необходимость загружать полные веб-шрифты.
  • Представляем нативную отсрочку (defer) для тегов <script>
  • Реализация отложенной загрузки для обработчиков JS.
  • Уменьшение размера страницы за счет удаления jQuery как зависимости.
  • Использование возможностей мультиплексирования HTTP/2 и разделения CSS.
Благодаря этим улучшениям XenForo 2.3 может похвастаться значительным улучшением по сравнению с 84 баллами Lighthouse в XenForo 2.2. Каждое улучшение играет решающую роль: от передовых методов загрузки JavaScript до оптимизации размера ответа и использования возможностей современных веб-серверов.

Но доказательство, как говорится, находится в пудинге:

1695806572958.png

Такая высокая производительность последовательно наблюдается во всем программном обеспечении:

1695806587070.png 1695806614902.png

Хотя мы очень близки к идеальному результату в 100, ожидаем еще больших улучшений с появлением нового стиля в XenForo 3.0.
 
Большое спасибо òbi, за переведенную большую часть на русский язык.
 
  • Мне нравится
Реакции: jois
Назад
Сверху Снизу