Situs web dan SEO JS "satu halaman"

128

Ada banyak alat keren untuk membuat situs web JavaScript "satu halaman" yang kuat saat ini. Menurut pendapat saya, ini dilakukan dengan membiarkan server bertindak sebagai API (dan tidak lebih) dan membiarkan klien menangani semua hal yang menghasilkan HTML. Masalah dengan "pola" ini adalah kurangnya dukungan mesin pencari. Saya dapat memikirkan dua solusi:

  1. Ketika pengguna memasuki situs web, biarkan server merender halaman persis seperti yang dilakukan klien saat bernavigasi. Jadi jika saya pergi ke http://example.com/my_pathserver secara langsung akan membuat hal yang sama seperti klien jika saya pergi /my_pathmelalui pushState.
  2. Biarkan server menyediakan situs web khusus hanya untuk bot mesin pencari. Jika pengguna normal mengunjungi http://example.com/my_pathserver harus memberinya versi berat JavaScript dari situs web. Tetapi jika bot Google mengunjungi, server harus memberikan HTML minimal dengan konten yang saya ingin Google indeks.

Solusi pertama dibahas lebih lanjut di sini . Saya telah mengerjakan situs web melakukan ini dan itu bukan pengalaman yang sangat menyenangkan. Ini bukan KERING dan dalam kasus saya, saya harus menggunakan dua mesin template yang berbeda untuk klien dan server.

Saya pikir saya telah melihat solusi kedua untuk beberapa situs web Flash yang bagus. Saya suka pendekatan ini lebih dari yang pertama dan dengan alat yang tepat di server itu bisa dilakukan tanpa rasa sakit.

Jadi yang saya benar-benar ingin tahu adalah sebagai berikut:

  • Bisakah Anda memikirkan solusi yang lebih baik?
  • Apa kerugiannya dengan solusi kedua? Jika Google dengan cara tertentu mengetahui bahwa saya tidak menayangkan konten yang sama persis untuk bot Google sebagai pengguna biasa, apakah saya kemudian akan dihukum dalam hasil pencarian?
pengguna544941
sumber

Jawaban:

44

Meskipun # 2 mungkin "lebih mudah" bagi Anda sebagai pengembang, itu hanya menyediakan perayapan mesin pencari. Dan ya, jika Google mengetahui bahwa Anda menyajikan konten yang berbeda, Anda mungkin akan dihukum (saya bukan ahli dalam hal itu, tetapi saya pernah mendengarnya terjadi).

Baik SEO dan aksesibilitas (tidak hanya untuk penyandang cacat, tetapi aksesibilitas melalui perangkat seluler, perangkat layar sentuh, dan platform berkemampuan internet / non-standar lainnya) keduanya memiliki filosofi mendasar yang serupa: peningkatan kaya semantik yang "dapat diakses" (yaitu dapat diakses, dilihat, dibaca, diproses, atau digunakan) untuk semua browser yang berbeda ini. Pembaca layar, perayap mesin pencari, atau pengguna dengan JavaScript diaktifkan, semuanya harus dapat menggunakan / mengindeks / memahami fungsi inti situs Anda tanpa masalah.

pushStatetidak menambah beban ini, dalam pengalaman saya. Ini hanya membawa apa yang dulunya menjadi renungan dan "jika kita punya waktu" ke garis depan pengembangan web.

Apa yang Anda gambarkan dalam opsi # 1 biasanya merupakan cara terbaik - tetapi, seperti masalah aksesibilitas dan SEO lainnya, melakukan hal ini dengan pushStateaplikasi JavaScript yang berat memerlukan perencanaan di muka atau akan menjadi beban yang signifikan. Itu harus dimasukkan ke dalam halaman dan arsitektur aplikasi dari awal - perkuatan sulit dan akan menyebabkan lebih banyak duplikasi daripada yang diperlukan.

