Siti Web e SEO "a pagina singola"


128

Al giorno d'oggi ci sono molti strumenti interessanti per creare potenti siti Web JavaScript a "pagina singola". A mio avviso, ciò viene fatto correttamente lasciando che il server funga da API (e niente di più) e che il client gestisca tutte le cose di generazione HTML. Il problema con questo "modello" è la mancanza di supporto ai motori di ricerca. Mi vengono in mente due soluzioni:

  1. Quando l'utente accede al sito Web, consenti al server di visualizzare la pagina esattamente come farebbe il client durante la navigazione. Quindi, se vado http://example.com/my_pathdirettamente al server, renderei la stessa cosa che farebbe il client se vado /my_pathattraverso pushState.
  2. Consenti al server di fornire un sito Web speciale solo per i robot dei motori di ricerca. Se un utente normale visita http://example.com/my_pathil server dovrebbe fornirgli una versione pesante del sito Web JavaScript. Ma se il bot di Google visita, il server dovrebbe fornirgli un minimo di HTML con il contenuto che voglio che Google indicizzi.

La prima soluzione è discussa più avanti qui . Ho lavorato su un sito Web facendo questo e non è stata un'esperienza molto bella. Non è ASCIUTTO e nel mio caso ho dovuto utilizzare due motori di template diversi per il client e il server.

Penso di aver visto la seconda soluzione per alcuni vecchi siti Web Flash. Mi piace questo approccio molto più del primo e con lo strumento giusto sul server potrebbe essere fatto abbastanza indolore.

Quindi quello che mi chiedo davvero è il seguente:

  • Riesci a pensare a una soluzione migliore?
  • Quali sono gli svantaggi della seconda soluzione? Se Google in qualche modo scopre che non sto offrendo esattamente lo stesso contenuto per il bot di Google come utente normale, verrei quindi punito nei risultati di ricerca?

Risposte:


44

Mentre # 2 potrebbe essere "più facile" per te come sviluppatore, fornisce solo la scansione dei motori di ricerca. E sì, se Google scopre che stai offrendo diversi contenuti, potresti essere penalizzato (non ne sono un esperto, ma ne ho sentito parlare).

Sia la SEO che l'accessibilità (non solo per le persone disabili, ma l'accessibilità tramite dispositivi mobili, dispositivi touchscreen e altre piattaforme informatiche / abilitate per Internet) hanno entrambe una filosofia di base simile: markup semanticamente ricco che è "accessibile" (cioè può essere accessibile, visualizzato, letto, elaborato o utilizzato in altro modo) su tutti questi diversi browser. Uno screen reader, un crawler del motore di ricerca o un utente con JavaScript abilitato, dovrebbero essere tutti in grado di utilizzare / indicizzare / comprendere le funzionalità principali del tuo sito senza problemi.

pushStatenon aggiunge a questo onere, nella mia esperienza. Porta solo ciò che era un ripensamento e "se abbiamo tempo" in prima linea nello sviluppo web.

Quello che descrivi nell'opzione n. 1 è di solito il modo migliore di procedere, ma, come altri problemi di accessibilità e SEO, farlo con pushStateun'app pesante di JavaScript richiede una pianificazione anticipata o diventerà un onere significativo. Dovrebbe essere inserito nella pagina e nell'architettura dell'applicazione dall'inizio: il retrofit è doloroso e causerà più duplicazioni del necessario.

Di pushStaterecente ho lavorato con SEO per un paio di applicazioni diverse e ho trovato quello che penso sia un buon approccio. Fondamentalmente segue il tuo articolo n. 1, ma tiene conto della non duplicazione di html / template.

La maggior parte delle informazioni sono disponibili in questi due post di blog:

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

e

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

L'essenza di ciò è che uso i modelli ERB o HAML (che eseguono Ruby on Rails, Sinatra, ecc.) Per il mio rendering lato server e per creare i modelli lato client che Backbone può usare, così come per le mie specifiche JavaScript di Jasmine. Ciò elimina la duplicazione del markup tra il lato server e il lato client.

Da lì, devi fare alcuni passi aggiuntivi per far funzionare il tuo JavaScript con l'HTML reso dal server - vero miglioramento progressivo; prendendo il markup semantico che è stato consegnato e migliorandolo con JavaScript.

