's Picture

Så tar vi kontroll över tredjepartsskript

Postad av Joel Abrahamsson

På större sajter finns ofta ett flertal tredjepartsskript. Det handlar om mätskript, feedback-knappar, typsnittshanterare, text-to-speech, kartor, mediaspelare, sociala delningar, A/B-testning, heatmaps och enkätundersökningar.
På en mediasajt med annonser såsom våran blir skripten ännu fler.

Skript som dessa består av två komponenter - en skripttag på sajten och en skriptfil hostad av en tredje part. Det finns tre problem med denna modell:

  • Ingen bundling - varje skriptfil laddas individuellt snarare än att paketeras ihop och laddas med ett enda request. Det blir med andra ord många enskilda requests. Dessutom är det inte alltid leverantören minifierar sitt skript.
  • Korta cache-tider - eftersom leverantören av skriptet inte styr skripttaggen kan de inte "cache busta" genom att ändra sökvägen till skriptet. För att kunna få ut nya versioner av skriptet krävs därför att leverantören levererar skriptet med korta cache-tider.
  • Externa beroenden - i de fall skripten laddas synkront har man ett beroende till en tredje part. Ligger skriptet i head innebär detta att den egna sajten kan upplevas som nere när det externa skriptet laddas långsamt.

Vid en första anblick är problemen ovan rätt svårlösta. Många väljer att helt enkelt acceptera att det är så här webben fungerar idag. Vi bestämde oss dock för att försöka hitta en bättre lösning.

Vår lösning

Kraven för en egen lösning var:

  • Ett tredjepartsskript som av någon anledning inte kan laddas asynkront ska inte kunna förstöra laddningen av vår sajt.
  • Flera tredjepartsskript ska kunna hämtas med ett enda request och vid behov dessutom kunna minifieras.
  • Tredjepartsskript ska ha HTTP headrar som säger till klienten att cachea dem för evigt.
  • Förändringar i tredjepartsskript ska slå igenom inom några minuter.

För att lösa detta skapade vi en ny applikation med namnet Assets Assembler (AA). I AA kan olika utvecklingsteam, i konfigurationsfiler, definiera paket (eller "bundles" som de kallas i AA). Ett paket består av ett namn samt en eller flera URL:ar till skriptfiler. När AA stöter på en definition för ett nytt paket gör den följande:

Steg 1Hämtar de skript som ska ingå i paketet från respektive URL. När AA har hämtat alla skriptfiler som ska ingå i paketet bakar den ihop dem till en ny fil.

Steg 2 Beräknar en checksumma baserat på innehållet i filen. Därefter döper den filen till paketets namn + checksumman och laddar upp filen till en bucket i Amazon S3. Filer i denna bucket går att nå genom vårt CDN (Akamai) och serveras med en max-age-header satt till ett år.

Steg 3Uppdaterar en resurs i vårt innehålls-API med paketets namn samt URL till paketet.

För existerande paket kontrollerar AA löpande om någon av de ingående skripten har förändrats. Om så har skett sker ovan steg igen för paketet. Dvs att när ett externt skript har uppdaterats kommer AA att skapa en ny version av paketet med ett nytt filnamn och uppdatera på vilken URL paketet nås i vårt API.

När vi renderar våra sajter skriver webbapplikationen inte längre ut en hårdkodad skripttag för varje tredjepartsskript. Istället går den och frågar API:et efter URL:en till ett eller flera paket och skapar skripttaggar för dessa.

På detta sätt hostar vi tredjepartsskripten själva, kan baka ihop dem och servera dem med långa cache-tider samtidigt som förändringar i skripten slår igenom inom loppet av några minuter. Dessutom får vi en trevlig bieffekt av detta; om något av de externa skripten innehåller något form av fel kan vi alltid rulla tillbaka till tidigare version genom att uppdatera URL:en till ett gammalt paket i API:et.

Generell lösning

Vår lösning innehåller ett antal system och teknologier som inte alla har tillgång till, men lösningen går att generalisera och applicera på i princip alla webbplatser. De komponenter man behöver är:

  • En definition av paket - kan vara en konfigurationsfil eller kod.
  • Någonstans där URL:ar till paket kan lagras.
  • Schemalagd funktionalitet som, vid behov, skapar eller uppdaterar paket och uppdaterar det ställe där URL:ar till dem lagras.
  • Dynamiskt skapade skripttaggar.

Hur man implementerar detta beror naturligtvis på vilka förutsättningar man har. För mig som arbetat en hel del med EPiServer i mina dagar är det nära till hands att exemplifiera med hur man skulle kunna implementera det där.

Med EPiServer skulle man kunna definiera paket i kod (en C#-klass) eller i en konfigurationsfil. Vidare skulle man kunna skapa ett schemalagt-jobb-plugin. Detta skulle hämta hem tredjepartsskripten, bygga paket som sparas i en VPP samt uppdatera en eller flera egenskaper på startsidan som pekar ut URL:ar till skripten. Slutligen skulle man inkludera skriptet i sina vyer med den dynamiska URL:en hämtad från startside-objektet.

Använder man istället WordPress skulle en tänkbar lösning kunna vara att definiera de skript som skall bundlas i en konfigurations-admin-sida. Med WP-Cron eller liknande schemaläggare kan vi kontinuerligt ha koll på om filerna har förändrats. Vid förändring kan vi då bygga paketet på nytt med ett nytt versionsnummer som vi använder som cachebust, den kan vi lagra i options eller som en transient. När vi sedan har det klara paketet och versionsnummer kan vi lägga till vårat script/paket i headers via wp_enqueue_script för att sedan rendera på front-end-sidan.

PS. Missa inte vår nästa bloggpost, följ oss på Twitter!

Till startsidan