无需登录 数据私有 本地保存

AMP HTML 校验器 - 实时检查 AMP 规范

28
0
0
0

AMP HTML 校验器

实时检查您的 AMP (Accelerated Mobile Pages) HTML 代码是否符合规范,快速定位问题并获取修复建议。

字符数: 0
校验结果
0 项检查

输入 AMP HTML 代码开始校验

支持实时检查和手动校验

AMP 常见问题与知识点

AMP (Accelerated Mobile Pages) 是 Google 主导的开源项目,旨在让移动网页加载速度更快。AMP 通过限制 HTML、CSS 和 JavaScript 的使用,并利用 AMP 缓存(AMP Cache)来预加载和优化页面。使用 AMP 可以显著提升页面加载速度、改善用户体验,并在 Google 搜索结果中获得更好的展示位置(如 Top Stories 轮播)。AMP 页面通常加载时间不到 1 秒。

AMP 页面必须满足以下核心要求:
  • 必须以 <!doctype html> 开头
  • <html> 标签必须包含 amp 属性
  • 必须包含 <meta charset="utf-8">
  • 必须包含 viewport meta 标签
  • 必须加载 AMP 核心脚本 https://cdn.ampproject.org/v0.js
  • 必须包含 <link rel="canonical"> 指向标准页面
  • 必须有 <style amp-boilerplate> 防闪烁样式
  • 禁止使用外部样式表,CSS 必须内联且不超过 50KB
  • 禁止自定义 JavaScript
  • 图片/视频等必须使用 AMP 专用组件

在 AMP 中,普通的 <img> 标签被禁止使用,必须替换为 <amp-img> 组件。AMP 需要提前知道图片的尺寸来进行布局计算,因此 <amp-img> 必须明确指定 widthheight 属性,并使用 layout="responsive" 实现响应式。类似地,<video> 需替换为 <amp-video><iframe> 需替换为 <amp-iframe>

AMP 只允许使用 内联样式,通过 <style amp-custom> 标签定义(一个页面只能有一个)。CSS 总大小不能超过 50KB(约 51,200 字节)。不允许使用外部样式表链接,也不允许在元素上使用 style 属性(内联 style)。如果需要使用 CSS 预处理器,需要在构建时将 CSS 内联到页面中。推荐使用关键 CSS 策略,只包含必要的样式。

AMP 严格禁止自定义 JavaScript。只有 AMP 框架的核心脚本和官方扩展组件脚本(如 amp-analyticsamp-formamp-carousel 等)被允许。配置数据可以通过 <script type="application/json"> 标签传递。结构化数据使用 <script type="application/ld+json">。这种限制确保了页面的安全性和高性能。

amp-boilerplate 是 AMP 必需的 CSS 代码片段,用于在页面加载时隐藏内容直到 AMP 框架完全加载,防止页面出现闪烁(FOUC)。它包括两部分:<style amp-boilerplate>(在 head 中)包含动画关键帧,以及 <noscript><style amp-boilerplate>(在 body 中)用于禁用 JavaScript 时的回退样式。这些代码必须完整且不能修改。

验证 AMP 页面有多种方式:
  • 在浏览器中打开 AMP 页面,在 URL 后添加 #development=1,打开浏览器控制台查看 AMP 验证信息
  • 使用 官方 AMP 验证器
  • 使用 Chrome 浏览器的 AMP Validator 扩展
  • 使用本工具进行快速本地校验
  • 在 CI/CD 流程中集成 AMP 验证

虽然 AMP 本身不是直接的排名因素,但它通过提升页面速度改善用户体验间接影响 SEO。AMP 页面在移动搜索结果中可能会显示闪电标志 ⚡,并有机会出现在 Google Top Stories 等特色位置。更快的加载速度降低了跳出率,提高了用户参与度,这些都是 Google 排名算法中的重要信号。此外,AMP 页面的规范链接有助于避免重复内容问题。
\n 有效的 AMP 页面\n \n \n \n\n\n
\n

Hello AMP World!

\n \n

这是一个有效的 AMP 页面。

\n
\n\n'; var invalidExample = '\n\n\n \n \n \n 无效的 AMP 页面\n\n\n 照片\n \n
内联样式
\n \n\n'; var minimalExample = '\n\n\n \n \n \n \n \n \n 最小 AMP 页面\n\n\n

Hello AMP!

