backbone.js “单页”JS网站和SEO

k5ifujac  于 2022-11-10  发布在  SEO
关注(0)|答案(9)|浏览(302)

现在有很多很酷的工具可以制作功能强大的“单页”JavaScript网站。在我看来,让服务器充当API(仅此而已),让客户端处理所有HTML生成的东西,这是正确的。这种“模式”的问题是缺乏搜索引擎支持。我可以想到两个解决方案:
1.当用户进入网站时,让服务器呈现的页面与客户端在导航时呈现的页面完全相同。因此,如果我直接访问http://example.com/my_path,服务器将呈现与客户端通过pushState访问/my_path时相同的内容。
1.让服务器为搜索引擎机器人提供一个特殊的网站。如果一个普通用户访问http://example.com/my_path,服务器应该给予他一个JavaScript很重的网站版本。但是如果谷歌机器人访问,服务器应该给它一些最小的HTML,上面有我想让谷歌索引的内容。
第一个解决方案是进一步讨论here。我一直在一个网站上这样做,这不是一个很好的经验。它不是干,在我的情况下,我不得不使用两个不同的模板引擎为客户端和服务器。
我想我已经看到了一些好的OL“Flash网站的第二个解决方案。我喜欢这种方法比第一个多得多,在服务器上使用正确的工具,它可以做得相当痛苦。
所以我真正想知道的是:

  • 你能想到更好的解决办法吗?
  • 第二种解决方案的缺点是什么?如果谷歌以某种方式发现我没有像普通用户一样为谷歌机器人提供完全相同的内容,那么我会在搜索结果中受到惩罚吗?
wqnecbli

wqnecbli1#

虽然#2对开发人员来说可能“更容易”,但它只提供搜索引擎抓取功能。是的,如果谷歌发现你提供不同的内容,你可能会受到惩罚(我不是这方面的Maven,但我听说过这种情况)。
搜索引擎优化和可访问性(不仅仅是针对残疾人,还包括通过移动的设备、触摸屏设备和其他非标准计算/互联网平台实现的可访问性)都有一个相似的基本理念:语义丰富的标记,这些标记对所有这些不同的浏览器都是“可访问的”(即可以访问、查看、阅读、处理或以其他方式使用)。屏幕阅读器、搜索引擎爬虫或启用JavaScript的用户都应该能够毫无问题地使用/索引/理解站点的核心功能。
根据我的经验,pushState并不会增加这种负担,它只会把曾经是事后的想法和“如果我们有时间”带到Web开发的前沿。
你在选项#1中描述的通常是最好的方法--但是,像其他可访问性和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,它会很好地工作。2你的每一个动作都将从服务器请求一个不同的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提供给视图示例,我 * 不是 * 调用render或让视图为我生成一个el,我有一个render方法,可以在视图启动并运行后,在页面全部是JavaScript的情况下使用。这让我可以重新如果需要,稍后再渲染视图。
在启用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支持。
希望能有所帮助。

xqkwcwgp

xqkwcwgp2#

我觉得你需要这个:http://code.google.com/web/ajaxcrawling/
你也可以安装一个特殊的后端,通过在服务器上运行javascript来“渲染”你的页面,然后将其提供给google。
把这两件事结合起来,你就有了一个解决方案,而不需要两次编程。(只要你的应用程序完全可以通过锚片段控制。)

ars1skjm

ars1skjm3#

