127

最近では、強力な「単一ページ」の JavaScript Web サイトを作成するための優れたツールがたくさんあります。私の意見では、これはサーバーを API として機能させ (それ以上のものは何もしない)、クライアントに HTML 生成のすべてを処理させることで正しく行われます。この「パターン」の問題は、検索エンジンのサポートがないことです。私は2つの解決策を考えることができます:

  1. ユーザーが Web サイトに入ると、クライアントがナビゲーションするときとまったく同じように、サーバーがページをレンダリングします。したがって、直接アクセスすると、サーバーはクライアントがpushStatehttp://example.com/my_pathを介してアクセスした場合と同じものをレンダリングします。/my_path
  2. サーバーが検索エンジン ボット専用の特別な Web サイトを提供できるようにします。通常のユーザーがhttp://example.com/my_pathアクセスすると、サーバーは Web サイトの JavaScript を多用したバージョンを提供する必要があります。しかし、Google ボットがアクセスした場合、サーバーは、Google にインデックスしてもらいたいコンテンツを含む最小限の HTML を提供する必要があります。

最初の解決策については、ここで詳しく説明します。私はこれを行うウェブサイトに取り組んできましたが、あまり良い経験ではありません. これは DRY ではなく、私の場合、クライアントとサーバーに 2 つの異なるテンプレート エンジンを使用する必要がありました。

古き良き Flash Web サイトで 2 番目の解決策を見たことがあると思います。私はこのアプローチが最初のアプローチよりもはるかに気に入っており、サーバー上の適切なツールを使用すると、非常に簡単に実行できます.

それで、私が本当に疑問に思っているのは、次のことです。

  • より良い解決策を考えられますか?
  • 2 番目のソリューションの欠点は何ですか? Google が何らかの方法で、私が Google ボットに対して通常のユーザーとまったく同じコンテンツを提供していないことを発見した場合、検索結果で処罰されますか?
4

9 に答える 9

44

#2 は開発者にとって「簡単」かもしれませんが、検索エンジンのクロールのみを提供します。そしてもちろん、Google があなたが別のコンテンツを配信していることを発見した場合、あなたは罰せられる可能性があります (私はその専門家ではありませんが、実際に起こっていると聞いたことがあります)。

SEO とアクセシビリティ (障害者だけでなく、モバイル デバイス、タッチ スクリーン デバイス、およびその他の非標準のコンピューティング/インターネット対応プラットフォームを介したアクセシビリティ) には、どちらも同様の根本的な哲学があります。これらすべての異なるブラウザに スクリーン リーダー、検索エンジン クローラー、または JavaScript が有効になっているユーザーは、サイトのコア機能を問題なく使用/インデックス作成/理解できる必要があります。

pushState私の経験では、この負担が増えることはありません。これまでは後付けで、「時間があれば」ということを Web 開発の最前線に持ち込むだけです。

オプション #1 で説明することが通常は最善の方法ですが、他のアクセシビリティや SEO の問題と同様pushStateに、JavaScript を多用するアプリでこれを行うには、事前の計画が必要です。そうしないと、かなりの負担になります。最初からページとアプリケーション アーキテクチャに組み込む必要があります。レトロフィットは苦痛であり、必要以上の重複が発生します。

私はpushState最近、いくつかの異なるアプリケーションで SEO に取り組んできましたが、良いアプローチだと思うものを見つけました。基本的にあなたの項目#1に従いますが、html /テンプレートを複製しないことを考慮しています。

ほとんどの情報は、次の 2 つのブログ投稿で見つけることができます。

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 仕様に使用することです。これにより、サーバー側とクライアント側の間でマークアップの重複がなくなります。

そこから、サーバーによってレンダリングされる HTML で JavaScript を機能させるために、いくつかの追加手順を実行する必要があります。配信されたセマンティック マークアップを取得し、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ます。最初のビューがロードされたときに、レンダリングを呼び出したり、ビューに生成させたりしていません。elビューが起動して実行され、ページがすべて JavaScript になった後に使用できる render メソッドがあります。これにより、後で必要に応じてビューを再レンダリングできます。

JavaScript を有効にして [Say My Name] ボタンをクリックすると、警告ボックスが表示されます。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>

次に、このリストをループして、バックボーン ビューとモデルを各<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サポートのために物事を進めるための優れた方法です。

それが役立つことを願っています。

于 2011-09-26T02:14:06.770 に答える
22

これが必要だと思います:http ://code.google.com/web/ajaxcrawling/

サーバーでjavascriptを実行してページを「レンダリング」し、それをgoogleに提供する特別なバックエンドをインストールすることもできます。

両方を組み合わせると、2回プログラミングしなくても解決策が得られます。(アプリがアンカーフラグメントを介して完全に制御可能である限り。)

于 2011-09-26T00:00:04.330 に答える
17