Ad esempio, sto creando un'applicazione per la galleria di immagini con pushState. Se richiesto /images/1dal server, eseguirà il rendering dell'intera galleria di immagini sul server e invierà tutto il codice HTML, CSS e JavaScript fino al browser. Se JavaScript è disabilitato, funzionerà perfettamente. Ogni azione intrapresa richiederà un URL diverso dal server e il server renderà tutto il markup per il tuo browser. Se hai JavaScript abilitato, tuttavia, JavaScript prenderà l'HTML già renderizzato insieme ad alcune variabili generate dal server e subentrerà da lì.

Ecco un esempio:

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

Dopo il rendering del server, JavaScript lo raccoglierà (usando una vista Backbone.js in questo esempio)

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")
  });
});

Questo è un esempio molto semplice, ma penso che ottenga il punto.

Quando installo la vista dopo il caricamento della pagina, sto fornendo il contenuto esistente del modulo che è stato reso dal server, all'istanza della vista come elper la vista. Non sto chiamando render o non ho la vista generare un elper me, quando viene caricata la prima vista. Ho un metodo di rendering disponibile dopo che la vista è attiva e funzionante e la pagina è tutta JavaScript. Questo mi consente di visualizzare nuovamente la vista in un secondo momento, se necessario.

Facendo clic sul pulsante "Pronuncia il mio nome" con JavaScript abilitato, verrà visualizzata una finestra di avviso. Senza JavaScript, sarebbe postare di nuovo sul server e il server potrebbe rendere il nome su un elemento HTML da qualche parte.

modificare

Considera un esempio più complesso, in cui hai un elenco che deve essere allegato (dai commenti sotto questo)

Supponi di avere un elenco di utenti in un <ul>tag. Questo elenco è stato reso dal server quando il browser ha effettuato una richiesta e il risultato è simile al seguente:

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

Ora è necessario scorrere questo elenco e collegare una vista e un modello Backbone a ciascuno degli <li>elementi. Con l'uso data-iddell'attributo, puoi trovare facilmente il modello da cui proviene ogni tag. Avrai quindi bisogno di una vista raccolta e di un elemento abbastanza intelligente da collegarsi a questo 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();

In questo esempio, UserListVieweseguirà il ciclo tra tutti i <li>tag e allegherà un oggetto vista con il modello corretto per ognuno. imposta un gestore eventi per l'evento di modifica del nome del modello e aggiorna il testo visualizzato dell'elemento quando si verifica una modifica.


Questo tipo di processo, per prendere l'html reso dal server e far sì che il mio JavaScript prenda il sopravvento e lo esegua, è un ottimo modo per far girare le cose per SEO, accessibilità e pushStatesupporto.

Spero che aiuti.


Ottengo il tuo punto, ma la cosa interessante è come viene eseguito il rendering dopo "il tuo JavaScript prende il sopravvento". In un esempio più complicato, potrebbe essere necessario utilizzare un modello non compilato sul client, eseguendo il ciclo tra un array di utenti per creare un elenco. La visualizzazione esegue nuovamente il rendering ogni volta che cambia il modello di un utente. Come lo faresti senza duplicare i modelli (e senza chiedere al server di rendere la vista per il client)?
user544941,

i 2 post di blog che ho collegato dovrebbero mostrare collettivamente come avere modelli che possono essere utilizzati sul client e sul server, senza duplicazione. il server dovrà rendere l'intera pagina se si desidera che sia accessibile e SEO friendly. ho aggiornato la mia risposta per includere un esempio più complesso di collegamento a un elenco utenti reso dal server
Derick Bailey

22

Penso che tu abbia bisogno di questo: http://code.google.com/web/ajaxcrawling/

Puoi anche installare un backend speciale che "esegue il rendering" della tua pagina eseguendo javascript sul server e quindi lo serve a google.

