Resources plugin 是用來處理網站的靜態資源,例如 *.js 及 *.css,由於 modern web design 在 front-end 用了愈來愈多 CSS/JavaScript,使得這些資源是否最佳化也會造成網站效能的影響。
在 Grails 1.3.x 需要自己安裝 resource plugin,這只需要一行指令:
grails install-plugin resources
在 BuildConfig 可以看到 plugins 區塊多了 resources 的宣告。
plugins {
// ...
compile ":resources:1.0.2"
}
Resources 是在編譯、建置階段處理靜態資源,因此並不會讓執行期速度變慢,雖然在 development 階段,它可能會多消耗一些處理效能,偶爾也會造成資源載入不正常。但是到 production 階段,它的效果通常都會帶來正面的幫助。
用 Grails 2.x 建立的網站專案,可以發現 grails-app/views/layout/home.gsp 已經使用 <g:layoutResources /> 的宣告。這是啟用 Resources plugin 的必要步驟,在 View 的 *.gsp 加上宣告,讓資源能在適當的位置載入。
「適當位置」包括在 </head> 以及 </body> 之前,例如:
<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
接下來,將所需要的資源以宣告方式加入 *.gsp,例如:
<head>
<!-- require modules -->
<r:require modules="jquery"/>
<r:layoutResources/>
</head>
在 Grails 2.x 已經預設提供 jquery plugin,而新版的 jquery plugin 有針對 Resources plugin 提供適當設定,因此在 <r:require ... /> 可以直接加入 jqeury 的 modules 設定。
對於專案本身的資源,必須建立 grails-app/conf/ApplicationResources.groovy 設定檔,將相關的資源分類定義成 module。例如以下的範例,是我自行將 Compass 編譯產生的 .css 定義成 "compass" 這個 module。
modules = {
application {
resource url: 'js/application.js'
}
compass {
resource url: [dir: '/stylesheets', file: 'screen.css'],
attrs: [media: 'screen, projection']
resource url: [dir: '/stylesheets', file: 'print.css'],
attrs: [media: 'print']
resource url: [dir: '/stylesheets', file: 'ie.css'],
attrs: [media: 'screen'],
wrapper: { s -> "<!--[if IE]>$s<![endif]-->" }
resource url: [dir: '/stylesheets', file: 'ie6.css'],
attrs: [media: 'screen'],
wrapper: { s -> "<!--[if lt IE 7]>$s<![endif]-->" }
}
因此,若網站頁面需要用到 compass 模組,就只需要在某些 *.gsp 加上:
<r:require modules="jquery, compass"/>
這麼做有什麼好處呢?當網站用到的資源多到某個程度,利用 Resources plugin modules 定義進行集中分類、版本管理,就可以讓靜態資源更容易維護。
以下是 jquery-colorbox 及 codemirror 的資源定義,在 modules 宣告中可以混合 *.css 及 *.js,將歸屬於同一個類別的資源定義在一起(但資料夾可以分開,例如 /css 及 /js)。
colorbox {
resource url: [dir: '/css', file: 'colorbox.css'], attrs: [media: 'screen']
resource url: [dir: '/js', file: 'jquery.colorbox-min.js']
}
codemirror {
resource url: [dir: '/codemirror/lib', file: 'codemirror.css'],
attrs: [media: 'screen']
resource url: [dir: '/codemirror/lib', file: 'codemirror.js']
resource url: [dir: '/codemirror/mode/rst', file: 'rst.js']
resource url: [dir: '/js', file: 'jquery.codemirror.js']
resource url: [dir: '/codemirror/lib/util', file: 'runmode.js']
}
但是 Resources plugin 如果功能只是這樣,那它存在的必要性其實就不高,也不會變成 Grails 2.x 的預設功能。因為在 Grails 中,使用 taglib(自訂標籤)同樣可以輕鬆達到相同目的,例如利用 SimpleTag 自行定義 <res:jquery />、<res:codemirror /> 等標籤,用起來也不會比較麻煩。
所以,Resources plugin 對資源進行定義、管理,其實只是第一個步驟而已,我們可以(選擇性)加裝其它以 Resources plugin 為基礎建立的 Plugins,來達到資源自動最佳化的需求。
以下的 Plugin 安裝指令,應該從套件名稱就能猜到它們的用途。
# Compress resources using GZip/Deflate
grails install-plugin zipped-resources
# Cache resources in browsers
grails install-plugin cache-headers grails install-plugin cached-resources
# Delivery resources using CDN
grails install-plugin cdn-resources
# Process less css
grails install-plugin lesscss-resources
# Pre-compress resources with YUI Compressor
grails install-plugin yui-minify-resources
zipped-resources 及 cached-resources 是 GrailsRocks 提供的 Plugins(開放源碼授權),只要安裝上去,就能立即得到靜態資源被壓縮、快取的效果。對於瀏覽器來說,差別只是存取 *.css 及 *.js 的檔案名稱被編碼過,其它並無副作用。
cdn-resources 可以和 zipped/cached 混搭使用,顧名思義就是透過 CDN(Content-Delivery-Network)來分散、加速瀏覽器對資源的存取,並減少網站伺服器的處理及頻寬負擔。它的原理相當簡單,開發者只要申請 Amazon CloudFront 或其他 CDN 服務,透過 CDN 來 mirror 網站的檔案,然後將 CDN URL 設定好,Grails 最後生成的 html 就會自動以 CDN URL 取代靜態資源原本的 URL。
使用 cdn-resources 需要以下的設定:
grails.resources.cdn.enabled = true
grails.resources.cdn.url = "http://cdn.your-web-name.com/"
因為有 enabled 選項可以自訂,所以建議只針對 production 開啟 cdn,至於 development 階段本來就沒有必要用 cdn,用了只會讓網站的開發、測試變得無法進行。
一般來說,使用 CDN 對於開發初期、內容不斷更新的網站,會帶來一些影響,因為大多 CDN 服務有 TTL(Time-To-Live)的限制,也就是內容可能已更新,但 CDN 伺服器仍然使用舊資料的快取來回應瀏覽器,造成網站發佈更新後的結果不一致、需要等候資源被更新(以 Amazon Cloud Front 來說,可能長達 24 小時)。
但 cdn-resources 與 zipped/cached-resources 搭配使用,理論上是能夠避免這種問題發生,因為 *.css / *.js 的檔案名稱會經過編碼,所以 JavaScript/CSS 靜態資源如果有更新,理論上就會產生新的檔案名稱,此時 CDN 服務就會重新讀取發佈的資料。當然,這在開發階段並不容易測試效果。
最後一個建議嘗試的套件是 yui-minify-resources,這個套件的使用方式也非常簡單,只要安裝好就會立即得到效果。它是利用 YUI Compressor 將 *.css/*.js 檔案進行壓縮處理,得到 *.min.css/*.min.js 最佳化的結果。
這裡也建議只將 yui-minify-resources 用於 production,如此一來,在 development 階段就能使用原始的 .css / .js 檔案以利除錯,而打包成 war 發佈之後又能自動化進行壓縮,可以同時兼顧 production 及 development 不同階段的需求。
回到文章一開始說到的「問題」,也就是許多舊的 Grails Plugins 並不支援 Resources plugin,主要原因是這些 plugin 附帶的靜態資源並沒有提供 resources 設定,造成在啟用 resources 時缺少檔案,瀏覽器讀不到(404 error)。
但是 2.x 遲早會成為 Grails 的主流版本,而 Resources plugin 帶來的利大於弊,所以這也是時候該淘汰那些不再繼續維護、更新的套件了;因為有了 Resources plugin,有一些 Grails plugin 僅有提供 3rd party js/css library 這種簡單功能,可以考慮自行打包 plugin 或藉由 resources modules 的宣告來達成。
Grails 是 VMWare / SpringSource 建立的開發框架,旨在提供 Java EE 開發者能利用 Groovy 程式語言、Rails 風格,簡化 Modern Web Application 的專案開發工作,既能延續利用現有的 Java Library,滿足企業級的開發需求,又能兼顧團隊對敏捷開發的追求。
如果您對 Grails 開發框架有興趣,歡迎加入我們的 Groovy Taiwan 社群。
沒有留言:
張貼留言