搶先一步
VMware 提供培訓和認證,助力你的職業發展。
瞭解更多Web 應用通常嚴重依賴我們所說的靜態資源,例如 Javascript、CSS 和影像檔案。在 Grails 應用中,它們被放置在專案的web-app目錄下,然後從 HTML 中引用。例如,
<link rel="stylesheet" href="${resource(dir: 'css', file: 'main.css')}" type="text/css">
將建立指向檔案web-app/css/main.css的連結。這一切都非常簡單。你甚至可能認為當前的支援已完全滿足任何人的需求。你還想做些什麼呢?
這是個好問題。答案取決於你應用的複雜性,但讓我們從上面提到的 CSS 連結示例開始。為什麼我們必須完整地輸入<link rel="..." href=...>?只需檢視副檔名,我們就知道該資源是一個 CSS 檔案。我們也知道 CSS 檔案應該使用上面的元素連結到 HTML 頁面中。所以我們基本上是在為一個 Grails 本身應該能夠處理的事情進行大量重複輸入。
“好的,”我聽到你這樣說,“但是我們可以為 CSS 連結新增一個標籤。”確實可以,而且 Grails 2 將包含一個<g:external/>標籤,它會智慧地為資源選擇合適的連結型別。但現在考慮一下生產環境:將 CSS 和 Javascript 檔案捆綁在一起不是個好主意嗎?壓縮它們怎麼樣?(順便說一句,雅虎提供了一個名為 ySlow 的工具,它可以為你提供關於這些最佳化的建議)。那麼我們如何實現這些最佳化呢?你可能可以臨時處理,但這樣做會限制你的選擇,因為你無法定義資源之間的關係。我們真正想要的是一種在中心位置宣告資源並指示哪些資源應該被處理和捆綁在一起的方法。
如果我還沒有說服你靜態資源可以有更好的管理方式,那麼請考慮一下 Grails 外掛。許多外掛提供了它們自己的靜態資源。事實上,有些外掛提供的資源彼此相同。例如,舊版本的 YUI、Bubbling 和 Grails UI 外掛都包含了 YUI 庫。我們真的需要多個完整的 Javascript 庫副本嗎?當然不需要。部分問題可以透過外掛間的依賴來解決,但這本身效果不佳,因為外掛無法宣告其某些資源依賴於另一個外掛中的特定資源。最終,開發者需要確保所有合適的資源(包括傳遞性依賴)都包含在每個頁面中。這可能需要一些反覆試驗才能確保所有必需的資源都連結到頁面中,並且順序正確。
幸運的是,我們可以做得更好。請看 Resources 外掛。
只需安裝這一個外掛,你就可以開始在可重用的模組中宣告你的靜態資源(Javascript、CSS 等)。然後你可以在模組之間定義依賴關係,這樣 Grails 就能準確知道一個模組需要哪些資源,包括那些在傳遞性依賴中指定的資源。此外,模組還能確保資源在頁面中總是按照正確的順序宣告,並且沒有重複。透過這種方式,模組極大地減輕了靜態資源管理的麻煩。
你可以在應用和外掛中宣告資源模組。有幾種方法可以實現,但最常見的方法是向專案中新增一個或多個專用的 artifact。對於應用來說,這可能是grails-app/conf/ApplicationResources.groovy。另一個例子是 YUI 外掛中的grails-app/conf/YuiPluginResources.groovy。這些檔案的基本結構如下所示
modules = {
core {
resource url: 'js/core.js', disposition: 'head'
resource url: 'js/ui.js'
resource url: 'css/main.css'
resource url: 'css/branding.css'
resource url: 'css/print.css', attrs: [media: 'print']
}
utils {
dependsOn 'jquery'
resource url: 'js/utils.js'
}
forms {
dependsOn 'core', 'utils'
resource url: 'css/forms.css'
resource url: 'js/forms.js'
}
}
"core"、"utils" 和 "forms" 是我們應用模組的名稱,正如你可能猜到的那樣,"forms" 模組依賴於 "core" 和 "utils"。你還可以看到 "utils" 模組依賴於 "jquery",這是一個由 jQuery 外掛提供的模組。這種結構最好透過圖表形式來表示
如果我們更深入地研究上面的模組定義,我們可以看到模組內的單個資源是使用 "resource" 加上 URL 來宣告的。這個 URL 是資源相對於專案中的web-app目錄的位置。如果你願意,你還可以新增額外的屬性來精細控制資源,特別是透過 "disposition" 設定。
有兩種標準的 disposition(處置):"head",表示資源放在 <head> 元素內;"defer",通常表示放在 body 的末尾(儘管你可以控制精確的位置,稍後你會看到)。預設情況下,CSS 檔案的 disposition 是 "head",而 Javascript 檔案使用 "defer",但這些預設設定可以在每個資源的基礎上被覆蓋。
現在我們有了定義簡單或複雜資源模組網路的基礎,這些網路可以跨越應用和外掛邊界。從使用者的角度來看,更好的是,外掛需要確保其模組包含適當的資源和依賴關係。
因此,Grails 現在可能知道你的應用及其外掛提供了哪些靜態資源,但它仍然不知道哪些頁面需要哪些資源。為此,你需要對 GSP 檔案進行一些修改。
如你所知,之前你必須在佈局和檢視中明確宣告所有 CSS 和 Javascript 連結。那麼 Resources 外掛如何改變這一點呢?不是放置指向單個資源的連結,而是宣告你的頁面需要哪些模組,這使得<head>程式碼塊更加簡潔。此外,你還可以指定資源連結應該放在頁面中的哪個位置。這是一個使用 Resources 外掛的非常簡單的佈局示例
<html>
<head>
...
<r:require modules="common, jquery"/>
<r:layoutResources/>
</head>
<body>
...
<r:layoutResources/>
</body>
</html>
如你所料,<r:require>標籤告訴 Grails 應該在頁面中包含哪些 靜態資源,而<r:layoutResources>標籤指定連結應該 放在哪裡。你應該有兩個<r:layoutResources>標籤:一個放在<head>中,另一個放在<body>中。任何 disposition 設定為 "head" 的資源將放在第一個標籤的位置,而 disposition 設定為 "defer" 的資源將插入到第二個標籤的位置。
就是這樣!大部分工作在於確保你的模組定義是正確的。
我到目前為止介紹的對於 CSS、Javascript 和 favicon 檔案非常有效,因為 Resources 外掛知道要為它們生成什麼連結以及將這些連結放在哪裡。但是內聯影像和指令碼怎麼辦呢?
Resources 外掛將內聯影像和指令碼稱為“即時資源”(ad-hoc resources)。這些資源通常不宣告在模組中,只是在頁面中遇到時進行處理。一個標準的內聯影像連結看起來像這樣
<img src="${resource(dir:'images',file:'grails_logo.png')}" ... />
那麼我們如何讓 Resources 外掛知道這個影像檔案呢?我們不需要!從 Grails 2.0 開始,<g:resource/>標籤(上面用作方法)如果外掛已安裝,會自動將資源註冊到外掛中。這意味著 mappers 執行的所有“魔法”(稍後我會講到)都將應用於給定的資源。當然,<g:resource/>可以用於任何型別的資源,而不僅僅是影像。
內聯指令碼有點不同,因為它們不是指向外部檔案的連結。但它們仍然可以從 Resources 外掛中受益。預設情況下,使用<g:javascript/>標籤宣告的所有內聯指令碼將按它們一貫的方式執行。但是如果你將<g:javascript/>替換為<r:script/>標籤,內聯指令碼將被移動到第二個<r:layoutResources/>的位置(它們的預設 disposition 是 "defer")。你也可以宣告一個明確的 disposition,以便指令碼進入<head>中。最重要的是,內聯指令碼保留了它們在頁面中宣告的順序。
還有一個需要提到的即時資源來源:CSS。樣式表是指定背景圖和其他型別圖片的常用方法,但 CSS 檔案不像 GSP 檔案那樣被處理。Resources 外掛是否知道這些連結呢?幸運的是,是的。即使 Resources 外掛修改了它管理的所有資源的 URL 路徑,這對你來說也無關緊要,因為外掛也會自動更新 CSS 檔案中的連結。一切都會正常工作!
就其本身而言,Resources 外掛使得靜態資源的管理比以前簡單得多。但這只是故事的一部分。由於外掛知道所有資源並控制它們的連結生成,它可以執行額外的處理以新增有趣的行為。這種處理是透過一個可擴充套件的 mappers 流水線完成的
不要將此圖表視為絕對正確參考:作為使用者,確切的流水線及其工作方式並不重要。關鍵是如果你願意,你可以新增自己的 mapper 實現,或者簡單地安裝一個提供自己 mapper 的外掛。結果是幾乎零成本地獲得了一些非常強大的功能。
例如,假設你已經安裝了 Resources 外掛,定義了一些模組,並且你的 GSP 檢視和佈局已設定為使用適當的標籤。只需安裝 Cached Resources 和 Zipped Resources 外掛,你將立即開始滿足一些 ySlow 建議,例如為所有靜態資源設定一個長期的 Expires 頭部和啟用 gzip 壓縮(儘管你可以停用特定檔案型別(如影像)的 gzip 壓縮,因為它們通常已經壓縮過了)。這不應該這麼容易,對吧?但它確實是。
Resources 外掛已經存在一段時間了,並且已經在一些生產站點中使用。它甚至在 Grails 網站 上使用。這不足為奇:它極大地改進了靜態資源管理,並提供了一個 mapper 流水線,實現了易於使用但功能強大的特性。經驗豐富的 Web 開發人員會立即注意到其中的區別,並且可以預見未來會有越來越多的外掛支援 Resources。它甚至有自己的使用者指南。
由於該外掛帶來的優勢,Grails 2 將其設為新 Grails 專案的預設外掛,並將其整合到一些核心標籤中,例如<g:resource>。但即使你還不能使用 Grails 2,你仍然可以在舊版本的 Grails 中使用它,並以很少的努力獲得出色的資源管理益處。它的一個關鍵設計原則是,它可以在任何 Grails 應用中安裝而不會破壞任何東西。
無論你使用哪個版本的 Grails,安裝此外掛都將改善你作為 Web 開發人員的生活。這可不是小事一樁。