pushState 和 SEO

Posted

技术标签:

【中文标题】pushState 和 SEO【英文标题】:pushState and SEO 【发布时间】:2011-09-05 20:04:44 【问题描述】:

很多人一直在说,使用 pushState 而不是 hashbang。

我不明白的是,如果不使用 hashbang,你将如何对搜索引擎友好?

大概您的 pushState 内容是由客户端 javascript 代码生成的。

场景是这样的:

我在example.com。我的用户点击了一个链接:href="example.com/blog"

pushState 捕获点击,更新 URL,从某处获取 JSON 文件,并在内容区域创建博客文章列表。

通过 hashbangs,谷歌知道去 escaped_fragment URL 来获取他们的静态内容。

使用 pushState,Google 什么也看不到,因为它无法使用 JavaScript 代码加载 JSON 并随后创建模板。

我能看到的唯一方法是在服务器端渲染模板,但这完全抵消了将应用层推送到客户端的好处。

那么我说得对吗,pushState 对客户端应用程序根本不友好?

【问题讨论】:

未来读者请注意:此问题已过时。 Read the official Google statement - 简而言之,googlebot 现在支持 JS。 【参考方案1】:

对于那些不想在 URL 中出现井字形的人来说,使用 Google 建议的元标记怎么样: <meta name="fragment" content="!">

查看此处了解更多信息: https://developers.google.com/webmasters/ajax-crawling/docs/getting-started

