måndag 25 februari 2013

RequireJS - Del3 - Optimering


Föregående inlägg (RequireJS - Del2 - Konfigurering) handlade om konfigurering av RequireJS. Detta inlägg kommer handla om RequireJS Optimizer , r.js.

r.js kan köras med Node.js (v0.4+) eller i Java (v1.6+ med Rhino) eller direkt i webbläsaren med Javascript. Det sistnämnda är endast till för att göra optimering av enskilda filer (har inte testat detta, se exempel). Rekommendationen är att köra i Node.js eftersom detta enligt uppgift är snabbast.

När vi definierat ett antal AMD-moduler, och lagt dessa i egna filer vill vi inte att besökarna av vår sajt ska behöva hämta alla våra moduler med ett HTTP-request per modul.
Vi vill givetvis slå ihop och minifiera alla våra AMD-moduler till en fil.

r.js körs enklast genom att skriva en byggfil: Som sedan kan anropas:

r.js -o build.js

Det går också bra att starta r.js från konsollen:

node r.js -o baseUrl=js paths.jquery=lib/jquery name=app out=app.min.js

Så, vad är det då som gör r.js speciellt? Räcker det inte att bara köra UglifyJS eller någon annan JS-komprimerare? Nej. För r.js håller koll på pluginmoduler och anonyma moduler:

// anonym modul
define(function() {});
Utan r.js hade anonyma moduler resulterat i script-fel, och plugin-moduler hade inte kunnat användas.

När scripten har minifierats till en fil och ligger ute i test/produktionsmiljön är det ofta svårt att debugga den minifierade koden. En fin sak som r.js också stödjer är Source Maps. Genom att lägga till följande i byggfilen kommer RequieJS Optimizer generera en app.min.js.src - fil:

Dock behöver UglifyJS2 användas, istället för UglifyJS (som används som standard). För att använda UglifyJS2, lägg till optimize=uglify2 som ett argument till byggscriptet:

r.js -o build.js optimize=uglify2

Debug med hjälp av Source Maps!

Har i denna post, endast gett en inblick i hur man kan använda RequireJS Optimizer. Som tur är finns det gott om dokumentation och en exempel-byggfil som innehåller all tänkbar r.js-konfiguration.

Detta var den sista delen i min introduktionsserie till RequireJS. Kommer troligen skriva ett till inlägg om pluginhantering och testning allt eftersom erfarenheterna blir fler.

Slutligen vill jag avsluta med ett sidospår.
Läste en intressant artikel läste på dailyjs.com - Meet the New Stack, Same as the Old Stack :
Five years ago, if you asked any client-side developer which library or framework to use the most likely answer would have been jQuery. Since then, client-side development has become far more complex. A friendly wrapper for DOM programming and a browser compatibility layer isn’t enough to help us write modern applications.
Idag innefattar modern javascript och webbutveckling:
  • Packethanterare
  • Modulsystem
  • Testramverk
  • Byggsystem
  • Templates

Med mera.

Det räcker alltså inte endast med att kunna några rader jQuery för att manipulera DOM-strukturen längre. I takt med att Javascript blir mer populärt, och används till mer och mer komplexa saker ställs det högre krav än så.

Därför har jag börjat snöa in mer och mer på optimering, testning och beroendehantering.


/Nils


torsdag 21 februari 2013

TDD-presentation .NET

Höll ett föredrag på jobbet om TDD i tisdags.

Presentationen finns här: http://www.rvl.io/nekman/test-driven-development
Demokoden finns här: https://github.com/nekman/TDD-demo

Projektet jag byggde vidare på var en simpel applikation för att ändra belopp på bankkonton. Inte direkt något seriöst projekt, men det räckte gott i demosyfte.

Tog upp 
Metodik XP, SOLID,
Argument för TDD,
Hur man (enligt regelboken) ska implementera kod enligt TDD (börja med att skriva testklassen först).

Hade lite problem med att få ett bra flyt under live-kodningen, men det har väl sannolikt att göra med att att man är lite ringrostig i Visual Studio efter snart 10 månaders harvande i Eclipse.

Kan i allafall inte skylla på miljön. Det var trevligt med .NET 4.5, Visual Studio 2012 och Resharper 7 :)

Tekniker
Moq - (Mockramverk)
NUnit - (Testramverk)
Castle Windsor - (IoC)
RavenDB - dokumentdatabas - noSQL (som jag länge velat testa)

Verktyg
NChrunch
Resharper 7.1


/Nils

måndag 11 februari 2013

RequireJS - Del 2 - Konfigurering


förra inlägget gavs ett intro till RequireJS. Egentligen skulle detta inlägg handla om optimering. Dock behöver jag först ge en introduktion till konfigurering av RequireJS.