Saya telah bekerja dengan pushStatedan SEO baru-baru ini untuk beberapa aplikasi yang berbeda, dan saya menemukan apa yang saya pikir merupakan pendekatan yang baik. Ini pada dasarnya mengikuti item Anda # 1, tetapi akun untuk tidak menduplikasi html / templat.

Sebagian besar info dapat ditemukan di dua posting blog ini:

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

dan

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

Intinya adalah saya menggunakan templat ERB atau HAML (menjalankan Ruby on Rails, Sinatra, dll) untuk render sisi server saya dan untuk membuat templat sisi klien yang dapat digunakan Backbone, serta untuk spesifikasi JavaScript Jasmine saya. Ini memotong duplikasi markup antara sisi server dan sisi klien.

Dari sana, Anda perlu mengambil beberapa langkah tambahan agar JavaScript Anda berfungsi dengan HTML yang diberikan oleh server - peningkatan progresif yang sebenarnya; mengambil markup semantik yang dikirimkan dan menyempurnakannya dengan JavaScript.

Misalnya, saya sedang membangun aplikasi galeri gambar dengan pushState. Jika Anda meminta /images/1dari server, itu akan merender seluruh galeri gambar di server dan mengirim semua HTML, CSS, dan JavaScript ke browser Anda. Jika Anda menonaktifkan JavaScript, itu akan berfungsi dengan baik. Setiap tindakan yang Anda lakukan akan meminta URL yang berbeda dari server dan server akan membuat semua markup untuk browser Anda. Jika Anda mengaktifkan JavaScript, JavaScript akan mengambil HTML yang sudah diberikan bersama dengan beberapa variabel yang dihasilkan oleh server dan mengambil alih dari sana.

Ini sebuah contoh:

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

Setelah server membuat ini, JavaScript akan mengambilnya (menggunakan tampilan Backbone.js dalam contoh ini)

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

Ini adalah contoh yang sangat sederhana, tapi saya pikir ini benar.

Ketika saya membuat tampilan setelah halaman dimuat, saya memberikan konten yang ada dari formulir yang diberikan oleh server, ke tampilan contoh sebagai eluntuk tampilan. Saya tidak memanggil render atau membuat view menghasilkan elbagi saya, ketika tampilan pertama dimuat. Saya memiliki metode render yang tersedia untuk setelah tampilan dan berjalan dan halaman semua JavaScript. Ini memungkinkan saya merender ulang tampilan nanti jika perlu.

Mengklik tombol "Say My Name" dengan JavaScript diaktifkan akan menyebabkan kotak peringatan. Tanpa JavaScript, itu akan dikirim kembali ke server dan server dapat membuat nama ke elemen html di suatu tempat.

Edit

Pertimbangkan contoh yang lebih kompleks, di mana Anda memiliki daftar yang perlu dilampirkan (dari komentar di bawah ini)

Katakanlah Anda memiliki daftar pengguna dalam sebuah <ul>tag. Daftar ini diberikan oleh server ketika browser membuat permintaan, dan hasilnya terlihat seperti:

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

Sekarang Anda perlu mengulang daftar ini dan melampirkan tampilan dan model Backbone untuk masing-masing <li>item. Dengan penggunaan data-idatribut, Anda dapat menemukan model yang berasal dari setiap tag dengan mudah. Anda kemudian akan memerlukan tampilan koleksi dan tampilan item yang cukup pintar untuk melampirkan dirinya ke html ini.

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

Dalam contoh ini, UserListViewkehendak akan melewati semua <li>tag dan melampirkan objek tampilan dengan model yang benar untuk masing-masing. itu mengatur pengendali acara untuk acara perubahan nama model dan memperbarui teks elemen yang ditampilkan ketika perubahan terjadi.


Proses semacam ini, untuk mengambil html yang diberikan server dan membuat JavaScript saya mengambil alih dan menjalankannya, adalah cara yang bagus untuk membuat semuanya bergulir untuk SEO, Aksesibilitas, dan pushStatedukungan.

Semoga itu bisa membantu.

