it-swarm.com.de

"Einseitige" JS-Websites und SEO

Es gibt heutzutage eine Menge cooler Tools, um leistungsstarke "einseitige" JavaScript-Websites zu erstellen. Meiner Meinung nach geschieht dies richtig, indem der Server als API fungiert (und nicht mehr) und der Client alle HTML-Generierungsaufgaben erledigt. Das Problem mit diesem "Muster" ist der Mangel an Suchmaschinenunterstützung. Ich kann mir zwei Lösungen vorstellen:

  1. Wenn der Benutzer die Website betritt, lässt der Server die Seite genau so rendern, wie es der Client bei der Navigation tun würde. Wenn ich also direkt zu http://example.com/my_path Gehe, rendert der Server das Gleiche wie der Client, wenn ich über pushState zu /my_path Gehe.
  2. Lassen Sie den Server eine spezielle Website nur für die Suchmaschinen-Bots bereitstellen. Wenn ein normaler Benutzer http://example.com/my_path Besucht, sollte ihm der Server eine JavaScript-starke Version der Website geben. Aber wenn der Google-Bot besucht, sollte der Server ihm minimalen HTML-Code mit dem Inhalt geben, den Google indizieren soll.

Die erste Lösung wird weiter besprochen hier . Ich habe an einer Website gearbeitet, die dies tut, und es ist keine sehr schöne Erfahrung. Es ist nicht DRY und in meinem Fall musste ich zwei verschiedene Template-Engines für den Client und den Server verwenden.

Ich denke, ich habe die zweite Lösung für einige gute alte Flash-Websites gesehen. Ich mag diesen Ansatz sehr viel mehr als den ersten und mit dem richtigen Tool auf dem Server könnte es ziemlich schmerzlos gemacht werden.

Also, was ich mich wirklich wundere, ist das Folgende:

  • Können Sie sich eine bessere Lösung vorstellen?
  • Was sind die Nachteile bei der zweiten Lösung? Wenn Google in irgendeiner Weise feststellt, dass ich nicht genau den gleichen Content für den Google-Bot wie ein regulärer Nutzer bereitstelle, werde ich dann in den Suchergebnissen bestraft?
128
user544941

Während # 2 für Sie als Entwickler "einfacher" ist, bietet es nur das Crawlen von Suchmaschinen. Und ja, wenn Google herausfindet, dass Sie unterschiedliche Inhalte liefern, werden Sie möglicherweise bestraft (ich bin kein Experte in diesem Bereich, aber ich habe davon gehört).

Sowohl die Suchmaschinenoptimierung als auch die Barrierefreiheit (nicht nur für Behinderte, sondern auch die Barrierefreiheit über Mobilgeräte, Touchscreen-Geräte und andere nicht standardmäßige rechner- und internetfähige Plattformen) basieren auf einer ähnlichen Philosophie: semantisch reiches Markup, das "barrierefrei" ist (dh zugänglich ist) Zugriff, Anzeige, Lesen, Verarbeiten oder anderweitige Verwendung) auf alle diese verschiedenen Browser. Ein Screenreader, ein Suchmaschinen-Crawler oder ein Benutzer mit aktiviertem JavaScript sollten in der Lage sein, die Kernfunktionalität Ihrer Site problemlos zu verwenden, zu indizieren und zu verstehen.

pushState trägt meiner Erfahrung nach nicht zu dieser Belastung bei. Es bringt nur das, was früher ein nachträglicher Gedanke war und "wenn wir Zeit haben" an die Spitze der Webentwicklung.

Was Sie in Option 1 beschreiben, ist in der Regel der beste Weg - aber wie bei anderen Zugänglichkeits- und SEO-Problemen erfordert dies mit pushState in einer JavaScript-starken App eine Vorausplanung, oder es wird zu einer signifikanten Last. Es sollte von Anfang an in die Seiten- und Anwendungsarchitektur eingebunden werden - die Nachrüstung ist schmerzhaft und verursacht mehr Duplikate als erforderlich.

Ich habe kürzlich für ein paar verschiedene Anwendungen mit pushState und SEO gearbeitet, und ich fand, was ich für einen guten Ansatz halte. Grundsätzlich folgt es Ihrem Artikel Nr. 1, berücksichtigt jedoch, dass HTML/Vorlagen nicht dupliziert werden.

Die meisten Informationen finden Sie in diesen beiden Blog-Posts:

http://lostechies.com/derickbailey/2011/09/06/test-driving-backbone-views-with-jquery-templates-the-jasmine-gem-and-jasmine-jquery/

und

http://lostechies.com/derickbailey/2011/06/22/rendering-a-Rails-partial-as-a-jquery-template/

