Piccolo hack: Combiner CSS / Minificatore ASP.NET Control
ATTENZIONE: Quando dico hack dico sul serio...questo è in NO WAY produzione pronta...è solo un campione in questa fase. Se fa esplodere il vostro cane, Don è colpa mia!
Ho pensato che ho gettato un lavoro in corso online affinchè la gente abbia un gioco con. Ciò è uno dei miei progetti piccoli dell'animale domestico (non ho permesso di scrivere il codice al lavoro... non un dev come I...[Me lo ricordo sempre!).
Allora, cosa fa?
Beh, uno dei principali problemi di prestazioni per i siti web attuali è il numero di richieste che fanno di nuovo al server, non il tempo effettivo di elaborazione lato server. scritto su questo dopo aver letto Steve Sounders eccellenteSiti web ad alte prestazioni [49] è stato francamente una sorpresa per me! Beh, comunque di recente sono stato giocando con un certo numero di controllo del server ASP.NET che mirano a rendere l'ottimizzazione del numero di chiamate del server un ASP.NET app deve fare molto meno. Il primo di questi è il (ancora) inedito Controllo Sprite CSS. Sotto è l'inizio del secondo; questo è stato scritto in un paio d'ore ieri notte come ho potuto dormire per pensare a come potrebbe funzionare. Il codice è MOLTO grezzo, ma dimostra il concetto abbastanza bene.
In sostanza volevo il minimo sforzo possibile per combinare e comprimere più <link rel=stylesheet> Le voci di file CSS insieme, minimizzarle usando Compressore YUI (per .NET... su Codeplex) e l'output di un tempo dipendente (in questo caso, Data Modificata per il file) hash per il nome del file (in una posizione fissa in questo momento, ...~/OutputCSS/...), quindi l'output di un tag nella pagina per questo nuovo file.
Allora, come funziona?
Beh, immagina di avere una pagina con alcune definizioni di stylesheet come questa:
1: <link type="text/css" rel="stylesheet" href="../../../build/logger/assets/logger.css"/>
2: <link type="text/css" rel="stylesheet" href="../../../build/yuitest/assets/testlogger.css"/>
3: <style type="text/css">
4: #container, #container2 {
5: width: 400px;
6: }
7:
8: .yui-carousel-element {
9: margin: 0;
10: padding: 0;
11: }
12:
13: .yui-carousel-element li {
14: border: none;
15: margin: 0;
16: padding: 0;
17: width: 100px;
18: }
19: </style>
Come potete vedere abbiamo due definizioni di file CSS e un tag di stile. Sarebbe bello se potessimo combinare e minificare questi ... beh... ecco come il mio piccolo controllo hacky permette di farlo:
1: <asp:CSSManager runat="server">
2: <link type="text/css" rel="stylesheet" href="~/build/logger/assets/logger.css"/>
3: <link type="text/css" rel="stylesheet" href="../../../build/yuitest/assets/testlogger.css"/>
4: <style type="text/css">
5: #container, #container2 {
6: width: 400px;
7: }
8:
9: .yui-carousel-element {
10: margin: 0;
11: padding: 0;
12: }
13:
14: .yui-carousel-element li {
15: border: none;
16: margin: 0;
17: padding: 0;
18: width: 100px;
19: }
20: </style>
21: </asp:CSSManager>
Potete vedere nel frammento sopra che abbiamo appena avvolto le definizioni CSS in <asp:CSSManager>…</asp:CSSManager> un'altra cosa, l'osservatore avrà notato che ora posso usare un ~/ nel href...it è un piacevole effetto collaterale di questo metodo...lets si evita il pazzo percorso traversale roba (.../../../)
Ovviamente questo è ancora un po' limitato, non funziona in Parziale Trust, non gestisce più scopi; diciamo, Livello del sito CSS, Livello di pagina CSS e Masterpages dove si desidera più fogli con contenuti diversi. Piano è quello di continuare a lavorare su questo per un po 'di tempo ... così quasi certamente aggiornare questo presto.
La fonte segue
1: using System;
2: using System.Collections.Generic;
3: using System.ComponentModel;
4: using System.Text;
5: using System.Web.UI;
6: using System.Web.UI.HtmlControls;
7: using System.IO;
8: using System.Security.Cryptography;
9: using Yahoo.Yui.Compressor;
10:
11: namespace CSSManager
12: {
13: [DefaultProperty("Text")]
14: [ToolboxData("<{0}:CSSManager runat=server></{0}:CSSManager>")]
15:
16: [ParseChildren(true, "cssFiles")]
17: public class CSSManager : Control, INamingContainer
18: {
19:
20: public const string FILE_PATH = "~/OutputCSS/{0}.css";
21:
22: public CSSManager() { }
23:
24: public List<HtmlGenericControl> CSSFiles
25: {
26: get;
27: set;
28: }
29:
30:
31: private StringBuilder combinedSheets = new StringBuilder();
32:
33: public string GetStyleSheet(string virtualPath)
34: {
35: string filePath = Context.Server.MapPath(virtualPath);
36: if (!string.IsNullOrEmpty(filePath))
37: {
38: return File.ReadAllText(filePath);
39: }
40: return string.Empty;
41:
42: }
43:
44: public string CalculateFileHash(string filePaths)
45: {
46: MD5CryptoServiceProvider csp = new MD5CryptoServiceProvider();
47: byte[] pathBytes = csp.ComputeHash(System.Text.UTF8Encoding.UTF8.GetBytes(filePaths));
48: return BitConverter.ToUInt64(pathBytes, 0).ToString();
49:
50: }
51:
52: public void WriteFile(string textToWrite, string fileHash)
53: {
54: File.WriteAllText(Context.Server.MapPath(string.Format(FILE_PATH, fileHash)), textToWrite);
55: }
56:
57: public string CreateDatedFilePath(string filePath)
58: {
59: FileInfo fi = new FileInfo(Context.Server.MapPath(filePath));
60: if (fi.Exists)
61: {
62: return fi.LastWriteTimeUtc.Ticks.ToString() + "#" + filePath;
63: }
64: return string.Empty;
65:
66:
67: }
68:
69: protected override void CreateChildControls()
70: {
71:
72: if (this.DesignMode == true)
73: {
74: foreach (var file in CSSFiles)
75: {
76: string rel = file.Attributes["rel"];
77:
78: string href = file.Attributes["href"];
79:
80: if (rel.ToLowerInvariant() == "stylesheet")
81: {
82: file.Attributes["href"] = this.ResolveClientUrl(Context.Server.MapPath(href));
83: this.Controls.Add(file);
84: }
85:
86: }
87: }
88: StringBuilder filePaths = new StringBuilder();
89: int i = 0;
90: foreach (var file in CSSFiles)
91: {
92: string tagName = file.TagName.ToLowerInvariant();
93: if (tagName == "link")
94: {
95: string rel = file.Attributes["rel"];
96: string href = file.Attributes["href"];
97:
98: if (rel.ToLowerInvariant() == "stylesheet")
99: {
100: string styleSheet = GetStyleSheet(href);
101: if (!string.IsNullOrEmpty(styleSheet))
102: combinedSheets.Append(styleSheet);
103: filePaths.AppendFormat("{0}#", CreateDatedFilePath(href));
104: }
105: }
106: else if (tagName == "style")
107: {
108: string inner = file.InnerHtml;
109: combinedSheets.Append(inner);
110: filePaths.Append("CSSSTYLE_" + i);
111: i++;
112: }
113:
114: }
115: string minimizedStyles = CssCompressor.Compress(combinedSheets.ToString(), 0, CssCompressionType.Hybrid);
116: string fileHash = CalculateFileHash(filePaths.ToString());
117: WriteFile(minimizedStyles, fileHash);
118:
119: HtmlGenericControl gen = new HtmlGenericControl();
120: gen.TagName = "link";
121: gen.Attributes.Add("href", this.ResolveClientUrl(string.Format(FILE_PATH, fileHash)));
122: gen.Attributes.Add("rel", "stylesheet");
123: this.Controls.Add(gen);
124: }
125:
126: }
127: }