...

воскресенье, 13 октября 2013 г.

[Перевод] Производительность фронтенда. Часть 3 — оптимизация шрифтов

От переводчика: Это восьмая статья из цикла о Node.js от команды Mozilla Identity, которая занимается проектом Persona.
Предыдущие статьи:








Мы смогли уменьшить объем шрифтов для Persona на 85%, с 300 до 45 килобайт, используя подмножества шрифтов. Эта статья рассказывает о том, как именно мы это сделали, и какие мы использовали инструменты.

Представляем connect-fonts




Connect-fonts — это middleware для Connect, которое улучшает производительность @font-face, раздавая клиентам подобранные специально для их языка подмножества шрифтов, уменьшая тем самым их размер. Connect-fonts также генерирует специфические для локали и браузера стили @font-face и CORS-заголовки для Firefox и IE9+. Для раздачи подмножеств шрифтов создаются так называемые font packs — поддиректории с подмножествами шрифтов плюс простой конфигурационный файл JSON. Некоторые наборы распространённых open source-шрифтов доступны в готовом виде в пакете npm, впрочем, создавать свои пакеты совсем нетрудно.

Если вы не слишком хорошо ориентируетесь в работе со шрифтами в интернете, мы собрали небольшую коллекцию ссылок по теме @font-face. [От переводчика: а на Хабре очень кстати статья, посвящённая производительности веб-шрифтов]



Статическая и динамическая загрузка шрифтов




Когда вы просто отдаёте один большой шрифтовой файл всем пользователям, всё довольно просто:

  1. пишете блок @font-face в вашем файле CSS;

  2. генерируете шрифтовые файл для веб из исходника в TTF или OTF и кладёте в директорию, к которой есть доступ у веб-сервера;

  3. настраиваете заголовки CORS, если ссылаетесь на шрифты с другого домена, так как политика одного источника Firefox и IE9+ распространяется и на шрифты.




Это несложные шаги. Первые два можно сделать ещё легче с помощью, например, FontSquirrel — онлайн генератора шрифтов, который автоматически создаст файлы шрифтов и код CSS. Для добавления заголовков CORS придётся почитать документацию Apache или Nginx, но и это не слишком трудно.

Но если вы хотите использовать все преимущества подмножеств шрифтов, всё усложняется. Вам нужны будут отдельные файлы шрифтов для каждой локали, и будет нужно динамически менять декларации @font-face, чтобы они указывали на правильные URL. Так же придётся разбираться и с CORS. Именно эти проблемы решает connect-fonts.


Подмножества шрифтов: обзор




По умолчанию, каждый шрифт содержит множество символов — латиницу, диакритические знаки для таких языков как французский или немецкий, дополнительные алфавиты, такие как греческий или кириллица. Многие шрифты также содержат символы, особенно если поддерживают Юникод (например, символ снеговика — ). Есть и шрифты с поддержкой иероглифов. Всё это содержится в файле шрифта, чтобы он был полезен максимально широкой аудитории. Такая гибкость ведёт к большим размерам файлов. Microsoft Arial Unicode, который содержит все символы Uncode 2.1, весит невероятные 22 мегабайта!

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


Выигрыш в производительности при использовании подмножеств шрифтов




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

Меньший размер файлов шрифтов означает более быструю загрузку и появление шрифтов на экране. Это особенно важно для мобильных устройств. Если пользователю доведётся зайти на сайт через 2G-соединение, уменьшение веса страницы на 50 килобайт может ускорить загрузку на 2-3 секунды. Ещё один момент — кеш в мобильных устройствах часто невелик, и у маленького шрифта больше шансов в нём задержаться.


Сравнение размеров полного шрифта Open Sans Regular с подмножествами для разных локалей:


То же самое в сжатом gzip виде:


Даже с учётом сжатия можно уменьшить размер шрифта на 80%, с 63 до 13 килобайт, в случае использования только английского подмножества Open Sans. И это только один шрифт — большинство сайтов используют несколько. Огромный потенциал для оптимизации!


Используя connect-fonts, мы в Mozilla Persona добились уменьшения на 85%, c 300 до 45 килобайт. Это эквивалентно паре секунд времени загрузки на типичном 3G-соединении и десятку секунд на 2G.


Дальнейшие оптимизации




Если вы хотите избавиться от каждого лишнего байта, можно настроить connect-fonts так, чтобы он возвращал CSS не в виде файла, а виде строки, которую можно будет включить в общий файл. Кроме того, connect-fonts по умолчанию генерирует минимальную декларацию @font-face, не включая в неё файлы в форматах, которые не принимает конкретный браузер.

Пример использования connect-fonts в приложении




Предположим, что у нас есть простое приложение на Express, которое сообщает клиенту текущее время:

// app.js
const
ejs = require('ejs'),
express = require('express'),
fs = require('fs');

