Как мы разогнали мобильную Lenta.ru до скорости света

в 11:31, , рубрики: accelerated mobile pages, amp, css, html, javascript, mobile development, mobile web, Мобильный веб

image

AMP — наверняка, вы уже слышали об этой технологии, продвигаемой Google. Казалось бы, еще одна модная технология для хипстеров, о которой скоро все забудут. Однако, в реальности эта фича уже работает в продакшне значительного числа новостных сайтов, среди которых такие гиганты, как the Guardian, Times, Washington Post, и прочая, прочая, прочая. Краткий рассказ о плюшках AMP уже был на страницах “Хабра”, а я хотел бы чуть более подробно сфокусироваться на том, как внедрять это в проект, и какой профит в действительности можно получить.

Очень кратко об том, как это работает

AMP — это технология, которая позволяет ускорить загрузку мобильных страниц. По своей сути это тот же самый HTML, но не просто HTML, а хитро оформленный по специальным правилам. Отрисовка страницы осуществляется при помощи специального скрипта (AMP runtime), который берёт на себя оптимизацию рендеринга.

Выполнение прочих скриптов на странице запрещено, чтобы своими изменениями DOM’а они не мешали нам оптимизировать. Это не значит, что на ваших страницах не будет интерактивности. Её можно сделать, но опять-таки по специальным правилам (наприме, в специальном компоненте).
Кроме того, гугл бот при парсинге обнаруживает AMP страницы, кеширует их содержимое и в дальнейшем будет отдавать их быстрее.

В выдаче Google AMP-ресурсы видны в специальной карусели в верхней части страницы.

Как мы разогнали мобильную Lenta.ru до скорости света - 2

Если же говорить об автоматическом редиректе на amp-версию при прямом заходе по ссылке, то поддержка браузерами amp-страниц и автоматический переход на них пока еще не реализована, а только ожидается. AMP хорош именно для контентных страниц, где нет значительного количества поведенческих скриптов (впрочем, различную интерактивную инфографику, мини-игры и т.п. мы можем реализовать внутри iframe).

Всё вышеперечисленное привело нас к намерению сделать AMP у себя. И вот как это было:

Шаг 1 — создаем новые шаблоны

На бэкенде мы используем Rails, в качестве шаблонизатора — Slim. Первым делом мы создаем новые пути в routes.rb для интересующих нас сущностей (в данном случае это будут новость и статья — основной контент нашего сайта). Принципиально не важно, какой именно адрес будет у amp — версии страниц — это может быть как поддомен amp.youpage.com/article, так и, например, youpage.com/article/amp, лишь бы схема была единообразной.

Затем мы создаем новые шаблоны для рендеринга AMP- версии наших страниц, и смело удаляем из них все лишнее.

Под лишним подразумеваются следующие вещи:
— Javascript-код
— Inline-стили
— Запрещенные в amp-html теги, как-то: object, frame, embed, select, form, input, option, textarea (впрочем, насчёт input спецификация еще может поменяться — следите за развитием проекта.)

В общем, надо выпилить всё, что усложняет рендеринг страницы или же может его блокировать.

def amp_strip_tags(text = '')
    text
      .gsub(%r{<img(.+?)/>}, '')
      .gsub(%r{<iframe.+?/iframe>}m, '')
      .gsub(%r{<script.+?</script>}m, '')
end

Особое внимание хотелось бы уделить head наших страничек. Он должен иметь достаточно явно определенную структуру, вот такую:

Простыня html

  <meta charset="utf-8">
  <title>...</title>
  <meta name=“viewport" content=”width=device-width,minimum-scale=1,initial-scale=1”
  <link rel="canonical" href="{ссылка на полную версию}" />
  <style amp-custom>{пользовательские стили}</style>
  <!-- Микроразметка -->
  <script type="application/ld+json">{...}</script>
  <!-- Предотвращение FOUC -->
  <style amp-boilerplate>
    body{
      -webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;
      -moz-animation: -amp-start 8s steps(1,end) 0s 1 normal both;
      -ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;
      animation:-amp-start 8s steps(1,end) 0s 1 normal both;
    }

    @-webkit-keyframes -amp-start{
      from{
        visibility:hidden
      }to{
        visibility:visible
      }
    }

    @-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}

    @-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}

    @-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}

    @keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}

  </style>
  <noscript>
    <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style>
  </noscript>
  <script async src="https://cdn.ampproject.org/v0.js"></script>

  {amp-components scripts}
 

Обратите внимание, что все скрипты подключаются с атрибутом async.

Шаг 2 — Перелопачиваем стили

В зависимости от реализации, мы можем сделать AMP-версию максимально похожей на обычную мобильную, или же полностью редизайнить её. Мы выбрали первый путь. Итак, для процессинга CSS мы используем Stylus, и БЭМ-style нэйминг. На мобильной версии мы имеем примерно такой набор файлов:

