AMP

amp-bind

通过数据绑定和表达式添加自定义互动方式。

必需的脚本
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
示例
教程 制作交互式 AMP 网页

概述

借助 amp-bind 组件,您可以通过数据绑定以及类似于 JS 的表达式,为 AMP 网页添加自定义的有状态互动方式。

观看此视频,简要了解 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 进行初始呈现。

运作方式

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> <!-- 也可以使用 [class] 添加或移除 CSS 类。 --> <p class="greenBackground" [class]="myAnimals[currentAnimal].style"> Each animal has a different background color. </p> <!-- 或通过 [src] 绑定更改图片的 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. 系统会根据定义为 'cat'currentAnimal状态进行更新。
  2. 系统会对依赖于 currentAnimal表达式进行求值:

    • 'This is a ' + currentAnimal + '.' => 'This is a cat.'
    • myAnimals[currentAnimal].style => 'redBackground'
    • myAnimals[currentAnimal].imageUrl => /img/cat.jpg
  3. 系统会对依赖于更改后的表达式的绑定进行更新:

    • 第一个 <p> 元素的文本将显示为“This is a cat.”
    • 第二个 <p> 元素的 class 属性将为“redBackground”。
    • amp-img 元素将显示一只猫的图片。

如需查看此示例的带代码注释版本,请观看在线演示

详细说明

状态

每个使用 amp-bind 的 AMP 文档都包含涵盖整个文档的可变 JSON 数据(即状态)。

通过 amp-state 对状态进行初始化

可通过 amp-state 组件对 amp-bind 的状态进行初始化:

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

表达式可通过点语法引用状态变量。在此示例中,myState.foo 的求解结果为 "bar"

  • <amp-state> 元素的子级 JSON 不能超过 100KB。
  • <amp-state> 元素还可以指定 CORS 网址,而不是子级 JSON 脚本。有关详情,请参阅附录

刷新状态

此组件支持 refresh 操作,该操作可用于刷新状态内容。

<amp-state id="amp-state" ...></amp-state> <!-- 点击该按钮将刷新并重新获取 amp-state 中的 json。 --> <button on="tap:amp-state.refresh"></button> 

通过 AMP.setState() 更新状态

AMP.setState() 操作可将对象字面量合并到状态中。例如,当用户按下方的按钮后,AMP.setState() 会将对象字面量与状态进行深度合并

<!-- 与 JavaScript 类似,您可以在  对象字面量的值中引用现有变量。 --> <button on="tap:AMP.setState({foo: 'bar', baz: myAmpState.someVariable})"></button> 

一般来说,嵌套对象的合并深度上限为 10。所有变量(包括由 amp-state 引入的变量)都可以被覆盖。

被特定事件触发后,AMP.setState() 还可以访问 event 属性的事件相关数据。

<!-- 此 <input> 元素的“change”事件包含  可通过“event.value”引用的“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 的区别

  • 表达式只能访问所在文档的状态
  • 表达式无权访问 windowdocument 等全局属性。
  • 只能使用列入白名单的函数和运算符。
  • 一般不允许使用自定义函数、类和循环。允许将箭头函数用作参数,如 Array.prototype.map
  • 未定义的变量和 array-index-out-of-bound 会返回 null,而不是 undefined,也不会引发错误。
  • 为了确保性能,单个表达式中目前最多可以使用 50 个操作数。如果这无法满足您的使用需求,请与我们联系

如需查看完整的表达式语法和实现,请参阅 bind-expr-impl.jisonbind-expression.js

示例

以下都是有效的表达式:

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

列入白名单的函数

对象类型 函数 示例
Array1 concat
filter
includes
indexOf
join
lastIndexOf
map
reduce
slice
some
sort (not-in-place)
splice (not-in-place)
// 返回 [1, 2, 3]。 [3, 2, 1].sort()
// 返回 [1, 3, 5]。 [1, 2, 3].map((x, i) => x + i)
// 返回 6。 [1, 2, 3].reduce((x, y) => x + y)
Number toExponential
toFixed
toPrecision
toString
// 返回 3。 (3.14).toFixed()
// 返回“3.14”。 (3.14).toString()
String charAt
charCodeAt
concat
indexOf
lastIndexOf
slice
split
substr
substring
toLowerCase
toUpperCase
// 返回“abcdef”。 abc'.concat('def')
Math2 abs
ceil
floor
max
min
random
round
sign
// 返回 1。 abs(-1)
Object2 keys
values
// 返回 ['a', 'b']。 keys({a: 1, b: 2})
// 返回 [1, 2]。 values({a: 1, b: 2}
Global2 encodeURI
encodeURIComponent
// 返回 '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-macro 重复使用 amp-bind 表达式片段。借助 amp-bind-macro 元素,您可以定义一个采用零个或多个参数并引用当前状态的表达式。您可以像调用函数一样调用宏,只需从文档中的任意位置引用宏的 id 属性值即可。

<amp-bind-macro id="circleArea" arguments="radius" expression="3.14 * radius * radius"></amp-bind-macro> <div> The circle has an area of <span [text]="circleArea(myCircle.radius)">0</span>. </div> 

宏还可以调用在其之前定义的其他宏,但无法以递归方式调用自身。

绑定

绑定 是一种采用 [property] 形式的特殊属性,用于将元素的属性关联到表达式。您还可以通过 data-amp-bind-property 形式使用另一种与 XML 兼容的语法。

