JavaScript 是网络平台的重要组成部分,因为它提供的很多功能可将网络转变成一个非常强大的应用平台。请设法让用户能够通过 Google 搜索轻松找到您的由 JavaScript 提供支持的网络应用,这样做有助于您在用户搜索您的网络应用所提供的内容时找到新用户并再度吸引现有用户。 虽然 Google 搜索使用常青 Chromium 来运行 JavaScript,但您可从几个方面进行优化。
本指南将介绍 Google 搜索对 JavaScript 的处理方式,以及与针对 Google 搜索改进 JavaScript 网络应用相关的最佳做法。
视频说明:
https://www.youtube.com/watch?v=nwGY-9lwTF4
Googlebot 对 JavaScript 的处理方式 #
Googlebot 对 JavaScript 网络应用的处理流程分为 3 大阶段:
- 抓取
- 呈现
- 编入索引
Googlebot 会抓取并呈现网页,然后将其编入索引。
当 Googlebot 尝试通过发出 HTTP 请求从抓取队列中抓取某个网址时,它首先会检查您是否允许抓取。Googlebot 会读取 robots.txt 文件。如果此文件将该网址标记为“disallowed”,Googlebot 就会跳过向该网址发出 HTTP 请求的操作,然后会跳过该网址。
接下来,Googlebot 会解析 HTML 链接的 href 属性中其他网址的响应,并将这些网址添加到抓取队列中。若不想让 Googlebot 发现链接,请使用 nofollow 机制。
抓取网址并解析 HTML 响应非常适用于经典网站或服务器端呈现的网页(在这些网站或网页中,HTTP 响应中的 HTML 包含所有内容)。某些 JavaScript 网站可能会使用应用 Shell 模型(在该模型中,初始 HTML 不包含实际内容,并且 Googlebot 需要执行 JavaScript,然后才能查看 JavaScript 生成的实际网页内容)。
Googlebot 会将所有网页都加入呈现队列,除非漫游器元标记或标头告知 Googlebot 不要将网页编入索引。网页在此队列中的存在时长可能会是几秒钟,但也可能会是更长时间。一旦 Googlebot 的资源允许,无头 Chromium 便会呈现相应网页并执行 JavaScript。Googlebot 会再次解析所呈现的链接 HTML,并将找到的网址加入抓取队列。Googlebot 还会使用所呈现的 HTML 将网页编入索引。
请注意,依然建议您采取服务器端呈现或预呈现,因为:(1) 它可让用户和抓取工具更快速地看到您的网站内容;(2) 并非所有漫游器都能运行 JavaScript。
用独特的标题和摘要描述网页 #
独特的描述性标题和有用的元描述可帮助用户快速找到符合其目标的最佳结果。我们的指南中解释了怎样才算是良好的标题和描述。
您可以使用 JavaScript 设置或更改元描述及标题。
编写兼容的代码 #
浏览器提供了很多 API,而 JavaScript 是一种快速演变的语言。Googlebot 对所支持的 API 和 JavaScript 功能有一些限制。若要确保您的代码与 Googlebot 兼容,请遵循我们的 JavaScript 问题排查指南。
使用有意义的 HTTP 状态代码 #
Googlebot 会通过 HTTP 状态代码确定抓取网页时是否出现了问题。
码表示必须登录才能访问网页)。您可以使用 HTTP 状态代码告知 Googlebot 某个网页是否已移至新网址,以便系统相应地更新索引。
下面列出了可用的 HTTP 状态代码及其适用情形:
HTTP 状态代码 | 适用情形 |
---|---|
301 / 302 | 网页已移至新网址。 |
401 / 403 | 因为权限不足,无法访问网页。 |
404 / 410 | 网页已不可用。 |
5xx | 服务器端出问题了。 |
避免单页应用中出现 404 错误 #
在客户端呈现的单页应用中,路由通常实现为客户端路由。 在这种情况下,使用有意义的 HTTP 状态代码可能是无法实现或不实际的。 为避免在使用客户端呈现和路由时出现软 404 错误,请采用以下策略之一:
- 使用 JavaScript 重定向至服务器响应 HTTP 404 状态代码(例如 /not-found)的网址。
- 使用 JavaScript 向错误页面添加 <meta name=”robots” content=”noindex”>。
以下是重定向方法的示例代码:
fetch(`/api/products/${productId}`) .then(response => response.json()) .then(product => { if(product.exists) { showProductDetails(product); // shows the product information on the page } else { // this product does not exist, so this is an error page. window.location.href = '/not-found'; // redirect to 404 page on the server. } })
以下是 noindex 方法的示例代码:
fetch(`/api/products/${productId}`) .then(response => response.json()) .then(product => { if(product.exists) { showProductDetails(product); // shows the product information on the page } else { // this product does not exist, so this is an error page. // Note: This example assumes there is no other meta robots tag present in the HTML. const metaRobots = document.createElement('meta'); metaRobots.name = 'robots'; metaRobots.content = 'noindex'; document.head.appendChild(metaRobots); } })
使用 History API 而非片段 #
Googlebot 在您的网页中查找链接时,只考虑 href HTML 链接属性中的网址。
对于实现客户端路由的单页应用,请使用 History API在 Web 应用的不同视图之间实现路由。为确保 Googlebot 能够找到链接,请避免使用片段加载不同的网页内容。 由于 Googlebot 不会抓取这些链接,因此以下示例是一种不好的做法:
<nav> <ul> <li><a href="#/products">Our products</a></li> <li><a href="#/services">Our services</a></li> </ul> </nav> <h1>Welcome to example.com!</h1> <div id="placeholder"> <p>Learn more about <a href="#/products">our products</a> and <a href="#/services">our services</p> </div> <script> window.addEventListener('hashchange', function goToPage() { // this function loads different content based on the current URL fragment const pageToLoad = window.location.hash.slice(1); // URL fragment document.getElementById('placeholder').innerHTML = load(pageToLoad); }); </script>
不过,您可以通过实现 History API,确保 Googlebot 可以访问链接网址:
<nav> <ul> <li><a href="/products">Our products</a></li> <li><a href="/services">Our services</a></li> </ul> </nav> <h1>Welcome to example.com!</h1> <div id="placeholder"> <p>Learn more about <a href="/products">our products</a> and <a href="/services">our services</p> </div> <script> function goToPage(event) { event.preventDefault(); // stop the browser from navigating to the destination URL. const hrefUrl = event.target.getAttribute('href'); const pageToLoad = hrefUrl.slice(1); // remove the leading slash document.getElementById('placeholder').innerHTML = load(pageToLoad); window.history.pushState({}, window.title, hrefUrl) // Update URL as well as browser history. } // Enable client-side routing for all links on the page document.querySelectorAll('a').forEach(link => link.addEventListener('click', goToPage)); </script>
慎重地使用漫游器元标记 #
您可以通过漫游器元标记阻止 Googlebot 将某个网页编入索引或跟踪链接。 例如,在网页顶部添加以下元标记可阻止 Googlebot 将该网页编入索引:
<!-- Googlebot won't index this page or follow links on this page --> <meta name="robots" content="noindex, nofollow">
您可以使用 JavaScript 将漫游器元标记添加到网页中或更改其内容。以下示例代码展示了如何使用 JavaScript 更改漫游器元标记以防止在 API 调用未返回内容时将当前网页编入索引。
fetch('/api/products/' + productId) .then(function (response) { return response.json(); }) .then(function (apiResponse) { if (apiResponse.isError) { // get the robots meta tag var metaRobots = document.querySelector('meta[name="robots"]'); // if there was no robots meta tag, add one if (!metaRobots) { metaRobots = document.createElement('meta'); metaRobots.setAttribute('name', 'robots'); document.head.appendChild(metaRobots); } // tell Googlebot to exclude this page from the index metaRobots.setAttribute('content', 'noindex'); // display an error message to the user errorMsg.textContent = 'This product is no longer available'; return; } // display product information // ... });
Googlebot 在运行 JavaScript 之前,如果在漫游器元标记中遇到 noindex,便不会呈现相应网页,也不会将其编入索引。
使用长效缓存 #
Googlebot 会主动缓存内容,以减少网络请求和资源使用量。WRS 可能会忽略缓存标头。这可能会导致 WRS 使用过时的 JavaScript 或 CSS 资源。为了避免这个问题,您可以创建内容指纹,使其成为文件名的一部分(如 main.2bb85551.js)。 指纹取决于文件的内容,因此更新每次都会生成不同的文件名。如需了解详情,请参阅 web.dev 长效缓存策略指南。
使用结构化数据 #
在您的网页上使用结构化数据时,您可以使用 JavaScript 生成所需的 JSON-LD 并将其注入网页中。请务必测试您的实现效果,避免出现问题。
遵循网络组件最佳做法 #
Googlebot 支持网络组件。当 Googlebot 呈现某个网页时,它会扁平化 shadow DOM 和 light DOM 内容。这意味着 Googlebot 只能看到在所呈现的 HTML 中可见的内容。要确保 Googlebot 在您的内容呈现后仍能看到该内容,请使用移动设备适合性测试或网址检查工具并查看所呈现的 HTML。
如果该内容在所呈现的 HTML 中不可见,Googlebot 将无法将其编入索引。
以下示例会创建一个网络组件,以便在其 shadow DOM 内显示 light DOM 内容。确保 light DOM 和 shadow DOM 内容均能显示在所呈现的 HTML 中的一种方法是使用 Slot 元素。
<script> class MyComponent extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); } connectedCallback() { let p = document.createElement('p'); p.innerHTML = 'Hello World, this is shadow DOM content. Here comes the light DOM: <slot></slot>'; this.shadowRoot.appendChild(p); } } window.customElements.define('my-component', MyComponent); </script> <my-component> <p>This is light DOM content. It's projected into the shadow DOM.</p> <p>WRS renders this content as well as the shadow DOM content.</p> </my-component>
呈现后,Googlebot 会将此内容编入索引:
<my-component> Hello World, this is shadow DOM content. Here comes the light DOM: <p>This is light DOM content. It's projected into the shadow DOM<p> <p>WRS renders this content as well as the shadow DOM content.</p> </my-component>
修正图片和延迟加载的内容 #
图片在带宽和性能方面的开销可能会非常高昂。一种可取的策略是:使用延迟加载方法,仅在用户即将看到图片时才加载图片。要确保以方便用户搜索的方式实现延迟加载,请遵循我们的延迟加载指南。