Rails: 只在需要的時候加載需要的 JavaScript
一年前我做了一個關于這個主題的演講。我第一次關注這個技術是因為 @dhh發布的他們在 37signals使用的一些觀點。我注意到他們 在模板視圖里</span>如何使用JavaScript,動態生成JavaScript這是它的關注點,這引起了我的思考。
與之前我們在前端加載全部javascript相比,為什么我們不加載最少量的JavaScript然后在用戶界面需要的時候再加載額外的JavaScript代碼呢?
我把這稱為響應式的JavaScript,你聽說過pjax這個 術語,或者 unobtrusive JavaScript,等等。目的是后端的JavaScript按前端動作加載。不管是GET、POST、DELETE等待,只要請求是ajax,響應將會是JavaScript而不是JSON。
你使用的一些產品中的一些實例或許已經使用這個 概念了。BCX、GitHub、Airbnb都以不同方式使用了這個概念。這一概念的問題在于它不綁定任何的庫或框架,你必須使用目前的工具來它工作。慶幸,現在已經有了一個JavaScript庫可以幫助你開始。jQuery-UJS可以讓你添加 remote=”true” 到任何鏈接,這些鏈接會自動轉換成一個Ajax調用。
首先,一個例子
class PostsController < ApplicationController
def index
@posts = Post.all
end
def show
@post = Post.find params[:id].to_i
respond_to do |format|
format.js
format.html
end
end
def create
#.. Create a post
respond_to do |format|
format.js
format.html
end
end
end</pre>
<!-- app/views/index.html.erb -->
<ul class="posts">
<%= @posts.each do |post| %>
<%= render_link_to_post(post) %>
<% end %>
</ul># app/views/create.js.erb
(function() {
var $post = $("<%= j render_link_to_post(@post) %>")
$post.hide().prependTo($("ul.posts")).fadeIn()
})()# app/views/show.js.erb
(function() {
var $post = $("<%= j render("overview", post: @post) %>")
$post.dialog("open")
})()<!-- app/views/show.html.erb -->
<div class="title">
<%= @post.title %>
</div>
<div class="body">
<%= @post.body %>
</div>
#helper.rb
def render_link_to_post(post)
link_to post.title, post_path(post), remote: true
end
在這個例子中,當有人點擊鏈接,它將會調用/posts/:id。如果請求是AJAX,它將調用show.js.erb,執行模板里的代碼。如果不是的話,它將調用show.html.erb。還有一個叫created.js.erb模板,當你通過Ajax表單創建一個資源時將被調用。表單被清理的盡可能簡單。
URL幫你定位到bug
假設你點擊鏈接時有一個bug,通過使用這種技術,你可以通過查看URL追溯起源。
舉例:posts/:id與與位于app/views/posts/show.js.erb的JS模板相關聯。
正如你在處理HTML視圖時所期望的一樣。
web頁面有生命期
回顧一下上面的例子。當你用Ajax表單發布一條post,按預設列表將生成一個與該post對應的鏈接。看一下請求的方法:它和你調用/posts時相同,這不是一種偶然。當你使用這種方法時會試圖避免代碼重復,ajax除了不最終生成頁面外與正常請求使用相同的組件。
這意味著,當你發布一個新post后,如果你刷新頁面,將會顯示相同的文章列表,因為它們使用了相同的機制。
復用相同的視圖邏輯
這是一個以前的擴展。既然在服務器端已經生成了HTML,你可能會在視圖里看到很多重復代碼,使用助手類或修飾器來簡化代碼將不會是件難事。
因為所有映射都來自從同一個地方,HTML的修改也可以在同一個地方完成。比如,如果我想要將列表中的鏈接替換成一個更具描述性的,我可以修改render_link_to_post(一個壞的函數名,原諒我)的實施細則以適應變化。
JavaScript在動作間的傳遞 & 頁面加載優化
現在,你的JavaScript在2個不同的地方加載:
- 由標簽鏈接加載JavaScript。在頁面加載時加載,運行時不能定制,JavaScript可以被預編譯;
- 作為用戶頁面動作的響應加載。
</ol>
對服務內容能更好的控制是這種方式的優點。如果你感覺解析代碼較多時慢,你可以一些請求時的負載抽取到標簽鏈接中加載。
當需要時加載需要的
由于網頁在服務器端的渲染,所以響應最小化時你有很多可選的庫,也許你可以試試jQuery、Zepto或其他什么的,但記住:JavaScript幾乎從來(我不知道是不是每個都是)不在加載時渲染頁面,加載完后才開始。
看看上面的例子。create.js.erb上有個問題,我可以用很好的功能和其他好東西來包裝它,但如果人們從不發布post這有什么意義呢。
這需要你維持一個平衡。當有有些東西大量使用是(每用戶每頁使用超過一次),像你其他庫一樣用標簽來加載它吧,其他的當HTML動作觸發時再加載吧。
可重復的動作(調試目的)
你是否遇到過這種情況,當你調試應用的時候3~4個不同的動作都提示你的javascript代碼里有bug?因為響應是獨立的JavaScript代碼,你只能將響應粘貼到控制臺一步步捕捉異常。
我做了什么呢,我拿掉了擋在我和我正在使用變量間的障礙,這樣,在控制臺了我可以將bug縮小到變量層面。
清晰
客戶端的JavaScript通常與通知或事件搭配使用,觸發這些事件、收到服務器更新,這一切瞬間就緒。通常瞬間見效的事通常難以追蹤。
當響應是JavaScript代碼時結果就很容易理解了。看看上面的例子,我詳細你能明白這兩個Ajax響應的預期行為。
就是這樣
這是我的選擇,你可也可以有自己的選擇。我希望你喜歡這篇文章且有了自己嘗試一下的沖動。
如果你實現了,在推ter給我留言告訴我它是如何運行的!