Derick Bailey
sumber
Saya mengerti maksud Anda, tetapi yang menarik adalah bagaimana rendering dilakukan setelah "JavaScript Anda mengambil alih". Dalam contoh yang lebih rumit, Anda mungkin harus menggunakan templat yang tidak dikompilasi pada klien, perulangan melalui array pengguna untuk membangun daftar. Tampilan mengubah ulang setiap kali model pengguna berubah. Bagaimana Anda melakukannya tanpa menduplikasi templat (dan tidak meminta server untuk membuat tampilan untuk klien)?
user544941
2 posting blog yang saya tautkan secara kolektif menunjukkan kepada Anda bagaimana memiliki templat yang dapat digunakan pada klien dan server - tidak perlu duplikasi. server harus membuat seluruh halaman jika Anda ingin diakses dan SEO friendly. saya telah memperbarui jawaban saya untuk menyertakan contoh yang lebih kompleks dari melampirkan ke daftar pengguna yang diberikan oleh server
Derick Bailey
22

Saya pikir Anda memerlukan ini: http://code.google.com/web/ajaxcrawling/

Anda juga dapat menginstal backend khusus yang "merender" halaman Anda dengan menjalankan javascript di server, dan kemudian menyajikannya ke google.

Kombinasikan keduanya dan Anda memiliki solusi tanpa memprogram dua kali. (Selama aplikasi Anda sepenuhnya dapat dikontrol melalui jangkar fragmen.)

Ariel
sumber
Sebenarnya, bukan itu yang saya cari. Itu adalah beberapa varian dari solusi pertama dan seperti yang saya sebutkan saya tidak terlalu senang dengan pendekatan itu.
user544941
2
Anda tidak membaca seluruh jawaban saya. Anda juga menggunakan backend khusus yang menjadikan javascript untuk Anda - Anda tidak menulis dua kali.
Ariel
Ya, saya memang membacanya. Tetapi jika saya benar, itu akan menjadi program yang hebat, karena itu harus mensimulasikan setiap tindakan yang memicu pushState. Atau, saya bisa memberikan tindakan untuk itu secara langsung, tetapi kemudian kita tidak begitu KERING lagi.
user544941
2
Saya pikir itu pada dasarnya adalah browser tanpa bagian depan. Tapi, ya, Anda harus membuat program sepenuhnya dapat dikontrol dari jangkar fragmen. Anda juga perlu memastikan semua tautan memiliki fragmen yang tepat di dalamnya, bersama dengan, atau bukannya, onClicks.
Ariel
17

