Các trang web và một trang web của JS


128

Có rất nhiều công cụ tuyệt vời để tạo các trang web JavaScript "một trang" mạnh mẽ hiện nay. Theo tôi, điều này được thực hiện đúng bằng cách cho phép máy chủ hoạt động như một API (và không có gì nữa) và để máy khách xử lý tất cả các công cụ tạo HTML. Vấn đề với "mẫu" này là thiếu hỗ trợ công cụ tìm kiếm. Tôi có thể nghĩ về hai giải pháp:

  1. Khi người dùng vào trang web, hãy để máy chủ hiển thị trang chính xác như máy khách khi điều hướng. Vì vậy, nếu tôi truy cập http://example.com/my_pathtrực tiếp, máy chủ sẽ hiển thị giống như máy khách nếu tôi truy cập /my_paththông qua PushState.
  2. Hãy để máy chủ cung cấp một trang web đặc biệt chỉ dành cho các bot công cụ tìm kiếm. Nếu một người dùng bình thường truy cập http://example.com/my_pathvào máy chủ sẽ cung cấp cho anh ta phiên bản nặng của trang web JavaScript. Nhưng nếu Google bot truy cập, máy chủ sẽ cung cấp cho nó một số HTML tối thiểu với nội dung tôi muốn Google lập chỉ mục.

Giải pháp đầu tiên được thảo luận thêm ở đây . Tôi đã làm việc trên một trang web làm điều này và nó không phải là một trải nghiệm tốt đẹp. Đó không phải là DRY và trong trường hợp của tôi, tôi đã phải sử dụng hai công cụ mẫu khác nhau cho máy khách và máy chủ.

Tôi nghĩ rằng tôi đã thấy giải pháp thứ hai cho một số trang web Flash tốt. Tôi thích cách tiếp cận này hơn nhiều so với cách đầu tiên và với công cụ phù hợp trên máy chủ, nó có thể được thực hiện khá dễ dàng.

Vì vậy, những gì tôi thực sự tự hỏi là sau đây:

  • Bạn có thể nghĩ ra giải pháp nào tốt hơn không?
  • Những bất lợi với giải pháp thứ hai là gì? Nếu Google bằng một cách nào đó phát hiện ra rằng tôi không phục vụ cùng một nội dung cho bot Google như một người dùng thông thường, thì tôi có bị phạt trong kết quả tìm kiếm không?

Câu trả lời:


44

Mặc dù # 2 có thể là "dễ dàng hơn" đối với bạn với tư cách là nhà phát triển, nhưng nó chỉ cung cấp khả năng thu thập dữ liệu của công cụ tìm kiếm. Và vâng, nếu Google phát hiện ra nội dung phục vụ khác nhau của bạn, bạn có thể bị phạt (Tôi không phải là chuyên gia về vấn đề đó, nhưng tôi đã nghe nói về việc đó xảy ra).

Cả SEO và khả năng truy cập (không chỉ dành cho người khuyết tật, mà cả khả năng truy cập qua thiết bị di động, thiết bị màn hình cảm ứng và các nền tảng hỗ trợ máy tính / internet không chuẩn khác) đều có một triết lý cơ bản tương tự: đánh dấu giàu ngữ nghĩa là "có thể truy cập" (nghĩa là có thể được truy cập, xem, đọc, xử lý hoặc sử dụng theo cách khác) cho tất cả các trình duyệt khác nhau này. Trình đọc màn hình, trình thu thập công cụ tìm kiếm hoặc người dùng đã bật JavaScript, tất cả đều có thể sử dụng / lập chỉ mục / hiểu chức năng cốt lõi của trang web của bạn mà không gặp sự cố.

pushStatekhông thêm vào gánh nặng này, theo kinh nghiệm của tôi. Nó chỉ đưa những gì từng là một suy nghĩ và "nếu chúng ta có thời gian" đi đầu trong phát triển web.

Những gì bạn mô tả trong tùy chọn # 1 thường là cách tốt nhất - nhưng, giống như các vấn đề về khả năng truy cập và SEO khác, thực hiện điều này với pushStatemột ứng dụng nặng JavaScript đòi hỏi phải lập kế hoạch trước hoặc nó sẽ trở thành gánh nặng đáng kể. Nó nên được đưa vào trang và kiến ​​trúc ứng dụng ngay từ đầu - trang bị thêm là đau đớn và sẽ gây ra nhiều sự trùng lặp hơn mức cần thiết.

