前陣子在接觸 rails turbolinks 時想說 turbolinks 怎這麼 suck,光是 GA、Facebook Pixel 等 tracking script 無法正常運作就讓人十分頭痛(當時直接怒拔...),但在最近因為看了這篇文章 瞬間懂了。

Turbolinks 是在 Ruby on Rails 4.0 被默認的一個 gem,當時(2013年)很多人在分享時都搭上一句標題「Turbolinks for Rails (like pjax)」。那什麼是 pjax?turbolinks?更詳細解說可看上述提及的文章,這邊就針對一些所獲的重點摘錄做分享。

講什麼是 pjax 跟 turbolinks 之前要先知道在 HTML5 的 History Interface及裡面的 pushState。這是文章內的解說

pushState允許Javascript在會話歷史中隨意儲存數據,並且綁定一個標題和可選的URL。back()和forward()方法允許我們瀏覽已存儲的會話歷史數據。通過這個方法,我們可以使用pushState保存當前頁的瀏覽歷史,並且動態的在不同狀態下,後退和前進,而不需要重新載入整個頁面。

PJAX & Turbolinks

簡單來說,這兩個東西的理念都是:當使用者點擊一個連結時,不需要重新載入整個頁面(平常使用網站會有突然閃爍、白一下的感覺),而是直接載入並替換需要更新的網站內容。

那有什麼不同?

1. 更新的範圍

PJAX 可以指定要更新哪個網頁元素,like this

$.pjax({url:'/authors',container:'#main'})

但 Turbolinks 是直接替換整個網頁的 <title> 和 <body>,而且預設所有網站內的超鏈結都屬於 turbolinks 範疇。所以不用特別指定哪些連結需要而去標記他們,Turbolinks 會自動處理(好像很夢幻,但若不懂會覺得網站很多雷...)。

只需要:

# Gemfile
gem "turbolinks"

# app/assets/javascripts/application.js
//= require turbolinks

2. 伺服器回應的內容

PJAX 也只需要伺服器回應元素的內容就好,像是

if request.headers['X-PJAX']
  render:layout
end

而 Turbolinks 不需要而外在 controller 做設定,照舊即可(有原生的 gem support 真好!)

當使用 document.ready 或是 $(function(){}) 時,這些事件只會在 DOM 完成載入時被觸發,但不會在 Turbolinks 更改網頁內容時觸發,所以可利用 page:change 這個事件

$(function(){
  initPage();
});
$(window).bind('page:change', function(){
  initPage();
});

另外若有像是 GA 或是 Pixel 等的 tracking script 記得也需要加入 page:change 的事件處理,或是將這些 script 擺在 <body> 內。

小結

雖然 PJAX 看起來可以讓客戶端要處理的數據量較小(伺服器只回應需要的部份),但在開發與設定上也更加的麻煩,讓開發者需要額外設定的工作也變更多,更違反了 Rails 快速開發的哲學。既然用 Rails 了,就好好體會其中的設計,使用原生內建的 turbolinks 吧!

BTW, 會體會這麼深是因為有個專案在 themeforest 買了個版,裡面大量使用 pjax,整個頁面切換很 smooth 很夢幻(所以才挑它...),但在整理 assets 時發現 pjax theme 根本 rails 天敵,建議大家若有買版(例如 themeforest)的習慣,要留意裡面是否有用 pjax,不然光要整理裡面的 js 就很暈...