Grails 2.0 倒數計時:靜態資源

工程 | Peter Ledbrook | 2011 年 6 月 30 日 | ...

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 檔案捆綁在一起不是一個好主意嗎?壓縮它們呢?(順帶一提,Yahoo 提供了一個名為 ySlow 的工具,它會給您關於這些最佳化的提示)。那麼我們如何實現這些最佳化呢?您也許可以臨時應急地做到,但這樣做會限制您的選擇,因為您無法定義資源之間的關係。我們真正想要的是一種在中心位置宣告資源並指示哪些資源應該被處理和捆綁在一起的方法。

如果我還沒有說服您,讓您相信可能有更好的方法來管理靜態資源,那麼請考慮 Grails 外掛程式。其中許多外掛程式都提供了自己的靜態資源。事實上,有些外掛程式提供了彼此相同的資源。例如,舊版本的 YUI、Bubbling 和 Grails UI 外掛程式都包含了 YUI 函式庫。我們真的需要整個 Javascript 函式庫的多個副本嗎?當然不需要。部分問題可以透過外掛程式間的依賴關係來解決,但單靠這種方法效果不佳,因為外掛程式無法聲明其某些資源依賴於另一個外掛程式中的特定資源。最終,開發人員有責任確保每個頁面都包含所有適當的資源(包括傳遞依賴)。這可能需要一些試錯,以確保所有需要的資源都已連結到頁面中,並且順序正確。

幸運的是,我們可以做得更好。請看 Resources 外掛程式

宣告式資源

只需安裝這一個外掛程式,您就可以開始在可重複使用的模組中宣告您的靜態資源(Javascript、CSS 等)。然後您可以定義模組之間的依賴關係,以便 Grails 確切知道模組需要哪些資源,包括傳遞依賴中指定的資源。作為額外的好處,模組確保資源始終以正確的順序在頁面中宣告,並且沒有重複項。這樣做,模組消除了靜態資源管理的大部分痛苦。

宣告模組

您可以在應用程式和外掛程式中宣告資源模組。有幾種方法可以做到這一點,但最常見的方法是向專案新增一個或多個專用工件。對於應用程式來說,這可能是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」,通常表示主體的末尾(儘管您可以控制確切的放置位置,稍後您會看到)。預設情況下,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 外掛程式將內嵌影像和腳本稱為「臨時資源」。這些資源通常不會在模組中宣告,而只是在頁面中遇到時才進行處理。標準的內嵌影像連結看起來像這樣

<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 檔案中的連結。一切都會正常運作!

魔法 mappers

Resources 外掛程式本身就使靜態資源的管理比以前簡單得多。但這只是故事的一部分。由於外掛程式知道所有資源並控制它們的連結產生,因此它可以執行額外的處理以新增有趣的功能。這種處理是由 mappers 的可擴展管道完成的

Resources Mapper Pipeline Diagram

不要將此圖表視為絕對和正確的參考:作為使用者,確切的管道及其運作方式並不重要。關鍵是,如果您願意,您可以新增自己的 mapper 實作,或者只是安裝一個提供一些 mapper 的外掛程式。結果是一些非常強大的功能,幾乎不需要付出任何努力。

例如,假設您已安裝 Resources 外掛程式,定義了一些模組,並且您的 GSP 檢視和版面配置已設定為使用適當的標籤。只需安裝 Cached ResourcesZipped Resources 外掛程式,您將立即開始滿足一些 ySlow 建議,例如為您的所有靜態資源提供長期有效的 Expires 標頭和 gzip 壓縮(儘管您可以為特定檔案類型(例如影像)停用 gzip 壓縮,這些檔案類型往往已經被壓縮)。這不應該這麼容易,對吧?但事實就是如此。

結論

Resources 外掛程式已經存在一段時間了,並且已經在一些生產網站中使用。它甚至在 Grails 網站上使用。難怪:它將大大改進的靜態資源管理與 mapper 管道結合在一起,從而實現易於使用但功能強大的功能。經驗豐富的 Web 開發人員會立即注意到差異,您可以預期未來會有 越來越多的外掛程式 支援 Resources。它甚至有自己的 使用者指南

由於外掛程式帶來的優勢,Grails 2 使其成為新 Grails 專案的預設外掛程式,並將其整合到一些核心標籤中,例如<g:resource>。但即使您還不能使用 Grails 2,您仍然可以在舊版本的 Grails 中使用它,並從輕鬆實現出色的資源管理中受益。它的關鍵設計原則之一是它可以安裝在任何 Grails 應用程式中,而不會破壞任何東西。

無論您使用哪個版本的 Grails,安裝此外掛程式都會改善您作為 Web 開發人員的生活。這是不容小覷的。

取得 Spring 電子報

隨時關注 Spring 電子報

訂閱

搶先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

查看全部