Rails 3:提高Ajax應用速度
原創【51CTO.com 5月18日外電頭條】而過去兩周里,我一直在尋找一些能夠進一步提高UI性能的方法,得到的成果主要是返回正確的HTTP狀態代碼,優化瀏覽器的緩存功能。
51CTO推薦專題:Ruby On Rails開發教程
具體地說,會返回兩種狀態代碼:
◆ 返回200-“Ok”,這告知瀏覽器服務器能夠成功的對請求進行響應。響應包含了從服務器返回的HTTP載荷中的數據。
◆ 返回304-“Not modified”,表示未修改,這告知瀏覽器所發出請求中的數據并沒有改變,因此可以從緩存中裝載數據。這種情況下,響應不包含HTTP載荷。
既然“Not modified”消息包含的數據要少得多(沒有內容,只有頭),這樣你就***返回到瀏覽器這里,當然你需要先確保瀏覽器的緩存中已經存在數據了。
在我的應用中,我發現服務器返回的200-響應比304-響應要多得多。這造成了兩方面的問題:
◆ 不得不傳輸比所需的更多的數據
◆ UI不得不處理更多數據
這兩方面出現的問題都會讓應用的速度變慢。雖然只是慢了一點,但在UI端還是足夠讓人察覺到了。幸運的是你只需要對Rails應用做幾個小修改,就能獲得應有的效果。
1在GET方法中使用stale?語句
- def show
- @list_item = @list.list_items.find( params[ :id ] )
- if stale?( :etag => @list_item, :last_modified => @list_item.updated_at.utc, :public => true )
- respond_with( @list_item )
- end
- end
stale?語句會通過響應發送回一個etag與一個last_modified日期。如果下一個請求是相同的URL,那么瀏覽器會把這個etag和last_modified日期發送給服務器。然后stale?方法會對這兩個參數進行分析,如果內容相同,則返回304,如果出現參數值不同,那么說明有新的內容,這樣返回200。
想知道更詳細的stale?方法的用法,可以查閱Rails的API文檔,以及閱讀Rails的手冊。
2 確保瀏覽器對每次請求都接收新的數據
上面的修改完成后,發生了一些有趣的事情。在很短的時間內,相同的Ajax行為被觸發了許多次,而瀏覽器并沒有向服務器發送一次請求,而都是從緩存中取得數據。雖然顯然讓UI變得快了很多,但這并不完全是我所想要的。我的目標是獲得***的性能,同時還要保證屏幕上出現正確與***的數據。
瀏覽器的緩沖行為受到了三個HTTP頭的flag狀態的影響:cache-controll、pragma和expires
想要關閉瀏覽器的緩存功能,你可以發送下面的代碼:
- def set_cache_buster
- response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
- response.headers["Pragma"] = "no-cache"
- response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
- end
然而我想要做的是這樣:
- def set_must_revalidate
- response.headers["Cache-Control"] = "must-revalidate"
- end
因為這么做可以讓瀏覽器在每次請求時檢查新加入的和被更新的數據,我在我的application_controller.rb中添加了這個方法,并且在before_filter控制器中加以調用。
3在返回集合的GET方法中使用stale?(例如索引)
上面的stale?例子是從控制器的show方法中取出的,這是網絡上非常通用的做法。如果想要使用這個方法返回一個集合,比如一個典型的控制器索引方法,那么需要想辦法找出當前的集合和上次請求中的是否相同。
我的ListKungFu網站有一個類型列表List,其中包含很多ListItem。每個ListItem從屬于一個List。為了在list_items_controller中找出某個ListItem集合是否有變化,我添加了名為list.updated_at的時間戳,每次寫入操作時都會更新。
在ListItem.rb中:
- class ListItem < ActiveRecord::Base
- belongs_to :list
- after_save :update_list
- after_destroy :update_list
- # [...]
- def update_list
- self.list.updated_at = Time.now
- self.list.save
- end
- end
這樣,list_items_controller的索引方法看上去就像這樣:
- def index
- @list_items = @list.list_items
- if stale?( :last_modified => @list.updated_at )
- respond_with( @list_items )
- end
- end
如果不使用updated_at字段,我也可以給List模型加上一個version字段,但這樣看起來沒什么必要。如果這個模型不適合你的應用,那么你需要找到另一種方法,檢查集合是否被修改了,比如計算一下集合中所有對象的校驗和,這也能行得通。
Rails 3.0 主要改進內容:
1. New Active Record query engine
示例代碼:
- users = User.where(:name => "david").limit(20)
- users.where("age > 29")
- # SELECT * FROM users
- # WHERE name = "david" AND age > 29
- # ORDER BY name
- # LIMIT 20
- users.order(:name).each { |user| puts user.name }
2. New router for Action Controller
示例代碼:
- resources :people do
- resource :avatar
- collection do
- get :winners, :losers
- end
- end
- # /sd34fgh/rooms
- scope ':token', :token => /\w{5,5}/ do
- resources :rooms
- end
- # /descriptions
- # /pl/descriptions
- # /en/descriptions
- scope '(:locale)', :locale => /en|pl/ do
- resources :descriptions
- root :to => 'projects#index'
- end
3. New Action Mailer
示例代碼:
- class Notifier < ActionMailer::Base
- default :from =>
- "Highrise <system@#{APPLICATION_DOMAIN}>"
- def new_project(digest, project, person)
- @digest, @project, @person = digest, project, person
- attachments['digest.pdf'] = digest.to_pdf
- attachments['logo.jpg'] = File.read(project.logo_path)
- mail(
- :subject => "Your digest for #{project.name}",
- :to => person.email_address_with_name
- ) do |format|
- format.text { render :text => "Something texty" }
- format.html { render :text => "Something <i>texty< span>i>" }
- end
- end
- end
4. Manage dependencies with Bundler
5. 默認啟用跨站點工具 XSS 保護
6. 告別字符編碼問題困擾
7. Active Model: Validations, callbacks, etc for all models
8. 官方的插件 API
9. 內部重構
10. Agnosticism with jQuery, rSpec, and Data Mapper
11. 文檔完善
Rails 是一個用于開發數據庫驅動的網絡應用程序的完整框架。Rails基于MVC(模型- 視圖- 控制器)設計模式。從視圖中的Ajax應用,到控制器中的訪問請求和反饋,到封裝數據庫的模型,Rails 為你提供一個純Ruby的開發環境。發布網站時,你只需要一個數據庫和一個網絡服務器即可。
【編輯推薦】