"Tek sayfalı" JS web siteleri ve SEO


128

Günümüzde güçlü "tek sayfalı" JavaScript web siteleri oluşturmak için birçok harika araç var. Bana göre, bu, sunucunun bir API olarak hareket etmesine izin vererek (ve başka bir şey değil) ve müşterinin tüm HTML oluşturma işlerini halletmesine izin vererek doğru bir şekilde yapılır. Bu "kalıp" ile ilgili sorun, arama motoru desteğinin olmamasıdır. İki çözüm düşünebilirim:

  1. Kullanıcı web sitesine girdiğinde, sunucunun sayfayı tam olarak istemcinin gezinme sırasında yapacağı gibi oluşturmasına izin verin. Yani, eğer http://example.com/my_pathdoğrudan gidersem , sunucu /my_pathpushState üzerinden geçersem istemcinin yapacağı şeyi işleyecektir.
  2. Sunucunun yalnızca arama motoru botları için özel bir web sitesi sağlamasına izin verin. Normal bir kullanıcı ziyaret ederse http://example.com/my_path, sunucu ona web sitesinin JavaScript ağır bir sürümünü vermelidir. Ancak Google botu ziyaret ederse, sunucunun Google’ın dizine eklemesini istediğim içerikle ona minimum düzeyde HTML vermesi gerekir.

İlk çözüm burada daha ayrıntılı tartışılmaktadır . Bunu yapan bir web sitesi üzerinde çalışıyorum ve bu çok hoş bir deneyim değil. KURU değil ve benim durumumda istemci ve sunucu için iki farklı şablon motoru kullanmak zorunda kaldım.

Sanırım bazı eski Flash web siteleri için ikinci çözümü gördüm. Bu yaklaşımı birincisinden çok daha fazla seviyorum ve sunucuda doğru araçla oldukça zahmetsizce yapılabilir.

Yani gerçekten merak ettiğim şey şudur:

  • Daha iyi bir çözüm düşünebiliyor musunuz?
  • İkinci çözümün dezavantajları nelerdir? Google bir şekilde normal bir kullanıcı olarak Google botu için aynı içeriği sunmadığımı anlarsa, arama sonuçlarında cezalandırılır mıyım?

Yanıtlar:


44

2 numara bir geliştirici olarak sizin için "daha kolay" olsa da, yalnızca arama motoru taraması sağlar. Ve evet, Google farklı içerik sunduğunuzu anlarsa, cezalandırılabilirsiniz (bu konuda uzman değilim, ancak bunun olduğunu duydum).