pushStateGần đây tôi đã làm việc và SEO cho một vài ứng dụng khác nhau và tôi thấy những gì tôi nghĩ là một cách tiếp cận tốt. Về cơ bản, nó tuân theo mục số 1 của bạn, nhưng tài khoản không sao chép html / mẫu.

Hầu hết các thông tin có thể được tìm thấy trong hai bài viết trên blog này:

http://lostechies.com/derickbailey/2011/09/06/test-dishing-backbone-view-with-jquery-temsheet-the-jasmine-gem-and-jasmine-jquery/

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

Điểm chính của nó là tôi sử dụng các mẫu ERB hoặc HAML (chạy Ruby on Rails, Sinatra, v.v.) để kết xuất phía máy chủ của tôi và để tạo các mẫu phía máy khách mà Backbone có thể sử dụng, cũng như cho các thông số JavaScript Jasmine của tôi. Điều này cắt bỏ sự trùng lặp đánh dấu giữa phía máy chủ và phía máy khách.

Từ đó, bạn cần thực hiện thêm một số bước để JavaScript của bạn hoạt động với HTML được máy chủ kết xuất - tăng cường tiến bộ thực sự; lấy đánh dấu ngữ nghĩa đã được phân phối và nâng cao nó bằng JavaScript.

Ví dụ: tôi đang xây dựng một ứng dụng thư viện ảnh pushState. Nếu bạn yêu cầu /images/1từ máy chủ, nó sẽ hiển thị toàn bộ thư viện hình ảnh trên máy chủ và gửi tất cả HTML, CSS và JavaScript xuống trình duyệt của bạn. Nếu bạn đã tắt JavaScript, nó sẽ hoạt động hoàn toàn tốt. Mỗi hành động bạn thực hiện sẽ yêu cầu một URL khác từ máy chủ và máy chủ sẽ hiển thị tất cả các đánh dấu cho trình duyệt của bạn. Tuy nhiên, nếu bạn đã bật JavaScript, JavaScript sẽ chọn HTML đã được kết xuất cùng với một vài biến được tạo bởi máy chủ và tiếp quản từ đó.

Đây là một ví dụ:

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

Sau khi máy chủ kết xuất lại, JavaScript sẽ chọn nó (sử dụng chế độ xem Backbone.js trong ví dụ này)

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

Đây là một ví dụ rất đơn giản, nhưng tôi nghĩ rằng nó có điểm.

Khi tôi mở chế độ xem sau khi tải trang, tôi sẽ cung cấp nội dung hiện có của biểu mẫu được máy chủ hiển thị, cho phiên bản xem như là elcho chế độ xem. Tôi không gọi render hoặc có chế độ xem tạo ra elcho tôi, khi chế độ xem đầu tiên được tải. Tôi có một phương thức kết xuất có sẵn sau khi chế độ xem được bật và chạy và trang là tất cả JavaScript. Điều này cho phép tôi hiển thị lại chế độ xem sau nếu tôi cần.

Nhấp vào nút "Nói tên tôi" với JavaScript được bật sẽ gây ra hộp cảnh báo. Nếu không có JavaScript, nó sẽ đăng lại máy chủ và máy chủ có thể hiển thị tên thành phần tử html ở đâu đó.

Biên tập

Hãy xem xét một ví dụ phức tạp hơn, nơi bạn có một danh sách cần được đính kèm (từ các bình luận bên dưới này)

Giả sử bạn có một danh sách người dùng trong một <ul>thẻ. Danh sách này được máy chủ hiển thị khi trình duyệt đưa ra yêu cầu và kết quả trông giống như:

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

Bây giờ bạn cần lặp qua danh sách này và đính kèm một khung nhìn và mô hình xương sống cho mỗi <li>mục. Với việc sử dụng data-idthuộc tính, bạn có thể tìm thấy mô hình mà mỗi thẻ xuất phát dễ dàng. Sau đó, bạn sẽ cần một chế độ xem bộ sưu tập và chế độ xem mục đủ thông minh để tự đính kèm vào html này.

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

Trong ví dụ này, UserListViewvòng lặp sẽ lặp qua tất cả các <li>thẻ và đính kèm một đối tượng khung nhìn với mô hình chính xác cho từng thẻ. nó thiết lập một trình xử lý sự kiện cho sự kiện thay đổi tên của mô hình và cập nhật văn bản được hiển thị của phần tử khi có thay đổi xảy ra.


Kiểu quy trình này, để lấy html mà máy chủ kết xuất và có JavaScript của tôi tiếp quản và chạy nó, là một cách tuyệt vời để khiến mọi thứ trở nên thuận lợi cho SEO, Trợ năng và pushStateHỗ trợ.

