這篇是我在 RubyConfChina 2013 的 Talk:Maintainable Rails View。
當初會整理這個 Talk 的原因是因為長久以來:相對於 View,在一個 Project 裡面,設計出乾淨的 Model 與 Controller,是相對簡單的。但事情一跑到 View,就會變得相當複雜。很難有一個基礎簡單的思路去整理這些糾結的線條。
所以我最後決定釋出一份這樣的整理指南。這其實也是我們 Rocodev 目前在用的 Rails View 整理技巧。
前情提要
要了解這些用法中間的轉折,首先我必須先解釋幾個前提,這是這些「整理方法」之所以被發明的原因:
- 在 View 裡面有 Logic 糾纏 ( if / else & other syntax )是不好的,這會導致 View 很難修改以及維護
- 在 View 裡面有 Logic 糾纏是不好的,這會導致 View Performance 下降 ( pure logic )。
- 在 View 裡面有 Logic 糾纏是不好的,這會導致 View Performance 嚴重下降 ( with data query )。而這包含在 Helper 裡面 perform data query。
這個 Talk 會包含以下幾個主題:
- Helper Best Pratices
- Partial Best Pratices
- 除了 Helper 與 Partial 之外的整理武器
- Object-Oriented View
我會在這篇文章裡面,介紹 18 個整理手法。
值得注意的是,這些手法是「循序漸進」的,也就是前面的手法未必是「最好」的,而是在「初期整理階段」是一個好的手法,而事情變得複雜的時候,你才需要越後面的技巧去協助整理。
1. Move logic to Helper
這是一段經常在 View 裡面直覺寫出來的判斷式。
- 如果只有一個條件,如
if current_user
,則不用進行整理 - 如果在第一次撰寫時,就發現會有兩個條件,則在最初撰寫時,就使用一個簡易的 helper 整理。
例:
i.e.
editable?(post)
並不是一個好的名字,不過可以先標上打上 # TODO: REFACTOR
,之後再回來整理。
2. Pre-decorate with Helper (常用欄位預先使用 Helper 整理)
在設計 Application 時,常常會遇到某些欄位,其實在初期設計時,就會不斷因為規格擴充,一直加上 helper 裝飾。比如 Topic 的 content :
在幾次的擴充之下,很快就會變成這樣:
而這樣的內容,整個 Application 可能有 10 個地方。每經過一次規格擴充,developer 就要改十次,還可能改漏掉。
針對這樣的情形,我們是建議在第一次在進行 Application 設計時,就針對這種「可能馬上就會被大幅擴充」的欄位進行 Helper 包裝。而不是「稍候再整理」
常見的情形如:
- render_post_author
- render_post_published_date
- render_post_title
- render_post_content
3. Use Ruby in Helper ALL THE TIME ( 全程在 Helper 裡面使用 Ruby )
有時候會因為要對 View 進行裝飾的原因,會被迫在 Helper 裡面設計出這種 Code
或者是
這是 非常不好
的設計手法,在 Ruby Helper 裡面穿插純 HTML 與 quote 記號,會很容易因為少關一個 quote,就導致 syntax error。另外一個潛在副作用是:Helper 被這樣一污染,Developer 因為害怕程式碼爆炸,很容易就降低了重構的意願。
因此,嚴格禁止
在 Ruby Helper 裡面穿插任何 HTML 標記。請使用任何可以生成 HTML 的 Ruby Helper 取代。
4. mix Helper & Partial (混合使用 Helper 與 Partial )
穿插 HTML 在 Helper 裡面還有另外一個後遺症。Helper 的輸出最後往往要用 raw
/ .html_safe
實作 HTML unescape。
從而造成了一個非常巨大的 security issue。Ruby on Rails 的標準預設是 HTML escape,避免了非常多會被 XSS 攻擊的可能。穿插 HTML 在 Helper 的設計,導致了一個巨大的曝險地位。
因此,只要遇到需要穿插稍微複雜 HTML 的場景,請不吝惜使用 Helper 與 Partial 穿插的技巧實作。如修改成以下的程式碼:
常見的設計情境如:
- category in list
- post title in breadcrumb
- user name with glyphicons
5. Tell, Don't ask
有些時候,開發者會在 New Relic 發現某個 view 的 Performance 低落,但是卻抓不出來實際的問題在哪裡。這是因為是慢在 helper 裡面。
這是一個相當經典的範例:
這是因為在 View / Helper 裡面被 query 的資料是不會 cache 起來的。在 helper 裡面才 撈
tags 出來,這樣的設計容易造成 N+1
問題,也會造成 template rendering 的效率低落。
改進方法:盡量先在外部查詢,再傳入 Helper 裡面「裝飾」
6. Wrap into a method ( 包裝成一個 model method )
有時候,我們會寫出這種 Helper code :
這段程式碼有兩個問題:
- Ask, Not Tell
- 問 name 的責任其實不應放在 Helper 裡面
可以作以下整理,搬到 Model 裡面,這樣 author_name
也容易實作 cache :
Maintainable Rails View 系列文目錄