现在有许多很酷的工具可以制作功能强大的“单页”JavaScript网站。在我看来,这是通过让服务器充当API(而不是更多)并让客户端处理所有HTML生成的东西来完成的。这种“模式”的问题是缺乏搜索引擎支持。我可以想到两个解决方案:
http://example.com/my_path
,那么当我通过pushState转到/my_path
时,服务器将呈现与客户端相同的内容。http://example.com/my_path
,服务器应该给他一个JavaScript重型版本的网站。但是,如果谷歌机器人访问,服务器应该给它一些最小的HTML与我希望谷歌索引的内容。第一个解决方案将进一步讨论here。我一直在网站上做这个,这不是一个非常好的经历。它不是DRY,在我的情况下,我必须为客户端和服务器使用两个不同的模板引擎。
我想我已经看到了一些优秀的Flash网站的第二个解决方案。我比第一种方法更喜欢这种方法,并且在服务器上使用正确的工具,它可以非常轻松地完成。
所以我真正想知道的是:
答案 0 :(得分:44)
虽然#2对于您作为开发人员来说可能“更容易”,但它只提供搜索引擎抓取功能。是的,如果Google发现您提供的服务内容不同,您可能会受到处罚(我不是专家,但我听说过这种情况)。
搜索引擎优化和可访问性(不仅适用于残障人士,而是通过移动设备,触摸屏设备和其他非标准计算/互联网平台实现的可访问性)都具有类似的基本理念:语义丰富的标记,即“可访问” (即可以访问,查看,阅读,处理或以其他方式使用)所有这些不同的浏览器。屏幕阅读器,搜索引擎爬虫或启用了JavaScript的用户都应该能够毫无问题地使用/索引/了解您网站的核心功能。
根据我的经验, pushState
并没有增加这个负担。它只会带来以前的事后想法和“如果我们有时间”走在网络发展的最前沿。
您在选项#1中的描述通常是最好的方式 - 但是,与其他辅助功能和搜索引擎优化问题一样,在JavaScript密集的应用中使用pushState
执行此操作需要预先规划,否则它将成为一个重大的负担。它应该从一开始就融入到页面和应用程序架构中 - 改造是痛苦的,并且会导致比必要的更多重复。
最近我和pushState
和SEO一起使用了几个不同的应用程序,我发现我认为这是一个很好的方法。它基本上遵循您的第1项,但不会重复html /模板。
大部分信息都可以在这两篇博文中找到:
和
http://lostechies.com/derickbailey/2011/06/22/rendering-a-rails-partial-as-a-jquery-template/
它的要点是我使用ERB或HAML模板(运行Ruby on Rails,Sinatra等)进行服务器端渲染,并创建Backbone可以使用的客户端模板,以及我的Jasmine JavaScript规范。这样就减少了服务器端和客户端之间的重复标记。
从那里,你需要采取一些额外的步骤让你的JavaScript使用服务器呈现的HTML - 真正的渐进增强;采用已交付的语义标记并使用JavaScript增强它。
例如,我正在使用pushState
构建图片库应用程序。如果您从服务器请求/images/1
,它将在服务器上呈现整个图像库,并将所有HTML,CSS和JavaScript发送到您的浏览器。如果您禁用了JavaScript,它将完美地运行。您执行的每个操作都将从服务器请求不同的URL,服务器将为您的浏览器呈现所有标记。但是,如果你启用了JavaScript,那么JavaScript将获取已经呈现的HTML以及服务器生成的一些变量并从那里接管。
以下是一个例子:
<form id="foo">
Name: <input id="name"><button id="say">Say My Name!</button>
</form>
在服务器呈现此内容之后,JavaScript会选择它(在此示例中使用Backbone.js视图)
FooView = Backbone.View.extend({
events: {
"change #name": "setName",
"click #say": "sayName"
},
setName: function(e){
var name = $(e.currentTarget).val();
this.model.set({name: name});
},
sayName: function(e){
e.preventDefault();
var name = this.model.get("name");
alert("Hello " + name);
},
render: function(){
// do some rendering here, for when this is just running JavaScript
}
});
$(function(){
var model = new MyModel();
var view = new FooView({
model: model,
el: $("#foo")
});
});
这是一个非常简单的例子,但我认为它得到了重点。
当我在页面加载后实例化视图时,我将提供服务器呈现的表单的现有内容作为视图的el
提供给视图实例。当加载第一个视图时,我不调用渲染或让视图为我生成el
。在视图启动并运行后,我有一个可用的渲染方法,页面都是JavaScript。这使我可以在以后重新渲染视图。
在启用JavaScript的情况下点击“说出我的名字”按钮会产生一个警告框。如果没有JavaScript,它会回发到服务器,服务器可以将名称呈现给某个地方的html元素。
修改强>
考虑一个更复杂的例子,你有一个需要附加的列表(来自下面的评论)
假设您有<ul>
标记中的用户列表。当浏览器发出请求时,服务器会呈现此列表,结果如下所示:
<ul id="user-list">
<li data-id="1">Bob
<li data-id="2">Mary
<li data-id="3">Frank
<li data-id="4">Jane
</ul>
现在,您需要遍历此列表并将Backbone视图和模型附加到每个<li>
项目。通过使用data-id
属性,您可以轻松找到每个标记的模型。然后,您将需要一个足够智能的集合视图和项目视图,以将其自身附加到此HTML。
UserListView = Backbone.View.extend({
attach: function(){
this.el = $("#user-list");
this.$("li").each(function(index){
var userEl = $(this);
var id = userEl.attr("data-id");
var user = this.collection.get(id);
new UserView({
model: user,
el: userEl
});
});
}
});
UserView = Backbone.View.extend({
initialize: function(){
this.model.bind("change:name", this.updateName, this);
},
updateName: function(model, val){
this.el.text(val);
}
});
var userData = {...};
var userList = new UserCollection(userData);
var userListView = new UserListView({collection: userList});
userListView.attach();
在此示例中,UserListView
将循环遍历所有<li>
标记,并为每个标记附加一个具有正确模型的视图对象。它为模型的名称更改事件设置事件处理程序,并在发生更改时更新元素的显示文本。
这种过程,采取服务器呈现的html并让我的JavaScript接管并运行它,是一个很好的方法来推动SEO,辅助功能和pushState
支持。
希望有所帮助。
答案 1 :(得分:22)
我认为你需要这个:http://code.google.com/web/ajaxcrawling/
您还可以安装一个特殊的后端,通过在服务器上运行javascript来“呈现”您的页面,然后将其提供给谷歌。
结合这两件事,你就有了解决方案而无需编程两次。 (只要你的应用程序可以通过锚片段完全控制。)
答案 2 :(得分:17)
所以,似乎主要关注的是DRY
<a href="/someotherpage">mylink</a>
上的其他“pushState”链接/某个页面时,服务器会将url重写为你的应用程序文件,将其加载到phantom.js中,并将生成的html发送到bot等等...... <a>
标签的简单应用程序来完成预处理。在这种情况下,处理404更容易,因为您只需检查是否存在具有包含url路径的名称的静态文件。以下是使用phantom.js进行搜索的几个例子:
http://backbonetutorials.com/seo-for-single-page-apps/
http://thedigitalself.com/blog/seo-and-javascript-with-phantomjs-server-side-rendering
答案 3 :(得分:4)
如果您使用的是Rails,请尝试poirot。这是一个宝石,使得重用mustache或handlebars模板客户端和服务器端变得简单。
在_some_thingy.html.mustache
等观看次数中创建一个文件。
渲染服务器端:
<%= render :partial => 'some_thingy', object: my_model %>
将模板放在客户端使用的位置:
<%= template_include_tag 'some_thingy' %>
Rendre客户端:
html = poirot.someThingy(my_model)
答案 4 :(得分:3)
从稍微不同的角度来看,您的第二个解决方案在辅助功能方面是正确的...您将为不能使用javascript的用户提供替代内容(那些有屏幕阅读器的用户,等等。)。
这会自动增加搜索引擎优化的好处,在我看来,不会被谷歌视为“顽皮”技术。
答案 5 :(得分:1)
有趣。我一直在寻找可行的解决方案,但它似乎很成问题。
我实际上更倾向于你的第二种方法:
让服务器仅为搜索引擎提供特殊网站 机器人。如果普通用户访问服务器http://example.com/my_path 应该给他一个JavaScript重型版本的网站。但如果 谷歌机器人访问,服务器应该给它一些最小的HTML 我希望Google将其编入索引的内容。
这是我解决问题的方法。虽然没有确认它可以工作,但它可能会为其他开发人员提供一些见解或想法。
假设您正在使用支持“推送状态”功能的JS框架,并且您的后端框架是Ruby on Rails。您有一个简单的博客网站,您希望搜索引擎将您的所有文章index
和show
页面编入索引。
假设您的路线设置如下:
resources :articles
match "*path", "main#index"
确保每个服务器端控制器呈现客户端框架运行所需的相同模板(html / css / javascript / etc)。如果请求中没有匹配的控制器(在此示例中我们只有ArticlesController
的RESTful操作集),那么只需匹配其他任何内容并只渲染模板并让客户端框架处理路由。命中控制器和命中通配符匹配器之间的唯一区别是能够根据请求禁用JavaScript的设备的URL呈现内容。
根据我的理解,渲染浏览器不可见的内容是个坏主意。因此,当谷歌对其进行索引时,人们通过谷歌访问某个给定的页面并且没有任何内容,那么您可能会受到惩罚。我想到的是,您在CSS中div
的{{1}}节点中呈现内容。
但是,我很确定如果你只是这样做并不重要:
display: none
然后使用JavaScript,当禁用JavaScript的设备打开页面时,它无法运行:
<div id="no-js">
<h1><%= @article.title %></h1>
<p><%= @article.description %></p>
<p><%= @article.content %></p>
</div>
通过这种方式,对于Google以及禁用JavaScript设备的任何人,他们都会看到原始/静态内容。所以内容 在物理上存在,任何使用JavaScript禁用设备的人都可以看到。
但是,当用户访问同一页面并且实际启用了 JavaScript时,$("#no-js").remove() # jQuery
节点将被删除,因此不会使您的应用程序混乱。然后,您的客户端框架将通过它的路由器处理请求,并显示用户在启用JavaScript时应该看到的内容。
我认为这可能是一种有效且相当简单的技术。虽然这可能取决于您的网站/应用程序的复杂性。
但是,如果不是,请纠正我。我以为我会分享我的想法。
答案 6 :(得分:1)
在服务器端使用NodeJS,浏览化您的客户端代码,并通过服务器端客户端路由每个http请求(静态http资源除外)uri,以提供第一个'bootsnap'(它所状态的页面的快照)。使用像jsdom这样的东西来处理服务器上的jquery dom-ops。在bootsnap返回后,设置websocket连接。可能最好通过在客户端上进行某种包装器连接来区分websocket客户端和服务器端客户端(服务器端客户端可以直接与服务器通信)。我一直在做这样的事情:https://github.com/jvanveen/rnet/
答案 7 :(得分:0)
使用Google Closure Template呈现页面。它编译为javascript或java,因此很容易在客户端或服务器端呈现页面。在第一次遇到每个客户端时,渲染html并在标题中添加javascript作为链接。 Crawler将仅读取html,但浏览器将执行您的脚本。浏览器的所有后续请求都可以针对api完成,以最大限度地减少流量。
答案 8 :(得分:0)
这可能会帮助您:https://github.com/sharjeel619/SPA-SEO
逻辑