Длинный список

Как мы разогнали мобильную Lenta.ru до скорости света - 3

Очевидно, что часть из них нам не потребуется. Поэтому в таблицу стилей для AMP войдут не все, а кроме того, вероятно, нам потребуются кое-какие исправления.
В частности, внутри стилей нельзя применять:
— Универсальный селектор * и :not()
— overflow: scroll, overflow: auto (нельзя делать скроллящиеся блоки)
— filter
— !important

Такой у нас получается корневой amp.styl:

@import 'reset'
@import 'variables'
@import 'mixins'
@import 'fonts'
@import 'general'
@import 'amp/icons'
@import 'amp/blocks/**/*.styl'
@import 'elements/button'
_apply_media_cache()

Избавившись от вышеперечисленного, и подправив стилевое оформление нужным образом, получившиеся стили надо вывести внутри получившейся страницы. Сборщик у нас webpack, и получившийся css — файл мы вставляем в шаблоне через такой хелпер:

def amp_stylesheet_content
    css_file_path = Rails.root.join('public').to_s + URI(stylesheet_path('amp.css')).path
    File.read(css_file_path)
end

Внутри head:

<style amp-custom>
  =amp_stylesheet_content.html_safe
</style>

(В данном случае мы используем raw html, так как slim не умеет при стандартных настройках создавать атрибуты без значения)

При этом важно, чтобы размер стилевого файла не превысил 50 кБ. В общем-то этого достаточно, к тому же не забываем о минификации. У нас стили заняли 35 кБ.

Шаг 3 — Медиа-контент должен быть внутри AMP-компонентов

В основном медиаконтент новостного сайта можно разделить на 4 категории:
— Изображения (возможно, серии изображений — галереи)
— Видео
— Виджеты социальных сетей
— Специальные интерактивы (например, мини-игры, инфографика и тому подобное)

Для того, чтобы выводить всё это, в AMP предусмотрены различные компоненты.

Итак: для изображений у нас есть очень похожий на HTML5 picture amp-img:

<amp-img alt="Екатерина Проничева" width="420px" height="280px" src="http://icdn.lenta.ru/images/2016/02/11/12/20160211121428311/pic_8dc2c0121a05e635e509d1981d4fda63.jpg" layout="responsive"></amp-img>

А также amp-carousel (годится не только для изображений, но и для прочего контента)

<amp-carousel width=300 height=400>
  <amp-img src="my-img1.png" width=300 height=400></amp-img>
  <amp-img src="my-img2.png" width=300 height=400></amp-img>
  <amp-img src="my-img3.png" width=300 height=400></amp-img>
</amp-carousel>

и amp-lightbox (обычный лайтбокс)

<button on="tap:my-lightbox">Хочу увидеть всё!</button>

<amp-lightbox id="my-lightbox" layout="nodisplay">
  <div class="lightbox">
    <amp-img src="my-full-image.jpg" width=300 height=800 on="tap:my-lightbox.close">
  </div>
</amp-lightbox>

Для видео — amp-video

<amp-video width=400 height=300 src="https://supervideo.com/videos/myvideo.mp4"
    poster="fallback-poster.jpg">
  <div fallback>
    <p>Ooops! Sorry, your browser doesn’t support HTML5 video!</p>
  </div>
  <source type="video/mp4" src="foo.mp4">
  <source type="video/webm" src="foo.webm">
</amp-video>

И разнокалиберный набор виджетов для популярных социальных сетей ( к сожалению, среди них пока отсутствует VK.)

Для замены удобно использовать свой хелпер:

def amp_img(cloud_unit, version, params={})
    if cloud_unit.versions.present? && cloud_unit.versions[version.to_s].present?
      version = cloud_unit.versions[version.to_s]

      if cloud_unit.caption.present?
        alt = cloud_unit.caption
      elsif cloud_unit.alt.present?
        alt = cloud_unit.alt
      else
        alt = ''
      end

      attrs = {
        alt:  strip_tags(alt),
        width: version.width.to_s + 'px',
        height: version.height.to_s + 'px',
        src: cloud_url(version.rel_url),
        class: params[:addclass],
        layout: params[:layout]
        #sizes: "(min-width: 650px) 50vw, 90vw"
      }
      content_tag('amp-img', nil, attrs)
    end
  end

Для того, чтобы редактору было удобно работать с контентом, в админке имеются т.н. “виджеты” — боксы, куда можно вставлять по выбору различный контент. Например, для вставки -instagram или -facebook поста редактору достаточно вставить ссылку на него, из которой затем формируется полноценный код виджета. Нам необходимы новые view для виджетов amp-версии, что достаточно просто — вытащенные регуляркой значения (например, код youtube-ролика) подставляются в шаблон соответствующего amp-компонента, который затем выводится на странице.

Кроме того, необходимо подключить библиотеки требуемых компонентов внутри head.

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

