6 vanliga prestandaproblem och dess lösningar

Att jobba med prestanda med mätbarhet via Google Page Speed är kanon, men det är ändå hur en webbplats upplevs som spelar roll. Anledningen till att man ändå bör intressera sig är för att inte missa att alla inte har samma förutsättningar som du. Särskilt inte om du sitter och tycker att prestandan är helt ok på en ny snabb kontorsdator på en fast trådbunden internetuppkoppling.
Det är en allt större andel som kommer till din webbplats med en halvtrött mobiltelefon på en slö 3G-uppkoppling, en uppkoppling som ofta har rätt stora problem med att ladda ner flertalet filer.

Nedan de vanligaste problemen med de 3000 första webbplatserna som optimeringsfunktionen testat. Allt detta ska du och din webbutvecklare klara av att lösa utan större ansträngning än 3-4 dagars arbete. Så där ja, nu fick du en tidsuppskattning på köpet – sätt igång 🙂

1. Glömt sätta förväntad livslängd på filer

När webbläsaren laddar ner en webbplats följer det med ett flertal filer. Förutom själva HTML-koden oftast ett antal bilder, stilmallar och javascript. Med dessa filer följer det med information till webbläsaren över hur länge filen förväntas vara aktuell – dess förväntade livslängd med befintligt innehåll alltså.

Ett allt för vanligt scenario är att favicon.ico, logotypen och andra filer som praktiskt taget aldrig uppdateras inte har någon livslängd satt. Det som händer då är att alla besökare riskerar att ladda hem filer som inte uppdaterats sedan ett tidigare besök.
Mest logiskt är att inte ladda hem filer som en återvändande besökare troligen redan har, därav ligger denna punkt som nummer ett.

Så här justerar du livslängd i Apache för WordPress via .htaccess

Filen .htaccess har en massa inställningar där du bland mycket annat kan ställa den generella livslängden på en viss typ av fil. Nedan har du exempel på hur det kan se ut. A2592000 betyder att filtyperna ska få 31 dagars livslängd innan webbläsaren laddar ner den på nytt.

# Expire images header
ExpiresActive On
ExpiresDefault A0
ExpiresByType image/gif A2592000
ExpiresByType image/png A2592000
ExpiresByType image/jpg A2592000
ExpiresByType image/jpeg A2592000
ExpiresByType image/ico A2592000
ExpiresByType text/css A2592000
ExpiresByType text/javascript A2592000

Så här justerar du livslängd för IIS 7.x

Välj sajt och sätt livslängd på filer

På bilden ovan ser du att du kan gå in i IIS Manager -> välja en sajt -> HTTP Headers -> Högerklicka och välj Common HTTP Headers. Sen om du tror att du sällan uppdaterar dina filer kan du gott välja 21 dagar istället för 2.

För dig som hellre gör sånt här via web.config ser det ut som nedan:

<system.webServer>
<staticContent>
            <!-- Set expire headers to 30 days for static content-->
             <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="21.00:00:00" />
</staticContent>        
</system.webServer>

2. Glömt aktivera komprimering av statiska filer

Många filer på en webbplats består av text, exempelvis HTML-filen, stilmallar och javascript. Dessa kan överföras i befintligt skick till besökarna eller så väljer man att komprimera dem så de går snabbt att skicka. Komprimering innebär att en algoritm gör så gott den kan att packa ihop en textfil så den blir så liten som möjligt. Algoritmen söker efter mönster och upprepningar, bland annat tjugo stycken mellanslag skulle den kunna komprimera.

Det är mycket vanligt att de som utvecklar webbplatser behåller mellanslag och tabbar för att koden ska vara läsbar och lättare att redigera vid behov. För att detta inte ska bidra till långsammare laddningstid för besökarna är komprimering en bra idé.
Komprimering effektiviserar mycket mer än bara mellanslag, inte sällan kan man skala bort 75% av filens storlek vilket bidrar till en snabbare användarupplevelse.

Aktivera komprimering i WordPress

Det finns plugin som sköter detta för dig som inte vågar röra tekniken eller bara vill utvärdera. Någon enskild vågar jag inte rekommendera då de som är bra kommer och går. Testa dig fram och ha Google Page Speed installerad i din webbläsare för att se resultat.

Följande kod kan du lägga in i .htaccess om tillägget mod_deflate finns på din server eller webbhotell – vilket det ofta gör:

<IfModule mod_deflate.c>
        # Insert filter
        SetOutputFilter DEFLATE

        # Netscape 4.x has some problems...
        BrowserMatch ^Mozilla/4 gzip-only-text/html

        # Netscape 4.06-4.08 have some more problems
        BrowserMatch ^Mozilla/4\.0[678] no-gzip

        # MSIE masquerades as Netscape, but it is fine
        # BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

        # NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
        # the above regex won't work. You can use the following
        # workaround to get the desired effect:
        BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html

        # Don't compress images
        SetEnvIfNoCase Request_URI \
        \.(?:gif|jpe?g|png)$ no-gzip dont-vary

        # Make sure proxies don't deliver the wrong content
        Header append Vary User-Agent env=!dont-vary
</IfModule>

Aktivera komprimering för IIS 7.x

Gå in i web.config och gör något liknande nedan förslag.

<system.webServer>
      <!-- compression settings-->
      <httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
          <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll"/>
          <dynamicTypes>
              <add mimeType="text/*" enabled="true"/>              
              <add mimeType="message/*" enabled="true"/>
              <add mimeType="application/javascript" enabled="true"/>
              <add mimeType="*/*" enabled="true"/>
          </dynamicTypes>
          <staticTypes>
              <add mimeType="text/*" enabled="true"/>
              <add mimeType="text/css" enabled="true"/>
              <add mimeType="message/*" enabled="true"/>
              <add mimeType="application/javascript" enabled="true"/>
              <add mimeType="*/*" enabled="false"/>
          </staticTypes>
      </httpCompression>
      <urlCompressiondoStaticCompression="true" doDynamicCompression="true"/>
      <!-- /compression settings-->
</system.webServer>

3. Behållt onödiga mellanslag, tabbar och kommentarer

Som utvecklare är man förstås mer glad om det är läsbar kod när man ska göra en ändring, just därför är det nog så pass vanligt att detta skickas ut i oförvanskat skick till besökarna på webbplatser.
Den absoluta majoriteten av de som intresserar sig för din webbplats läser inte koden bakom kulisserna och bryr sig helt enkelt inte om huruvida kodens läsbarhet är hög. Däremot prioriterar de att inte vänta längre än nödvändigt på att få se innehåll, därmed bör man se till att tvätta bort onödiga mellanslag och tabbar i kod innan den når besökaren men låta utvecklaren ha en chans att ha läsbarhet.

Så här kan man ta bort onödiga tomrum för IIS 7.x

ASP.NET MVC 4 finns minifiering av filer. I global.asax kan du lägga till följande för att ta med alla filer i mappen Scripts i sajtens hemkatalog och samtidigt minifiera dem:

Bundle a = newBundle("~/scripts/js", newJsMinify());
a.AddDirectory("~/Scripts", "*.js");
BundleTable.Bundles.Add(a);

För att adressera denna sammanslagna fil skriver man följande:

<script type="text/javascript" src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/scripts/js")"></script>

Samma lösning fungerar för stilmallar om du istället pekar ut CSS-mappen.

Byggskript för Visual Studio

Byggskrip, MSBuild, för Visual Studio kan slå samman javascript och stilmallar innan publicering

Byggskript i Visual Studio är ett fungerande alternativ för dig som inte vill göra det manuellt. Nedan ett exempel på byggskript och stödfiler kan du ladda ner här:

$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)MSBuild\MSBuildSettings.xml" /p:CssOutputFile="$(TargetDir)..\content\style.min.css" /p:JavaScriptOutputFile="$(TargetDir)..\scripts\scripts.min.js"

Genom att biblioteket Yahoo.Yui.Compressor.dll anropas när projektet byggs kommer onödigt tomrum att tas bort och publiceringsklar stilmall och javascript skapas. Se bara till att peka ut rätt version av fil på den skarpa sajten, förslagsvis via appsettings.config

Ta bort onödiga tomrum i Apache för WordPress

Plugin finns för WordPress, men de varierar i kvalitet. Testa dig fram och var beredd på att jaga buggar. De brukar gå under namn som optimizer, minifier och liknande. Alternativet är att slå samman filerna innan publicering av ändringar.

Ta bort tabbar och mellanslag manuellt

Det finns flertalet verktyg som gör detta åt dig så du kan ladda upp en minifierad version. Själv har jag använt YSlow för Firefox, titta under fliken med verktyg.

4. Inte optimerat sina bilder

Göteborgs-Posten skickar 130 Kb helt i onödan

Praktiskt taget alla webbplatser använder bilder till utsmyckning, logotyper och för att visa koncept. Bilder är efter video det tyngsta materialet man lägger ut på en webbplats och har ofta en god optimeringspotential. De bilder som kan vara värt att fokusera först på är de som laddas in oavsett vilken sida besökaren tittar på, med andra ord logotypen, ikoner, eventuella bakgrundsbilder etc.

Förutom att via ditt bildbehandlingsprogram spara bilder för webben med lagom optimering finns det nästan alltid mer att ta bort. Det brukar ofta kallas för förlustfri optimering men är åtminstone gjort så att man med blotta ögat inte ska se någon skillnad.

Som exemplet ovan skickar Göteborgs-Posten 130 Kb helt i onödan. Det är inte jättemycket, åtminstone förrän någon på skakig mobiluppkoppling är redo att ge upp och inte tänker vänta några sekunder till.
Många företag och organisation använder bildsystem till sina webbplatser. De brukar ofta marknadsföras med att de “tar hand om bildförminskning och optimering” men det betyder att systemet tar bort det värsta och att mycket finns att önska. Särskilt vaksam ska man vara med sidor med många tumnaglar.

Några alternativ för manuell bildoptimering

ImageOptim för Mac låter dig dra över en hel upload-katalog och sedan går den loss på alla bilder som kan optimeras. Dessutom ersätter den bilderna där de låg så det är grymt smidigt för den som vågar. Har använt den själv och det har gått hur bra som helst.

Smush.it kan fixa detta på alla bilder för en enskild sida. Samma funktion kan man nå under fliken Verktyg om du har YSlow som plugin till Firefox eller Chrome.

5. Mängder av stilmallar, javascript och små ikoner

CSS Sprites för ramverket Twitter Bootstrap

Problemet är främst att det ibland är på tok för många enskilda filer. Varje fil som ska laddas ner till en besökare har en ledtid för att börja skickas vilket om det är många filer kan multipliceras med antalet filer.
Detsamma gäller ofta de som använder ikon-bibliotek, som FamFamFam med flera, där man laddar in ibland hundratals mycket små bilder. Varje liten fil bidrar med sin egna ganska onödiga väntetid och kan slås samman med en teknik kallad CSS Sprites.
Sprites innebär att man har en bildfil som i sin tur innehåller flera bilder. Med hjälp av CSS får man sedan ett litet titthål gentemot bilden och kan på det sättet se en bild i taget.

Bilden ovan är ett exempel på sprite från Glyphicons som följer med i designramverket Twitter Bootstrap. Istället för att varje fil laddas in för sig laddar man hem alla på en gång.

Problem man kan råka ut för med stilmallar och javascript är att filerna kombineras i fel ordning och kan därmed få ett lite slumpartat beteende. Kolla hur de kombineras och att de kombineras i rätt ordning precis som i den ordning du läser in dem på webbplatsen.

Kombinera bildfiler med CSS sprites

Antingen låter man en mycket erfaren gränssnittsutvecklare göra detta manuellt, men ofta kommer man en bra bit på vägen genom verktyg som SpriteMe för att ge en indikation på hur många bilder man kan kombinera. SpriteMe ger dig även en sprite-bild och stilmallskod till bilden.

