Μικρή αμυχή: CSS Combiner / Minifier ASP.NET Control
ΠΡΟΣΟΧΗ: Όταν λέω hack το εννοώ...αυτό είναι σε ΔΕΝ way παραγωγή έτοιμο...αυτό είναι μόνο ένα δείγμα σε αυτό το στάδιο. Αν αυτό κάνει το σκυλί σας να εκραγεί, μην κατηγορείτε εμένα!
Σκέφτηκα ότι θα ρίξει ένα έργο σε εξέλιξη σε απευθείας σύνδεση για τους ανθρώπους για να έχουν ένα έργο με. Αυτό είναι ένα από τα μικρά μου έργα κατοικίδιο ζώο (Δεν μπορώ να γράψω κώδικα στην εργασία...δεν είναι dev όπως I[Πάντα μου θύμιζε!).
Λοιπόν, τι κάνει;
Λοιπόν, ένα από τα κύρια ζητήματα απόδοσης για τις τρέχουσες ιστοσελίδες είναι ο αριθμός των αιτημάτων που κάνουν πίσω στο διακομιστή, όχι ο πραγματικός χρόνος επεξεργασίας server-side. Έγραψα γι' αυτό. μετά την ανάγνωση Steve Sounders εξαιρετικόΔικτυακοί τόποι υψηλής απόδοσης (High Performance Websites) ήταν ειλικρινά μια έκπληξη για μένα! Τέλος πάντων, όπως και να έχει πρόσφατα Έχω παίξει με έναν αριθμό του ASP.NET server ελέγχου που στοχεύει στη βελτιστοποίηση του αριθμού των κλήσεων διακομιστή μια εφαρμογή ASP.NET πρέπει να κάνει πολύ λιγότερα. Το πρώτο από αυτά είναι το (ως τώρα) αδημοσίευτο CSS Sprite Control. Παρακάτω είναι η αρχή του δεύτερου; αυτό γράφτηκε σε μερικές ώρες χθες το βράδυ, καθώς δεν μπορούσα να κοιμηθώ για να σκεφτώ πώς μπορεί να λειτουργήσει. Ο κώδικας είναι πολύ τραχύς, αλλά δείχνει την έννοια αρκετά καλά.
Στην ουσία ήθελα την ελάχιστη δυνατή προσπάθεια για να συνδυάσω και να συμπιέσω πολλαπλά <link rel= φύλλο στυλ> Καταχωρήσεις αρχείων CSS μαζί, ελαχιστοποιήστε τους χρησιμοποιώντας Συμπιεστής YUI (για .NET... σε Codeplex) και έξοδος ενός χρόνου εξαρτημένου (σε αυτή την περίπτωση, Ημερομηνία Τροποποιήθηκε για το αρχείο) hash για το όνομα του αρχείου (σε μια σταθερή θέση αυτή τη στιγμή, ~/OutputCSS/...), στη συνέχεια, έξοδος μια ετικέτα στη σελίδα για αυτό το νέο αρχείο.
Λοιπόν, πώς λειτουργεί;
Λοιπόν, φαντάσου ότι είχες μια σελίδα με κάποιους ορισμούς στυλ φύλλο σαν αυτό:
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>
Όπως μπορείτε να δείτε έχουμε δύο ορισμούς αρχείων CSS και μια ετικέτα στυλ. Δεν θα ήταν ωραίο αν θα μπορούσαμε να συνδυάσουμε και να εξορύξουμε αυτά... Λοιπόν...εδώ είναι το πώς ο μικρός μου έλεγχος hacky σας επιτρέπει να το κάνετε:
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>
Μπορείτε να δείτε στην κορυφή ότι μόλις τύλιξε τους ορισμούς CSS σε <asp:CSSManager>…</asp:CSSManager> Ένα άλλο πράγμα, ο παρατηρητής θα έχει παρατηρήσει ότι μπορώ τώρα να χρησιμοποιήσω ένα ~/ στο href... είναι μια ωραία παρενέργεια αυτής της μεθόδου...lets μπορείτε να αποφύγετε το τρελό μονοπάτι traversal πράγματα (.../../.././)
Προφανώς αυτό εξακολουθεί να είναι λίγο περιορισμένο, δεν λειτουργεί σε Μερική Trust, doesnt χειρίζεται πολλαπλά πεδία; ας πούμε, επίπεδο CSS, επίπεδο σελίδας CSS και Masterpages όπου θέλετε πολλαπλά φύλλα με διαφορετικά περιεχόμενα. Σχέδιο είναι να συνεχίσετε να εργάζεστε σε αυτό για λίγο ... έτσι θα σχεδόν σίγουρα ενημερώσετε αυτό σύντομα.
Η πηγή ακολουθεί
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: }