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 enddef 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給我留言告訴我它是如何運行的!