Ruby on Rails 的終極 Unobstrusive jQuery 方案

使用jQuery,你的RoR Application 可享有所有 Unobtrusive JavaScript 帶來的優點,使編碼和Markup絕對分開,又可以要最快的速度建立所有用戶端的功能和介面效果。

現在的 RoR + Prototype 方案,其中最大一個問題就是如何處理 ySlow 作者 Steve Souders 極度重視的 "Put CSS at top", "Put Javascript At bottom"問題。不少人正為這問題煩惱。

以下文章將討論如何建立一個完全使用 jQuery ,不使用 Prototype 的方法。

Rails 組群亦提供了一些 jQuery 方案,可惜一般方案沒有完全利用 Unobstrusive Programming 的優點,使編碼胡亂放在頁面中間。

這次我們會看看結合 content_for 和 jRails (http://ennerchi.com/projects/jrails),以建立最好的方案。




第一步: 安裝 jQuery


首先建立一個 Layouts。在 你的 controller 檔 (例如 /app/controllers/blogs_controller.rb) 的頂部插入編碼


class BlogsController < ApplicationController
layout "application_layout"
end

然後,在/views/layouts 內新增 "application_layout.html.erb",並貼上編碼

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>

<%=stylesheet_link_tag "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.0/themes/cupertino/jquery-ui.css" %>

</head>
<body>
<%= yield %>
</body>
</html>
<!-- include javascripts -->
<%=javascript_include_tag("http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js") %>
<%=javascript_include_tag("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/jquery-ui.min.js") %>
<!--//include javascripts //-->
<!-- jquery and other functions-->
<%javascript_tag do %>
$(document).ready(function() {
<%= yield :js_ready %>
});
<%end %>
<!-- //jquery and other functions //-->


以上編碼有五點要注意:

stylesheet_link_tag 從 Google CDN 包括了 jQuery UI 這jQuery UI 基本 的stylesheet
<%= yield %> 將頁面主要內容放在原始檔中間位置。
底下兩行是 javascript_include_tag 編碼,把 jQuery 的原始檔,從 Google CDN 包括至網頁。這裡使用了有別於一般 include javascript 放在<head></head>中的方法。原因是"Put CSS at top", "Put Javscript At bottom", 使網頁以最高效率運作。
最後幾行 <%javascript_tag do %>...<%end%> 是輸出 Javascript 原始檔的地方。這幾行對於分開 Program 和 Markup 最為重要。還有,這裡利用了jQuery 出名的"$(document).ready()",使所有javascript 等候至 HTML 下載妥當後才執行。
注意編碼總共有兩處 "yield" 編碼。 使用這 'yield',使輸出可以放在不同的地方。我們利用了這特性,使 HTML 被放在原始檔的中間,而Javascript 被放到原始檔的底部。

將javascript_include_tag 放在<head></head>, ySlow 立即指出問題。

將javascript_include_tag 放在<head></head>, ySlow 立即指出問題。雖然還有Grade A的分數,但是加多幾個Javascript include,分數便會急降。最重要是,下載速度已經慢了很多。


將javascript_include_tag 放在最底, ySlow 有 Grade A 的分數。
Javascript 放在最底部使網頁加速。

現在頁面可以使用 jQuery 了!


第二步:使用jQuery 和 jQuery UI

首先,我們要測試 jQuery UI 的介面效果。為此,我們先在頁面加上一排常用的Tabs。

在測試的頁面 (例如: /views/blogs/index.html.erb) 加入以下編碼:

<div id="section_tabs">
<ul>
<li><%=link_to "Introduction", "#intro"%></li>
<li><%=link_to "Contact Us", "#contact_us"%></li>
<li><%=link_to "About Me", "#about_me"%></li>
</ul>
<div id="intro">
Hello, it is nice to meet you.
</div>
<div id="contact_us">
Email: arthurccube@nowhere.com
</div>
<div id="about_me">
I am someone.
</div>
</div>
<% content_for :js_ready do %>
jQuery('#section_tabs').tabs();
<% end %>


以上編碼建立了 Tabs 的 DOM 結構,單單一句"jQuery('#section_tabs').tabs();", 便完成tabs 所需的所有 javascript 。jQuery UI 聰明地使用selector "id" (#section_tabs) 去找出了解相關 Tabs 的目標,並對相關 DOM 結構修改為Tabs 內容。

而 "<% content_for :js_ready do %>" 這段編碼,是對應在 application_layout 的 "<%= yield :js_ready %>",兩段編碼的結果,是令到相關的javascript 放到頁面的最底部。


jQuery UI 成功建立了一個Tabs。


這時我們再查看ySlow,"Put Javascript At Bottom" 仍然是完美的!



第三步: Rails 去 Prototype 化和Unobstrusive 化


Unobstrusive Javascript 的意思是 "把功能('行為層面')和網面的結構/內容和演示分開"(http://en.wikipedia.org/wiki/Unobtrusive_JavaScript)。

可惜,RoR 因為使用了很多即用即寫的Prototype Helpers,使Javascript 和網頁變得難以分割。

令人高興的是,RoR的 Overriding 功能十分廣泛,我們可以輕易的把有問題的Helpers 修改。

筆者選了最典型的問題Helper - observe_field 來做例子。

首先,安裝 jRails Plugins ,使相關Helpers 使用jQuery:

./script/plugin install http://ennerchi.googlecode.com/svn/trunk/plugins/jrails


在'<%= javascript_include_tag("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/jquery-ui.min.js") %>' 之後插入編碼:


<%= javascript_include_tag("jrails.js") %>

這段編碼把 jRails 所寫的 Javascript 放到頁面。

重啟Application,然後,在剛才的測試頁面 '/app/views/blogs/index.html.erb' 頂部插入編碼:

<% form_tag 'search',
{:id => 'form_search'} do %>
Search: <%=text_field_tag "query"%>
<%= observe_field(:query,
:url => { :controller => :blogs, :action => :search },
:frequency => 0.5,
:update => :intro,
:with => :input)
%>
<% end %>

以上編碼的 observe_field 監察一個名為 'query' 的輸入(即是<%=text_field_tag "query"%> 的輸出)。等會,如果用者在瀏覽器修改這欄位的輸入,observe_field 便把資料送到在 blogs controller 的 search action。


檢視編碼後可看到Javascript 被隨意放在HTML 編碼的中間。

所以,為解決這 Unobstrusive 問題,要修改兩個檔案。

首先在 /app/views/layouts/application_layout.html.erb 最底部插入編碼:

<%= yield :rails_helpers %>

這段編碼用來輸出我們將會修改的 Rails Helper 內容,因為我們將會修改的 observe_field 自己也有一個<script></script> tag, 這個 "yield :rails_helpers"不要放進任何"javascript_tag"內。

現在我們在 '/app/helpers/application_helper.rb' 插入編碼:

# override the existing observe_field putting the javascripts at bottom
def observe_field(field_id, options = {})
content_for :rails_helpers do
super(field_id, options )
end

end

以上編碼Override 了 RoR 原本的沒有 Unobstrusive 概念 的 'observe_field' Helper,使把輸出放到 :rails_helpers,即是整個網頁的最底部。

現在刷新頁面,再檢視原始檔。所有javascript,包括observe_field的編碼也在最底部!



所有javascript 也在最底部!

現在,使observe_field 可以回傳結果,我們在 '/app/controllers/blogs_controller.rb'插入:

def search
render :text => "searching results from query <u><i>'#{params[:input]}'</i></u> @ #{Time.now}"
end


不用按鈕,在Search 輸入的字句也被送到伺服器中。

把Javascript 放在最底的結果是,整個網頁的HTML 結構和相片可以用最快的速度下載到用戶端,即是最重要的'Put Javascript at bottom"!

這個observe_field 只是一個Unobstrusive 化一個Rails Helper 的例子。讀者可用相同的放法修改所有相關的Helper,令Javascript 不存於頁面中間。





第四步:安裝特別的 jQuery Plugins


現在您的系統擁有Rails 和 jQuery。基本上可以安裝的工具很全面,筆者在此列出一個例子。

我們將安裝一個非常好用的 simple auto_complete (http://github.com/grosser/simple_auto_complete/tree/master)

首先,在 http://github.com/grosser/simple_auto_complete/tree/master 下載 simple_auto_complete plugins。

解壓後將文件夾放在/app/vendoer/plugins 內。

在剛下載文件夾找到 '/example_js/javascripts',把 'jquery.autocomplete.js' 複製到 '/public/javascripts'。

並把在 '/example_js/stylesheets' 的 'jquery.autocomplete.css' 複製到 '/public/stylesheets'。

重啟Application。

所有相關的Javascript 和 Stylesheets 已放到了適當位置。

現在修改 '/app/views/layouts/application_layout.html.erb',使這些檔案包括到網頁輸出。

在 '<head>...</head>' 之間任何位置插入編碼:

<%=stylesheet_link_tag "jquery.autocomplete.css" %>


在 '<%=javascript_include_tag("jrails.js")%>' 下面插入編碼:

<%=javascript_include_tag("jquery.autocomplete.js") %>


在'/app/controllers/blogs_controller.rb' 插入以下編碼:

autocomplete_for :blog, :title do |items|
items.map{|item| "#{item.id}: <b>#{item.title}</b>"}.join("\n")
end


註:以上編碼假設您有 blog.rb,其擁有欄名 title。讀者可改作其他 model 和欄名也有效。

在測試的頁面 /views/blogs/index.html.erb 加入以下編碼:

<% form_for :blog do |f|%>
Autocomplete: <%= f.text_field :auto_user_name, :class => 'autocomplete', 'autocomplete_url'=>autocomplete_for_blog_title_blogs_path %>
<%end %>
<%content_for :js_ready do %>
//autocomplete
$('input.autocomplete').each(function(){
var input = $(this);
input.autocomplete(input.attr('autocomplete_url'));
});
<%end %>


這段編碼首先建立關於 blog 的一張表格。然後,使用 jQuery (i.e. $) 的編碼監察相關輸入。

留意輸入會被送到 'autocomplete_for_blog_title_blogs_path' 這路徑。

所以我們要在 routes.rb 加入:

map.resources :blogs, :collection => { :autocomplete_for_blog_title => :get}


autocomplete 功能完成,輸入的字句會自動回傳提示。


第五步:Forgery Token

基於安全理由,網站可能啟動了 forgery token. 我們可以將所有 Ajax 也加上 forgery token ,使相關伺服器要求被接納。


在 "/app/views/layouts/application.html.erb" 內的 "$(document).ready(function() {" 這句編碼下面加入:

// All non-GET requests will add the authenticity token
// if not already present in the data packet
jQuery("body").bind("ajaxSend", function(elm, xhr, s) {
if (s.type == "GET") return;

if (s.data && s.data.match(new RegExp("\\b" + window._auth_token_name + "="))) return;
if (s.data) {
s.data = s.data + "&";
} else {
s.data = "";
// if there was no data, jQuery didn't set the content-type
xhr.setRequestHeader("Content-Type", s.contentType);
}
var auth_token = encodeURIComponent(window._auth_token_name);

s.data = s.data || "";
if (s.data.indexOf(auth_token) < 0 ) s.data += (s.data ? "&" : "") + auth_token + "=" + encodeURIComponent(window._auth_token);
});


總結

雖然RoR 本身提供了很多有用的 Helpers,但是RoR 和 Prototype 密不可分的結構,使優化網頁變得複雜。

這違反了Obstrusive 概念。

筆者演示了怎樣把 Prototype 從 RoR 分開,而且將Javascript 放在原始檔的底部。

之後,我們利用RoR 方便的 Overriding 特性,把一些Helpers 修改為輸出在原始檔最底部 (:rails_helpers)。

最後,我們可以享受各個好用的RoR 或 jQuery Plugins ,亦容易的使程式 Obstrusive化。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值