Assets 与Ajax 应用程式

Assets静态档案

Assets指的是JavaScriptStylesheets和图档等静态档案,这些档案并不会随Requests不同而有所不同。而在Rails目录中,只有public这个目录是公开读取的,所以通常我们会将静态档案都放在public这个目录下,好让浏览器可以直接读取。但是随着JavaScriptStylesheet档案越来越多时,如何管理这些档案变为一项议题,为了加快浏览器的下载速度,我们会合并JavaScriptStylesheet档案,来减少浏览器Request下载次数。更进一步的还会压缩这些档案来加速下载时间。像是Yahoo!Google都有各自开源出自己的压缩工具YUI CompressorClosure 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.jsapp/assets/stylesheets/application.css,这两个档案看起来充满注解,其实它是个manifest档案,列出了所有要载入的静态档案,这些档案的位置依照惯例放在app/assetsvendor/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_jqueryrequire_jquery_ujs会载入JQueryRailsJQuery 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.cssapplication.js


如何处理图片

放在app/assets/p_w_picpaths下的图片该怎么使用呢?在实际布署后,Rails会将档案名称加以编码,例如rails.png会变成rails-bd9ad5a560b5a3a7be0808c5cd76a798.png这么做的原因是当图片有变更的时候,编码就会不同而有不同的档名,这样就可以避免浏览器快取到旧的档案。也因为档案名称会变动,所以放在app/assets/p_w_picpaths下的图片,要用的时候就没有办法写死档名。在一般的View中,可以使用p_w_picpath_tag这个Helper

<%= p_w_picpath_tag("rails.png") %>

如果在CSS里的话,有两种办法:一是将档案命名为erb结尾,例如app/assets/stylesheets/main.css.erb,然后使用asset_path这个Helper

h1 {background-p_w_picpath: url('<%= asset_path("rails.png") %>');}

另一种方法是使用SassSCSS语法。其中SCSS相容于CSS例如命名为app/assets/stylesheets/main.css.scss,然后使用p_w_picpath-url这个Sass提供的方法:

h1 {background-p_w_picpath: p_w_picpath-url("rails.png")}

如果是js档案中想要拿图片的位置,就只能用js.erb的格式,然后内嵌asset_path Helper方法了。


Precompile编译静态档案

放在app/assets目录下的AssetsRails支援使用不同的语法,例如使用Sass语法产生CSSCoffeeScript产生JavaScript,或是用ERb样板也可以。使用的方法是将附档名命名成.css.sass.css.scss.js.coffee.css.erb.js.erbRails就会编译出结果给浏览器。

Sass是一种CSS3语法的扩充,可以使用巢状、变数、混入、选择子继承等等功能,可以更有效率有弹性的撰写StylesheetSass最后会编译出合法的CSS让浏览器使用。使用上它区分成两种形式的语法scsssass,前者可以与现有的css程式码直接混合在一起,后者则透过缩排来省略了大括号{} (就像Python用缩排一样)。CoffeeScript也是类似原理,它是一种迷你的程式语言,编译之后会输出可读性高、符合JavaScript Lint规范的JavaScript程式码。

学习这两种新语法需要额外的时间投入,但是对需要常常撰写CSSJavaScript的设计师来说,应该是很不错的工具,读者可以自行斟酌是否采用。如果你不打算使用的话,可以编辑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.jsapplication.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

AjaxAsynchronous JavaScript and XML的缩写,是一种不需要重新整理页面,透过JavaScript来与伺服器交换资料、更新网页内容的技术。目的在于改善使用者的操作介面,提升流畅度。它主要是透过浏览器提供的XMLHttpRequestObject来达成,不过因为跨浏览器的困难度,大多数人们会选择使用JavaScript Library来处理Ajax,例如JQueryYUI等。虽然Ajax的缩写中包括XML,但是实务上并不一定要用XML格式,事实上也已经很少人使用XML当作传输的格式了。总归来说,依照Ajax使用的格式分类,有三种方式:

  • 向伺服器请求HTML 片段,然后客户端浏览器上的JavaScript 再替换掉页面上的元素

  • 向伺服器请求JavaScript 程式脚本,然后客户端浏览器执行它

  • 向伺服器请求JSON 或XML 资料格式,然后客户端浏览器的JavaScript 解析后再动作。

第一种方式非常简单,但是限制是一次只能更新一小块内容。Rails比较常见使用第二种方式,容易使用弹性又大。而第三种方式则将JavaScript程式都放在客户端浏览器上,相较于第二种则多了解析JSON或XML的部份。Web API的设计角度来看,与表现层无关的JSON格式是比较干净的,可以获得比较好的重复使用性。

讲解JavaScriptJQuery语法已经超过本书范围,本章接下来会假设读者已经有基本认识。身为一个Web程式设计师,不得不对JavaScript要有基本了解。

Unobtrusive JavaScript

Rails从3.0开始将支援AjaxJavaScript都改成用Unobtrusive UJS)< /span>的方式。什么是Unobtrusive呢?用个范例来说吧:

link_to 'Remove', event_path(1), :method => :delete

Rails 3之前,会输出:

<a  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.jsend

新增app/views/events/_event.html.erb,内容与show.html.erb 相同

新增app/views/events/show.js.erb,内容如下

$('#content').html("<%= escape_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这套工具。SafariChrome也有内建类似Firebug的开发工具。



转载自:http://blog.csdn.net/aabbcc456aa/article/details/7482349