つまり、主な関心事はDRYであると思われます

  • pushState を使用している場合は、サーバーがすべての URL (画像などを提供するためのファイル拡張子を含まない) に対して同じ正確なコードを送信するようにします。 " -- すべてのリクエストがまったく同じコードを受け取ります。ある種の URL 書き換えエンジンが必要です。少しの html を提供することもでき、残りは CDN から取得できます (require.js を使用して依存関係を管理します - https://stackoverflow.com/a/13813102/1595913を参照してください)。
  • (リンクを URL スキームに変換し、静的または動的ソースをクエリしてコンテンツの存在をテストすることにより、リンクの有効性をテストします。有効でない場合は、404 応答を送信します。)
  • リクエストが Google ボットからのものでない場合は、通常どおり処理します。
  • リクエストが Google ボットからのものである場合は、phantom.js -- ヘッドレス Webkit ブラウザー ( 「ヘッドレス ブラウザーは、視覚的なインターフェイスのないフル機能の Web ブラウザーです。」 ) を使用して、サーバー上で html と JavaScript をレンダリングし、グーグルボット結果の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 と同じメカニズムを使用してページの静的バージョンを作成し、静的ページを Google ボットに提供することで、営業時間外に検索エンジン用にサイト ページを前処理することができます。<a>前処理は、タグを解析できるいくつかの単純なアプリで実行できます。この場合、url パスを含む名前の静的ファイルの存在を簡単に確認できるため、404 の処理は簡単です。
  • #! サイト リンクの 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

于 2012-12-12T04:51:42.963 に答える
4

Rails を使用している場合は、poirotを試してください。これは、口ひげハンドルバーのテンプレートをクライアント側とサーバー側で再利用することを非常に簡単にする宝石です。

のようなビューでファイルを作成します_some_thingy.html.mustache

サーバー側のレンダリング:

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

クライアント側で使用するテンプレートを頭に入れます。

<%= template_include_tag 'some_thingy' %>

Rendre クライアント側:

html = poirot.someThingy(my_model)
于 2012-05-23T12:56:23.610 に答える
3

少し違う角度から見ると、2番目の解決策はアクセシビリティの観点から正しいものです... javascriptを使用できないユーザー(スクリーンリーダーなどを使用しているユーザー)に代替コンテンツを提供します。

これは自動的にSEOの利点を追加し、私の意見では、Googleによって「いたずらな」テクニックとは見なされません。

于 2011-09-25T23:59:35.687 に答える
1

Interesting. I have been searching around for viable solutions but it seems to be quite problematic.

I was actually leaning more towards your 2nd approach:

Let the server provide a special website only for the search engine bots. If a normal user visits http://example.com/my_path the server should give him a JavaScript heavy version of the website. But if the Google bot visits, the server should give it some minimal HTML with the content I want Google to index.

Here's my take on solving the problem. Although it is not confirmed to work, it might provide some insight or idea's for other developers.

Assume you're using a JS framework that supports "push state" functionality, and your backend framework is Ruby on Rails. You have a simple blog site and you would like search engines to index all your article index and show pages.

Let's say you have your routes set up like this:

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

Ensure that every server-side controller renders the same template that your client-side framework requires to run (html/css/javascript/etc). If none of the controllers are matched in the request (in this example we only have a RESTful set of actions for the ArticlesController), then just match anything else and just render the template and let the client-side framework handle the routing. The only difference between hitting a controller and hitting the wildcard matcher would be the ability to render content based on the URL that was requested to JavaScript-disabled devices.

From what I understand it is a bad idea to render content that isn't visible to browsers. So when Google indexes it, people go through Google to visit a given page and there isn't any content, then you're probably going to be penalised. What comes to mind is that you render content in a div node that you display: none in CSS.

However, I'm pretty sure it doesn't matter if you simply do this:

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

And then using JavaScript, which doesn't get run when a JavaScript-disabled device opens the page:

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

This way, for Google, and for anyone with JavaScript-disabled devices, they would see the raw/static content. So the content is physically there and is visible to anyone with JavaScript-disabled devices.

But, when a user visits the same page and actually has JavaScript enabled, the #no-js node will be removed so it doesn't clutter up your application. Then your client-side framework will handle the request through it's router and display what a user should see when JavaScript is enabled.

I think this might be a valid and fairly easy technique to use. Although that might depend on the complexity of your website/application.

Though, please correct me if it isn't. Just thought I'd share my thoughts.

于 2012-08-16T23:11:33.027 に答える
1

サーバー側で NodeJS を使用し、クライアント側のコードをブラウザ化して、各 http リクエスト (静的な http リソースを除く) の uri をサーバー側のクライアントを介してルーティングし、最初の「ブートスナップ」(ページの状態のスナップショット) を提供します。jsdom のようなものを使用して、サーバー上で jquery dom-ops を処理します。ブートスナップが戻ったら、websocket 接続をセットアップします。おそらく、クライアント側である種のラッパー接続を作成することにより、WebSocket クライアントとサーバー側クライアントを区別するのが最善です (サーバー側クライアントはサーバーと直接通信できます)。私はこのようなことに取り組んできました: https://github.com/jvanveen/rnet/

于 2013-07-26T15:40:26.523 に答える
0

Google Closure Templateを使用してページをレンダリングします。JavaScript または Java にコンパイルされるため、クライアント側またはサーバー側でページを簡単にレンダリングできます。すべてのクライアントとの最初の出会いで、html をレンダリングし、javascript をリンクとしてヘッダーに追加します。クローラーは html のみを読み取りますが、ブラウザーはスクリプトを実行します。ブラウザーからの後続のすべての要求は、トラフィックを最小限に抑えるために API に対して行うことができます。

于 2012-05-11T22:03:28.613 に答える