🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
Ajax是Asynchronous JavaScript and XML的縮寫,是一種不需要重新整理頁面,透過JavaScript來與伺服器交換資料、更新網頁內容的技術。目的在於改善使用者的操作介面,提昇流暢度。它主要是透過瀏覽器提供的`XMLHttpRequestObject`來達成,不過因為跨瀏覽器的困難度,大多數人們會選擇使用JavaScript Library來處理Ajax,例如JQuery。雖然Ajax的縮寫中包括XML,但是實務上並不一定要用XML格式,事實上也已經很少人使用XML當作傳輸的格式了。總歸來說,依照Ajax使用的格式分類,有三種方式: * 向伺服器請求 HTML 片段,然後客戶端瀏覽器上的 JavaScript 再替換掉頁面上的元素 * 向伺服器請求 JavaScript 程式腳本,然後客戶端瀏覽器執行它 * 向伺服器請求 JSON 或 XML 資料格式,然後客戶端瀏覽器的 JavaScript 解析後再動作。 第一種方式非常簡單,但是限制是一次只能更新一小塊內容。Rails 預設使用第二種方式,程式撰寫容易。而第三種方式則將 JavaScript 程式都放在客戶端瀏覽器上,相較於第二種則多了解析 JSON 或 XML 的部份。以Web API的設計角度來看,與表現層無關的JSON格式是比較乾淨的,可以獲得比較好的重複使用性。 講解JavaScript和JQuery語法已經超過本書範圍,本章接下來會假設讀者已經有基本認識。身為一個Web程式設計師,不得不對JavaScript要有基本了解。 ## Unobtrusive JavaScript Rails 從 3.0 後將Ajax使用的JavaScript都改成Unobtrusive JavaScript(UJS)方式。什麼是Unobtrusive呢?用個範例來說吧,它會將超連結改成用表單DELETE送出,並且用一個提示視窗來作確認: ~~~ link_to 'Remove', event_path(1), :method => :delete, :data => { :confirm => "Sure?" } ~~~ 在Rails 3之前,會輸出: ~~~ <a onclick="if (confirm('Sure?')) { var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;" href="/events/1">Remove</a> ~~~ 在Rails 3之後,會輸出: ~~~ <a rel="nofollow" data-confirm="Sure?" data-method="delete" class="delete" href="/events/1">Remove</a> ~~~ Unobtrusive也就是將JavaScript程式與HTML完全分開,除了可以讓HTML碼乾淨之外,也可以支援更換不同的JavaScript Library,例如把Rails內建的jQuery換成[Protytype.js](http://prototypejs.org/)或[angular.js](https://angularjs.org/)等等。 > 在Layout中有輸出一段`<%= csrf_meta_tag %>`的作用就是搭配給UJS使用的,讓JavaScript可以拿到CSRF安全驗證碼,我們會在安全一章討論到什麼是CSRF。 ### Ajax 表單 除了超連結 link_to 加上 :remote 可以變成 Ajax 之外,表單 form_for 也可以加上`:remote`變成 Ajax。 ~~~ form_for @user, :remote => true ~~~ ### Ajax 按鈕 同理於超連結 link_to,按鈕 button_to 加上`:remote => true`參數也會變成 Ajax。 除了已經看過的` :data => { :confirm => "Are you Sure?" }`之外,`disable_with`可以避免使用者連續按下送出: ~~~ button_to "Remove", user_path(@user), :data => { :disable_with => 'Removing' } ~~~ ## 第一種方式:替換 HTML 片段 編輯 app/views/events/index.html.erb 最下方加入: ~~~ <%= link_to 'Hello!', welcome_say_hello_path, :id => "ajax-load" %> <div id="content"> </div> <script> $(document).ready(function() { $('#ajax-load').click( function(){ $('#content').load( $(this).attr("href") ); return false; }); }); </script> ~~~ 如此點下超連結後,就會把回傳的HTML置入到`<div id="content">`裡面。 ## 第二種方式:使用 JavaScript 腳本 編輯 app/views/events/index.html.erb,在迴圈中間加入 ~~~ <%= link_to 'ajax show', event_path(event), :remote => true %> ~~~ 在迴圈外插入一個`<div id="event_area"></div>` 編輯 app/controllers/events_controller.rb,在 show action 中加入 ~~~ respond_to do |format| format.html format.js end ~~~ 新增 app/views/events/_event.html.erb,內容與 show.html.erb 相同 新增 app/views/events/show.js.erb,內容如下 ~~~ $('#event_area').html("<%= escape_javascript(render :partial => 'event') %>") .css({ backgroundColor: '#ffff99' }); ~~~ 瀏覽 http://localhost:3000/events > `escape_javascript()`可以縮寫為`j()`。 ## 第三種方式:使用 JSON 資料格式 JavaScript Object Notation(JSON)是一種源自JavaScript的資料格式,是目前Web應用程式之間的準標準資料交換格式,在Rails之中,每個物件都有`to_json`方法可以很方便的轉換資料格式。 ~~~ <%= link_to 'ajax show', event_path(event), :remote => true, :data => { :type => :json }, :class => "ajax_update" %> ~~~ 點擊ajax show就會送出Ajax request了,但接下來要怎麼撰寫處理JSON的程式呢? ~~~ <script> $(document).ready(function() { $('.ajax_update').on("ajax:success", function(event, data) { var event_area = $('#event_area'); event_area.html( data.name ); }); }); </script> ~~~ 當然,你也可以把HTML片段當做JSON的資料來傳遞。 另一種JSON的變形是JSONP(JSON with Padding),將JSON資料包在一個JavaScript function裡,這個做的用處是讓這個API可以跨網域被呼叫。要回傳JSONP格式,只需要在`render :json`時多一個參數是`:callback`即可 ~~~ respond_to do |format| format.json { render :json => @user.to_json, :callback => "process_user" } end ~~~ ## Turbolinks 事實上,Rails預設讓每個換頁都用上了Ajax技巧,這一招叫做[Turbolinks](https://github.com/rails/turbolinks),在預設的Gemfile中可以看到`gem "turbolinks"`,以及Layout中的`data-turbolinks-track`。 它的作用是讓每一個超連結都只用Ajax的方式將整個`body`內容替換掉,這樣換頁時就不需要重新載入`head`部份的標籤,包括JavaScript和CSS等等,目的是可以改善換頁時的速度。 要特別注意因為它沒有整頁重新載入,所以如果有放在application.js裡面的`$(document).ready`程式就變成只有第一次載入頁面才執行到,換頁時就失效了。這時候必須改成`$(document).on("page:change", function(){ ...})`。 如果想要明顯體會它的效果,可以在`app/assets/javascripts/application.js`裡面加上 ~~~ Turbolinks.enableProgressBar(); ~~~ 最後,因為它會影響JavaScript的Event Bindings行為,所以在搭配一些JavaScript比較吃重的應用程式,例如使用JavaScript Framework如Backbone、AngularJS、Ember時會移除不用,以免互相影響。 ## 更多線上資源 * [Working with JavaScript in Rails](http://guides.rubyonrails.org/working_with_javascript_in_rails.html)