var app = express.createServer(),
tpl = fs.readFileSync(__dirname, '/tpl.ejs', 'utf8');

app.get('/time', function(req, res) {
var output = ejs.render(tpl, {
currentTime: new Date()
});
res.send(output);
});

app.listen(8765, '127.0.0.1');




используя самый примитивный шаблон:

// tpl.ejs
<!doctype html>
<p>the time is <%= currentTime %>




Подключим connect-fonts, чтобы отдавать локализованное подмножество Open Sans — одного из нескольких включённых в пакеты готовых наборов.

Изменения в приложении




Сначала установим нужные пакеты:

$ npm install connect-fonts
$ npm install connect-fonts-opensans




Подключим middleware:

// app.js с изменениями для использования connect-fonts
const
ejs = require('ejs'),
express = require('express'),
fs = require('fs'),
// добавлено:
connect_fonts = require('connect-fonts'),
opensans = require('connect-fonts-opensans');

var app = express.createServer(),
tpl = fs.readFileSync(__dirname, '/tpl.ejs', 'utf8');




Инициализируем модуль:

// продолжение app.js
// добавим этот вызов app.use:
app.use(connect_fonts.setup({
fonts: [opensans],
allow_origin: 'http://localhost:8765'
})




connect_fonts.setup() принимает следующие аргументы:

  • fonts — массив необходимых шрифтов

  • allow_origin — источник, из которого берём шрифты; connect-fonts использует эту информацию для установки заголовка Access-Control-Allow-Origin для браузеров Firefox 3.5+ и IE9+.

  • ua (необязательный) — список user-agents, которым мы отдаём шрифты. По умолчанию connect-fonts определяет user-agent по запросу, самостоятельно решая, какие форматы файлов ключить в декларацию. Это поведение может быть изменено, если передать ua: 'all' — тогда будут включены все имеющиеся форматы.




Внутри маршрута нужно передать локаль в шаблон:

// продолжение app.js
app.get('/time', function(req, res) {
var output = ejs.render(tpl, {
// передаём локаль в шаблон:
userLocale: detectLocale(req),
currentTime: new Date()
});
res.send(output);
});




Для определения локали Mozilla Persona использует i18n-abide. Есть ещё очень неплохой модуль locale, оба они доступны через npm. Но, чтобы не сложнять пример, мы просто возьмем первые два символа из поля запроса Accept-language:

// примитивное определение локали
function detectLocale(req) {
return req.headers['accept-language'].slice(0,2);
}

app.listen(8765, '127.0.0.1');
// конец app.js




Изменения в шаблоне




Теперь нужно обновить шаблон. Connect-fonts предполагает, что маршруты прописаны в виде:

/:locale/:font-list/fonts.css




например:

/fr/opensans-regular,opensans-italics/fonts.css




В нашем случае надо добавить ссылку на файл стилей по тому пути, который ожидает connect-fonts:

// tpl.ejs - изменения для connect-fonts
<!doctype html>
<link href="/<%= userLocale %>/opensans-regular/fonts.css" rel="stylesheet">




и добавить в код страницы стиль для использования шрифта:

// продолжение tpl.ejs
<style>
body { font-family: "Open Sans", "sans-serif"; }
</style>
<p>the time is <%= currentTime %>




Вот и всё. CSS, созданный connect-fonts, зависит от локали пользователя и его браузера. Вот пример для английской локали:

* это пример вывода при свойстве ua установленном в 'all' */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: url('/fonts/en/opensans-regular.eot');
src: local('Open Sans'),
local('OpenSans'),
url('/fonts/en/opensans-regular.eot#') format('embedded-opentype'),
url('/fonts/en/opensans-regular.woff') format('woff'),
url('/fonts/en/opensans-regular.ttf') format('truetype'),
url('/fonts/en/opensans-regular.svg#Open Sans') format('svg');
}




Если вы хотите сэкономить один HTTP запрос, вы можете использовать метод connect_fonts.generate_css(), чтобы получать код CSS в виде строки, и включить её в общий файл CSS.

Теперь наше маленькое приложение красиво показывает время. Его полный исходный код доступен на Гитхабе. Мы использовали готовый font pack, но создать собственный совсем не трудно. Инструкция есть в readme.


Зключение




Подмножества могут дать очень большой выигрыш в производительности для сайтов, которые используют веб-шрифты. connect-fonts делает немало работы со шрифтами в многоязычном приложении. Даже если ваш сайт подерживает только один язык, вы всё равно можете использовать этот модуль чтобы урезать шрифты до единственной необходимой вам локали и автоматически генрировать CSS и заголовки CORS, плюс это облегчит последующую интернационализацию.

Дальнейшие оптимизации могут состоять в исключении хинтинга для платформ, которые его не поддерживают (всех кроме Windows), автоматическом сжатии шрифтов и установке заголовков для кеширования.






Продолжение следует...

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий