Prova questo:
Ho recentemente completato un bel po 'di ricerca e di conseguente sviluppo sul posto di lavoro che va abbastanza lontano per migliorare le prestazioni di front-end della nostra applicazione web. Ho pensato di condividere la soluzione di base qui.
La prima cosa ovvia da fare è eseguire il benchmark del tuo sito utilizzando Yahoo YSlow e Google PageSpeed. Questi evidenzieranno i miglioramenti delle prestazioni "a basso impatto" da apportare. A meno che non lo abbiate già fatto, i suggerimenti risultanti includeranno quasi certamente la combinazione, la minimizzazione e il gzip del contenuto statico.
I passi che andremo a svolgere sono:
Scrivi una consuetudine HTTPHandler per combinare e Minimizza CSS. Scrivere un HTTPHandler personalizzato per combinare e minificare JS. Includere un meccanismo per garantire che quanto sopra faccia la loro magia solo quando l'applicazione non è in modalità di debug. Scrivere un controllo Web lato server personalizzato per mantenere facilmente l'inclusione di file css/js. Abilita GZIP di determinati tipi di contenuto su IIS 6. Destra, cominciamo con CSSHandler.asax che implementa l'interfaccia .NET IHttpHandler:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
namespace WebApplication1
{
public class CssHandler : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext context)
{
string[] cssFiles = context.Request.QueryString["cssfiles"].Split(',');
List<string> files = new List<string>();
StringBuilder response = new StringBuilder();
foreach (string cssFile in cssFiles)
{
if (!cssFile.EndsWith(".css", StringComparison.OrdinalIgnoreCase))
{
//log custom exception
context.Response.StatusCode = 403;
return;
}
try
{
string filePath = context.Server.MapPath(cssFile);
string css = File.ReadAllText(filePath);
string compressedCss = Yahoo.Yui.Compressor.CssCompressor.Compress(css);
response.Append(compressedCss);
}
catch (Exception ex)
{
//log exception
context.Response.StatusCode = 500;
return;
}
}
context.Response.Write(response.ToString());
string version = "1.0"; //your dynamic version number
context.Response.ContentType = "text/css";
context.Response.AddFileDependencies(files.ToArray());
HttpCachePolicy cache = context.Response.Cache;
cache.SetCacheability(HttpCacheability.Public);
cache.VaryByParams["cssfiles"] = true;
cache.SetETag(version);
cache.SetLastModifiedFromFileDependencies();
cache.SetMaxAge(TimeSpan.FromDays(14));
cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
}
}
}
Ok, ora qualche spiegazione:
proprietà IsReusable:
Non abbiamo a che fare con istanze specifiche, il che significa che possiamo riutilizzare in sicurezza la stessa istanza del gestore per gestire più richieste, poiché ProcessRequest è thread-safe. Ulteriori informazioni.
metodo ProcessRequest:
Niente di troppo frenetico succedendo qui. Stiamo eseguendo il looping dei file CSS che ci sono stati dati (vedi il CSSControl in basso per come stanno arrivando) e comprimendoli ognuno, usando una porta .NET di Yahoo YUICompressor, prima di aggiungere il contenuto al flusso di risposta in uscita.
Il resto del metodo riguarda l'impostazione di alcune proprietà di memorizzazione nella cache HTTP per ottimizzare ulteriormente il modo in cui il client browser scarica (o, a seconda dei casi, il contenuto).
Impostiamo gli ETAG nel codice in modo che possano essere uguali su tutte le macchine nella nostra server farm. Impostiamo le dipendenze Response e Cache sui nostri file effettivi quindi, se dovessero essere sostituiti, la cache verrà invalidata. Impostiamo Cacheability in modo che i proxy possano memorizzare nella cache. We VaryByParams utilizza il nostro attributo cssfiles, in modo che possiamo memorizzare nella cache per gruppo di file CSS inviato tramite il gestore. Ed ecco il CSSControl, un controllo personalizzato sul lato server che eredita .NET LiteralControl.
anteriore:
<customcontrols:csscontrol id="cssControl" runat="server">
<CustomControls:Stylesheet File="main.css" />
<CustomControls:Stylesheet File="layout.css" />
<CustomControls:Stylesheet File="formatting.css" />
</customcontrols:csscontrol>
Indietro:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Linq;
using TTC.iTropics.Utilities;
namespace WebApplication1
{
[DefaultProperty("Stylesheets")]
[ParseChildren(true, "Stylesheets")]
public class CssControl : LiteralControl
{
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<Stylesheet> Stylesheets { get; set; }
public CssControl()
{
Stylesheets = new List<Stylesheet>();
}
protected override void Render(HtmlTextWriter output)
{
if (HttpContext.Current.IsDebuggingEnabled)
{
const string format = "<link rel=\"Stylesheet\" href=\"stylesheets/{0}\"></link>";
foreach (Stylesheet sheet in Stylesheets)
output.Write(format, sheet.File);
}
else
{
const string format = "<link type=\"text/css\" rel=\"Stylesheet\" href=\"stylesheets/CssHandler.ashx?cssfiles={0}&version={1}\"/>";
IEnumerable<string> stylesheetsArray = Stylesheets.Select(s => s.File);
string stylesheets = String.Join(",", stylesheetsArray.ToArray());
string version = "1.00" //your version number
output.Write(format, stylesheets, version);
}
}
}
public class Stylesheet
{
public string File { get; set; }
}
}
HttpContext.Current.IsDebuggingEnabled è collegato al seguente impostazione nel vostro Web.config:
<system.web>
<compilation debug="false">
</system.web>
Quindi, in sostanza, se il sito è in modalità debug si ottiene HTML markup come questo:
<link rel="Stylesheet" href="stylesheets/formatting.css"></link>
<link rel="Stylesheet" href="stylesheets/layout.css"></link
<link rel="Stylesheet" href="stylesheets/main.css"></link>
Ma se siete in modalità di produzione (debug = false), è' ll ottenere markup come questo:
<link type="text/css" rel="Stylesheet" href="CssHandler.ashx?cssfiles=main.css,layout.css,formatting.css&version=1.0"/>
quest'ultimo sarà poi ovviamente invocare il CSSHandler, che si occuperà di combinare, minifying e cache-preparando sul suo sito web CSS statico.
Tutto quanto sopra possono poi essere duplicato anche per il vostro contenuto statico JavaScript:
`JSHandler.ashx:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
namespace WebApplication1
{
public class JSHandler : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext context)
{
string[] jsFiles = context.Request.QueryString["jsfiles"].Split(',');
List<string> files = new List<string>();
StringBuilder response = new StringBuilder();
foreach (string jsFile in jsFiles)
{
if (!jsFile.EndsWith(".js", StringComparison.OrdinalIgnoreCase))
{
//log custom exception
context.Response.StatusCode = 403;
return;
}
try
{
string filePath = context.Server.MapPath(jsFile);
files.Add(filePath);
string js = File.ReadAllText(filePath);
string compressedJS = Yahoo.Yui.Compressor.JavaScriptCompressor.Compress(js);
response.Append(compressedJS);
}
catch (Exception ex)
{
//log exception
context.Response.StatusCode = 500;
return;
}
}
context.Response.Write(response.ToString());
string version = "1.0"; //your dynamic version number here
context.Response.ContentType = "application/javascript";
context.Response.AddFileDependencies(files.ToArray());
HttpCachePolicy cache = context.Response.Cache;
cache.SetCacheability(HttpCacheability.Public);
cache.VaryByParams["jsfiles"] = true;
cache.VaryByParams["version"] = true;
cache.SetETag(version);
cache.SetLastModifiedFromFileDependencies();
cache.SetMaxAge(TimeSpan.FromDays(14));
cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
}
}
}
E il suo accompagnamento JSControl:
anteriore:
<customcontrols:JSControl ID="jsControl" runat="server">
<customcontrols:Script File="jquery/jquery-1.3.2.js" />
<customcontrols:Script File="main.js" />
<customcontrols:Script File="creditcardpayments.js" />
</customcontrols:JSControl>
Indietro:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Linq;
namespace WebApplication1
{
[DefaultProperty("Scripts")]
[ParseChildren(true, "Scripts")]
public class JSControl : LiteralControl
{
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<Script> Scripts { get; set; }
public JSControl()
{
Scripts = new List<Script>();
}
protected override void Render(HtmlTextWriter writer)
{
if (HttpContext.Current.IsDebuggingEnabled)
{
const string format = "<script src=\"scripts\\{0}\"></script>";
foreach (Script script in Scripts)
writer.Write(format, script.File);
}
else
{
IEnumerable<string> scriptsArray = Scripts.Select(s => s.File);
string scripts = String.Join(",", scriptsArray.ToArray());
string version = "1.0" //your dynamic version number
const string format = "<script src=\"scripts/JsHandler.ashx?jsfiles={0}&version={1}\"></script>";
writer.Write(format, scripts, version);
}
}
}
public class Script
{
public string File { get; set; }
}
}
Abilitazione GZIP:
Come Jeff Atwood dice, consentendo Gzip sul server del sito web è un gioco da ragazzi. Dopo un po 'tracciato, ho deciso di consentire Gzip sui seguenti tipi di file:
.css .js axd (i file di Microsoft Javascript) aspx (solito web ASP.NET Forms contenuti) .ashx (I nostri gestori) per abilitare la compressione HTTP sul server IIS 6.0 web:
Aprire IIS, fare clic sulla scheda Siti web, servizi, attivare Compress applicazione File e Compress Statico Files Arrestare IIS Aprire IIS metabase in Blocco note (C: \ WINDOWS \ system32 \ inetsrv \ MetaBase.xml) - e fare un backup se si è nervosi su queste cose Individuare e ove rwrite i due IIsCompressionScheme e uno gli elementi IIsCompressionSchemes con il seguente:
E questo è tutto! Questo ci ha permesso di risparmiare un sacco di larghezza di banda e ha portato a un'applicazione web più reattiva.
Divertiti!
Molto interessato a vedere cosa fanno le persone qui. La porta della compressione YUI sembra il posto migliore da cui iniziare. –
Qualcuno ha delle soluzioni usando YUI? – Charlino
Qualche cosa è il "papero d'anatra" significa che è buono o cattivo? – Mark