AMP

amp-bind

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

Скрипт
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
Примеры
РуководстваКак создавать интерактивные AMP-страницы

Обзор

Компонент amp-bind позволяет добавлять на AMP-страницы настраиваемые интерактивные функции. Для этого используется привязка данных и простые выражения, похожие на JavaScript.

Чтобы получить общие сведения о компоненте amp-bind, посмотрите видео.

Простой пример

В примере ниже текст элемента <p> меняется с Hello World на Hello amp-bind.

<p [text]="'Hello ' + foo">Hello World</p>

<button on="tap:AMP.setState({foo: 'amp-bind'})">Say "Hello amp-bind"</button>

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

Как это работает?

У amp-bind есть три основных компонента:

  1. Состояние: изменяемое состояние JSON для всего документа. В примере выше состояние пусто, пока не будет нажата кнопка. После нажатия оно меняется на {foo: 'amp-bind'}.
  2. Выражения: похожие на JavaScript выражения, которые могут ссылаться на состояние. В примере выше использовано одно выражение: 'Hello ' + foo. Оно объединяет постоянную строку 'Hello ' и переменную состояния foo. В выражении можно использовать не более 100 операндов.
  3. Привязки: это специальные атрибуты вида [property], которые связывают свойство элемента с выражением. В примере выше есть одна привязка, [text], которая обновляет текст элемента <p> каждый раз, когда меняется значение выражения.

При использовании amp-bind особое внимание уделяется скорости, безопасности и эффективности AMP-страниц.

Чуть более сложный пример

<!-- Сложные вложенные данные JSON размещены в <amp-state>элементах.-->
<amp-state id="myAnimals">
  <script type="application/json">
    {
      "dog": {
        "imageUrl": "/img/dog.jpg",
        "style": "greenBackground"
      },
      "cat": {
        "imageUrl": "/img/cat.jpg",
        "style": "redBackground"
      }
    }
  </script>
</amp-state>

<p [text]="'This is a ' + currentAnimal + '.'">This is a dog.</p>

<!-- Классы CSS также можно добавлять и удалять с помощью [class]. -->
<p class="greenBackground" [class]="myAnimals[currentAnimal].style">
  Для каждого животного задан свой цвет фона.
</p>

<!-- Вы также можете изменить источник изображения с помощью привязки [src]. -->
<amp-img width="300" height="200" src="/img/dog.jpg" [src]="myAnimals[currentAnimal].imageUrl">
</amp-img>

<button on="tap:AMP.setState({currentAnimal: 'cat'})">Set to Cat</button>

При нажатии кнопки происходит следующее:

  1. Состояние обновляется, для currentAnimal указывается значение 'cat'.
  2. Выражения, которые зависят от значения currentAnimal, оцениваются:

    • 'Это ' + currentAnimal + '.' => 'Это кошка.'
    • myAnimals[currentAnimal].style => 'redBackground'
    • myAnimals[currentAnimal].imageUrl => /img/cat.jpg
  3. Привязки, которые зависят от измененных выражений, обновляются:

    • Текст первого элемента <p> выглядит как "Это кошка.".
    • Во втором элементе <p> для атрибута class задается значение "redBackground".
    • Элемент amp-img выводит изображение кошки.

Воспользуйтесь демо-версией этого примера с аннотациями к коду.

Описание

Состояние

У каждого AMP-документа, в котором используется amp-bind, есть общее изменяемое состояние JSON.

Инициализация состояния с помощью amp-state

Состояние amp-bind может быть инициализировано с помощью компонента amp-state:

<amp-state id="myState">
  <script type="application/json">
    {
      "foo": "bar"
      }
  </script>
</amp-state>

Выражения могут ссылаться на переменные состояния через точечный синтаксис. В этом примере myState.foo оценивается как "bar".

  • Максимальный размер дочернего скрипта JSON для элемента <amp-state> составляет 100 КБ.
  • Элемент <amp-state> также может указывать URL CORS вместо дочернего скрипта JSON. Более подробные сведения см. в Приложении.

Обновление состояния

Этот компонент поддерживает действие refresh для обновления контента в состоянии.

<amp-state id="amp-state" ...></amp-state>
<!-- Clicking the button will refresh and refetch the json in amp-state. -->
<button on="tap:amp-state.refresh"></button>

