虽然选项#2对开发者来说可能更"容易",但它只提供搜索引擎爬行。如果Google发现您提供了不同的内容,您可能会受到惩罚(我不是专家,但我听说过这种情况发生)。
SEO和可访问性(不仅针对残障人士,而且还包括通过移动设备、触屏设备和其他非标准计算/互联网启用平台进行可访问性)都有着相似的基本理念:语义丰富的标记是“可访问的”(即可以被访问、查看、阅读、处理或以其他方式使用),适用于所有这些不同的浏览器。屏幕阅读器、搜索引擎爬虫或启用JavaScript的用户都应该能够使用/索引/理解您站点的核心功能。
pushState
从我的经验来看并不会增加这个负担。它只是把之前被忽视的和“如果我们有时间”的东西带到了Web开发的前沿。
你描述的第一种选择通常是最好的选择-但是,像其他可访问性和SEO问题一样,在JavaScript重度应用程序中使用pushState
需要事先规划,否则它将成为一个重大负担。它应该从一开始就融入到页面和应用程序架构中--后期改装很痛苦,并会导致比必要的更多的重复。
我最近在几个不同的应用程序中使用pushState
和SEO,我发现了一种我认为不错的方法。它基本上遵循了你的第一条建议,但考虑到不重复html/模板。
这些信息大部分可以在以下两篇博客文章中找到:
http://lostechies.com/derickbailey/2011/09/06/test-driving-backbone-views-with-jquery-templates-the-jasmine-gem-and-jasmine-jquery/
和
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(){
}
});
$(function(){
var model = new MyModel();
var view = new FooView({
model: model,
el: $("#foo")
});
});
这是一个非常简单的示例,但我认为它表达了重点。
当页面加载后,我实例化视图时,将服务器呈现的表单的现有内容作为视图的el
提供给视图实例。 在加载第一个视图时,我不会调用render或让视图为我生成el
。 在视图已启动并且页面全部都是JavaScript之后,我可以使用可用的render方法重新渲染视图,以便稍后再次需要。
启用JavaScript后,单击“Say My Name”按钮将导致弹出警告框。 没有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
支持等方面有很大帮助。
希望能对您有所帮助。