状态发生变化时,系统会对表达式重新求值,并根据新的表达式结果更新绑定元素的属性。

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]
在更新后的网址上显示文档。
更改文档的标题。
<amp-iframe> [src] 更改 iframe 的来源网址。
<amp-img> [alt]
[attribution]
[src]
[srcset]
绑定到 [src] 时,请务必同时绑定到 [srcset],以便绑定在缓存中正常发挥作用。
请参阅相应的 amp-img 属性
<amp-lightbox> [open]* 切换灯箱的显示。提示:在灯箱关闭时,使用 on="lightboxClose: AMP.setState(...)" 更新变量。
<amp-list> [src] 如果表达式为字符串,则从字符串网址获取并呈现 JSON。 如果表达式为对象或数组,则呈现表达式数据。
<amp-selector> [selected]*
[disabled]
更改当前所选的子元素,
这些元素由其 option 属性值标识。支持多个选择项对应的值列表(以英文逗号分隔)。查看示例
<amp-state> [src] 从新网址获取 JSON,并将其合并到现有状态。请注意,以下更新将忽略 <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 属性
<sup>*</sup>表示可绑定的属性,而且没有不可绑定的对应项。 

调试

在开发模式下进行测试(使用网址片段 #development=1),以便在开发过程中发现警告和错误,并可以使用特殊的调试函数。

警告

在开发模式下,如果绑定属性的默认值与相应表达式的初始结果不一致,amp-bind 会发出警告。这有助于防止出现因其他状态变量发生变化而导致的意外变化。例如:

<!-- 该元素的默认类值 ('def') 与 [class] ('abc') 的表达式结果不一致,因此在开发模式下将发出警告。--> <p class="def" [class]="'abc'"></p> 

在开发模式下,当解除对未定义变量或属性的引用时,amp-bind 也会发出警告。这同样有助于防止出现因 null 表达式结果而导致的意外变化。例如:

<amp-state id="myAmpState"> <script type="application/json"> { "foo": 123 } </script> </amp-state></p> <!-- amp-state#myAmpState 没有 `bar` 变量,因此在开发模式下将发出警告。--> <p [text]="myAmpState.bar">Some placeholder text.</p> 

错误

使用 amp-bind 时,可能会遇到以下几种运行时错误。

类型 消息 建议
无效绑定 不允许绑定到 <P> 上的 [someBogusAttribute]。 仅使用列入白名单的绑定
语法错误 …中存在表达式编译错误 确认表达式是否存在拼写错误。
函数未列入白名单 alert 不是受支持的函数。 仅使用列入白名单的函数
结果已经过净化处理 对于 [href],“javascript:alert(1)”不是有效的结果。 避免使用加入黑名单的网址协议或表达式,否则会导致无法通过 AMP 验证工具的验证。
CSP 违规 被拒绝通过“blob:...”创建工作器,因为它违反了《内容安全政策》的以下指令… default-src blob: 添加到来源的《内容安全政策》。amp-bind 会将耗用资源较多的工作委派给专门的网络工作器,以确保实现良好的性能。

调试状态

利用 AMP.printState() 将当前状态输出到控制台。

附录

<amp-state> 规范

amp-state 元素可以包含子级 <script> 元素,也可以 包含 src 属性(其中包含指向远程 JSON 端点的 CORS 网址),但不能同时包含这两者。

<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 会对向 JSON 端点发出的 XMLHttpRequest (XHR) 进行批处理,也就是说,您可以在 AMP 网页上将单个 JSON 数据请求用作多个使用方(如多个 amp-state 元素)的数据源。例如,如果您的 amp-state 元素向某个端点发出 XHR,那么在该 XHR 传输期间,向同一端点发送的所有后续 XHR 都不会触发,系统将只返回第一个 XHR 的结果。

属性

src 远程端点的网址,该端点将返回 JSON,以便更新此 amp-state。这必须是 CORS HTTP 服务。 src 属性支持所有标准网址变量替换。如需了解详情,请参阅替换指南
该端点必须符合 AMP 中的 CORS 请求规范中规定的要求。
credentials(可选) credentials 选项定义为通过 Fetch API 指定的值。
  • 支持的值:`omit`、`include`
  • 默认值:`omit`
要发送凭据,请传递 include 的值。如果此值已设置,响应必须遵循 AMP CORS 安全指南

通过 AMP.setState() 进行深度合并

调用 AMP.setState() 时,amp-bind 会将所提供的对象字面量与当前状态进行深度合并。除了以递归方式合并的嵌套对象之外,对象字面量的所有变量都会直接写入到状态。状态中的基元和数组始终会被对象字面量中的同名变量覆盖。

请参考以下示例:

{ <!-- 状态为空 --> } 
<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.nameemployee.vehicle 键未发生变化。

请注意,如果通过包含循环引用的对象字面量调用 AMP.setState()amp-bind 会抛出错误。

移除变量

AMP.setState() 中将现有状态变量的值设为 null 可移除该变量。从上一个示例中的状态开始,按下:

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

会将状态更改为:

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

同样,按下:

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

会将状态更改为:

{ <!-- 状态为空 --> } 

表达式语法

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 
需要更多帮助?

您已多次阅读本文档,但它仍未能涵盖您的所有问题?也许其他人也这么觉得:在 Stack Overflow 上与他们联系。

前往 Stack Overflow
发现错误或缺少功能?

AMP 项目强烈鼓励您参与并做出贡献!我们希望您能成为我们开放源代码社区的持续参与者,但我们也欢迎您对所热衷问题做出一次性贡献。

前往 GitHub