W3Schools har ett enkelt exempel som visar hur det fungerar.

Kombinera flera javascript & stilmallar i ASP.NET MVC 4

ASP.NET MVC 4 finns så kallad bundling av filer. i global.asax kan du lägga till följande för att ta med alla filer i mappen Scripts i sajtens hemkatalog och samtidigt packa ihop dem:

Bundle a = newBundle("~/scripts/js", newJsMinify());
a.AddDirectory("~/Scripts", "*.js");
BundleTable.Bundles.Add(a);

För att adressera denna sammanslagna fil skriver man följande:

<script type="text/javascript" src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/scripts/js")"></script>

Samma lösning fungerar för stilmallar om du istället pekar ut CSS-mappen.

Byggskript för Visual Studio kombinerar javascript och stilmallar till få filer

Byggskrip, MSBuild, för Visual Studio kan slå samman javascript och stilmallar innan publicering

Byggskript i Visual Studio är ett fungerande alternativ för dig som inte vill göra det manuellt. Nedan ett exempel på byggskript och stödfiler kan du ladda ner här:

$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)MSBuild\MSBuildSettings.xml" /p:CssOutputFile="$(TargetDir)..\content\style.min.css" /p:JavaScriptOutputFile="$(TargetDir)..\scripts\scripts.min.js"

I filen MSBuildSettings.xml gör du inställningar för vilka stilmallar och javascript som ska kombineras och bli klara för publicering. Se bara till att peka ut rätt version av fil på den skarpa sajten, förslagsvis via appsettings.config

6. Laddar in massor med javascript direkt vid sidladdning

Många moderna webbplatser använder sig väldigt mycket av javascript för att ge ett rikt användargränssnitt och för att förenkla interaktionen med formulär eller touchbaserade enheter. Flertalet av dem fungerar bra även om inte javascript är aktiverat hos besökaren.
Dock är det mycket vanligt att all javascript-kod laddas hem innan övriga filer på sidan hämtats, dessutom körs javascript-koden ofta innan resten av webbplatsen är klar eller presenterad för besökaren. Så först väntar man på att hämta hem javascript, sen väntar man på att det ska köra klart. Det här är mest ett problem på långsamma enheter, särskilt om de har en långsam uppkoppling, och bidrar till mycket väntande.

Inte sällan ska några hundra kilobyte javascript både hämtas och köras i webbläsaren innan den fortsätter med något besökaren kan se på skärmen.

Det är inte riktigt så enkelt som att göra om en befintlig sajt till att ladda in javascript i efterhand, men det kan vara värt att pröva om det går och fungerar. Det kan hända att webbplatsen inte är planerad för att visa upp sig på ett vettigt sätt utan javascript och om så är fallet får man istället spetsa till sina krav inför nästa ombyggnation.

En teknik kallar defer kan användas och här har du ett skript för just detta och koden nedan påstås att det stödjer “alla” webbläsare, men testa för guds skull på de plattformar du prioriterar:)

Försena inladdning av javascript med ett defer-script

Nedan är ett skript som många kör med. Om du kollar koden så läses jQuery 1.7.1 och jQuery Unobtrusive in, detta efter att sidan laddats in i övrigt.

<script type="text/javascript">

            // Add a script element as a child of the body
            function downloadJSAtOnload() {
                var element = document.createElement("script");
                element.src = "http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js";
                document.body.appendChild(element);
                var unobtrusive = document.createElement("script");
                unobtrusive.src = "http://ajax.aspnetcdn.com/ajax/mvc/3.0/jquery.unobtrusive-ajax.min.js";
                document.body.appendChild(unobtrusive);
            }

            // Check for browser support of event handling capability
            if (window.addEventListener)
                window.addEventListener("load", downloadJSAtOnload, false);
            else if (window.attachEvent)
                window.attachEvent("onload", downloadJSAtOnload);
            else window.onload = downloadJSAtOnload;
</script>

Har du tips för miljöer jag inte tagit upp? Lämna en kommentar så uppdaterar jag.