pushState and SEO

83

很多人都在说,要使用pushState而不是hashbang。

但我不明白的是,如果不使用hashbang,你怎么才能做到对搜索引擎友好呢?

推测你的pushState内容是由客户端JavaScript代码生成的。

场景如下:

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

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

使用hashbang时,谷歌知道要去escaped_fragment URL获取他们的静态内容。

使用pushState时,Google看不到任何东西,因为它无法使用JavaScript代码加载JSON,随后创建模板。

唯一的解决方法似乎是在服务器端呈现模板,但这完全抵消了将应用程序层推送到客户端的好处。

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


未来的读者请注意:这个问题已经过时阅读官方谷歌声明 - 简而言之,googlebot现在支持JS了。 - mik01aj
3个回答

98

如果需要搜索引擎读取您的内容,pushState是否不利?

不是,关于 pushState 的讨论旨在通过更好看的 URL 实现与 hashbangs 相同的一般过程。想想使用 hashbangs 时会发生什么...

你说:

使用 hashbangs 时,谷歌知道要去转义片段 URL 获取其静态内容。

换句话说,

  1. 谷歌看到链接 example.com/#!/blog
  2. 谷歌请求 example.com/?_escaped_fragment_=/blog
  3. 返回用户应该看到的内容的快照

正如你所看到的,它已经依赖于服务器。如果您未从服务器提供内容的快照,则您的网站无法得到正确索引。

那么 Google 如何使用 pushState 查看任何内容呢?

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

实际上,Google 将查看可以请求的 site.example/blog 上的任何内容。URL 仍然指向服务器上的资源,客户端仍然遵守此契约。当然,对于现代客户端,JavaScript 打开了在不刷新页面的情况下检索和与内容交互的新可能性,但契约是相同的。

因此,pushState 的预期优雅之处在于为所有用户提供相同的内容,包括旧用户和新用户、支持 JS 和不支持 JS 的用户,但新用户可以获得增强体验

如何让 Google 查看您的内容?

  1. Facebook的做法是,在URL site.example/blog 上提供与客户端应用程序推送/blog到状态时要转换为的相同内容。 (据我所知,Facebook还没有使用pushState,但他们在使用哈希标记实现这一点)

  2. Twitter的方法是将所有传入的URL重定向到哈希标记等效项。换句话说,指向“/blog”的链接会将/blog推送到状态中。但如果直接请求它,则浏览器最终会到达#!/blog。(对于Googlebot,这将路由到_escaped_fragment_,就像您想要的那样。对于其他客户端,您可以使用pushState返回到漂亮的URL)。

那么使用pushState会失去_escaped_fragment_功能吗?

在几个不同的评论中,您说

转义片段完全不同。您可以提供纯净的未主题化内容,缓存的内容,并且不会承受正常页面的负载。

理想的解决方案是谷歌要么使用JavaScript网站,要么实现某种方式来了解即使针对pushstate网站也有逃脱片段URL(robots.txt?)。

您提到的好处并不仅限于_escaped_fragment_。它可以为您进行重写并使用特殊命名的GET参数,这实际上只是一种实现细节。您可以使用标准URL完全做到这一点,换句话说,使用mod_rewrite或服务器的等效项将/blog重写为/?content=/blog

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