\n\n'; // 核心校验函数 function validateAMP(code) { var results = []; var errors = 0; var warnings = 0; var passes = 0; if (!code || code.trim() === '') { return { results: [{ type: 'info', message: '请输入 AMP HTML 代码以开始校验。', label: '提示' }], errors: 0, warnings: 0, passes: 0, total: 1 }; } var codeLower = code.toLowerCase(); var hasHtmlTag = /]/i.test(code); var hasHeadTag = /]/i.test(code); var hasBodyTag = /]/i.test(code); // 1. DOCTYPE 检查 if (/^\s*<!doctype html> 开头。', label: '通过' }); passes++; } else if (/<!doctype html>。', label: '警告' }); warnings++; } else { results.push({ type: 'error', message: '缺少 DOCTYPE 声明。AMP 页面必须以 <!doctype html> 开头。', label: '错误' }); errors++; } // 2. HTML 标签 amp/⚡ 属性检查 if (hasHtmlTag) { var htmlOpenTag = code.match(/]*>/i); if (htmlOpenTag) { var htmlTagStr = htmlOpenTag[0].toLowerCase(); if (htmlTagStr.indexOf('⚡') !== -1 || /\bamp\b/.test(htmlTagStr)) { results.push({ type: 'pass', message: '<html> 标签包含必需的 amp 属性。', label: '通过' }); passes++; } else { results.push({ type: 'error', message: '<html> 标签必须包含 amp 属性来标识 AMP 页面。', label: '错误' }); errors++; } } } else { results.push({ type: 'error', message: '未找到 <html> 标签。AMP 页面必须包含完整的 HTML 结构。', label: '错误' }); errors++; } // 3. Meta charset 检查 if (/]*charset\s*=\s*["']?utf-8["']?/i.test(code)) { results.push({ type: 'pass', message: '<meta charset="utf-8"> 已正确声明。', label: '通过' }); passes++; } else if (/]*charset/i.test(code)) { results.push({ type: 'warning', message: '找到了 charset meta 标签,但值可能不是 utf-8。AMP 要求使用 UTF-8 编码。', label: '警告' }); warnings++; } else { results.push({ type: 'error', message: '缺少 <meta charset="utf-8"> 标签。这是 AMP 的强制要求。', label: '错误' }); errors++; } // 4. Viewport meta 检查 if (/]*name\s*=\s*["']viewport["'][^>]*>/i.test(code)) { var viewportMatch = code.match(/]*name\s*=\s*["']viewport["'][^>]*>/i); if (viewportMatch && /width\s*=\s*device-width.*initial-scale\s*=\s*1/i.test(viewportMatch[0])) { results.push({ type: 'pass', message: 'Viewport meta 标签正确,包含 width=device-width, initial-scale=1。', label: '通过' }); passes++; } else { results.push({ type: 'warning', message: 'Viewport meta 标签存在但内容可能不完整。建议使用 width=device-width, initial-scale=1。', label: '警告' }); warnings++; } } else { results.push({ type: 'error', message: '缺少 viewport meta 标签。AMP 要求 <meta name="viewport" content="width=device-width, initial-scale=1">。', label: '错误' }); errors++; } // 5. AMP 核心脚本检查 if (code.indexOf(ampScriptSrc) !== -1) { if (/
]*async[^>]*src\s*=\s*["']https:\/\/cdn\.ampproject\.org\/v0\.js["'][^>]*>/i.test(code)) { results.push({ type: 'pass', message: 'AMP 核心脚本正确加载,包含 async 属性。', label: '通过' }); passes++; } else { results.push({ type: 'warning', message: 'AMP 核心脚本存在但可能缺少 async 属性。', label: '警告' }); warnings++; } } else { results.push({ type: 'error', message: '缺少 AMP 核心脚本 <script async src="https://cdn.ampproject.org/v0.js"></script>。', label: '错误' }); errors++; } // 6. Canonical link 检查 if (/]*rel\s*=\s*["']canonical["'][^>]*>/i.test(code)) { var canonMatch = code.match(/]*rel\s*=\s*["']canonical["'][^>]*>/i); if (canonMatch && /href\s*=\s*["']https?:\/\//i.test(canonMatch[0])) { results.push({ type: 'pass', message: 'Canonical 链接已正确设置,指向有效的 URL。', label: '通过' }); passes++; } else if (canonMatch && /href/i.test(canonMatch[0])) { results.push({ type: 'warning', message: 'Canonical 链接存在但 href 可能不是完整的 URL。建议使用绝对 URL。', label: '警告' }); warnings++; } else { results.push({ type: 'warning', message: 'Canonical 链接存在但缺少有效的 href 属性。', label: '警告' }); warnings++; } } else { results.push({ type: 'error', message: '缺少 <link rel="canonical"> 标签。每个 AMP 页面必须指向其对应的标准页面。', label: '错误' }); errors++; } // 7. amp-boilerplate 检查 var hasBoilerplateStyle = /]*amp-boilerplate[^>]*>/i.test(code); var hasNoscriptBoilerplate = /]*>[\s\S]*?]*amp-boilerplate[^>]*>/i.test(code); if (hasBoilerplateStyle && hasNoscriptBoilerplate) { var bpMatch = code.match(/]*amp-boilerplate[^>]*>([\s\S]*?)<\/style>/i); var bpContent = bpMatch ? bpMatch[1] : ''; var hasKeywords = boilerplateKeywords.every(function(kw) { return bpContent.toLowerCase().indexOf(kw.toLowerCase()) !== -1; }); if (hasKeywords) { results.push({ type: 'pass', message: '<style amp-boilerplate> 内容完整,包含必需的防闪烁样式。', label: '通过' }); passes++; } else { results.push({ type: 'warning', message: '<style amp-boilerplate> 存在但内容可能不完整。请确保使用官方提供的完整代码。', label: '警告' }); warnings++; } if (hasNoscriptBoilerplate) { results.push({ type: 'pass', message: '<noscript> 中的 amp-boilerplate 回退样式已正确设置。', label: '通过' }); passes++; } } else { if (!hasBoilerplateStyle) { results.push({ type: 'error', message: '缺少 <style amp-boilerplate>。AMP 需要此样式来防止页面内容闪烁(FOUC)。', label: '错误' }); errors++; } if (!hasNoscriptBoilerplate) { results.push({ type: 'error', message: '缺少 <noscript><style amp-boilerplate>。这是禁用 JavaScript 时的回退样式。', label: '错误' }); errors++; } } // 8. 禁止的标签检查 var forbiddenTags = [ { pattern: /]*>/gi, replacement: '', name: '' }, { pattern: /]*>/gi, replacement: '', name: '