不幸的是,我认为 Nicole 没有澄清我认为 OP 遇到的问题。问题很简单,如果我们不使用 hash-bang,我们就不知道我们向谁提供内容。 Pushstate 并没有为我们解决这个问题。我们不希望搜索引擎告诉最终用户导航到一些会输出未格式化 JSON 的 URL。相反,我们创建通过 AJAX 检索数据并以我们喜欢的方式将其呈现给用户的 URL(触发对更多 URL 的其他调用)。如果用户不是人类,那么作为替代方案,我们可以提供一个 html 快照,以便搜索引擎可以正确地将人类用户引导到他们希望在该 URL 上找到请求的数据(并且以一种可呈现的方式)。但最终的挑战是我们如何确定用户类型?是的,我们可以使用 .htaccess 或其他东西来重写我们检测到的搜索引擎机器人的 URL,但我不确定这是多么全面和面向未来。谷歌也有可能会惩罚做这种事情的人,但我还没有完全研究过。所以(pushstate + google's meta tag)组合似乎是一个可能的解决方案。

【讨论】:

@NickC,好的,我明白了,所以现在我认为更好的解决方案是在没有任何 JS 的情况下最初显示内容。但是在您的 JS 顶部(在页面加载并准备好 dom 之后)立即运行一些代码来隐藏最初显示的 HTML 内容或用 JS 增强功能替换它。例如,我使用jquery datagrids,所以我会先显示一个HTML表格,然后立即加载JS,将显示的普通表格数据转换/隐藏/替换为JS网格版本。然后从那时起,任何其他 ajax 请求都可以作为 JSON 与通过 pushstate 更新的 URL 配对提供。 您对建议的解决方案有何体验?谷歌是否索引了这个“临时”HTML?它是否正确显示在相关的谷歌搜索中?也不意味着体验有点“紧张”,因为初始的 HTML 页面是用 JS 生成的 html“刷新”的? @NileshKale 这是我制定的解决方案,它很好地完成了这项工作:***.com/questions/22824991/…。我只是传递了一个 HTML 表以及带有 JSON 等效项的 jqgrid(与 HTML 中的内容相同)。 SEO 读取 HTML,用户通过 ajax 获得升级的体验和所有后续请求。使用 pushstate,我可以根据用户对网格的排序/分页方式更新 URL(不需要 hashbang)。这允许用户保存 URL 并返回相同的结果。 我会在几天后尝试对我的答案进行编辑以更好地解释。 AJAX 抓取方案现已弃用:developers.google.com/webmasters/ajax-crawling/docs/…。建议更换使用它的网站:plus.google.com/+JohnMueller/posts/LT4fU7kFB8W【参考方案2】:

所有关于 pushState 和 #! 的有趣讨论,我仍然看不到 pushState 如何取代原始发帖人所要求的 #! 的目的。

我们使 99% 基于 JavaScript 的 Ajax 站点/应用程序可 SEO 的解决方案当然是使用 #!。由于客户端渲染是通过 HTML、JavaScript 和 php 完成的,我们在由我们的页面登陆控制的加载器中使用以下逻辑。 HTML 文件与 JavaScript 和 PHP 完全分离,因为我们希望两者都使用相同的 HTML(大部分情况下)。 JavaScript 和 PHP 做的事情基本相同,但 PHP 代码不那么复杂,因为 JavaScript 提供了更丰富的用户体验。

JavaScript 使用 jQuery 将它想要的内容注入 HTML。 PHP 使用 PHPQuery 将它想要的内容注入 HTML - 使用“几乎”相同的逻辑,但更简单,因为 PHP 版本将仅用于显示带有 SEOable 链接的 SEOable 版本,而不像 JavaScript 版本那样进行交互。

都是构成页面的三个组件,page.htm、page.js 和 page.php 存在于任何使用转义片段来知道是否加载 PHP 版本来代替 JavaScript 版本的东西。对于非 SEOable 内容(例如只有在用户登录后才能看到的页面),不需要存在 PHP 版本。一切都很简单。

我仍然对一些前端开发人员如何在不使用服务器端技术和浏览器技术的情况下开发出色的网站(利用 Google Docs 的丰富性)感到困惑......如果甚至没有启用 JavaScript,那么我们的 99 % JavaScript 解决方案当然不会在没有 PHP 的情况下做任何事情。

如果启用了 JavaScript,则可以有一个不错的 URL 登陆 PHP 提供的页面并重定向到 JavaScript 版本,但从用户的角度来看这并不好,因为用户是更重要的受众。

附带说明。如果您只是制作一个无需任何 JavaScript 即可运行的简单网站,那么如果您想逐步将用户体验从简单的静态渲染内容提升为更好的内容,那么我可以看到 pushState 很有用,但是如果您想为用户提供获得最佳体验...假设您用 JavaScript 或 Google Docs 之类的东西编写的最新游戏,那么它用于此解决方案的方式有些限制,因为与愿景相比,优雅地回退只能走这么远,用户体验会很痛苦网站。

【讨论】:

【参考方案3】:

如果您需要搜索引擎来阅读您的内容,pushState 是否不好?

不,关于pushState 的讨论旨在完成与 hashbangs 相同的一般过程,但 URL 更好看。想想当你使用 hashbang 时会发生什么……

你说:

借助 hashbang,Google 知道要转到 escaped_fragment URL 以获取其静态内容。

也就是说,

    Google 看到了指向 example.com/#!/blog 的链接 谷歌请求example.com/?_escaped_fragment_=/blogreturn a snapshot of the content the user should see

如您所见,它已经依赖于服务器。 如果您没有提供来自服务器的内容快照,则说明您的网站没有被正确编入索引。

那么 Google 将如何看待 pushState 的内容?

使用 pushState,google 什么也看不到,因为它无法使用 javascript 加载 json 并随后创建模板。

实际上,Google 会在site.com/blog 看到它可以请求的任何内容。 URL 仍然指向服务器上的资源,客户端仍然遵守这个约定。当然,对于现代客户来说,Javascript 为检索内容和与内容交互提供了新的可能性,而无需 页面 刷新,但合同是相同的。

所以pushState 的预期优雅之处在于它为所有用户提供相同的内容,新老用户,支持 JS 的用户和不支持 JS 的用户,但新用户 get an enhanced experience。

如何让 Google 看到您的内容?

    Facebook 方法 — 在 URL site.com/blog 上提供相同的内容,当您将 /blog 推送到状态时,您的客户端应用程序将转换为该内容。 (据我所知,Facebook 尚未使用 pushState,但他们使用 hashbangs 来做到这一点)

    Twitter 方法 - 将所有传入 URL 重定向到等效的 hashbang。换句话说,指向“/b​​log”的链接将/blog 推送到状态。但如果直接请求,浏览器会以#!/blog 结束。 (对于 Googlebot,这将根据需要路由到 _escaped_fragment_。对于其他客户端,您可以将 pushState 返回到漂亮的 URL)。

那么您是否会因pushState 而失去_escaped_fragment_ 功能?

在几个不同的 cmets 中,你说

转义片段完全不同。您可以提供纯非主题内容、缓存内容,而不会像普通页面那样承受负载。

理想的解决方案是 Google 要么创建 JavaScript 网站,要么实施某种方式来了解即使对于 pushstate 网站(robots.txt?)也存在转义片段 URL。

您提到的好处并不局限于_escaped_fragment_。它为您进行重写并使用特别命名的GET 参数实际上是一个实现细节。没有什么特别之处是您无法使用标准 URL 实现的 - 换句话说,您可以使用 mod_rewrite 或您的服务器的等效项自行将 /blog 重写为 /?content=/blog

如果您根本不提供服务器端内容怎么办?

如果您无法重写 URL 并在/blog(或您推送到浏览器中的任何状态)提供某种内容,那么您的服务器实际上不再遵守 HTTP 合同.

这很重要,因为页面重新加载(无论出于何种原因)都会在此 URL 处提取内容。 (请参阅https://wiki.mozilla.org/Firefox_3.6/PushState_Security_Review —“如果推送了新 URI,则查看源和重新加载都将获取新 URI 的内容。”)

在客户端绘制用户界面并通过 JS API 加载内容并不是一个糟糕的目标,只是它没有真正考虑到 HTTP 和 URL,而且它基本上不向后兼容。

目前,这正是 hashbang 的用途——表示在客户端而非服务器上导航的不同页面状态。例如,重新加载将加载 same 资源,然后该资源可以读取、解析和处理散列值。

碰巧它们也被(特别是 Facebook 和 Twitter)用于将历史记录更改为服务器端位置,而无需刷新页面。 正是在这些用例中,人们建议为 pushState 放弃 hashbang。

如果您在客户端呈现所有内容,您应该将pushState 视为更方便的历史 API 的一部分,而不是使用 hashbangs 的出路。

【讨论】:

@Harry - 你读过我剩下的答案了吗? URL 是 URL - 表示资源定位器。服务器是否认为内容存在于site.com/blog?如果没有,那么它对搜索引擎不存在。 pushState 的目的不是解决这个问题。是为了方便。 Hashbangs 也不能解决这个问题,_escaped_fragment_ 是一个复杂的解决方法,它仍然依赖于服务器拥有 JS 生成内容的 snapshot(如您所说,普通用户可以看到)。 pushState 实际上简化了这一切。 @Harry - 在 URL 被设计为服务客户端内容之前,它们仍然引用服务器上的资源,并且客户端会以这种方式对待它们,包括机器人。这并不意味着您在客户端上尽可能多地做的目标是无效的,但目前可能必须使用(丑陋的)hashbangs 来完成。我已经为您的用例更新了答案。 @Harry 首先,我只是脱离了谷歌所说的他们为_escaped_fragment_ 所做的事情,我不知道你具体做什么。但是根据 Google 的说法,当您看到该查询参数时,我假设您必须通过服务器提供某种内容。在您的情况下,这需要一些技巧,但您可以提供一些 <noscript> 内容或来自 /blog 的其他内容,然后让 JS 构建您想要的页面。或者,您可以尝试检测机器人并故意提供完全不同的内容。 再一次,正确和最佳的答案没有被选为正确...不好,不好。 如果我有一个类似:<a href="product/productName" onclick="showProduct(product)">A product</a> 的链接,并且 onclick 以“preventDefault()”开头,那么 AJAXly 会将有关产品的新内容加载到页面中,并确保该链接“.../product/productName” 将加载特定产品内容将包含在服务器响应中的页面版本 --- 因此,该站点仍将动态工作,但仍将提供静态内容直接去产品链接对吗?这种方式不需要pushState或hashbang,不是吗?

以上是关于pushState 和 SEO的主要内容,如果未能解决你的问题,请参考以下文章

前端路由的实现 —— History的pushState和replaceState用法

Internet Explorer 是不是支持 pushState 和 replaceState?

httpd.conf 和 HTML5 pushstate()

通过JS拦截 pushState 和 replaceState 事件

基于H5 pushState和Ajax完美实现页面局部刷新

使用ajax和history.pushState无刷新改变页面URL