Jadi, sepertinya perhatian utama adalah KERING

  • Jika Anda menggunakan pushState, minta server Anda mengirim kode persis yang sama untuk semua url (yang tidak mengandung ekstensi file untuk menyajikan gambar, dll.) "/ Mydir / myfile", "/ myotherdir / myotherfile" atau root "/ "- semua permintaan menerima kode persis sama. Anda perlu memiliki beberapa jenis mesin penulisan ulang url. Anda juga dapat menyajikan sedikit html dan sisanya dapat berasal dari CDN Anda (menggunakan require.js untuk mengelola dependensi - lihat https://stackoverflow.com/a/13813102/1595913 ).
  • (uji validitas tautan dengan mengonversi tautan ke skema url Anda dan menguji keberadaan konten dengan menanyakan sumber statis atau dinamis. jika tidak valid, kirim respons 404.)
  • Ketika permintaan bukan dari bot google, Anda hanya memproses secara normal.
  • Jika permintaan dari google bot, Anda menggunakan phantom.js - browser webkit tanpa kepala ( "Browser tanpa kepala hanyalah browser web berfitur lengkap tanpa antarmuka visual." ) Untuk membuat html dan javascript di server dan mengirim google bot menghasilkan html. Ketika bot mem-parsing html, ia bisa mengenai tautan / push "pushState" lainnya di server <a href="https://stackoverflow.com/someotherpage">mylink</a>, server menulis ulang url ke file aplikasi Anda, memuatnya di phantom.js dan html yang dihasilkan dikirim ke bot, dan seterusnya. ..
  • Untuk html Anda, saya berasumsi Anda menggunakan tautan normal dengan semacam pembajakan (misalnya menggunakan dengan backbone.js https://stackoverflow.com/a/9331734/1595913 )
  • Untuk menghindari kebingungan dengan tautan apa pun, pisahkan kode api Anda yang melayani json ke dalam subdomain terpisah, misalnya api.mysite.com
  • Untuk meningkatkan kinerja, Anda dapat melakukan pra-proses halaman situs Anda untuk mesin pencari terlebih dahulu selama jam-jam tidak aktif dengan membuat versi statis halaman menggunakan mekanisme yang sama dengan phantom.js dan akibatnya melayani halaman statis ke google bots. Preprocessing dapat dilakukan dengan beberapa aplikasi sederhana yang dapat mengurai <a>tag. Dalam hal ini penanganan 404 lebih mudah karena Anda cukup memeriksa keberadaan file statis dengan nama yang berisi jalur url.
  • Jika Anda menggunakan #! sintaks hash bang untuk tautan situs Anda berlaku skenario yang sama, kecuali bahwa mesin server url penulisan ulang akan mencari _escaped_fragment_ di url dan akan memformat url ke skema url Anda.
  • Ada beberapa integrasi node.js dengan phantom.js di github dan Anda dapat menggunakan node.js sebagai server web untuk menghasilkan output html.

Berikut adalah beberapa contoh menggunakan phantom.js untuk seo:

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

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

Leonidaz
sumber
4

Jika Anda menggunakan Rails, coba poirot . Ini adalah permata yang membuatnya sangat mudah untuk menggunakan kembali kumis atau setang templat sisi klien dan server.

Buat file dalam tampilan seperti Anda _some_thingy.html.mustache.

Sisi server render:

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

Letakkan templat kepala Anda untuk penggunaan sisi klien:

<%= template_include_tag 'some_thingy' %>

Sisi klien Rendre:

html = poirot.someThingy(my_model)
Tim Scott
sumber
3

Untuk mengambil sudut yang sedikit berbeda, solusi kedua Anda akan menjadi yang benar dalam hal aksesibilitas ... Anda akan memberikan konten alternatif kepada pengguna yang tidak dapat menggunakan javascript (yang dengan pembaca layar, dll.).

Ini secara otomatis akan menambah manfaat SEO dan, menurut saya, tidak akan dilihat sebagai teknik 'nakal' oleh Google.

Clive
sumber
Dan adakah yang membuktikan bahwa Anda salah? Sudah lama sejak komentar itu diposting
jkulak
1

Menarik. Saya telah mencari-cari solusi yang layak tetapi tampaknya cukup bermasalah.

Saya sebenarnya lebih condong ke arah pendekatan kedua:

Biarkan server menyediakan situs web khusus hanya untuk bot mesin pencari. Jika pengguna normal mengunjungi http://example.com/my_path server harus memberinya versi berat JavaScript dari situs web. Tetapi jika bot Google mengunjungi, server harus memberikan HTML minimal dengan konten yang saya ingin Google indeks.

Inilah pendapat saya tentang memecahkan masalah. Meskipun tidak dikonfirmasi untuk bekerja, mungkin memberikan beberapa wawasan atau ide untuk pengembang lain.

Asumsikan Anda menggunakan kerangka kerja JS yang mendukung fungsionalitas "push state", dan kerangka kerja backend Anda adalah Ruby on Rails. Anda memiliki situs blog sederhana dan Anda ingin mesin pencari mengindeks semua artikel indexdan showhalaman Anda.

Katakanlah Anda mengatur rute Anda seperti ini:

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

Pastikan bahwa setiap pengontrol sisi-server membuat templat yang sama dengan yang harus dijalankan oleh kerangka kerja sisi klien Anda (html / css / javascript / dll). Jika tidak ada pengontrol yang cocok dengan permintaan (dalam contoh ini kami hanya memiliki serangkaian tindakan tenang untuk ArticlesController), maka hanya cocok dengan yang lain dan render templat dan biarkan kerangka sisi klien menangani perutean. Satu-satunya perbedaan antara memukul controller dan memukul wildcard matcher adalah kemampuan untuk merender konten berdasarkan URL yang diminta ke perangkat yang dinonaktifkan JavaScript.

Dari apa yang saya mengerti adalah ide yang buruk untuk membuat konten yang tidak terlihat oleh browser. Jadi ketika Google mengindeksnya, orang-orang pergi melalui Google untuk mengunjungi halaman yang diberikan dan tidak ada konten, maka Anda mungkin akan dihukum. Apa yang terlintas dalam pikiran adalah bahwa Anda membuat konten dalam divsimpul yang Anda display: nonedalam CSS.

Namun, saya cukup yakin tidak masalah jika Anda hanya melakukan ini:

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

Dan kemudian menggunakan JavaScript, yang tidak bisa dijalankan ketika perangkat yang dinonaktifkan JavaScript membuka halaman:

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

Dengan cara ini, untuk Google, dan bagi siapa saja dengan perangkat yang dinonaktifkan JavaScript, mereka akan melihat konten mentah / statis. Jadi kontennya ada secara fisik dan dapat dilihat oleh siapa saja dengan perangkat yang dinonaktifkan JavaScript.

Tetapi, ketika pengguna mengunjungi halaman yang sama dan benar-benar telah mengaktifkan JavaScript, #no-jsnode akan dihapus sehingga tidak mengacaukan aplikasi Anda. Kemudian kerangka kerja sisi klien Anda akan menangani permintaan melalui router dan menampilkan apa yang harus dilihat pengguna saat JavaScript diaktifkan.

Saya pikir ini mungkin teknik yang valid dan cukup mudah digunakan. Meskipun itu mungkin tergantung pada kompleksitas situs web / aplikasi Anda.

Padahal, tolong perbaiki saya jika tidak. Hanya berpikir saya akan membagikan pemikiran saya.

Michael van Rooijen
sumber
1
Nah, jika Anda pertama kali menampilkan konten dan sedikit kemudian menghapusnya, maka kemungkinan besar pengguna akhir mungkin melihat bahwa konten berkedip / berkedip di browser-nya :) Terutama jika itu browser yang lambat, konten HTML ukuran besar yang Anda coba tampilkan / hapus dan beberapa tunda sebelum kode JS Anda dimuat dan dijalankan. Apa yang Anda pikirkan?
Evereq
1

Gunakan NodeJS di sisi server, perambankan kode sisi klien Anda dan rutekan setiap permintaan http (kecuali untuk sumber daya http statis) melalui klien di sisi server untuk memberikan 'bootsnap' pertama (snapshot dari halaman keadaannya). Gunakan sesuatu seperti jsdom untuk menangani dom-ops jquery di server. Setelah bootsnap kembali, siapkan koneksi websocket. Mungkin yang terbaik untuk membedakan antara klien websocket dan klien di sisi server dengan membuat semacam koneksi wrapper di sisi klien (klien di sisi server dapat langsung berkomunikasi dengan server). Saya telah mengerjakan sesuatu seperti ini: https://github.com/jvanveen/rnet/

Phrearch
sumber
0

Gunakan Google Closure Template untuk merender halaman. Ini mengkompilasi ke javascript atau java, sehingga mudah untuk membuat halaman baik di sisi klien atau server. Pada pertemuan pertama dengan setiap klien, render html dan tambahkan javascript sebagai tautan di header. Crawler akan membaca html saja tetapi browser akan menjalankan skrip Anda. Semua permintaan selanjutnya dari browser dapat dilakukan terhadap api untuk meminimalkan lalu lintas.

Aleš Kotnik
sumber