访问次数
我的另一个总结性博客: todototry 大米粒

Rails 3:提高Ajax应用速度

    我建立了一个列表网站,ListKungFo,其中大量使用了Ajax,目前为止网站运行非常良好。而过去两周里,我一直在寻找一些能够进一步提高UI性能的方法,现在写出来和大家分享。

【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?语句

  1. def show  
  2.   @list_item = @list.list_items.find( params[ :id ] )  
  3.   if stale?( :etag => @list_item, :last_modified => @list_item.updated_at.utc, :public => true )  
  4.     respond_with( @list_item )  
  5.   end  
  6. end  

stale?语句会通过响应发送回一个etag与一个last_modified日期。如果下一个请求是相同的URL,那么浏览器会把这个etag和last_modified日期发送给服务器。然后stale?方法会对这两个参数进行分析,如果内容相同,则返回304,如果出现参数值不同,那么说明有新的内容,这样返回200。

Rails 3新特性:提高Ajax应用速度

想知道更详细的stale?方法的用法,可以查阅Rails的API文档,以及阅读Rails的手册。

2 确保浏览器对每次请求都接收新的数据

上面的修改完成后,发生了一些有趣的事情。在很短的时间内,相同的Ajax行为被触发了许多次,而浏览器并没有向服务器发送一次请求,而都是从缓存中取得数据。虽然显然让UI变得快了很多,但这并不完全是我所想要的。我的目标是获得最佳的性能,同时还要保证屏幕上出现正确与最新的数据。

浏览器的缓冲行为受到了三个HTTP头的flag状态的影响:cache-controll、pragma和expires

想要关闭浏览器的缓存功能,你可以发送下面的代码:

  1. def set_cache_buster  
  2.     response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"  
  3.     response.headers["Pragma"] = "no-cache"  
  4.     response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"  
  5.   end 

然而我想要做的是这样:

  1. def set_must_revalidate  
  2.     response.headers["Cache-Control"] = "must-revalidate"  
  3.   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中:

  1. class ListItem < ActiveRecord::Base 
  2.   belongs_to :list  
  3.   after_save :update_list  
  4.   after_destroy :update_list  
  5.    
  6.   # [...]  
  7.    
  8.   def update_list  
  9.     self.list.updated_at = Time.now  
  10.     self.list.save  
  11.   end  
  12. end 

这样,list_items_controller的索引方法看上去就像这样:

  1. def index  
  2.     @list_items = @list.list_items  
  3.    
  4.     if stale?( :last_modified => @list.updated_at )  
  5.       respond_with( @list_items )  
  6.     end  
  7.   end 

如果不使用updated_at字段,我也可以给List模型加上一个version字段,但这样看起来没什么必要。如果这个模型不适合你的应用,那么你需要找到另一种方法,检查集合是否被修改了,比如计算一下集合中所有对象的校验和,这也能行得通。

Rails 3.0 主要改进内容:

1. New Active Record query engine

示例代码:

  1. users = User.where(:name => "david").limit(20)  
  2. users.where("age > 29")  
  3. # SELECT * FROM users  
  4. # WHERE name = "david" AND age > 29   
  5. # ORDER BY name  
  6. # LIMIT 20  
  7. users.order(:name).each { |user| puts user.name }  

2. New router for Action Controller

示例代码:

  1. resources :people do  
  2.   resource :avatar  
  3.  
  4.   collection do  
  5.     get :winners, :losers  
  6.   end  
  7. end  
  8.  
  9. # /sd34fgh/rooms  
  10. scope ':token', :token => /\w{5,5}/ do  
  11.   resources :rooms  
  12. end  
  13.  
  14. # /descriptions  
  15. # /pl/descriptions  
  16. # /en/descriptions  
  17. scope '(:locale)', :locale => /en|pl/ do  
  18.   resources :descriptions  
  19.   root :to => 'projects#index'  
  20. end 

3. New Action Mailer

示例代码:

  1. class Notifier < ActionMailer::Base 
  2.   default :from => 
  3.     "Highrise <system@#{APPLICATION_DOMAIN}>"   
  4.  
  5.   def new_project(digest, project, person)  
  6.     @digest, @project, @person = digest, project, person  
  7.  
  8.     attachments['digest.pdf'] = digest.to_pdf  
  9.     attachments['logo.jpg']   = File.read(project.logo_path)  
  10.  
  11.     mail(  
  12.       :subject => "Your digest for #{project.name}",  
  13.       :to => person.email_address_with_name  
  14.     ) do |format|  
  15.       format.text { render :text => "Something texty" }  
  16.       format.html { render :text => "Something <i>textyi>" }  
  17.     end  
  18.   end  
  19. 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的开发环境。发布网站时,你只需要一个数据库和一个网络服务器即可。

【编辑推荐】

posted @ 2011-10-11 13:54  fandyst  阅读(341)  评论(0编辑  收藏  举报