“单页”JS 网站和 SEO

IT技术 javascript seo backbone.js pushstate singlepage
2021-03-14 22:07:15

现在有很多很酷的工具可以制作强大的“单页”JavaScript 网站。在我看来,这是通过让服务器充当 API(仅此而已)并让客户端处理所有 HTML 生成内容来完成的。这种“模式”的问题在于缺乏搜索引擎支持。我可以想到两种解决方案:

  1. 当用户进入网站时,让服务器完全按照客户端在导航时的方式呈现页面。因此,如果我http://example.com/my_path直接访问,服务器将呈现与客户端/my_path通过 pushState 访问相同的内容。
  2. 让服务器只为搜索引擎机器人提供一个特殊的网站。如果普通用户访问http://example.com/my_path服务器应该给他网站的 JavaScript 重度版本。但是如果 Google bot 访问,服务器应该给它一些最小的 HTML,其中包含我希望 Google 索引的内容。

此处将进一步讨论第一种解决方案我一直在一个网站上做这件事,这不是一个很好的体验。这不是 DRY,在我的情况下,我不得不为客户端和服务器使用两个不同的模板引擎。

我想我已经看到了一些不错的 Flash 网站的第二个解决方案。我比第一种方法更喜欢这种方法,并且在服务器上使用正确的工具可以轻松完成。

所以我真正想知道的是以下内容:

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

虽然#2 对您作为开发人员来说可能“更容易”,但它仅提供搜索引擎抓取功能。是的,如果 Google 发现您提供不同的内容,您可能会受到处罚(我不是这方面的专家,但我听说过这种情况)。

SEO 和可访问性(不仅适用于残疾人,还可以通过移动设备、触摸屏设备和其他非标准计算/互联网平台实现的可访问性)都具有相似的基本理念:语义丰富的标记是“可访问的”(即可以被访问、查看、阅读、处理或以其他方式使用)到所有这些不同的浏览器。屏幕阅读器、搜索引擎爬虫或启用了 JavaScript 的用户都应该能够毫无问题地使用/索引/理解您网站的核心功能。

pushState根据我的经验,不会增加这种负担。它只会将过去的事后想法和“如果我们有时间”带到 Web 开发的最前沿。

您在选项 #1 中所描述的通常是最好的方法 - 但是,与其他可访问性和 SEO 问题一样,pushState在 JavaScript 密集型应用程序中执行此操作需要预先计划,否则将成为重大负担。它应该从一开始就融入到页面和应用程序架构中——改造是痛苦的,并且会导致不必要的重复。

我最近一直在pushState为几个不同的应用程序使用SEO,我发现我认为这是一个很好的方法。它基本上遵循您的第 1 项,但不复制 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(){
    // 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对我来说,第一个视图加载时。在视图启动并运行并且页面都是 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支持工作顺利进行的好方法

希望有帮助。

我明白你的意思,但有趣的是在“你的 JavaScript 接管”之后渲染是如何完成的。在更复杂的示例中,您可能必须在客户端使用未编译的模板,循环遍历用户数组以构建列表。每次用户的模型更改时,视图都会重新呈现。如果不复制模板(并且不要求服务器为客户端呈现视图),您将如何做到这一点?
2021-04-22 22:07:15
我链接的 2 篇博客文章应该共同向您展示如何拥有可在客户端和服务器上使用的模板 - 无需重复。如果您希望它易于访问且对 SEO 友好,服务器将需要呈现整个页面。我已经更新了我的答案,以包含一个更复杂的附加到由服务器呈现的用户列表的示例
2021-04-30 22:07:15

我认为你需要这个:http : //code.google.com/web/ajaxcrawling/

您还可以安装一个特殊的后端,通过在服务器上运行 javascript 来“呈现”您的页面,然后将其提供给 google。

结合这两件事,你就有了一个解决方案,而无需对事物进行两次编程。(只要您的应用程序可以通过锚点片段完全控制。)

其实,这不是我要找的。这些是第一个解决方案的一些变体,正如我所提到的,我对这种方法不太满意。
2021-04-16 22:07:15
你没有阅读我的整个答案。您还可以使用一个特殊的后端来为您呈现 javascript - 您不会写两次东西。
2021-04-28 22:07:15
我认为它基本上是一个没有前端的浏览器。但是,是的,您确实必须使程序完全可以从锚点片段进行控制。您还需要确保所有链接都包含正确的片段,以及或代替 onClick。
2021-05-08 22:07:15
是的,我确实读过。但是,如果我确实理解正确,那将是一个非常糟糕的程序,因为它必须模拟触发 pushState 的每个动作。或者,我可以直接为它提供操作,但是我们不再那么枯燥了。
2021-05-13 22:07:15

所以,似乎主要关注的是干燥

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

如果您使用 Rails,请尝试poirot这是一个宝石,它使重用mustache把手模板客户端和服务器端变得非常简单

在您的视图中创建一个文件,例如_some_thingy.html.mustache.

渲染服务器端:

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

把模板放在你的头上供客户端使用:

<%= template_include_tag 'some_thingy' %>

渲染客户端:

html = poirot.someThingy(my_model)

从稍微不同的角度来看,您的第二个解决方案在可访问性方面是正确的……您将为无法使用 javascript 的用户(具有屏幕阅读器等的用户)提供替代内容。

这会自动增加 SEO 的好处,在我看来,不会被谷歌视为“顽皮”的技术。

有没有人证明你错了?评论发表已经有一段时间了
2021-05-07 22:07:15