Combina entrambe le cose e avrai una soluzione senza programmare le cose due volte. (Finché l'app è completamente controllabile tramite frammenti di ancoraggio.)


In realtà, non è quello che sto cercando. Queste sono alcune varianti della prima soluzione e, come ho già detto, non sono molto contento di questo approccio.
user544941

2
Non hai letto tutta la mia risposta. Usi anche un backend speciale che rende javascript per te - non scrivi cose due volte.
Ariel,

Sì, l'ho letto. Ma se ti facessi bene sarebbe un programma infernale, dal momento che dovrebbe simulare ogni azione che innesca il pushState. In alternativa, potrei dargli le azioni direttamente, ma poi non siamo più così ASCIUTTI.
user544941

2
Penso che sia fondamentalmente un browser senza la parte anteriore. Ma sì, devi rendere il programma completamente controllabile da frammenti di ancoraggio. È inoltre necessario assicurarsi che tutti i collegamenti contengano il frammento corretto, insieme a, o invece di, suClicks.
Ariel,

17

Quindi, sembra che la preoccupazione principale sia quella di ESSERE SECCO

  • Se stai usando pushState chiedi al tuo server di inviare lo stesso codice esatto per tutti gli URL (che non contengono un'estensione di file per pubblicare immagini, ecc.) "/ Mydir / myfile", "/ myotherdir / myotherfile" o root "/ "- tutte le richieste ricevono lo stesso codice esatto. Devi avere un motore di riscrittura dell'URL di qualche tipo. Puoi anche servire un po 'di html e il resto può provenire dalla tua CDN (usando request.js per gestire le dipendenze - vedi https://stackoverflow.com/a/13813102/1595913 ).
  • (verifica la validità del link convertendo il link nel tuo schema URL e verificando l'esistenza del contenuto eseguendo una query su una fonte statica o dinamica. Se non è valida invia una risposta 404).
  • Quando la richiesta non proviene da un bot di Google, è sufficiente elaborare normalmente.
  • Se la richiesta proviene da un bot di Google, si utilizza phantom.js - browser webkit senza testa ( "Un browser senza testa è semplicemente un browser Web completo senza interfaccia visiva" ) per eseguire il rendering di html e javascript sul server e inviare il google bot l'html risultante. Man mano che il bot analizza l'html, può colpire altri tuoi link "pushState" / qualche pagina sul server<a href="https://stackoverflow.com/someotherpage">mylink</a> , il server riscrive l'URL nel file dell'applicazione, lo carica in phantom.js e l'html risultante viene inviato al bot e così via. ..
  • Per il tuo html suppongo che tu stia usando collegamenti normali con un qualche tipo di dirottamento (ad es. Usando con backbone.js https://stackoverflow.com/a/9331734/1595913 )
  • Per evitare confusione con qualsiasi collegamento, separa il tuo codice api che serve json in un sottodominio separato, ad esempio api.mysite.com
  • Per migliorare le prestazioni, è possibile pre-elaborare le pagine del sito per i motori di ricerca in anticipo durante le ore di inattività creando versioni statiche delle pagine utilizzando lo stesso meccanismo con phantom.js e, di conseguenza, servire le pagine statiche ai bot di Google. La preelaborazione può essere eseguita con alcune semplici app in grado di analizzare<a> tag. In questo caso la gestione di 404 è più semplice poiché puoi semplicemente verificare l'esistenza del file statico con un nome che contiene il percorso dell'URL.
  • Se usi #! La sintassi hash bang per i collegamenti del tuo sito si applica a uno scenario simile, tranne per il fatto che il motore del server url riscrivere cercherebbe _escaped_fragment_ nell'URL e formatterà l'URL al tuo schema url.
  • Esistono un paio di integrazioni di node.js con phantom.js su github e puoi usare node.js come server web per produrre output html.

Ecco un paio di esempi usando phantom.js per seo:

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

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


4

Se stai usando Rails, prova poirot . È un gioiello che rende estremamente semplice riutilizzare i modelli di baffi o manubri lato client e server.

Crea un file nelle tue viste come _some_thingy.html.mustache.

Rendering lato server:

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

Metti il ​​modello in testa per l'uso lato client:

<%= template_include_tag 'some_thingy' %>

Rendre lato client:

html = poirot.someThingy(my_model)

3

Per assumere un angolo leggermente diverso, la tua seconda soluzione sarebbe quella corretta in termini di accessibilità ... forniresti contenuti alternativi agli utenti che non possono usare javascript (quelli con screen reader, ecc.).

Ciò aggiungerebbe automaticamente i vantaggi del SEO e, secondo me, non sarebbe considerato una tecnica "cattiva" da parte di Google.


E qualcuno ha dimostrato che ti sbagli? È passato del tempo da quando il commento è stato pubblicato
jkulak

1

Interessante. Ho cercato soluzioni praticabili, ma sembra essere abbastanza problematico.

In realtà mi stavo sporgendo maggiormente verso il tuo secondo approccio:

Consenti al server di fornire un sito Web speciale solo per i robot dei motori di ricerca. Se un utente normale visita http://example.com/my_path, il server dovrebbe fornirgli una versione pesante JavaScript del sito Web. Ma se il bot di Google visita, il server dovrebbe fornirgli un minimo di HTML con il contenuto che voglio che Google indicizzi.

Ecco la mia opinione sulla soluzione del problema. Sebbene non sia confermato che funzioni, potrebbe fornire alcune idee o idee per altri sviluppatori.

Supponiamo di utilizzare un framework JS che supporta la funzionalità "push state" e che il framework back-end è Ruby on Rails. Hai un semplice sito di blog e desideri che i motori di ricerca indicizzino tutto il tuo articolo indexe le tue showpagine.

Supponiamo che tu abbia impostato i tuoi percorsi in questo modo:

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

Assicurarsi che ogni controller lato server esegua il rendering dello stesso modello richiesto per l'esecuzione del framework lato client (html / css / javascript / etc). Se nessuno dei controller corrisponde nella richiesta (in questo esempio abbiamo solo una serie di azioni RESTful per il ArticlesController), quindi basta abbinare qualsiasi altra cosa e renderizzare il modello e lasciare che il framework lato client gestisca il routing. L'unica differenza tra colpire un controller e colpire il matcher jolly sarebbe la possibilità di eseguire il rendering del contenuto in base all'URL richiesto ai dispositivi con disabilitazione JavaScript.

Da quello che ho capito, è una cattiva idea rendere il contenuto non visibile ai browser. Quindi, quando Google lo indicizza, le persone visitano Google per visitare una determinata pagina e non ci sono contenuti, quindi verrai probabilmente penalizzato. Ciò che viene in mente è che si esegue il rendering del contenuto in un divnodo che si è display: nonein CSS.

Tuttavia, sono abbastanza sicuro che non importa se lo fai semplicemente:

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

E quindi usare JavaScript, che non viene eseguito quando un dispositivo disabilitato JavaScript apre la pagina:

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

In questo modo, per Google e per chiunque disponga di dispositivi con disabilitazione JavaScript, vedrebbero il contenuto non elaborato / statico. Quindi il contenuto è fisicamente presente ed è visibile a chiunque disponga di dispositivi con disabilitazione JavaScript.

Ma, quando un utente visita stessa pagina e in realtà ha permesso JavaScript, il#no-js nodo verrà rimosso in modo da non ingombrare la vostra applicazione. Quindi il framework lato client gestirà la richiesta tramite il suo router e visualizzerà ciò che un utente dovrebbe vedere quando JavaScript è abilitato.

Penso che questa potrebbe essere una tecnica valida e abbastanza facile da usare. Anche se ciò potrebbe dipendere dalla complessità del tuo sito Web / applicazione.

Tuttavia, per favore correggimi se non lo è. Ho pensato di condividere i miei pensieri.


1
Bene, se prima visualizzi il contenuto e dopo un po 'lo rimuovi, molto probabilmente l'utente finale potrebbe notare che il contenuto lampeggia / sfarfallio nel suo browser :) Soprattutto se si tratta di un browser lento, di enormi dimensioni di contenuto HTML che provi a visualizzare / rimuovere e alcuni ritardo prima che il codice JS venga caricato ed eseguito. Cosa ne pensi?
Evereq,

1

Usa NodeJS sul lato server, naviga il codice del lato client e instrada l'uri di ogni richiesta HTTP (ad eccezione delle risorse http statiche) attraverso un client lato server per fornire il primo "bootsnap" (un'istantanea della pagina in cui si trova). Usa qualcosa come jsdom per gestire le dom-op jquery sul server. Una volta restituito il bootsnap, imposta la connessione al websocket. Probabilmente è meglio distinguere tra un client websocket e un client lato server creando una sorta di connessione wrapper sul lato client (il client lato server può comunicare direttamente con il server). Ho lavorato su qualcosa del genere: https://github.com/jvanveen/rnet/


0

Utilizza il modello di chiusura di Google per eseguire il rendering delle pagine. Si compila in javascript o java, quindi è facile eseguire il rendering della pagina sul lato client o server. Al primo incontro con ogni client, renderizza l'html e aggiungi javascript come link nell'intestazione. Il crawler leggerà solo l'html ma il browser eseguirà il tuo script. Tutte le richieste successive dal browser potrebbero essere inviate contro l'API per ridurre al minimo il traffico.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.