amp-script
简介
amp-script
组件允许您运行自定义 JavaScript。您的代码在 Web Worker 中运行,并有一些限制。
设置
首先,您需要导入 amp-script
扩展。
<script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>
对于内联脚本,您需要生成脚本哈希。在开发期间,使用 data-ampdevmode
属性禁用此要求。访问文档了解更多信息。
<meta name="amp-script-src" content="sha384-iER2Cy-P1498h1B-1f3ngpVEa9NG1xIxKqg0rNkRX0e7p5s0GYdit1MRKsELIQe8 sha384-UPY0FmlOzIjSqWqMgbuaEbqIdvpGY_FzCuTAyoLdrFJb2NYf8cPWJlugA0rUbXjL
从 URL 加载脚本
要从 URL 加载您的脚本,请使用 src
属性。此示例加载并运行名为 hello.js
的脚本。有效的 AMP 要求所有 URL 都是绝对的,并使用 https
。
这是 hello-world.js
中的脚本
const button = document.getElementById('hello-url'); button.addEventListener('click', () => { const h1 = document.createElement('h1'); h1.textContent = 'Hello World!'; document.body.appendChild(h1); });
这是 HTML
<amp-script layout="container" src="https://amp.org.cn/documentation/examples/components/amp-script/hello-world.js" class="sample">
<button id="hello-url">Say hello!</button>
</amp-script>
使用内联脚本
您还可以内联包含脚本并通过 id
引用它。请注意,在脚本中,您需要设置 type=text/plain
和 target=amp-script
。
<amp-script layout="container" script="hello-world" class="sample">
<button id="hello-inline">Say hello!</button>
</amp-script>
<script id="hello-world" type="text/plain" target="amp-script">
const button = document.getElementById('hello-inline');
button.addEventListener('click', () => {
const h1 = document.createElement('h1');
h1.textContent = 'Hello World!';
document.body.appendChild(h1);
});
</script>
amp-script
将其子元素作为虚拟 DOM 传递给您的脚本 - 而不是整个 DOM。对于您的脚本,这些子元素就是 DOM。因此,document.body
指的是 amp-script
标签内的内容,而不是实际的 body
。 document.body.appendChild(...)
实际上是在 amp-script
元素内添加一个元素。使用 fetch API
amp-script
支持 fetch API。如果 amp-script
知道脚本无法更改组件的高度,它允许我们在加载时更新页面。在这里,我们使用 fixed-height
布局,并在 HTML 属性中指定 height
。有关详细信息,请参阅文档。
<amp-script layout="fixed-height" height="36" script="time-script" class="sample">
<div>
The time at page load was: <span id="time" class="answer-text"></span>
</div>
</amp-script>
<script id="time-script" type="text/plain" target="amp-script">
const fetchCurrentTime = async () => {
const response = await fetch('https://amp.org.cn/documentation/examples/api/time');
const data = await response.json();
const span = document.getElementById('time');
span.textContent = data.time;
}
fetchCurrentTime();
</script>
多次获取
在大小可以更改的容器中,您的代码可以在最终 fetch()
完成后的 5 秒内进行 DOM 更改。此示例对一个慢速 API 进行多次调用。它在每次调用返回时显示结果。
<amp-script layout="container" script="multi-fetch-script" class="sample">
<button id="multi-fetch">How slow is our API?</button>
</amp-script>
<script id="multi-fetch-script" type="text/plain" target="amp-script">
const randomTime = () => Math.floor(Math.random() * 10) + 5;
const button = document.getElementById('multi-fetch');
function tripleFetch() {
for (let i =0; i < 3; i++) {
fetch('https://amp.org.cn/documentation/examples/api/slow-text?delay=' + randomTime())
.then(response => response.text())
.then(insertText);
}
}
function insertText(text) {
const div = document.createElement('div');
div.textContent = text;
document.body.appendChild(div);
}
button.addEventListener('click', tripleFetch);
</script>
amp-list
的数据源
<amp-script>
函数可以用作 <amp-list>
的数据源。
-
使用
exportFunction()
使该函数对<amp-list>
可见。 -
在
<amp-list>
的src
属性中指定脚本和函数。使用src="amp-script:{scriptID}:functionName"
的形式,其中{scriptID}
是<amp-script>
的id
,而{functionName}
是导出的函数的名称。 -
您可以在
<amp-script>
中使用nodom
属性来指示<amp-script>
不需要 DOM。这可以提高性能,因为amp-script
不需要加载或执行其虚拟 DOM 实现。
<div class="sample">
<amp-script id="dataFunctions" script="amp-list-source-script" nodom></amp-script>
<script id="amp-list-source-script" type="text/plain" target="amp-script">
function fetchData() {
return fetch('https://amp.org.cn/static/samples/json/todo.json')
.then(response => response.json())
.then(transformData);
}
function transformData(json) {
let newEntries =
json.items.map(
entry => (entry.done ? 'Already done: ' : 'To do: ') + entry.title
);
return { items: newEntries };
}
exportFunction('fetchData', fetchData);
</script>
<amp-list width="auto" height="70" layout="fixed-height" src="amp-script:dataFunctions.fetchData">
<template type="amp-mustache">
<div>{{.}}</div>
</template>
</amp-list>
</div>
使用 WebSocket 进行实时更新
amp-script
支持 WebSockets。此示例模拟了一个实时博客。
<amp-script layout="fixed-height" height="200" script="live-blog-script" class="sample" sandbox="allow-forms">
<button id="live-blog-start">Start live blog</button>
<div id="live-blog-area"></div>
</amp-script>
<script id="live-blog-script" type="text/plain" target="amp-script">
const button = document.getElementById('live-blog-start');
const blogDiv = document.getElementById('live-blog-area');
button.addEventListener("click", () => {
button.setAttribute('disabled', '');
button.textContent = 'Live blog begun';
const socket = new WebSocket('wss://amp.org.cn/documentation/examples/api/socket/live-blog');
socket.onmessage = event => {
let newDiv = document.createElement('div');
let time = new Date().toLocaleTimeString();
newDiv.innerHTML = `<span class="time">${time}: </span><span>${event.data}</span>`;
blogDiv.appendChild(newDiv);
};
});
</script>
显示实时数据
您还可以使用 setInterval()
或 setTimeout
获取最新数据。
<amp-script layout="fixed-height" height="36" script="live-time-script" class="sample">
<div>
The current time is: <span id="live-time" class="answer-text"></span>
</div>
</amp-script>
<script id="live-time-script" type="text/plain" target="amp-script">
const span = document.getElementById('live-time');
const fetchCurrentTime = async () => {
const response = await fetch('https://amp.org.cn/documentation/examples/api/time');
const data = await response.json();
span.textContent = data.time;
}
setInterval(fetchCurrentTime, 1000);
</script>
自定义表单验证
您还可以使用 amp-script
实现自定义表单验证。当输入字段仅包含大写字母时,此脚本会启用按钮。
<amp-script layout="container" script="form-validation-script" sandbox="allow-forms" class="sample">
<input id="validated-input" placeholder="Only uppercase letters allowed...">
<button id="validated-input-submit" disabled>Submit</button>
</amp-script>
<script id="form-validation-script" type="text/plain" target="amp-script">
const submitButton = document.getElementById('validated-input-submit');
const validatedInput = document.getElementById('validated-input');
function allUpper() {
let isValid = /^[A-Z]+$/.test(validatedInput.value);
if (isValid) {
submitButton.removeAttribute('disabled');
} else {
submitButton.setAttribute('disabled', '');
}
}
validatedInput.addEventListener('input', allUpper);
</script>
检测操作系统
您的脚本可以访问全局对象,如 navigator
。此脚本使用此对象尝试猜测您设备的操作系统。
<amp-script layout="fixed-height" height="36" script="user-agent-script" class="sample">
<div>
Your operating system is:
<span id="operating-system" class="answer-text"></span>
</div>
</amp-script>
<script id="user-agent-script" type="text/plain" target="amp-script">
// Adapted with gratitude from https://stackoverflow.com/a/38241481
function getOS() {
const userAgent = navigator.userAgent,
platform = navigator.platform,
macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'],
windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
iosPlatforms = ['iPhone', 'iPad', 'iPod'];
if (macosPlatforms.includes(platform)) {
return 'Mac OS';
} else if (iosPlatforms.includes(platform)) {
return 'iOS';
} else if (windowsPlatforms.includes(platform)) {
return 'Windows';
} else if (/Android/.test(userAgent)) {
return 'Android';
} else if (/Linux/.test(platform)) {
return 'Linux';
}
return 'Unknown';
}
const span = document.getElementById('operating-system');
span.textContent = getOS();
</script>
个性化
同样,您可以使用 navigator
对象或其他方式来为您的用户个性化内容。以下脚本检测浏览器的语言并显示本地化的问候语。
<amp-script layout="fixed-height" height="40" script="translation-script" class="sample">
<h2 id="translated-greeting"></h2>
</amp-script>
<script id="translation-script" type="text/plain" target="amp-script">
const translationMap = {
'en': 'Hello',
'fr': 'Bonjour',
'es': 'Hola',
'hi': 'हैलो',
'zh': '你好',
'pr': 'Olá'
};
const lang = navigator.language.slice(0, 2);
let translation = translationMap[lang];
if (!translation) {
translation = "Couldn't recognize your language. So: Saluton";
}
let greeting = document.getElementById('translated-greeting');
greeting.innerHTML = translation + '!';
</script>
与 <amp-state>
交互
您的脚本可以使用状态变量和绑定来影响 <amp-script>
组件外部的区域。在这里,当单击按钮时,我们将状态变量的值设置为图像 URL。该状态变量绑定到 <amp-img>
的 src
属性。
<amp-state id="imgSrc">
<script type="application/json">
"product1_640x426.jpg"
</script>
</amp-state>
<amp-img layout="responsive" height="426" width="640" src="https://amp.org.cn/static/samples/img/product1_640x426.jpg" [src]="'https://amp.org.cn/static/samples/img/' + imgSrc"></amp-img>
<amp-script layout="container" script="state-script" class="sample">
<button id="apple-button" class="fruit-button">Apple</button>
<button id="orange-button" class="fruit-button">Orange</button>
</amp-script>
<script id="state-script" type="text/plain" target="amp-script">
const appleButton = document.getElementById('apple-button');
const orangeButton = document.getElementById('orange-button');
appleButton.addEventListener(
'click',
() => AMP.setState({imgSrc: 'product1_640x426.jpg'})
);
orangeButton.addEventListener(
'click',
() => AMP.setState({imgSrc: 'product2_640x426.jpg'})
);
</script>
与 AMP 组件交互
您的脚本可以使用状态变量和绑定来与 AMP 组件通信。将组件中的属性绑定到包含状态变量的表达式。当您的脚本修改该状态变量时,更改将传播到该组件。同样,如果 AMP 组件更改了状态变量的值,您的脚本可以获取新值。此脚本为一个按钮提供动力,该按钮以随机方向发送图像轮播。
幻灯片 1,共 3 张
<div id="carousel-sample">
<amp-carousel type="slides" layout="responsive" width="450" height="300" controls loop [slide]="slideIndex" on="slideChange: AMP.setState({slideIndex: event.index})">
<amp-img src="https://amp.org.cn/static/inline-examples/images/image1.jpg" layout="responsive" width="450" height="300"></amp-img>
<amp-img src="https://amp.org.cn/static/inline-examples/images/image2.jpg" layout="responsive" width="450" height="300"></amp-img>
<amp-img src="https://amp.org.cn/static/inline-examples/images/image3.jpg" layout="responsive" width="450" height="300"></amp-img>
</amp-carousel>
<p>Slide <span [text]="slideIndex + 1">1</span> of 3</p>
<amp-script layout="container" script="carousel-script" class="sample">
<button id="carousel-button" class="fruit-button">
Random direction
</button>
</amp-script>
<script id="carousel-script" type="text/plain" target="amp-script">
const button = document.getElementById('carousel-button');
async function randomSlideDirection() {
let oldSlide = Number(await AMP.getState('slideIndex'));
let addend = Math.ceil(Math.random() * 2);
let newSlide = (oldSlide + addend) % 3;
AMP.setState({slideIndex: newSlide});
}
button.addEventListener('click', randomSlideDirection);
</script>
</div>
本地存储
您的脚本可以访问 Web Storage API。这使您可以在浏览器会话之间持久保存用户信息。在这里,我们使用 localStorage
来跟踪用户名。如果您设置了用户名,然后重新加载此页面,则用户名将保持设置状态。
<amp-script layout="fixed-height" script="local-storage-script" height="110" class="sample">
<div>
<span>Current username: </span>
<b id="username-display"></b>
</div>
<div>
<label>Enter new username:</label>
<input id="username-input" type="text" maxlength="20">
</div>
<button id="username-submit">Submit</button>
</amp-script>
<script id="local-storage-script" type="text/plain" target="amp-script">
const submit = document.getElementById('username-submit');
const input = document.getElementById('username-input');
const display = document.getElementById('username-display');
const oldUsername = localStorage.getItem('username');
display.textContent = oldUsername ? oldUsername : '(not set)';
function setUsername() {
const newUsername = input.value;
localStorage.setItem('username', newUsername);
display.textContent = newUsername;
}
submit.addEventListener('click', setUsername);
</script>
如果此页面上的说明没有涵盖您的所有问题,请随时与其他 AMP 用户联系以讨论您的确切用例。
转到 Stack Overflow 未解释的功能?AMP 项目强烈鼓励您的参与和贡献!我们希望您成为我们开源社区的持续参与者,但我们也欢迎您为特别感兴趣的问题做出一次性贡献。
在 GitHub 上编辑示例