Mong rằng sẽ giúp.


Tôi hiểu ý của bạn, nhưng điều thú vị là cách kết xuất được thực hiện sau khi "JavaScript của bạn tiếp quản". Trong một ví dụ phức tạp hơn, bạn có thể phải sử dụng một mẫu chưa được biên dịch trên máy khách, lặp qua một mảng người dùng để tạo danh sách. Khung nhìn hiển thị lại mỗi khi mô hình của người dùng thay đổi. Làm thế nào bạn có thể làm điều đó mà không cần sao chép các mẫu (và không yêu cầu máy chủ hiển thị chế độ xem cho máy khách)?
dùng544941

2 bài đăng blog tôi liên kết sẽ chỉ cho bạn cách để có các mẫu có thể được sử dụng trên máy khách và máy chủ - không cần sao chép. máy chủ sẽ cần hiển thị toàn bộ trang nếu bạn muốn nó có thể truy cập và thân thiện với SEO. Tôi đã cập nhật câu trả lời của mình để bao gồm một ví dụ phức tạp hơn về việc đính kèm vào danh sách người dùng được máy chủ kết xuất
Derick Bailey

22

Tôi nghĩ bạn cần điều này: http://code.google.com.vn/web/ajaxcrawling/

Bạn cũng có thể cài đặt một phụ trợ đặc biệt "kết xuất" trang của bạn bằng cách chạy javascript trên máy chủ, sau đó phục vụ nó cho google.

Kết hợp cả hai thứ và bạn có một giải pháp mà không cần lập trình mọi thứ hai lần. (Miễn là ứng dụng của bạn được kiểm soát hoàn toàn thông qua các đoạn neo.)


Trên thực tế, đó không phải là những gì tôi đang tìm kiếm. Đó là một số biến thể của giải pháp đầu tiên và như tôi đã đề cập, tôi không hài lòng lắm với cách tiếp cận đó.
dùng544941

2
Bạn đã không đọc toàn bộ câu trả lời của tôi. Bạn cũng sử dụng một phụ trợ đặc biệt kết xuất javascript cho bạn - bạn không viết hai lần.
Ariel

Vâng, tôi đã đọc nó. Nhưng nếu tôi hiểu đúng thì đó sẽ là một địa ngục của một chương trình, vì nó sẽ phải mô phỏng mọi hành động kích hoạt PushState. Ngoài ra, tôi có thể đưa ra các hành động trực tiếp cho nó, nhưng sau đó chúng tôi không còn KHÔ nữa.
dùng544941

2
Tôi nghĩ rằng về cơ bản nó là một trình duyệt không có mặt trước. Nhưng, vâng, bạn phải làm cho chương trình hoàn toàn có thể kiểm soát được từ các đoạn neo. Bạn cũng cần đảm bảo tất cả các liên kết có đoạn thích hợp trong đó, cùng với, hoặc thay vì onClicks.
Ariel

17

