amp-bind
Description
利用数据绑定和类似于 JS 的简单表达式,使元素因应用户操作或数据变更而变化。
Required Scripts
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
示例
通过数据绑定和表达式添加自定义互动方式。
必需的脚本 | <script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script> |
示例 | |
教程 | 制作交互式 AMP 网页 |
概述
借助 amp-bind
组件,您可以通过数据绑定以及类似于 JS 的表达式,为 AMP 网页添加自定义的有状态互动方式。
一个简单示例
在下面的示例中,点按相应按钮可将 <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
包含三个主要组件:
- 状态:涵盖整个文档的可变 JSON 状态。在上面的示例中,在点按相应按钮之前,状态为空。在点按相应按钮之后,状态为
{foo: 'amp-bind'}
。 - 表达式:类似于 JavaScript 的表达式,可引用状态。上面的示例中包含单个表达式,即
'Hello ' + foo
,该表达式用于将字符串字面量'Hello '
和状态变量foo
连接在一起。一个表达式中最多可以使用 100 个操作数。 - 绑定:一些采用
[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>
按下相应按钮后:
- 系统会根据定义为
'cat'
的currentAnimal
对状态进行更新。 -
系统会对依赖于
currentAnimal
的表达式进行求值:'This is a ' + currentAnimal + '.'
=>'This is a cat.'
myAnimals[currentAnimal].style
=>'redBackground'
myAnimals[currentAnimal].imageUrl
=>/img/cat.jpg
-
系统会对依赖于更改后的表达式的绑定进行更新:
- 第一个
<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 的区别
- 表达式只能访问所在文档的状态。
- 表达式无权访问
window
或document
等全局属性。 - 只能使用列入白名单的函数和运算符。
- 一般不允许使用自定义函数、类和循环。允许将箭头函数用作参数,如
Array.prototype.map
。 - 未定义的变量和 array-index-out-of-bound 会返回
null
,而不是undefined
,也不会引发错误。 - 为了确保性能,单个表达式中目前最多可以使用 50 个操作数。如果这无法满足您的使用需求,请与我们联系。
如需查看完整的表达式语法和实现,请参阅 bind-expr-impl.jison 和 bind-expression.js。
示例
以下都是有效的表达式:
1 + '1' // 11
1 + (+'1') // 2
!0 // true
null || 'default' // 'default'
列入白名单的函数
对象类型 | 函数 | 示例 |
---|---|---|
Array 1 | 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') |
Math 2 | abs ceil floor max min random round sign | // 返回 1。 abs(-1) |
Object 2 | keys values | // 返回 ['a', 'b']。 keys({a: 1, b: 2}) // 返回 [1, 2]。 values({a: 1, b: 2} |
Global 2 | 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 指定的值。
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.name
和 employee.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