转载 http://blog.csdn.net/aabbcc456aa/article/details/7482349
本章目錄
- Asset 靜態檔案
- 第一種方式:替換 HTML 片段
- 第二種方式:使用 JavaScript 腳本
- 第三種方式:使用 JSON 資料格式
- Ajax 表單
- Ajax 按鈕
- 關於除錯
Assets 與 Ajax 應用程式
Assets靜態檔案
Assets指的是JavaScript、Stylesheets和圖檔等靜態檔案,這些檔案並不會隨Requests不同而有所不同。而在Rails目錄中,只有public這個目錄是公開讀取的,所以通常我們會將靜態檔案都放在public這個目錄下,好讓瀏覽器可以直接讀取。但是隨著JavaScript和Stylesheet檔案越來越多時,如何管理這些檔案變為一項議題,為了加快瀏覽器的下載速度,我們會合併JavaScript和Stylesheet檔案,來減少瀏覽器Request下載次數。更進一步的還會壓縮這些檔案來加速下載時間。像是Yahoo!和Google都有各自開源出自己的壓縮工具YUI Compressor和Closure Compiler。
Rails 3.1引進了一項新功能叫做Assets pipeline,這個功能可以讓我們突破public目錄限制,可以將靜態檔案依需求放在不同目錄下,Rails會幫你組合並壓縮起來。特別是有一些Rails的外掛套件需要使用JavaScript等靜態檔案,在沒有這個功能之前,我們必須將JavaScript等檔案複製放在public目錄下,這樣瀏覽器才能讀取的到。
Rails 3.1之前版本想要有類似的功能,筆者推薦可以安裝Jammit這套工具來管理Assets。如果是升級到3.1的話,記得在config/application.rb中加上config.assets.enabled = true
才會啟用這個功能。
Assets的位置在app/assets/下,首先最重要的就是app/assets/javascripts/application.js和app/assets/stylesheets/application.css,這兩個檔案看起來充滿註解,其實它是個manifest檔案,列出了所有要載入的靜態檔案,這些檔案的位置依照慣例放在app/assets或vendor/assets目錄下。
讓我們先看看application.js
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
其中的require_jquery
和require_jquery_ujs
會載入JQuery和Rails的JQuery adapater,這是因為我們在Gemfile中有裝jquery-rails這個套件,所以這裡可以讀取的到。而require_tree .
會載入這個目錄下的所有JavaScript檔案。總之,這個manifest的最後輸出結果就是通通壓縮成一個application.js檔案。
同理application.css也是一樣載入所有stylesheets目錄下的CSS檔案,最後壓縮成application.css:
/*
* This is a manifest file that'll automatically include all the stylesheets available in this directory
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
* the top of the compiled file, but it's generally better to create a new file per style scope.
*= require_self
*= require_tree .
*/
讓我們看看View,在Layout檔案中:
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
因為最後輸出都壓縮成一個檔案了,所以這裡只需要載入application.css和application.js。
如何處理圖片
放在app/assets/images下的圖片該怎麼使用呢?在實際佈署後,Rails會將檔案名稱加以編碼,例如rails.png會變成rails-bd9ad5a560b5a3a7be0808c5cd76a798.png。這麼做的原因是當圖片有變更的時候,編碼就會不同而有不同的檔名,這樣就可以避免瀏覽器快取到舊的檔案。也因為檔案名稱會變動,所以放在app/assets/images下的圖片,要用的時候就沒有辦法寫死檔名。在一般的View中,可以使用image_tag
這個Helper:
<%= image_tag("rails.png") %>
如果在CSS裡的話,有兩種辦法:一是將檔案命名為erb結尾,例如app/assets/stylesheets/main.css.erb,然後使用asset_path
這個Helper:
h1 {
background-image: url('<%= asset_path("rails.png") %>');
}
另一種方法是使用Sass或SCSS語法。其中SCSS相容於CSS。例如命名為app/assets/stylesheets/main.css.scss,然後使用image-url
這個Sass提供的方法:
h1 {
background-image: image-url("rails.png")
}
如果是js檔案中想要拿圖片的位置,就只能用js.erb的格式,然後內嵌asset_path Helper方法了。
Precompile編譯靜態檔案
放在app/assets目錄下的Assets,Rails支援使用不同的語法,例如使用Sass語法產生CSS、CoffeeScript產生JavaScript,或是用ERb樣板也可以。使用的方法是將附檔名命名成.css.sass、.css.scss、.js.coffee、.css.erb或.js.erb,Rails就會編譯出結果給瀏覽器。
Sass是一種CSS3語法的擴充,可以使用巢狀、變數、混入、選擇子繼承等等功能,可以更有效率有彈性的撰寫Stylesheet。Sass最後會編譯出合法的CSS讓瀏覽器使用。使用上它區分成兩種形式的語法scss和sass,前者可以與現有的css程式碼直接混合在一起,後者則透過縮排來省略了大括號{}
(就像Python用縮排一樣)。CoffeeScript也是類似原理,它是一種迷你的程式語言,編譯之後會輸出可讀性高、符合JavaScript Lint規範的JavaScript程式碼。
學習這兩種新語法需要額外的時間投入,但是對需要常常撰寫CSS和JavaScript的設計師來說,應該是很不錯的工具,讀者可以自行斟酌是否採用。如果你不打算使用的話,可以編輯Gemfile拿掉以下:
gem 'sass'
gem 'coffee-script'
如果採用Sass的話,推薦還可以採用Compass這套CSS框架的框架。
編譯出最後結果
開發的時候,Rails會自動將Asset的壓縮結果快取在tmp下,所以開發者不需要特別處理。但是實際正式上線時,最後壓縮的檔案還是必須放在public目錄下由網頁伺服器直接提供(或是由CDN)效能較好,以下的rake指令可以產生出來:
rake assets:precompile
產生出來的檔案在public/assets/下。
rake assets:clean
這樣就會刪除。
注意,如果在開發模式下執行了rake assets:precompile
,那麼因為放在public/assets/下的靜態檔案會優先丟給瀏覽器,所以這時候再修改app/assets下的原始碼會沒有作用。所以,開發時請記得要刪除這個目錄。
不過,如果你有很多靜態檔案的話,開發模式下每次都要重新編譯太辛苦了。你可以先compile一次,然後把public/assets/下目前正在開發的檔案砍掉即可。
如何拆成數個壓縮檔
上述的application.js或application.css中,預設會壓縮所有app/assets目錄下的檔案,如果你需要拆開,只需要修改其中的內容把require_tree
那行移除,那麼就只會壓縮你所指定的目錄或檔案。
例如,要新增新的Manifest檔案的話,假設叫做app/assets/javascripts/search.js,內容如:
//= require ./foobar
這樣就會將assets/javascripts/foobar這個目錄下的檔案通通壓縮成search.js,而在View中:
<%= javascript_include_tag "application" %>
<%= javascript_include_tag "search" %>
就會載入。注意到如果啟用了assets功能,javascript_include_tag
只能接受一個參數,即Manifest檔案的名稱。
為了讓rake assets:precompile
也能產生新的壓縮檔案,你還需要編輯config/environments/production.rb加入:
config.assets.precompile += %w( search.js )
如何關閉這個功能
也可以不使用這個功能,請修改config/application.rb將以下設定改成false
:
config.assets.enabled = false
這樣的話,在View中就必須列出所有你要載入的Script檔案:
<%= stylesheet_link_tag "reset", "application", :cache => "all" %>
<%= javascript_include_tag 'jquery', 'rails', 'application', :cache => "all" %>
這些檔案都必須放在public目錄下。而加上cache
參數Rails會合併這些檔案,但是並不會壓縮。
Ajax
Ajax是Asynchronous JavaScript and XML的縮寫,是一種不需要重新整理頁面,透過JavaScript來與伺服器交換資料、更新網頁內容的技術。目的在於改善使用者的操作介面,提昇流暢度。它主要是透過瀏覽器提供的XMLHttpRequestObject
來達成,不過因為跨瀏覽器的困難度,大多數人們會選擇使用JavaScript Library來處理Ajax,例如JQuery、YUI等。雖然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呢? 用個範例來說吧:
link_to 'Remove', event_path(1), :method => :delete
在Rails 3之前,會輸出:
<a onclick="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-method="delete" class="delete" href="/events/1">Remove</a>
Unobtrusive也就是將JavaScript程式與HTML完全分開了。
第一種方式:替換 HTML 片段
編輯 app/views/events/index.html.erb 最下方加入:
<%= link_to "say hello", { :controller => "welcome", :action => "say" }, :id => "ajax-load" %>
<div id="content">
</div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$('#ajax-load').click( function(){
$('#content').load( $(this).attr("href") );
return false;
});
});
</script>
第二種方式:使用 JavaScript 腳本
編輯 app/views/events/index.html.erb,在迴圈中間加入
<%= link_to 'ajax show', event_path(event), :remote => true, "data-type" => "script" %>
編輯 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,內容如下
$('#content').html("<%= escape_javascript(render :partial => 'event') %>")
.css({ backgroundColor: '#ffff99' });
瀏覽 http://localhost:3000/events
escape_javascript()
可以縮寫為j()
。
j()
是Rails 3.1之後版本才有
第三種方式:使用 JSON 資料格式
JavaScript Object Notation(JSON)是一種源自JavaScript的資料格式,是目前Web應用程式之間的準標準資料交換格式,在Rails之中,每個物件都有to_json
方法可以很方便的轉換資料格式。 (TODO)
<%= link_to 'ajax show', event_path(event), :remote => true, "data-type" => :json, :id => "update_foobar" %>
點擊ajax show就會送出Ajax request了,但接下來要怎麼撰寫處理JSON的程式呢?
$(function() {
$('#update_foobar').bind("ajax:success", function(event, data) {
var foobar = $('#foobar');
foobar.html( data.foobar_number );
});
});
當然,你也可以把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
或是使用
Rack::JSONP
這個Middleware,只要有?callback=
參數就會自動變成JSONP。等同於上述使用:callback => params[:callback]
。
Ajax 表單
除了超連結 link_to 加上 :remote 可以變成 Ajax 之外,表單 form_for 也可以加上 :remote 變成 Ajax。
form_for(@user, :remote => true
Ajax 按鈕
同理於超連結 link_to,按鈕 button_to 加上 :remote => true 參數也會變成 Ajax。
關於除錯
使用 Ajax 後,因為頁面沒有經過重新整理,發送的 request 請後是在背景執行的,所以在除錯的時候需要有額外的工具來協助我們觀察 JavaScript 運作的情形。推薦您安裝Firefox 瀏覽器以及 Firebug 這套工具。在Safari或Chrome也有內建類似Firebug的開發工