Hem SEO hem de erişilebilirlik (yalnızca engelli kişiler için değil, aynı zamanda mobil cihazlar, dokunmatik ekranlı cihazlar ve diğer standart dışı bilgi işlem / internet etkin platformlar aracılığıyla erişilebilirlik) benzer bir temel felsefeye sahiptir: "erişilebilir" olan (yani, tüm bu farklı tarayıcılara erişilmesi, görüntülenmesi, okunması, işlenmesi veya başka şekilde kullanılması. Bir ekran okuyucu, bir arama motoru tarayıcısı veya JavaScript'in etkin olduğu bir kullanıcı, sitenizin temel işlevlerini sorunsuz bir şekilde kullanabilir / dizine ekleyebilir / anlayabilmelidir.

pushStatebenim deneyimime göre bu yüke bir şey eklemiyor. Sadece sonradan düşünülen şeyleri ve "zamanımız varsa" web geliştirmenin ön saflarına taşır.

1. seçenekte açıkladığınız şey genellikle gitmenin en iyi yoludur - ancak diğer erişilebilirlik ve SEO sorunları gibi, pushState JavaScript ağırlıklı bir uygulamada yapmak önceden planlama gerektirir, aksi takdirde önemli bir yük haline gelir. Başlangıçtan itibaren sayfaya ve uygulama mimarisine dahil edilmelidir - güçlendirme zahmetlidir ve gerekenden daha fazla tekrarlamaya neden olur.

İle çalışıyorum pushStateSon zamanlarda birkaç farklı uygulama için ve SEO ve iyi bir yaklaşım olduğunu düşündüğüm şeyi buldum. Temelde 1 numaralı öğenizi takip eder, ancak html / şablonların kopyalanmamasını sağlar.

Bilgilerin çoğu şu iki blog gönderisinde bulunabilir:

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

ve

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

İşin özü, ERB veya HAML şablonlarını (Ruby on Rails, Sinatra, vb. Çalıştıran) sunucu tarafı renderım için ve Backbone'un kullanabileceği istemci tarafı şablonları ile Jasmine JavaScript spesifikasyonlarım oluşturmak için kullanıyorum. Bu, sunucu tarafı ile istemci tarafı arasındaki işaretlemenin kopyalanmasını engeller.

Buradan, JavaScript'inizin sunucu tarafından oluşturulan HTML ile çalışmasını sağlamak için birkaç ek adım atmanız gerekir - gerçek aşamalı geliştirme; teslim edilen anlamsal biçimlendirmeyi almak ve onu JavaScript ile geliştirmek.

Örneğin, ile bir resim galerisi uygulaması oluşturuyorum pushState. /images/1Sunucudan talep ederseniz , tüm resim galerisini sunucuda işler ve tüm HTML, CSS ve JavaScript'i tarayıcınıza gönderir. JavaScript'i devre dışı bıraktıysanız, mükemmel bir şekilde çalışacaktır. Yaptığınız her işlem, sunucudan farklı bir URL talep edecek ve sunucu, tarayıcınız için tüm işaretlemeyi işleyecektir. JavaScript'i etkinleştirdiyseniz, JavaScript zaten oluşturulmuş HTML'yi sunucu tarafından oluşturulan birkaç değişkenle birlikte alır ve buradan devralır.

İşte bir örnek:

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

Sunucu bunu oluşturduktan sonra, JavaScript onu alır (bu örnekte bir Backbone.js görünümü kullanarak)

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

Bu çok basit bir örnek ama bence asıl noktayı anlıyor.

Sayfa yüklendikten sonra görünümü başlattığımda, sunucu tarafından oluşturulan formun mevcut içeriğini görünüm eliçin olduğu gibi görünüm örneğine sağlıyorum. Ben am değil işlemek arayarak veya görünümü bir oluşturmak zorunda elbirinci görünümü yüklendiğinde, benim için. Görünüm açılıp çalıştıktan ve sayfanın tamamı JavaScript olduktan sonra kullanabileceğim bir oluşturma yöntemim var. Bu, gerekirse görünümü daha sonra yeniden oluşturmama izin veriyor.

JavaScript etkinken "Adımı Söyle" düğmesine tıklamak bir uyarı kutusuna neden olacaktır. JavaScript olmasaydı, sunucuya geri gönderirdi ve sunucu, adı bir yerde bir html öğesine dönüştürebilirdi.

Düzenle

Eklenmesi gereken bir listenin olduğu daha karmaşık bir örnek düşünün (bunun altındaki yorumlardan)

Bir <ul>etikette bir kullanıcı listeniz olduğunu varsayalım. Bu liste, tarayıcı bir istekte bulunduğunda sunucu tarafından oluşturuldu ve sonuç şuna benzer:

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

Şimdi bu listeden geçmeniz ve her <li>öğeye bir Omurga görünümü ve model eklemeniz gerekiyor . data-idÖzniteliğin kullanımıyla, her bir etiketin geldiği modeli kolayca bulabilirsiniz. Ardından, kendisini bu html'ye eklemek için yeterince akıllı bir koleksiyon görünümüne ve öğe görünümüne ihtiyacınız olacak.

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

Bu örnekte, UserListViewtüm <li>etiketlerde döngü oluşturacak ve her biri için doğru modele sahip bir görünüm nesnesi ekleyecektir. modelin isim değişikliği olayı için bir olay işleyicisi kurar ve bir değişiklik meydana geldiğinde öğenin görüntülenen metnini günceller.


Sunucunun oluşturduğu html'yi alıp JavaScript'imin onu devralması ve çalıştırması için bu tür bir süreç, SEO, Erişilebilirlik ve pushStatedestek için işleri ilerletmenin harika bir yoludur .

Umarım yardımcı olur.


Demek istediğini anladım, ancak ilginç olan, "JavaScript'iniz devraldıktan" sonra oluşturmanın nasıl yapıldığı. Daha karmaşık bir örnekte, istemcide bir liste oluşturmak için bir kullanıcı dizisi aracılığıyla derlenmemiş bir şablon kullanmanız gerekebilir. Görünüm, bir kullanıcının modeli her değiştiğinde yeniden oluşturulur. Bunu, şablonları kopyalamadan (ve sunucudan istemcinin görünümünü oluşturmasını istemeden) nasıl yapardınız?
user544941

Bağladığım 2 blog gönderisi, müşteride ve sunucuda kullanılabilecek şablonlara nasıl sahip olunacağını toplu olarak göstermelidir - çoğaltma gerekmez. Erişilebilir ve SEO dostu olmasını istiyorsanız sunucunun sayfanın tamamını oluşturması gerekecektir.
Yanıtımı

22

Buna ihtiyacın olduğunu düşünüyorum: http://code.google.com/web/ajaxcrawling/

Ayrıca, sunucuda javascript çalıştırarak sayfanızı "oluşturan" ve ardından bunu google'a sunan özel bir arka uç da yükleyebilirsiniz.

Her iki şeyi birleştirin ve her şeyi iki kez programlamadan bir çözüme sahip olun. (Uygulamanız bağlantı parçaları aracılığıyla tamamen kontrol edilebildiği sürece.)


Aslında aradığım bu değil. Bunlar ilk çözümün bazı varyantları ve bahsettiğim gibi bu yaklaşımdan pek memnun değilim.
user544941

2
Cevabımın tamamını okumadın. Ayrıca, javascript'i sizin için işleyen özel bir arka uç kullanırsınız - bir şeyleri iki kez yazmazsınız.
Ariel

Evet, okudum. Ama eğer seni doğru anlasaydım, bu müthiş bir program olurdu, çünkü pushState'i tetikleyen her eylemi simüle etmek zorunda kalacaktı. Alternatif olarak, eylemleri doğrudan ona verebilirim, ancak artık o kadar KURU değiliz.
user544941

2
Temelde ön tarafı olmayan bir tarayıcı olduğunu düşünüyorum. Ancak, evet, programı çapa parçalarından tamamen kontrol edilebilir hale getirmelisiniz. Ayrıca, tüm bağlantıların onClicks ile birlikte veya onClicks yerine uygun parçaya sahip olduğundan emin olmanız gerekir.
Ariel

17

Öyleyse, asıl endişenin KURU olmak olduğu görülüyor

  • PushState kullanıyorsanız, sunucunuzun tüm url'ler için aynı kodu göndermesini sağlayın (resimleri sunmak için bir dosya uzantısı içermeyenler, vb.) "/ Mydir / myfile", "/ myotherdir / myotherfile" or root "/ "- tüm istekler aynı kodu alır. Bir tür url yeniden yazma motoruna ihtiyacınız var. Ayrıca küçük bir html biti sunabilirsiniz ve geri kalanı CDN'nizden gelebilir (bağımlılıkları yönetmek için require.js'yi kullanarak - bkz. Https://stackoverflow.com/a/13813102/1595913 ).
  • (URL şemanıza olan bağlantıyı dönüştürerek ve statik veya dinamik bir kaynağı sorgulayarak içeriğin varlığını test ederek bağlantının geçerliliğini test edin. Geçerli değilse 404 yanıtı gönderin.)
  • İstek bir google botundan gelmediğinde, sadece normal şekilde işlem yaparsınız.
  • İstek bir google botundan geliyorsa , sunucuda html ve javascript oluşturmak ve göndermek için phantom.js - başsız webkit tarayıcısı ( "Başsız tarayıcı, görsel arabirimi olmayan tam özellikli bir web tarayıcısıdır." ) Kullanırsınız . google bot sonuçta ortaya çıkan html. Bot html'yi ayrıştırırken, sunucudaki diğer "pushState" bağlantılarınıza / sayfanıza ulaşabilir <a href="https://stackoverflow.com/someotherpage">mylink</a>, sunucu url'yi uygulama dosyanıza yeniden yazar, phantom.js'ye yükler ve elde edilen html bota gönderilir, vb. ..
  • Html'niz için, bir tür ele geçirme içeren normal bağlantılar kullandığınızı varsayıyorum (ör., Backbone.js ile https://stackoverflow.com/a/9331734/1595913 )
  • Herhangi bir bağlantıyla karışıklığı önlemek için, json'a hizmet eden api kodunuzu ayrı bir alt alan adına ayırın, örneğin api.mysite.com
  • Performansı artırmak için, phantom.js ile aynı mekanizmayı kullanarak sayfaların statik sürümlerini oluşturarak mesai saatleri dışında site sayfalarınızı arama motorları için önceden işleyebilir ve sonuç olarak statik sayfaları google botlarına sunabilirsiniz. Ön işleme, <a>etiketleri ayrıştırabilen bazı basit uygulamalarla yapılabilir . Bu durumda, url yolunu içeren bir ada sahip statik dosyanın varlığını kontrol edebileceğinizden, 404'ün işlenmesi daha kolaydır.
  • Eğer kullanırsan #! Site bağlantılarınız için hash bang sözdizimi benzer bir senaryo geçerlidir; tek fark, yeniden yazma url sunucusu motorunun url'de _escaped_fragment_'ı araması ve url'yi url şemanıza göre biçimlendirmesidir.
  • Github üzerinde phantom.js ile node.js'nin birkaç entegrasyonu vardır ve html çıktısı üretmek için web sunucusu olarak node.js'yi kullanabilirsiniz.

İşte seo için phantom.js kullanan birkaç örnek:

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

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


4

Rails kullanıyorsanız, poirot deneyin . Bıyık veya gidon şablonlarını istemci ve sunucu tarafında yeniden kullanmayı son derece basit hale getiren bir mücevher .

Görünümlerinizde gibi bir dosya oluşturun _some_thingy.html.mustache.

Sunucu tarafı oluşturma:

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

Şablonu müşteri tarafında kullanmak için kafanıza koyun:

<%= template_include_tag 'some_thingy' %>

Rendre istemci tarafı:

html = poirot.someThingy(my_model)

3

Biraz farklı bir açıdan bakarsak, ikinci çözümünüz erişilebilirlik açısından doğru çözüm olacaktır ... javascript kullanamayan kullanıcılara (ekran okuyuculu olanlar vb.) Alternatif içerik sağlarsınız.

Bu otomatik olarak SEO'nun faydalarını ekler ve bence Google tarafından 'yaramaz' bir teknik olarak görülmez.


Ve kimse senin yanıldığını kanıtladı mı? Yorumun yayınlanmasından bu yana biraz zaman geçti
jkulak

1

İlginç. Uygulanabilir çözümler arıyordum ama oldukça sorunlu görünüyor.

Aslında 2. yaklaşımınıza daha çok eğiliyordum:

Sunucunun yalnızca arama motoru botları için özel bir web sitesi sağlamasına izin verin. Normal bir kullanıcı http://example.com/my_path adresini ziyaret ederse , sunucu ona web sitesinin JavaScript ağırlıklı bir sürümünü vermelidir. Ancak Google botu ziyaret ederse, sunucunun Google’a dizine eklemesini istediğim içerikle birlikte minimum düzeyde HTML vermesi gerekir.

İşte benim problemi çözme konusundaki düşüncem. Çalıştığı doğrulanmamış olsa da, diğer geliştiriciler için bazı bilgiler veya fikirler sağlayabilir.

"Push durumu" işlevini destekleyen bir JS çerçevesi kullandığınızı ve arka uç çerçevenizin Ruby on Rails olduğunu varsayalım. Basit bir blog siteniz var ve arama motorlarının tüm makale indexve showsayfalarınızı indekslemesini istiyorsunuz .

Diyelim ki rotalarınız şu şekilde ayarlandı:

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

Her sunucu tarafı denetleyicisinin, istemci tarafı çerçevenizin çalıştırması gereken aynı şablonu oluşturduğundan emin olun (html / css / javascript / vb.). Denetleyicilerin hiçbiri istekte eşleşmiyorsa (bu örnekte, yalnızca RESTful eylemler kümesine sahibiz ArticlesController), o zaman başka herhangi bir şeyi eşleştirin ve şablonu oluşturun ve istemci tarafı çerçevesinin yönlendirmeyi işlemesine izin verin. Bir denetleyiciye basmakla joker eşleştiriciye basmak arasındaki tek fark, içeriği JavaScript devre dışı bırakılmış cihazlardan istenen URL'ye göre oluşturma yeteneğidir.

Anladığım kadarıyla tarayıcılar tarafından görülemeyen içeriği oluşturmak kötü bir fikir. Dolayısıyla, Google bunu indekslediğinde, insanlar Google üzerinden belirli bir sayfayı ziyaret ediyorlar ve herhangi bir içerik yok, o zaman muhtemelen cezalandırılacaksınız. Akla gelen , CSS'de bulunduğunuz bir divdüğümde içerik oluşturmanızdır display: none.

Ancak, bunu basitçe yapmanın önemli olmadığına eminim:

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

Ve sonra JavaScript devre dışı bırakılmış bir cihaz sayfayı açtığında çalışmayan JavaScript kullanarak:

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

Bu şekilde, Google ve JavaScript devre dışı cihazlara sahip herkes ham / statik içeriği görebilir. Yani içerik olduğu fiziksel olarak orada ve JavaScript özürlü cihazlarla herkes tarafından görülebilir.

Aslında bir kullanıcının ziyaret aynı sayfa ve zaman, olan JavaScript etkin, #no-jsdüğüm o başvurunuzu kadar kalabalık etmez böylece kaldırılacaktır. Ardından, istemci tarafı çerçeveniz, isteği yönlendiricisi aracılığıyla ele alır ve JavaScript etkinleştirildiğinde bir kullanıcının ne görmesi gerektiğini görüntüler.

Bunun geçerli ve kullanımı oldukça kolay bir teknik olabileceğini düşünüyorum. Bu, web sitenizin / uygulamanızın karmaşıklığına bağlı olsa da.

Yine de, değilse lütfen beni düzeltin. Düşüncelerimi paylaşacağımı düşündüm.


1
İlk önce içeriği görüntüler ve biraz sonra kaldırırsanız, büyük olasılıkla son kullanıcı tarayıcısında içeriğin yanıp söndüğünü / titrediğini fark edebilir :) Özellikle yavaş bir tarayıcıysa, büyük boyutta HTML içeriği göstermeye / kaldırmaya çalışıyorsunuz ve bazılarını JS kodunuz yüklenmeden ve yürütülmeden önce gecikme. Ne düşündüğünü?
Evereq

1

Sunucu tarafında NodeJS kullanın, istemci tarafındaki kodunuza göz atın ve her http-isteğini (statik http kaynakları hariç), ilk 'önyükleme'yi (sayfanın durumunun anlık görüntüsünü) sağlamak için bir sunucu tarafı istemcisi aracılığıyla yönlendirin. Sunucudaki jquery dom-op'larını işlemek için jsdom gibi bir şey kullanın. Bootnap geri döndükten sonra websocket bağlantısını kurun. İstemci tarafında bir tür sarmalayıcı bağlantısı kurarak bir websocket istemcisi ile sunucu tarafı istemcisi arasında ayrım yapmak muhtemelen en iyisidir (sunucu tarafı istemcisi doğrudan sunucuyla iletişim kurabilir). Bunun gibi bir şey üzerinde çalışıyorum: https://github.com/jvanveen/rnet/


0

Sayfaları oluşturmak için Google Kapatma Şablonunu kullanın . Javascript veya java için derlenir, böylece sayfayı istemci veya sunucu tarafında oluşturmak kolaydır. Her istemciyle ilk karşılaştığınızda, html'yi oluşturun ve javascript'i başlıkta bağlantı olarak ekleyin. Tarayıcı yalnızca html'yi okur, ancak tarayıcı komut dosyanızı çalıştırır. Tarayıcıdan sonraki tüm istekler, trafiği en aza indirmek için api'ye karşı yapılabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.