Если мы хотим внедрить в страницу какой-то совсем специальный контент, то мы можем воспользоваться amp-iframe и реализовать в нём любое, самое причудливое поведение, не будучи ограниченными в использовании js.

Есть ряд ограничений на его использование — например, контент внутри amp-iframe может сервиться только через https, кроме того, amp-iframe не должен располагаться, в первом экране. Ну, и конечно, содержимое не может обращаться к DOM страницы, и тем более как-то его изменять. Все, что происходит в песочнице — останется в песочнице.
Загрузка элементов происходит асинхронно, так что, скорее всего, при плохом коннекте вы сначала не увидите их содержимого, а только placeholder (который, кстати можно кастомизировать или же вообще отключить). Зато после того, как контент загрузится, он станет виден безо всякого FOUC!
Вместо виджетов социальных кнопок приходится применить обычный noscript-вариант. Вот, например, для VK:

.b-socials__button
        a.b-socials__image.b-socials__image-vk href="http://vk.com/share.php?url=#{extlink}" target='_blank' title=("Поделиться ссылкой во Вконтакте")

Шаг 4 — Микроразметка

Для AMP-страниц Google настоятельно требует введение микроразметки. На самом деле, значительная часть сайтов уже использует такую разметку, если же у вас ее пока нет — то самое время начать!
В качестве словаря используется schema.org, в данном случае — под наши цели отлично подходит сущность NewsArticle. Вставить разметку в страницу можно при помощи JSON-LD (на мой взгляд, наиболее удобный способ) или используя microdata — специальные атрибуты, устанавливающиеся для тегов с определённым контентом. Все необходимые данные для этого, скорее всего, у вас уже есть, и остается только подставить их в шаблон.

Шаг 5 — Аналитика и реклама

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

<amp-pixel src="https://foo.com/pixel?RANDOM"></amp-pixel>

Очень удобно — вместо RANDOM будет подставляться рандомное число.
Также есть нативный компонент Google Analytics — amp-analytics

<amp-analytics id="analytics1" type="googleanalytics"><script type="application/json">{
  "vars": {
    "account": "UA-*******"
  },
  "triggers": {
    "trackPageview": {
      "on": "visible",
      "request": "pageview"
    }
  }
}</script></amp-analytics>

В принципе, мы решили, что стоит заменить гору счетчиков одним Google Analytics, однако если для вас этот вариант непозволителен, то возможности есть.

Что касается рекламы, то в текущий момент поддерживаются в основном системы доставки рекламного контента, распространенные в США. Чтобы подружиться, например, с adFox, можно сделать вот что: получить ссылку на содержимое баннера и разместить ее внутри amp-iframe заданного размера. Таким образом, реклама придет асинхронно и никогда не сможет помешать загрузке странички.

В рамках нашего проекта рекламу в AMP мы пока не внедряли. Наслаждайтесь свободой :)

Шаг 6 — Проверяем, все ли у нас хорошо

После того, как вы успешно собрали ваши AMP-версии страниц, необходимо проверить, все ли соответствует спецификации, чтобы поисковый бот Google обрабатывал их и включал в поисковую выдачу.

Во-первых, прямо на локальной машине или dev-сервере можно открыть проверяемую страничку с хэш-параметром #development=1, а затем заглянуть в developer console.

Если все OK, вы увидите примерно такое сообщение:

Как мы разогнали мобильную Lenta.ru до скорости света - 4

Если же нет, встроенный валидатор покажет, где обнаружены ошибки, и даже приведёт ссылки на документацию:

Как мы разогнали мобильную Lenta.ru до скорости света - 5

После того, как вы выкатились в production, загляните сюда, и проверьте, что microdata парсится нормально и имеет необходимые поля.

Кроме того, в инструментах вебмастера Google вы можете найти вкладку “Страницы для мобильных устройств”, где собирается статистика по проиндексированным страницам, там вы сможете найти в удобном виде статистику и информацию об ошибках по всему сайту, если таковые появятся, но будьте внимательны — индексирование происходит довольно нечасто (раз в несколько дней), так что после внесения изменений придется подождать.

Замеры скорости

Скорость загрузки amp-версий страниц значительно выше, притом, чем хуже качество соединения, тем более заметна разница. В целом, можно говорить о как минимум 2х-кратном росте скорости загрузки. Сравним при помощи tools.pingdom.com показатели обычной мобильной странички:

Как мы разогнали мобильную Lenta.ru до скорости света - 6

и amp-версии:

Как мы разогнали мобильную Lenta.ru до скорости света - 7

Разница, как говорится, налицо. Особенно интересно рассмотреть waterfall загрузки ресурсов и сравнить (эту задачу оставлю для интересующихся читателей).

Для подробностей в нюансах рекомендую посмотреть официальный github проекта и документацию.

Автор: Rambler&Co

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js