Vì vậy, có vẻ như mối quan tâm chính là KHÔ

  • Nếu bạn đang sử dụng PushState, máy chủ của bạn sẽ gửi cùng một mã chính xác cho tất cả các url (không chứa phần mở rộng tệp để phục vụ hình ảnh, v.v.) "/ mydir / myfile", "/ myotherdir / myotherfile" hoặc root "/ "- tất cả các yêu cầu nhận được cùng một mã chính xác. Bạn cần phải có một số loại công cụ viết lại url. Bạn cũng có thể phục vụ một chút html và phần còn lại có thể đến từ CDN của bạn (sử dụng allow.js để quản lý các phụ thuộc - xem https://stackoverflow.com/a/13813102/1595913 ).
  • (kiểm tra tính hợp lệ của liên kết bằng cách chuyển đổi liên kết sang sơ đồ url của bạn và kiểm tra sự tồn tại của nội dung bằng cách truy vấn nguồn tĩnh hoặc nguồn động. Nếu không hợp lệ, hãy gửi phản hồi 404.)
  • Khi yêu cầu không phải từ google bot, bạn chỉ cần xử lý bình thường.
  • Nếu yêu cầu đến từ google bot, bạn sử dụng ph Phantom.js - trình duyệt webkit không đầu ( "Trình duyệt không đầu đơn giản là trình duyệt web đầy đủ tính năng không có giao diện trực quan." ) Để hiển thị html và javascript trên máy chủ và gửi google bot kết quả html. Khi bot phân tích cú pháp html, nó có thể truy cập các liên kết / somepage "PushState" khác của bạn trên máy chủ <a href="https://stackoverflow.com/someotherpage">mylink</a>, máy chủ sẽ ghi lại url vào tệp ứng dụng của bạn, tải nó trong ph Phantom.js và html kết quả được gửi đến bot, v.v. ..
  • Đối với html của bạn, tôi cho rằng bạn đang sử dụng các liên kết bình thường với một số loại không tặc (ví dụ: sử dụng với backbone.js https://stackoverflow.com/a/9331734/1595913 )
  • Để tránh nhầm lẫn với bất kỳ liên kết nào, hãy tách mã api của bạn phục vụ json thành một tên miền phụ riêng biệt, ví dụ: api.mysite.com
  • Để cải thiện hiệu suất, bạn có thể xử lý trước các trang web của mình cho các công cụ tìm kiếm trước giờ làm việc bằng cách tạo các phiên bản tĩnh của các trang bằng cùng một cơ chế với ph Phantom.js và do đó phục vụ các trang tĩnh cho bot bot. Tiền xử lý có thể được thực hiện với một số ứng dụng đơn giản có thể phân tích <a>các thẻ. Trong trường hợp này, việc xử lý 404 dễ dàng hơn vì bạn chỉ cần kiểm tra sự tồn tại của tệp tĩnh với tên có chứa đường dẫn url.
  • Nếu bạn dùng #! cú pháp băm bang cho trang web của bạn liên kết một kịch bản tương tự được áp dụng, ngoại trừ việc công cụ máy chủ url viết lại sẽ tìm ra _escoped_fragment_ trong url và sẽ định dạng url cho sơ đồ url của bạn.
  • Có một vài tích hợp của node.js với ph Phantom.js trên github và bạn có thể sử dụng node.js làm máy chủ web để tạo đầu ra html.

Dưới đây là một vài ví dụ sử dụng ph Phantom.js cho seo:

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

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


4

Nếu bạn đang sử dụng Rails, hãy thử poirot . Đó là một viên ngọc làm cho nó trở nên đơn giản để sử dụng lại ria mép hoặc tay cầm các mẫu máy khách và máy chủ.

Tạo một tập tin trong quan điểm của bạn như thế nào _some_thingy.html.mustache.

Kết xuất phía máy chủ:

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

Đặt mẫu đầu của bạn để sử dụng phía khách hàng:

<%= template_include_tag 'some_thingy' %>

Phía khách hàng của Rendre:

html = poirot.someThingy(my_model)

3

Để có một góc độ hơi khác, giải pháp thứ hai của bạn sẽ là giải pháp chính xác về khả năng truy cập ... bạn sẽ cung cấp nội dung thay thế cho người dùng không thể sử dụng javascript (những người có trình đọc màn hình, v.v.).

Điều này sẽ tự động thêm các lợi ích của SEO và theo tôi, sẽ không bị Google coi là một kỹ thuật 'nghịch ngợm'.


Và có ai làm bạn chứng minh bạn sai? Đã được một thời gian kể từ khi bình luận được đăng
jkulak

1

Hấp dẫn. Tôi đã tìm kiếm xung quanh các giải pháp khả thi nhưng có vẻ như nó khá có vấn đề.

Tôi thực sự đã nghiêng nhiều hơn về cách tiếp cận thứ 2 của bạn:

Hãy để máy chủ cung cấp một trang web đặc biệt chỉ dành cho các bot công cụ tìm kiếm. Nếu người dùng bình thường truy cập http://example.com/my_path , máy chủ sẽ cung cấp cho anh ta phiên bản nặng của trang web. Nhưng nếu Google bot truy cập, máy chủ sẽ cung cấp cho nó một số HTML tối thiểu với nội dung tôi muốn Google lập chỉ mục.

Đây là cách tôi giải quyết vấn đề. Mặc dù nó không được xác nhận để hoạt động, nhưng nó có thể cung cấp một số hiểu biết hoặc ý tưởng cho các nhà phát triển khác.

Giả sử bạn đang sử dụng khung công tác JS hỗ trợ chức năng "trạng thái đẩy" và khung phụ trợ của bạn là Ruby on Rails. Bạn có một trang blog đơn giản và bạn muốn các công cụ tìm kiếm lập chỉ mục tất cả các bài viết indexshowtrang của bạn .

Giả sử bạn có các tuyến đường của mình được thiết lập như thế này:

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

Đảm bảo rằng mọi bộ điều khiển phía máy chủ kết xuất cùng một khuôn mẫu mà khung công tác phía máy khách của bạn yêu cầu để chạy (html / css / javascript / etc). Nếu không có bộ điều khiển nào được khớp trong yêu cầu (trong ví dụ này, chúng ta chỉ có một bộ hành động RESTful cho ArticlesController), thì chỉ cần khớp bất cứ thứ gì khác và chỉ hiển thị mẫu và để khung công tác phía máy khách xử lý định tuyến. Sự khác biệt duy nhất giữa việc nhấn bộ điều khiển và nhấn trình so khớp ký tự đại diện là khả năng hiển thị nội dung dựa trên URL được yêu cầu cho các thiết bị bị vô hiệu hóa JavaScript.

Theo những gì tôi hiểu, đó là một ý tưởng tồi để hiển thị nội dung không hiển thị cho trình duyệt. Vì vậy, khi Google lập chỉ mục, mọi người sẽ truy cập Google để truy cập một trang nhất định và không có bất kỳ nội dung nào, thì có lẽ bạn sẽ bị phạt. Điều bạn nghĩ đến là bạn kết xuất nội dung trong một divnút mà bạn display: nonebằng CSS.

Tuy nhiên, tôi khá chắc chắn rằng nó không thành vấn đề nếu bạn chỉ đơn giản làm điều này:

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

Và sau đó sử dụng JavaScript, không chạy được khi thiết bị bị tắt JavaScript mở trang:

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

Bằng cách này, đối với Google và cho bất kỳ ai có thiết bị bị tắt JavaScript, họ sẽ thấy nội dung thô / tĩnh. Vì vậy, nội dung về thể chất có và hiển thị với bất cứ ai với các thiết bị hoạt Javascript khuyết tật.

Nhưng, khi người dùng truy cập vào cùng một trang và thực sự đã bật JavaScript, #no-jsnút sẽ bị xóa để nó không làm lộn xộn ứng dụng của bạn. Sau đó, khung phía máy khách của bạn sẽ xử lý yêu cầu thông qua bộ định tuyến của nó và hiển thị những gì người dùng sẽ thấy khi JavaScript được bật.

Tôi nghĩ rằng đây có thể là một kỹ thuật hợp lệ và khá dễ sử dụng. Mặc dù điều đó có thể phụ thuộc vào độ phức tạp của trang web / ứng dụng của bạn.

Mặc dù, xin vui lòng sửa cho tôi nếu nó không. Chỉ cần nghĩ rằng tôi sẽ chia sẻ suy nghĩ của tôi.


1
Chà, nếu bạn lần đầu tiên hiển thị nội dung và một lát sau sẽ xóa nội dung đó, thì hầu hết người dùng cuối có thể nhận thấy rằng nội dung nhấp nháy / nhấp nháy trong trình duyệt của anh ấy :) Đặc biệt nếu đó là trình duyệt chậm, kích thước khổng lồ của nội dung HTML bạn cố gắng hiển thị / xóa và một số trì hoãn trước khi mã JS của bạn tải và thực thi. Bạn nghĩ gì?
Evereq

1

Sử dụng NodeJS trên máy chủ, trình duyệt mã máy khách của bạn và định tuyến từng uri của yêu cầu http (ngoại trừ tài nguyên http tĩnh) thông qua ứng dụng khách máy chủ để cung cấp 'bootnap' đầu tiên (ảnh chụp nhanh về trạng thái của trang). Sử dụng một cái gì đó như jsdom để xử lý jquery dom-op trên máy chủ. Sau khi khởi động trở lại, thiết lập kết nối websocket. Có lẽ tốt nhất để phân biệt giữa máy khách websocket và máy khách bên máy chủ bằng cách tạo một loại kết nối trình bao bọc nào đó trên máy khách (máy khách bên máy chủ có thể giao tiếp trực tiếp với máy chủ). Tôi đã làm việc trên một cái gì đó như thế này: https://github.com/jvanveen/rnet/


0

Sử dụng Google Đóng mẫu để kết xuất trang. Nó biên dịch thành javascript hoặc java, vì vậy thật dễ dàng để hiển thị trang ở phía máy khách hoặc máy chủ. Trong lần gặp đầu tiên với mọi khách hàng, hãy kết xuất html và thêm javascript làm liên kết trong tiêu đề. Trình thu thập thông tin sẽ chỉ đọc html nhưng trình duyệt sẽ thực thi tập lệnh của bạn. Tất cả các yêu cầu tiếp theo từ trình duyệt có thể được thực hiện đối với api để giảm thiểu lưu lượng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.