Обновление состояния с помощью AMP.setState()

Действие AMP.setState() объединяет объектный литерал с состоянием. Например, при нажатии кнопки ниже AMP.setState() выполняется глубокое слияние объектного литерала и состояния.

<!-- Like JavaScript, you can reference existing
      variables in the values of the object literal. -->
<button on="tap:AMP.setState({foo: 'bar', baz: myAmpState.someVariable})"></button>

Как правило, вложенные объекты объединяются до максимальной глубины 10. Все переменные, в том числе добавленные amp-state, могут быть переопределены.

Если действие AMP.setState() вызывается определенными событиями, то оно также может получить доступ к данным, связанным с событием, в свойстве event.

<!-- The "change" event of this <input> element contains
      a "value" variable that can be referenced via "event.value". -->
<input type="range" on="change:AMP.setState({myRangeValue: event.value})">

Изменение истории с помощью AMP.pushState()

Действие AMP.pushState() аналогично AMP.setState(), но оно также передает новую запись в стек истории браузера. При извлечении этой записи (например, при переходе назад) восстанавливаются предыдущие значения переменных, установленные AMP.pushState().

Пример:

<button on="tap:AMP.pushState({foo: '123'})">Set 'foo' to 123</button>
  • При нажатии кнопки для переменной foo задается значение 123, а также создается новая запись в истории.
  • При переходе назад восстанавливается прежнее значение переменной foo, "bar" (аналогично вызову AMP.setState({foo: 'bar'}).

Выражения

Выражения похожи на JavaScript, однако есть ряд важных отличий.

Отличия от JavaScript

  • Выражения могут получать только состояние всего документа.
  • У выражений нет доступа к глобальным переменным, таким как window и document.
  • Можно использовать только функции и операторы из белого списка.
  • Запрещены пользовательские функции, классы и циклы. Стрелочные функции разрешены как параметры, например Array.prototype.map.
  • Неопределенные переменные и индекс, который выходит за границы массива, возвращают значение null вместо undefined или ошибок.
  • Для повышения эффективности в настоящее время каждое выражение поддерживает не более 50 операндов. Если вам требуется больше, свяжитесь с нами.

Грамматика и реализация полного выражения представлены в bind-expr-impl.jison и bind-expression.js.

Примеры

Ниже приведены все допустимые выражения:

1 + '1'           // 11
1 + (+'1')        // 2
!0                // true
null || 'default' // 'default'

Функции в белом списке

Тип объектаФункцииПример
Array1concat
filter
includes
indexOf
join
lastIndexOf
map
reduce
slice
some
sort (не по месту)
splice (не по месту)
// Returns [1, 2, 3].
          [3, 2, 1].sort()
// Returns [1, 3, 5].
            [1, 2, 3].map((x, i) => x + i)
// Returns 6.
              [1, 2, 3].reduce((x, y) => x + y)
NumbertoExponential
toFixed
toPrecision
toString
// Returns 3.
                (3.14).toFixed()
// Returns '3.14'.
                  (3.14).toString()
StringcharAt
charCodeAt
concat
indexOf
lastIndexOf
slice
split
substr
substring
toLowerCase
toUpperCase
// Returns 'abcdef'.
                      abc'.concat('def')
Math2abs
ceil
floor
max
min
random
round
sign
// Returns 1.
                          abs(-1)
Object2keys
values
// Returns ['a', 'b'].
                            keys({a: 1, b: 2})
// Returns [1, 2].
                              values({a: 1, b: 2}
Global2encodeURI
encodeURIComponent
// Returns 'Hello%20world'.
                                encodeURIComponent('Hello world')

1В стрелочных функциях с одним параметром не должно быть скобок. Используйте x => x + 1 вместо (x) => x + 1. Функции sort() и splice() не работают по месту расположения, а возвращают измененные копии.

2У статических функций нет пространства имен. Используйте abs(-1) вместо Math.abs(-1).

Определение макросов с помощью amp-bind-macro

Фрагменты выражения amp-bind можно использовать повторно. Для этого потребуется определить amp-bind-macro. Элемент amp-bind-macro позволяет определить выражение, которое принимает 0 или более аргументов и ссылается на текущее состояние. Макрос можно вызвать как функцию, если сослаться на значение атрибута id из любой части документа.

<amp-bind-macro id="circleArea" arguments="radius" expression="3.14 * radius * radius"></amp-bind-macro>

<div>
  Площадь круга равна <span [text]="circleArea(myCircle.radius)">0</span>.
</div>

Макрос также может вызывать другие макросы, определенные перед ним. Рекурсивный вызов одного и того же макроса не допускается.

Привязки

Привязка – это специальный атрибут вида [property], который связывает свойство элемента с выражением. Можно также использовать альтернативный синтаксис, совместимый с XML, в виде data-amp-bind-property.

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

Элемент amp-bind поддерживает привязку данных к четырем типам состояния.

ТипАтрибутыОписание
Node.textContent[text]Поддерживается большинством текстовых элементов.
Классы CSS[class]Результатом выражения должна быть строка, разделенная пробелами.
Атрибут hidden[hidden]Это должно быть логическое выражение.
Размер элементов AMP[width]
[height]
Меняет ширину и/или высоту AMP-элемента.
Атрибуты для конкретных элементовРазные атрибуты

Примечания о привязках:

  • Привязка к innerHTML запрещена в целях безопасности.
  • Все привязки атрибутов очищаются от небезопасных значений (например, javascript:).
  • Результаты логического выражения меняют логические атрибуты. Пример: <amp-video [controls]="expr"...>. Если expr имеет значение true, в элементе <amp-video> есть атрибут controls. Если expr имеет значение false, атрибут controls удаляется.
  • Квадратные скобки [ и ] в названиях атрибутов могут вызывать проблемы при записи XML (XHTML, JSX) или при записи атрибутов с помощью DOM API. В таких случаях используйте альтернативный синтаксис: data-amp-bind-x="foo" вместо [x]="foo".

Атрибуты для конкретных элементов

Ниже перечислены все компоненты и атрибуты, к которым разрешена привязка.

КомпонентАтрибутыДействия
<amp-brightcove>[data-account]
[data-embed]
[data-player]
[data-player-id]
[data-playlist-id]
[data-video-id]
Меняет отображаемое видео Brightcove.
<amp-carousel type=slides>[slide]*Меняет текущий отображаемый индекс слайдов. См. пример.
<amp-date-picker>[min]
[max]
Устанавливает самую раннюю дату для выбора.
Устанавливает самую позднюю дату для выбора.
<amp-google-document-embed>[src]
[title]
Отображает документ по обновленному URL.
Меняет заголовок документа.
<amp-iframe>[src]Меняет исходный URL окна iframe.
<amp-img>[alt]
[attribution]
[src]
[srcset]
При связывании с [src] убедитесь, что выполняется также привязка к [srcset]. Это необходимо для поддержки кеша.
См. соответствующие атрибуты amp-img.
<amp-lightbox>[open]*Переключает отображение окна просмотра. Совет: используйте on="lightboxClose: AMP.setState(...)" для обновления переменных, когда окно просмотра закрыто.
<amp-list>[src]Для строковых выражений извлекает и отображает JSON из URL строки. Для объектов и массивов отображает данные выражения.
<amp-selector>[selected]*
[disabled]
Изменяет выбранные в данный момент дочерние элементы,
определяя их по значению атрибута option. Поддерживает список значений, разделенный запятыми, для множественного выбора. См. пример.
<amp-state>[src]Извлекает JSON из нового URL и объединяет его с существующим состоянием. Учтите, что в следующем обновлении элементы <amp-state> будут игнорироваться для предотвращения циклов.
<amp-video>[alt]
[attribution]
[controls]
[loop]
[poster]
[preload]
[src]
См. соответствующие атрибуты amp-video.
<amp-youtube>[data-videoid]Меняет отображаемое видео YouTube.
<a>[href]Меняет ссылку.
<button>[disabled]
[type]
[value]
См. соответствующие атрибуты button.
<details>[open]См. соответствующие атрибуты details.
<fieldset>[disabled]Включает или отключает набор полей.
<image>[xlink:href]
См. соответствующие атрибуты image.
<input>[accept]
[accessKey]
[autocomplete]
[checked]
[disabled]
[height]
[inputmode]
[max]
[maxlength]
[min]
[minlength]
[multiple]
[pattern]
[placeholder]
[readonly]
[required]
[selectiondirection]
[size]
[spellcheck]
[step]
[type]
[value]
[width]
См. соответствующие атрибуты input.
<option>[disabled]
[label]
[selected]
[value]
См. соответствующие атрибуты option.
<optgroup>[disabled]
[label]
См. соответствующие атрибуты optgroup.
<select>[autofocus]
[disabled]
[multiple]
[required]
[size]
См. соответствующие атрибуты select.
<source>[src]
[type]
См. соответствующие атрибуты source.
<track>[label]
[src]
[srclang]
См. соответствующие атрибуты track.
<textarea>[autocomplete]
[autofocus]
[cols]
[disabled]
[maxlength]
[minlength]
[placeholder]
[readonly]
[required]
[rows]
[selectiondirection]
[selectionend]
[selectionstart]
[spellcheck]
[wrap]
См. соответствующие атрибуты textarea.

*Привязываемые атрибуты, у которых нет аналогов без привязок.

Отладка

Выполните тест в режиме разработчика (с фрагментом URL #development=1). Вы увидите предупреждения и ошибки, а также сможете воспользоваться специальными функциями отладки.

Предупреждения

В режиме разработчика amp-bind выдает предупреждение, если значение по умолчанию для связанного атрибута не соответствует начальному результату соответствующего выражения. Это помогает предотвратить непреднамеренные мутации, вызванные изменениями в других переменных состояния. Пример:

<!-- The element's default class value ('def') doesn't match the expression result for [class] ('abc'),
so a warning will be issued in development mode. -->

<p class="def" [class]="'abc'"></p>

В режиме разработчика amp-bind также выдает предупреждение при разыменовании неопределенных переменных или свойств. Это также помогает предотвратить непреднамеренные мутации из-за результатов выражения null. Пример:

<amp-state id="myAmpState">
  <script type="application/json">
    { "foo": 123 }
</script>
</amp-state></p>

<!-- The amp-state#myAmpState does not have a `bar` variable, so a warning
  will be issued in development mode. -->
<p [text]="myAmpState.bar">Текст в теге.</p>

Ошибки

При работе с amp-bind в среде выполнения возникает несколько типов ошибок.

ТипСообщениеПодсказка
Неверная привязкаПривязка к атрибуту [НазваниеАтрибута] в теге <P> не допускается.Используйте только привязки из белого списка.
Синтаксическая ошибкаОшибка компиляции выражения: …Убедитесь, что в выражении нет опечаток.
Функции, не входящие в белый списокФункция оповещения не поддерживается.Используйте только функции из белого списка.
Недопустимый результатРезультат "javascript:alert(1)" недопустим для [href].Избегайте запрещенных URL-протоколов или выражений, которые не поддерживаются валидатором AMP.
Нарушение CSPНельзя создать рабочий процесс для 'blob:...', поскольку это нарушает следующую директиву Политики безопасности контента:…Добавьте default-src blob: в свою Политику безопасности контента. Чтобы обеспечить высокую эффективность, компонент amp-bind передает дорогостоящую работу специальному процессу Web Worker.

Состояние отладки

Чтобы вывести текущее состояние на консоль, воспользуйтесь AMP.printState().

Приложение

Спецификация <amp-state>

Элемент amp-state может содержать дочерний элемент <script> ИЛИ атрибут src с URL CORS для удаленной конечной точки JSON. Сочетать эти функции нельзя.

<amp-state id="myLocalState">
  <script type="application/json">
    {
      "foo": "bar"
      }
  </script>
</amp-state>

<amp-state id="myRemoteState" src="https://data.com/articles.json">
</amp-state>

Пакетная обработка запросов XHR

AMP объединяет запросы XHR для конечных точек JSON. Поэтому ы можете использовать один запрос данных JSON как источник для нескольких покупателей (то есть нескольких элементов amp-state) на AMP-странице. Например, если элемент amp-state отправляет запрос XHR to an в конечную точку, в процессе отправки все последующие запросы XHR к этой же конечной точке не будут отправляться. Результаты придут в ответе на первый запрос.

Атрибуты

srcURL удаленной конечной точки, которая будет возвращать JSON для обновления состояния amp-state. Это должна быть служба CORS HTTP. Атрибут src поддерживает все стандартные замены переменных URL. Подробная информация приведена в этом руководстве.

Конечная точка должна соответствовать требованиям, указанным в Спецификации CORS для запросов на AMP-страницах.

credentials (необязательно)Определяет параметр credentials в соответствии с Fetch API.
  • Поддерживаемые значения: `omit`, `include`.
  • Значение по умолчанию: `omit`.
Чтобы отправить учетные данные, передайте значение include. Если это значение установлено, ответ будет соответствовать требованиям по безопасности CORS для AMP.

Глубокое слияние с применением AMP.setState()

При вызове AMP.setState() компонент amp-bind выполняет глубокое слияние объектного литерала с текущим состоянием. Все переменные из литерала записываются непосредственно в состояние, кроме вложенных объектов, которые рекурсивно объединяются. Примитивы и массивы в состоянии всегда переопределяются переменными с теми же именами из объектного литерала.

Пример:

{
  <!-- State is empty -->
  }
<button on="tap:AMP.setState({employee: {name: 'John Smith', age: 47, vehicle: 'Car'}})"...></button>
<button on="tap:AMP.setState({employee: {age: 64}})"...></button>

При нажатии первой кнопки состояние меняется:

{
  employee: {
    name: 'John Smith',
    age: 47,
    vehicle: 'Car',
    }
  }

При нажатии второй кнопки amp-bind выполняет рекурсивное слияние аргумента объектного литерала ({employee: {age: 64}}) с текущим состоянием.

{
  employee: {
    name: 'John Smith',
    age: 64,
    vehicle: 'Car',
    }
  }

Значение employee.age удалось обновить, но employee.name и employee.vehicle не изменились.

Учтите, что amp-bind выдаст ошибку, если вы обратитесь к AMP.setState() с объектным литералом, в котором есть циклические ссылки.

Удаление переменной

Чтобы удалить существующую переменную состояния, задайте для нее значение null в AMP.setState(). Рассмотрим состояние из предыдущего примера. При нажатии кнопки:

<button on="tap:AMP.setState({employee: {vehicle: null}})"...></button>

Состояние меняется на следующее:

{
  employee: {
    name: 'John Smith',
    age: 48,
    }
  }

Аналогично при использовании такого кода:

<button on="tap:AMP.setState({employee: null})"...></button>

Состояние меняется на следующее:

{
  <!-- State is empty -->
  }

Грамматика выражения

Грамматика выражений amp-bind похожа на BNF:

expr:
    operation
  | invocation
  | member_access
  | '(' expr ')'
  | variable
  | literal

operation:
    '!' expr
  | '-' expr
  | '+' expr
  | expr '+' expr
  | expr '-' expr
  | expr '*' expr
  | expr '/' expr
  | expr '%' expr
  | expr '&&' expr
  | expr '||' expr
  | expr '<=' expr
  | expr '<' expr
  | expr '>=' expr
  | expr '>' expr
  | expr '!=' expr
  | expr '==' expr
  | expr '?' expr ':' expr

invocation:
    expr '.' NAME args

args:
    '(' ')'
  | '(' array ')'
  ;

member_access:
    expr member
  ;

member:
    '.' NAME
  | '[' expr ']'

variable:
    NAME
  ;

literal:
    STRING
  | NUMBER
  | TRUE
  | FALSE
  | NULL
  | object_literal
  | array_literal

array_literal:
    '[' ']'
  | '[' array ']'

array:
    expr
  | array ',' expr

object_literal:
    '{' '}'
  | '{' object '}'

object:
    key_value
  | object ',' key_value

key_value:
  expr ':' expr
Need more help?

You've read this document a dozen times but it doesn't really cover all of your questions? Maybe other people felt the same: reach out to them on Stack Overflow.

Go to Stack Overflow
Found a bug or missing a feature?

The AMP project strongly encourages your participation and contributions! We hope you'll become an ongoing participant in our open source community but we also welcome one-off contributions for the issues you're particularly passionate about.

Go to GitHub