Säg att jag har följande JS-filer i min applikation:

 | - js
     | - lib
         | - backbone.min.js
         | - jquery.min.js
         | - underscore.min.js
         | - require.min.js
     | - amazing-app.js

amazing-app.js är min huvudfil och denna har ett beroende till backbone.min.js. Backbone har beroenden till jQuery samt Underscore. Med RequireJS skulle jag kunna definiera denna modul på följande sätt:

Dock finns det två problem med ovanstående lösning.
  1. Referenserna till Underscore och Backbone blir undefined. Anledningen till detta är att Underscore och Backbone inte registrerar sig själva som AMD-moduler (jQuery v1. 7+ gör dock detta).
  2. Det är jobbigt att skriva hela sökvägen (js/lib/namn) för att inkludera en fil.
Lösningen heter require.conig:

(I ovanstående konfiguration hämtar jag scripten från ett CDN. Givetvis hade jag lika gärna kunnat peka på t.ex. js/lib/backbone.min, men i utvecklingsfasen är det smidigt att köra mot ett CDN tycker jag.)

shim gör att RequireJS ser till att ett script som inte är AMD-kompatibelt kan användas ändå, genom att exportera det globalt och se till att factory funktionen får dessa som inparametrar.
paths mappar ett dependency genom det alias man valt för modulen.

Med ovanstående konfiguration kan jag nu istället skriva:

Därför lägger jag in denna konfiguration i en egen JS-fil så att jag får följande struktur:

 | - js
     | - lib
         | - backbone.min.js
         | - jquery.min.js
         | - underscore.min.js
         | - require.min.js
     | - config.js
     | - amazing-app.js

Normalt när man inkluderar JS-filer gör man detta i head-taggen eller i slutet av sidan. Med ovanstående exempel kunde det sett ut ungefär såhär: Dock behöver vi inte göra det nu, när vi använder RequireJS. Istället räcker följande script-inkludering: data-main attributet säger åt RequireJS att ladda in och exekvera scriptet js/config.js direkt efter att RequireJS har laddats. Om vi dessutom vill starta upp amazing-app från vår config.js räcker det med att göra följande modifiering:

Detta var en introduktion till konfigurering av RequireJS. Jag har dock endast skrapat lite på ytan, men förhoppningsvis täckt in några av de viktigaste delarna.

Nu laddas varje fil in med RequireJS. Dock laddas filerna in en och en, och så vill man inte ha det i sin produktionsmiljö.

Därför kommer nästa blogg-post handla om optimering!

PS: Bifogar även en liten bonus till @buurd och @demassinner som suttit med kompabilitetsproblem mellan olika versioner av jQuery. Givetvis kan RequireJS lösa detta problem!

https://gist.github.com/nekman/4758785

:)

 /Nils

söndag 3 februari 2013

RequireJS - Del1 - Introduktion.

Tänkte i några delposter försöka gå igenom erfarenheter av RequireJS.  Denna del kommer ge en  introduktion av RequireJS.


"RequireJS is a JavaScript file and module loader. It is optimized for in-browser use, but it can be used in other JavaScript environments, like Rhino and Node. Using a modular script loader like RequireJS will improve the speed and quality of your code."
RequireJS (Github) är skrivet av James Burke, som är väldigt aktiv i Javascript Communityn.

Genom att använda RequireJS separerar man kod och beroenden genom att lägga koden i olika s.k. moduler. Dessa moduler kallas för Asynchronous Module Definition (AMD) och kan ses som en förlängning av module pattern, men utan att använda globala variabler.

En modul definieras av metoden:
define('module-id', ['dependencies'], factory).

Där module-id är modulens id, dependencies är en array av beroenden till modulen och factory är implementationen/returvärdet av modulen. De första parametrarna, module-id och dependencies är valbara.

Exempel 1

Definiera en samling böcker i form av en books modul:

Definiera en library modul med beroende till books modulen ovan:
Modulen library kan nu användas genom att begära den med require:

Eftersom library är definerat som modul-id, vet RequireJS vilken modul som ska hämtas. Om library inte vore definierat som ett modul-id hade istället RequireJS försökt att hämta filen library.js (mha ett XMLHttpRequest)!

Testa ovanstående kod på: http://jsfiddle.net/erG4S/

Tanken med RequireJS är att varje modul som definieras med define läggs i en egen JS-fil. Detta leder till många olika JS-filer, vilket är bra då man slipper den vanligt förekommande "spagettikod problematiken" av stora JS-filer som gör "allt" (t.ex. GOD-classes). Små separerade moduler är alltså (självklart) att föredra både underhållsmässigt och testmässigt. Dock vill man inte ha ett HTTP-request per AMD-modul (antal HTTP-requests ska man av prestandaskäl försöka hålla ner).

Lösningen på detta problem heter RequireJS Optimzer som kombinerar och packar ihop alla moduler till en fil.

Mer om optimering och AMD-moduler i nästa del!

/Nils