Das Wesentliche dabei ist, dass ich ERB- oder HAML-Vorlagen (mit Ruby auf Rails, Sinatra usw.)) für mein serverseitiges Rendering verwende und die clientseitigen Vorlagen erstelle, die Backbone ebenfalls verwenden kann Wie bei meinen Jasmine-JavaScript-Spezifikationen: Dadurch werden doppelte Markups zwischen der Server- und der Clientseite vermieden.

Von dort aus müssen Sie einige zusätzliche Schritte ausführen, damit Ihr JavaScript mit dem vom Server gerenderten HTML-Code funktioniert - eine echte progressive Verbesserung. Nehmen Sie das mitgelieferte semantische Markup und erweitern Sie es mit JavaScript.

Ich erstelle zum Beispiel eine Bildergalerie-Anwendung mit pushState. Wenn Sie vom Server /images/1 Anfordern, wird die gesamte Bildergalerie auf dem Server gerendert und der gesamte HTML-, CSS- und JavaScript-Code an Ihren Browser gesendet. Wenn Sie JavaScript deaktiviert haben, funktioniert es einwandfrei. Bei jeder Aktion, die Sie ausführen, wird eine andere URL vom Server angefordert, und der Server rendert das gesamte Markup für Ihren Browser. Wenn Sie jedoch JavaScript aktiviert haben, nimmt das JavaScript das bereits gerenderte HTML zusammen mit einigen vom Server generierten Variablen auf und übernimmt von dort aus.

Hier ist ein Beispiel:

<form id="foo">
  Name: <input id="name"><button id="say">Say My Name!</button>
</form>

Nachdem der Server dies gerendert hat, wird es vom JavaScript abgerufen (in diesem Beispiel mit der Ansicht "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")
  });
});

Dies ist ein sehr einfaches Beispiel, aber ich denke, es bringt den Punkt auf den Punkt.

Wenn ich die Ansicht nach dem Laden der Seite instanziiere, stelle ich der Ansichtsinstanz den vorhandenen Inhalt des vom Server gerenderten Formulars als el für die Ansicht zur Verfügung. Ich rufe not render auf oder lasse die Ansicht ein el für mich generieren, wenn die erste Ansicht geladen wird. Ich habe eine Render-Methode zur Verfügung, nachdem die Ansicht läuft und die Seite alle JavaScript ist. Auf diese Weise kann ich die Ansicht später bei Bedarf erneut rendern.

Wenn Sie bei aktiviertem JavaScript auf die Schaltfläche "Meinen Namen sagen" klicken, wird eine Warnmeldung angezeigt. Ohne JavaScript würde es zurück zum Server senden und der Server könnte den Namen eines HTML-Elements irgendwo rendern.

Bearbeiten

Stellen Sie sich ein komplexeres Beispiel vor, in dem Sie eine Liste haben, die angehängt werden muss (aus den Kommentaren darunter).

Angenommen, Sie haben eine Liste von Benutzern in einem <ul> - Tag. Diese Liste wurde vom Server gerendert, als der Browser eine Anfrage stellte, und das Ergebnis sieht ungefähr so ​​aus:

<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>

Jetzt müssen Sie diese Liste durchlaufen und jedem der <li> - Elemente eine Backbone-Ansicht und ein Modell hinzufügen. Mit dem Attribut data-id Können Sie das Modell ermitteln, von dem jedes Tag stammt. Sie benötigen dann eine Sammlungsansicht und eine Elementansicht, die intelligent genug sind, um sich selbst an diesen HTML-Code anzuhängen.

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();

In diesem Beispiel durchläuft UserListView alle <li> - Tags und hängt für jedes ein Ansichtsobjekt mit dem richtigen Modell an. Es richtet eine Ereignisbehandlungsroutine für das Ereignis zur Änderung des Modellnamens ein und aktualisiert den angezeigten Text des Elements, wenn eine Änderung auftritt.


Diese Art von Prozess, den vom Server gerenderten HTML-Code zu übernehmen und von meinem JavaScript ausführen zu lassen, ist eine großartige Möglichkeit, SEO, Barrierefreiheit und pushState -Unterstützung ins Rollen zu bringen.

Hoffentlich hilft das.

44
Derick Bailey

Ich denke, Sie benötigen Folgendes: http://code.google.com/web/ajaxcrawling/

Sie können auch ein spezielles Backend installieren, das Ihre Seite "rendert", indem Sie Javascript auf dem Server ausführen und es dann für Google bereitstellen.

Kombinieren Sie beide Dinge und Sie haben eine Lösung, ohne die Dinge zweimal zu programmieren. (Solange Ihre App vollständig über Ankerfragmente steuerbar ist.)

22
Ariel