如果你无法重写URL并在/blog(或者你推入浏览器的任何状态)提供某种内容,那么你的服务器实际上已经不再遵守HTTP协议。这一点非常重要,因为任何原因导致页面重新加载都会拉取该URL下的内容。(参见https://wiki.mozilla.org/Firefox_3.6/PushState_Security_Review --“查看源代码和重新加载都会获取新URI下的内容。”)
并不是说客户端渲染用户界面,并通过JS API加载内容是一个坏的目标,只是这在HTTP和URL中没有被充分考虑,而且基本上不具备向后兼容性。
目前,哈希路由的确就是用于表示在客户端导航而不是在服务器上导航的不同页面状态。例如重新加载将会加载相同的资源,然后读取、解析和处理散列值。
只是恰巧它们已经被使用过(尤其是被Facebook和Twitter使用)来将历史记录更改为服务器端位置而不需要刷新页面。正是在这些用例中,人们建议放弃使用哈希路由,转而使用pushState。
如果你在客户端渲染所有内容,那么你应该把pushState视为更便捷的历史记录API的一部分,而不是使用哈希路由的替代方法。

3
你看完我的回答了吗?URL就是URL,意思是资源定位器。服务器是否认为内容存在于site.com/blog?如果没有,那么对搜索引擎来说就不存在。pushState的目的不是为了解决这个问题,而是为了方便。Hashbang也无法解决这个问题,_escaped_fragment_是一个复杂的解决方案,仍然依赖于服务器具有JS生成的内容的快照(正如你所说,普通用户所见)。pushState实际上简化了所有这些问题。 - Nicole
1
@Harry - 直到URL被设计为提供客户端内容,它们仍然指向服务器上的资源,并且客户端将以此方式处理它们,包括机器人。这并不意味着你尽可能在客户端上完成任务的目标是无效的,但暂时可能需要使用(丑陋的)哈希符号来实现。我已经更新了我的答案以适应你的用例。 - Nicole
1
@Harry 首先,我只是根据 Google 所说的 _escaped_fragment_ 做出推断,我不知道你具体做了什么。但从 Google 的说法来看,我认为当你看到那个查询参数时,必须通过服务器提供某种类型的内容。在你的情况下,这将需要一些诡计,但你可以从 /blog 提供一些 <noscript> 内容或其他内容,然后使用 JS 构建所需的页面。或者,你可以尝试检测机器人并有意地提供完全不同的内容。 - Nicole
2
再一次,正确和最好的答案没有被选为正确答案......糟糕,糟糕。 - user1598585
1
如果我有一个链接,例如:<a href="product/productName" onclick="showProduct(product)">产品</a>,并且onclick以“preventDefault()”开头,则会通过AJAX在页面中加载有关该产品的新内容,并确保链接“.../product/productName”将加载包含特定产品内容的页面版本,从服务器响应---因此,网站仍将动态工作,但也可以通过直接转到产品链接来获得静态内容,对吗?这种方式不需要pushState或hashbang,对吗? - Yuval A.
显示剩余4条评论

17
使用Google建议的meta标签来避免在URL中使用哈希碰撞的方法如下:<meta name="fragment" content="!">,更多信息请参见这里
不幸的是,我认为Nicole没有澄清我认为原始帖子存在的问题。问题就是如果我们不使用哈希碰撞,我们就不知道我们正在为哪些用户提供内容。Pushstate不能解决这个问题。我们不希望搜索引擎告诉最终用户导航到某个URL,然后输出未格式化的JSON。相反,我们创建一些URL(触发其他调用更多URL),通过AJAX检索数据,并以我们喜欢的方式呈现给用户。如果用户不是人类,则作为替代方案,我们可以提供HTML快照,以便搜索引擎可以正确地将人类用户重定向到他们期望在该URL上找到请求的数据(并以可呈现的方式)。但最大的挑战是如何确定用户类型?是的,我们可能可以使用.htaccess或类似工具为我们检测到的搜索引擎机器人重写URL,但我不确定这种方法有多完美和具有未来性。Google也可能因此惩罚人们,但我还没有完全研究过。因此,(pushstate + Google的meta标签)组合似乎是一个可能的解决方案。

3
@NickC,好的我明白了,现在我认为更好的解决方案是最初不使用任何JS来显示内容。但是在你的JS代码顶部(页面加载并且DOM准备就绪后)立即运行一些代码来隐藏最初显示的HTML内容或用JS增强替换它。例如,我使用jquery datagrids,所以我会先显示一个HTML表格,然后立即加载JS将常规表格数据转换/隐藏/替换为JS网格版本。从那时起,任何其他ajax请求都可以配对JSON一起提供,并通过pushstate更新URL。 - prograhammer
你对你所建议的解决方案有什么经验吗?Google是否索引了这个“临时”的HTML?它在相关的Google搜索中是否正确显示?另外,这是否意味着体验有些“不稳定”,因为初始的HTML页面是通过JS生成的而被“刷新”了呢? - Nilesh Kale
@NileshKale 这是我设计的解决方案,它非常好地完成了工作:http://stackoverflow.com/questions/22824991/jqgrid-progressive-enhancement-successfully-progresses-from-html-to-local-js/22827448#22827448。我传递了一个HTML表格以及与HTML等效的JSON jqgrid。SEO读取HTML,并且用户通过ajax获得升级体验和所有后续请求。使用pushstate,我可以根据用户对网格的排序/分页来更新URL(无需哈希标记)。这允许用户保存URL并返回到相同的结果。 - prograhammer
我会在几天内尝试对我的回答进行编辑,以更好地解释。 - prograhammer
1
AJAX-crawling方案现已被弃用:https://developers.google.com/webmasters/ajax-crawling/docs/getting-started。建议更改使用它的网站:https://plus.google.com/+JohnMueller/posts/LT4fU7kFB8W。 - Protector one

2
所有关于pushState和#!的有趣讨论,我仍然看不出pushState如何替代#!作为原帖所问的目的。
我们的解决方案是使用#!来使我们99%基于JavaScript的Ajax站点/应用程序可被搜索引擎索引。由于客户端呈现是通过HTML、JavaScript和PHP完成的,因此我们在由页面着陆控制的加载器中使用以下逻辑。HTML文件完全与JavaScript和PHP分离,因为我们希望两者(大部分)使用相同的HTML。JavaScript和PHP主要做相同的事情,但是PHP代码比JavaScript代码简单得多,因为JavaScript提供了更丰富的用户体验。
JavaScript使用jQuery将其想要的内容注入到HTML中。PHP使用PHPQuery将其想要的内容注入到HTML中 - 使用“几乎”相同的逻辑,但是PHP版本要简单得多,因为PHP版本仅用于显示具有SEO友好链接的可优化版本,而不像JavaScript版本那样与之交互。
所有三个组成页面的组件,page.htm、page.js和page.php都存在于任何使用转义片段的内容中,以确定是否在JavaScript版本的位置上加载PHP版本。对于不可搜索引擎优化的内容(例如只能在用户登录后查看的页面),不需要存在PHP版本。这很简单明了。
我仍然困惑于一些前端开发人员如何在不使用服务器端技术与浏览器技术结合的情况下开发出伟大的网站(具有Google Docs的丰富性)...如果甚至没有启用JavaScript,则我们99%基于JavaScript的解决方案当然无法在没有PHP的情况下运行。
如果您要制作一个简单的网站,它可以在没有任何JavaScript的情况下运行,那么我可以看出pushState对于逐渐提高用户体验非常有用,从简单的静态呈现内容到更好的东西,但是如果您想让用户从一开始就获得最佳体验...比如您最新的JavaScript编写的游戏或类似Google Docs的东西,那么将其用于此解决方案的用途有些有限,因为优雅地回退只能走到这一步,在用户体验与网站愿景相比时,会感到痛苦。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接