所以,看起来主要的问题是干燥

  • 如果你使用pushState,让你的服务器为所有的url发送相同的代码(不包含提供图像等的文件扩展名)“/mydir/myfile”,“/myotherdir/myotherfile”或root“/”--所有的请求都收到相同的代码。你需要有某种URL重写引擎。你也可以提供一小部分html,其余的可以来自你的CDN(使用require.js管理依赖项--请参见https://stackoverflow.com/a/13813102/1595913)。
  • (测试链接的有效性,方法是将链接转换为您的url方案,并通过查询静态或动态源来测试内容是否存在。如果无效,则发送404响应。)
  • 当请求不是来自谷歌机器人时,您只需正常处理。
  • 如果请求来自google bot,则使用phantom.js -- headless webkit浏览器(“无头浏览器只是一个没有可视界面的全功能Web浏览器。")在服务器上呈现html和javascript,并将生成的html发送给google bot。bot解析html时,它可以点击服务器<a href="/someotherpage">mylink</a>上的其他“pushState”链接/somepage,服务器将url重写到您的应用程序文件中,将其加载到phantom.js中,然后将生成的html发送到bot,依此类推...
  • 对于您的html,我假设您使用的是带有某种劫持的正常链接(例如,使用backbone.js https://stackoverflow.com/a/9331734/1595913
  • 为了避免与任何链接混淆,请将为json提供服务的api代码分隔到单独的子域中,例如api.mysite.com
  • 为了提高性能,你可以在下班时间提前为搜索引擎预处理你的网站页面,方法是使用与phantom.js相同的机制创建页面的静态版本,然后将静态页面提供给谷歌机器人。预处理可以通过一些简单的应用程序来完成,这些应用程序可以解析<a>标签。在这种情况下,处理404更容易,因为您可以简单地检查名称中包含url路径的静态文件是否存在。
  • 如果你对你的网站链接使用#!哈希键语法,类似的情况也适用,除了重写url服务器引擎会在url中寻找_escaped_fragment_,并将url格式化为你的url方案。
  • github上有node.js和phantom.js的几个集成,你可以使用node.js作为web服务器来生成html输出。

下面是几个使用phantom.js进行seo的例子:
http://backbonetutorials.com/seo-for-single-page-apps/
http://thedigitalself.com/blog/seo-and-javascript-with-phantomjs-server-side-rendering

wgeznvg7

wgeznvg74#

如果你使用Rails,可以试试poirot,它是一个让客户端和服务器端重用mustachehandlebars模板变得非常简单的工具。
在您的视图中创建一个文件,如_some_thingy.html.mustache
渲染服务器端:

<%= render :partial => 'some_thingy', object: my_model %>

将模板放在您的头脑中供客户端用途:

<%= template_include_tag 'some_thingy' %>

Rendre客户端:

html = poirot.someThingy(my_model)
mu0hgdu0

mu0hgdu05#

换一个稍微不同的Angular ,你的第二个解决方案在可访问性方面是正确的...你将为不能使用javascript的用户(那些有屏幕阅读器的用户等)提供替代内容。
这将自动增加搜索引擎优化的好处,在我看来,不会被视为一个'顽皮'的技术由谷歌。

mkshixfv

mkshixfv6#

有意思。我一直在寻找可行的解决方案,但似乎很有问题。
我其实更倾向于你的第二种方法:
让服务器为搜索引擎机器人提供一个专门的网站。如果一个普通用户访问http://example.com/my_path,服务器应该给予他一个JavaScript很重的网站版本。但是如果谷歌机器人访问,服务器应该给它一些最小的HTML,上面有我希望谷歌索引的内容。
这是我对解决这个问题的看法。虽然它还没有被证实有效,但它可能会为其他开发人员提供一些见解或想法。
假设您使用的是支持“推送状态”功能的JS框架,后端框架是Ruby on Rails,您有一个简单的博客站点,希望搜索引擎为您的所有文章indexshow页面建立索引。
假设您的路线设置如下:

resources :articles
match "*path", "main#index"

确保每个服务器端控制器都呈现客户端框架运行所需的相同模板(html/css/javascript/etc)。如果请求中没有匹配的控制器,(在本例中,我们只有ArticlesController的一组REST风格的操作),然后只匹配其他任何内容,只呈现模板,让客户端框架处理路由。点击控制器和点击通配符匹配器之间的唯一区别是,能够根据请求的URL将内容呈现给禁用JavaScript的设备。
据我所知,渲染浏览器看不到的内容是一个坏主意。因此,当谷歌索引它,人们通过谷歌访问一个给定的页面,并没有任何内容,那么你可能会受到惩罚。想到的是,你渲染的内容在一个div节点,你display: none在CSS中。
不过,我敢肯定,如果你简单地这样做,那也没关系:

<div id="no-js">
  <h1><%= @article.title %></h1>
  <p><%= @article.description %></p>
  <p><%= @article.content %></p>
</div>

然后使用JavaScript,当禁用JavaScript的设备打开页面时,JavaScript不会运行:

$("#no-js").remove() # jQuery

这样,对于Google和任何使用禁用JavaScript的设备的人来说,他们将看到原始/静态内容。因此,内容 * 是 * 物理存在的,并且对于任何使用禁用JavaScript的设备的人都是可见的。
但是,当用户访问同一个页面并且实际上 * 启用了 * JavaScript时,#no-js节点将被删除,这样它就不会扰乱应用程序。然后,客户端框架将通过其路由器处理请求,并显示启用JavaScript时用户应该看到的内容。
我认为这可能是一个有效的和相当容易使用的技术。虽然这可能取决于您的网站/应用程序的复杂性。
不过,如果不是的话请纠正我。我只是想分享一下我的想法。

ehxuflar

ehxuflar7#

在服务器端使用NodeJS,浏览客户端代码并路由每个http请求的(静态HTTP资源除外)URI通过服务器端客户端提供第一个“bootsnap”在服务器上使用jsdom来处理jquerydom-ops.在bootsnap返回后,设置WebSocket连接。可能最好通过在客户端建立某种 Package 器连接来区分websocket客户端和服务器端客户端(服务器端的客户机可以直接与服务器通信)。我一直在做这样的事情:https://github.com/jvanveen/rnet/

mnemlml8

mnemlml88#

使用Google Closure Template呈现页。它编译为javascript或java,因此很容易在客户端或服务器端呈现页。在第一次遇到每个客户端时,呈现html并将javascript作为链接添加到标头中。Crawler将只读取html,但浏览器将执行您的脚本。来自浏览器的所有后续请求都可以在api中完成,以最大限度地减少流量。

gywdnpxw

gywdnpxw9#

这可能会帮助您:https://github.com/sharjeel619/SPA-SEO

逻辑

  • 浏览器向服务器请求单页应用程序,该应用程序将从单个index.html文件中加载。
  • 您可以编写一些中间服务器代码,用于拦截客户端请求,并区分请求是来自浏览器还是来自某个社交爬虫机器人。
  • 如果请求来自某个爬虫机器人,则对后端服务器进行API调用,收集所需的数据,将该数据填充到html meta标记中,并将这些标记以字符串格式返回给客户端。
  • 如果请求不是来自某个爬虫机器人,那么只需从单页面应用程序的builddist文件夹中返回index.html文件。

相关问题