Das Hauptanliegen scheint also TROCKEN zu sein

  • Wenn Sie pushState verwenden, muss Ihr Server für alle URLs (die keine Dateierweiterung zum Bereitstellen von Bildern usw. enthalten) den gleichen genauen Code senden. "/ Mydir/myfile", "/ myotherdir/myotherfile" oder root "/ "- alle Anfragen erhalten den gleichen genauen Code. Sie müssen eine Art URL-Rewrite-Engine haben. Sie können auch ein kleines Stückchen HTML bereitstellen, und der Rest kann von Ihrem CDN stammen (mithilfe von require.js können Abhängigkeiten verwaltet werden - siehe https://stackoverflow.com/a/13813102/159591 ).
  • (Testen Sie die Gültigkeit des Links, indem Sie den Link in Ihr URL-Schema konvertieren und auf Vorhandensein von Inhalten prüfen, indem Sie eine statische oder dynamische Quelle abfragen. Wenn der Link ungültig ist, senden Sie eine 404-Antwort.)
  • Wenn die Anfrage nicht von einem Google Bot stammt, wird sie normal verarbeitet.
  • Wenn die Anfrage von einem Google Bot stammt, verwenden Sie phantom.js - headless Webkit-Browser ( "Ein headless Browser ist einfach ein voll ausgestatteter Webbrowser ohne visuelle Oberfläche." ) um html und javascript auf dem server zu rendern und dem google bot das resultierende html zu senden. Während der Bot den HTML-Code analysiert, kann er Ihre anderen "pushState" -Links/Seiten auf dem Server <a href="/someotherpage">mylink</a>, der Server schreibt die URL in Ihre Anwendungsdatei um, lädt sie in phantom.js und das resultierende HTML wird an den Bot gesendet, und so weiter ...
  • Für dein HTML gehe ich davon aus, dass du normale Links mit irgendeiner Art von Hijacking verwendest (z. B. mit backbone.js https://stackoverflow.com/a/9331734/159591 )
  • Um Verwechslungen mit Links zu vermeiden, trennen Sie Ihren API-Code, der JSON dient, in eine separate Unterdomäne, z. api.mysite.com
  • Um die Leistung zu verbessern, können Sie Ihre Websiteseiten für Suchmaschinen vorab außerhalb der Geschäftszeiten verarbeiten, indem Sie statische Versionen der Seiten mit demselben Mechanismus wie phantom.js erstellen und die statischen Seiten anschließend für Google Bots bereitstellen. Die Vorverarbeitung kann mit einer einfachen App erfolgen, die <a> Stichworte. In diesem Fall ist die Behandlung von 404 einfacher, da Sie einfach die Existenz der statischen Datei mit einem Namen überprüfen können, der den URL-Pfad enthält.
  • Wenn du benutzt #! Hash-Bang-Syntax für Ihre Site-Links Es gilt ein ähnliches Szenario, mit der Ausnahme, dass die URL-Server-Engine nach _escaped_fragment_ in der URL Ausschau hält und die URL gemäß Ihrem URL-Schema formatiert.
  • Es gibt einige Integrationen von node.js mit phantom.js auf github und Sie können node.js als Webserver verwenden, um HTML-Ausgaben zu erstellen.

Hier sind einige Beispiele, in denen phantom.js für seo verwendet wird:

http://backbonetutorials.com/seo-for-single-page-apps/

http://thedigitalself.com/blog/seo-and-javascript-with-phantomjs-server-side-rendering

17
Leonidaz

Wenn Sie Rails verwenden, versuchen Sie poirot . Es ist ein Juwel, das die Wiederverwendung von Schnurrbart oder Lenker clientseitigen und serverseitigen Vorlagen zum Kinderspiel macht.

Erstellen Sie eine Datei in Ihren Ansichten wie _some_thingy.html.mustache.

Serverseite rendern:

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

Legen Sie die Vorlage für die clientseitige Verwendung auf Ihren Kopf:

<%= template_include_tag 'some_thingy' %>

Rendre Client-Seite:

html = poirot.someThingy(my_model)
4
Tim Scott

Um einen etwas anderen Blickwinkel einzunehmen, wäre Ihre zweite Lösung die richtige in Bezug auf Barrierefreiheit ... Sie würden Benutzern, die kein Javascript verwenden können (solche mit Bildschirmleseprogrammen usw.), alternativen Inhalt bereitstellen. .

Dies fügt automatisch die Vorteile von SEO hinzu und würde meiner Meinung nach von Google nicht als "ungezogene" Technik angesehen.

3
Clive

Interessant. Ich habe nach praktikablen Lösungen gesucht, aber es scheint ziemlich problematisch zu sein.

Eigentlich habe ich mich mehr auf Ihren 2. Ansatz konzentriert:

Lassen Sie den Server eine spezielle Website nur für die Suchmaschinen-Bots bereitstellen. Wenn ein normaler Benutzer http://example.com/my_path besucht, sollte ihm der Server eine JavaScript-starke Version der Website geben. Aber wenn der Google-Bot ihn besucht, sollte der Server ihm minimalen HTML-Code mit dem Inhalt geben, den Google indizieren soll.

Hier ist meine Meinung zur Lösung des Problems. Es ist zwar nicht bestätigt, dass es funktioniert, aber es kann anderen Entwicklern Einblicke oder Ideen geben.

Angenommen, Sie verwenden ein JS-Framework, das die Push-Status-Funktionalität unterstützt, und Ihr Backend-Framework ist Ruby on Rails. Sie haben eine einfache Blog-Site und möchten, dass Suchmaschinen alle Ihre Blogs indizieren Artikel index und show Seiten.

Nehmen wir an, Sie haben Ihre Routen folgendermaßen eingerichtet:

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

Stellen Sie sicher, dass jeder serverseitige Controller dieselbe Vorlage rendert, die Ihr clientseitiges Framework zum Ausführen benötigt (html/css/javascript/etc). Wenn keiner der Controller in der Anforderung übereinstimmt (in diesem Beispiel haben wir nur einen REST-Satz von Aktionen für ArticlesController), stimmen Sie einfach mit etwas anderem überein und rendern Sie einfach die Vorlage und lassen Sie das clientseitige Framework verarbeiten das routing. Der einzige Unterschied zwischen der Aktivierung eines Controllers und der Aktivierung des Platzhalter-Matchers besteht in der Möglichkeit, Inhalte basierend auf der URL zu rendern, die für JavaScript-deaktivierte Geräte angefordert wurde.

Soweit ich weiß, ist es eine schlechte Idee, Inhalte zu rendern, die für Browser nicht sichtbar sind. Wenn Google es indiziert, gehen die Leute über Google, um eine bestimmte Seite zu besuchen, und es gibt keinen Inhalt, dann werden Sie wahrscheinlich bestraft. Mir fällt ein, dass Sie Inhalte in einem div Knoten rendern, den Sie display: none in CSS.

Ich bin mir jedoch ziemlich sicher, dass es keine Rolle spielt, wenn Sie dies einfach tun:

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

Und dann mit JavaScript, das nicht ausgeführt wird, wenn ein JavaScript-deaktiviertes Gerät die Seite öffnet:

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

Auf diese Weise wird Google und allen Nutzern mit JavaScript-deaktivierten Geräten der unformatierte/statische Inhalt angezeigt. Der Inhalt ist also physisch vorhanden und für jeden mit JavaScript-deaktivierten Geräten sichtbar.

Wenn ein Benutzer dieselbe Seite besucht und tatsächlich JavaScript aktiviert hat, wird #no-js Knoten wird entfernt, damit Ihre Anwendung nicht überladen wird. Dann verarbeitet Ihr clientseitiges Framework die Anforderung über den Router und zeigt an, was ein Benutzer sehen soll, wenn JavaScript aktiviert ist.

Ich denke, dies könnte eine gültige und ziemlich einfach anzuwendende Technik sein. Dies hängt jedoch möglicherweise von der Komplexität Ihrer Website/Anwendung ab.

Bitte korrigieren Sie mich, wenn dies nicht der Fall ist. Ich dachte nur, ich würde meine Gedanken teilen.

1

Verwenden Sie NodeJS auf der Serverseite, durchsuchen Sie Ihren clientseitigen Code und leiten Sie die URL jeder http-Anforderung (mit Ausnahme der statischen http-Ressourcen) durch einen serverseitigen Client, um den ersten "bootsnap" (einen Schnappschuss der Seite, deren Status) bereitzustellen. Verwenden Sie so etwas wie jsdom, um jquery dom-ops auf dem Server zu verarbeiten. Richten Sie nach der Rückkehr des Boot-Snaps die Websocket-Verbindung ein. Wahrscheinlich am besten, um zwischen einem Websocket-Client und einem serverseitigen Client zu unterscheiden, indem Sie eine Art Wrapper-Verbindung auf der Clientseite herstellen (serverseitiger Client kann direkt mit dem Server kommunizieren). Ich habe an so etwas gearbeitet: https://github.com/jvanveen/rnet/

1
Phrearch

Verwenden Sie Google Closure Template , um Seiten zu rendern. Es wird in Javascript oder Java kompiliert, sodass es einfach ist, die Seite entweder auf der Client- oder der Serverseite zu rendern. Rendern Sie bei der ersten Begegnung mit jedem Kunden den HTML-Code und fügen Sie Javascript als Link in der Kopfzeile hinzu. Crawler liest nur das HTML, aber der Browser führt Ihr Skript aus. Alle nachfolgenden Anforderungen des Browsers können gegen die API ausgeführt werden, um den Datenverkehr zu minimieren.

0
Aleš Kotnik