Avatar billede lone_a_p Praktikant
06. november 2011 - 12:39 Der er 22 kommentarer og
1 løsning

Javascript Tidsmæssig Udfordring Mobil

Kære eksperter,

Jeg er ved at udvikle en mobil-udgave af mit site. Hertil benytter jeg jquery mobile.

Men jeg har en stor tidsmæssig udfordring. Hver gang en side loades, skal den hente javascript-filerne, hvilket tager ekstremt lang tid.

Jeg har forbedret dette til, at filerne nu kun hentes på én side (fordi alt nu kører i en side, og jeg henter de enkelte sider ind via ajax-kald og udskifter indhold)

Problemet er blot, at den lange lange loadtid er på første side ... og her vil eventuelle nytilkomne brugere allerede forsvinde, da det tager evigheder (oftest 13, men helt op til 40 sek).

Jeg ville jo gerne, at mobilbrowseren kunne cache javascriptfilerne, så de ikke absolut skal hentes ved hvert kald. Men jeg behøver hjælp til dette.

Hvordan kan mobilen cache en javascript-fil?

Mange sider er ekstremt hurtige på mobilen, så det kan simpelthen ikke passe, at der ikke kan gøres noget.

Alle idéer er velkomne! :)

Test kan foretages via mobilen på www.madital.dk/m (LOGIN:  email:demo@madital.dk  password:demo)

Mvh Lone
Avatar billede olsensweb.dk Ekspert
06. november 2011 - 14:54 #1
>Mange sider er ekstremt hurtige på mobilen, så det kan simpelthen ikke passe, at der ikke kan gøres noget.
her er nogle bud

  • sørger for at man laver valid code, så browseren ikke skal bruge tid på at gætte, på hvordan en code skal fortolkes
  • man laver opdatering af skærmen via AJAX, så det kun er rå data an sender til brugeren istedet for hele siden hver gang
  • laver siden så simpel som muligt, fjern alt overflødigt (KISS)
  • reducerer brug af grafik til minimum, og reducerer billed størrelse/opløsning til minimum
  • undgår at bruge framework så som jquery som du skriver på din side
  • <script src="/m/includes/jquery-1.6.4.min.js"></script> <!-- 89,5 KB -->
    <script src="/m/includes/jquery.mobile-1.0rc1.min.js"></script> <!-- 77 KB -->
  • minimizer code (remove whitespaces, and comments)
  • undgår brug af flash
Avatar billede olebole Juniormester
06. november 2011 - 16:10 #2
<ole>

Det er et problem, mange bøvler med. Der er en løsning i omløb, hvor man bruger local storage - og ved indlæsningen bruges eval (læs: evil) på den gemte streng. Rent selvmord!

Er der overhovedet grund til at bruge jQuery? Ofte bruges det til ting, der kan gøres med langt mindre kode

/mvh
</bole>
Avatar billede olebole Juniormester
06. november 2011 - 17:20 #3
Jeg har lige talt op, at du på første side henter mere end 300KB - og det var trehundrede ...! Gad vide, hvordan det tal forholder sig til den samlede mængde reelle informations data, der ligger på sitet?

300KB svarer vel til omkring 50-60% af informationsmængden i tonstunge madbibler som Ali-Bab eller Escoffiers Store Kogebog. 300KB script til at hente og vise lidt informationer er langt ude i proportionshegnet! Det kunne sagtens gøres med 20-30KB velskrevet kode ... og ikke med mindre pænt resultat.

Min anbefaling vil - hvor ubehageligt det end må lyde - være at skrive mobilsitet om fra bunden. Det kræver dog, du lærer at bruge JS, DOM, CSS og Ajax, uden brug af 'kaloriefyldte færdigretter uden særlig næringsværdi' - hvis du forstår analogien. Det handler om råvarer af god kvalitet og velovervejet behandling af dem  *o)
Avatar billede lone_a_p Praktikant
06. november 2011 - 22:37 #4
ronols>
Jeg har nu gjort brug af css-sprite og minified alle mine js.
Herudover har jeg to sider som jeg pre-loadede i starten, men som er bedre at tage i etaper - det gør jeg så nu :)
Herudover bruger jeg noget, der hedder headjs.com (hvilket tillader at hente flere js-filer på én gang, hvilket man ellers ikke kan).
Første side tager nu 3-4 sek at hente :) Hvilket er ok. TAK :)
Vil du lægge et svar til point?

olebole>tak for din deltagelse
Jeg er interesseret i at vide, hvordan du kommer frem til de 300KB. Hvordan kan man finde ud af dette tal og hvad er "normalen"?

Mvh Lone
Avatar billede olebole Juniormester
07. november 2011 - 08:08 #5
Jeg downloadede bare dine JS-filer og tjekkede, hvor meget de fyldte tilsammen (læg dem i em mappe og tag Egenskaber på mappen). Jeg ved så godt, de bliver sendt gziped, men uanset, hvormeget de minimeres, er det stadig meget kode at folde ud og initiere.

Det var da i det mindste noget, at du fik pillet en trediedel af functions-mad-motion.js, men du har stadig funktionsnavne på op til 39(!) tegn - og variabelnavne på 20-30 tegn er ikke sjældne. Jeg ved ikke med varabelnavnene (det er mange at få overblik over), men umiddelbart vil jeg tro, dine funktionsnavne er ca. 20 tegn i gennemsnit. Det fylder enormt!

Derudover er der masser af HTML-strenge, der er næsten ens og funktioner, der gør næsten det samme. Der må være masser, der kan skrives sammen.

Et problem med dine funktionsnavne er selvfølgelig, at alle funktioner ligger i det globale scope. I førnævnte fil bliver det alene til lidt over 160 unikke navne. Det kræver fantasi og rigtig mange bogstaver  *o)

Blev koden skrevet objekt orienteret - eller hvis du i det mindste lagde dine funktioner i forskellige namespaces - kunne koden garanteret gøres væsentlig mindre (og formodentlig mere overskuelig).

Du kan under alle omstændigheder formindske filen til måske 1/3 - 1/6 af den nuværende størrelse, hvis du ændrede alle lokale variabelnavne til et, to og tre bogstavs længde. Hvorfor f.eks:


$(".moveSelect").live('change',
    function(event){
        var basicElementCodeToMove, basicElementCodeToMoveTo;
        basicElementCodeToMove = this.id.substring(0,this.id.length-11); //fx product_1_100_200_300
        basicElementCodeToMoveTo = $("#"+this.id).val();
        moveHeader(basicElementCodeToMove, basicElementCodeToMoveTo);
    }
);


- når du ligeså godt kunne skrive:


$(".moveSelect").live('change',
    function(event){
        /* var basicElementCodeToMove, basicElementCodeToMoveTo; */
        var e, e2;
        e = this.id.substring(0,this.id.length-11); //fx product_1_100_200_300
        e2 = $("#"+this.id).val();
        moveHeader(e, e2);
    }
);


Der røg alene 40% (kommentaren fjernes automatisk ved minimering)!

Strenge, der bruges flere/mange gange, bør også defineres som en variabel ét sted. Ikke alene hjælper det gevaldigt på filstørrelsen - det hjælper også på performance ved større applikationer, hvor der genereres og bruges mange strenge.

Når det er sagt, så bruger du ikke ret meget af al den kode, du downloader. Det samme kunne gøres med langt mindre kode ... måske helt ned til 10-20%!
Avatar billede olsensweb.dk Ekspert
07. november 2011 - 15:28 #6
du kan da godt få et svar hvis du vil.

jeg kan se du også har ryddet op i dine js på index siden

Forrige 6_11 Næste Madital.dk.htm fyldte 112.122kb vedlagte filer: 356kb i 16 filer
Forrige 7_11 Næste Madital.dk.htm fylder  10.751kb vedlagte filer: 285kb i 11 filer, slettet 5 billeder (ialt 5k)

men som ole skriver du bør nok kritisk gennemgå din code og omskrive den så den
Avatar billede olsensweb.dk Ekspert
07. november 2011 - 15:30 #7
- så den kun indeholder det mest nødvendig
Avatar billede lone_a_p Praktikant
07. november 2011 - 17:07 #8
ronols> tak for svar, jeg deler point ud, når jeg lukker tråden
________________________

olebole> tak for din uddybning. Jeg vil ændre mine variable til få bogstaver. :)

Bliver filerne sendt gzipped pr. automatik? For jeg har da godt nok undret mig over, at man kan downloade qzipped filer fra jquery mobile.

Angående næsten ens strenge/funktioner. Jeg er klar over dette, og vil se, hvad jeg kan gøre.

Angående global scope. Dette ved jeg meget lidt om. Jeg ved, at det handler om "det område en variabel eller funktion kan benyttes" men jeg ved faktisk ikke, hvordan jeg ellers skal gøre, så det må jeg se, om jeg kan finde svar på.

Namespaces? Et indtil videre ukendt begreb for mig.

Jeg forstår ikke det med, om en kode bliver skrevet objektorienteret eller ej. Man skal jo alligevel kode funktionaliteten. Vil du forklare mig dette, hvis det kan gøres i forholdsvis få ord? Er det i princippet ikke lige så godt, bare man har overblikket?

Mht. det at jeg ikke bruger ret meget af koden. Det har du ret i - i al fald ikke på side 1. Men jeg henter til gengæld også kun filerne én gang pr. login, da resten af indholdet hentes igennem ajax. Jeg har fundet dette som den bedste løsning, da det netop tager rigtig lang tid at hente javascripten ind på mobilen.

Jeg er ikke ekspert i at udvikle kode, det er jeg klar over. Men jeg har dog et site, som rigtig mange personer benytter og er rigtig glade for :) Og man bliver jo heldigvis hele tiden bedre mht. koden.

Mvh Lone
Avatar billede olebole Juniormester
07. november 2011 - 22:34 #9
Ser man på os som biologiske maskiner, giver det nok væsentligt mere mening i at være ekspert i, hvordan disse maskiners brændsel sammensættes mest optimalt - samt hvordan de bedst vedligeholdes. Så kan man altid spørge os kropsbevidsthedspygmæer til råds om noget så naturfjernt som programmering. Der er absolut ingen skam i ikke at være kodeekspert *o)

Objektorientering er en lidt omfangsrig ting at forklare. Som udgangspunkt kan man dog sige, at det er en afspejling af den måde, vi i forvejen opfatter os selv og hele vores omverden.

Et objekt er en instans af en klasse. Objektet har nogle egenskaber (properties) og metoder (noget, objektet kan gøre/udføre). En metode er blot en funktion, der er knyttet til objektet.

Jeg er således en instans af klassen Menneske. Enkelthederne omkring, hvordan jeg blev instantieret vil jeg overlade til fantasien - men programmatisk ville det se sådan ud:

var ole = new Menneske();


Vi tager et par af objektets properties:

var foo = ole.haircolor; // foo indeholder nu 'gray'
var bar = ole.age; // bar indeholder nu '56'

- og et par metoder:

ole.eat("steak", "potatoes", "salad", "gravy");
var snoring = false;
ole.sleep(snoring);


I JavaScript ligger alle globale funktioner og variabler (funktioner og metoder, der er erklæret udenfor funktioner) faktisk som metoder og properties på window objektet:

var foo = "bar";
alert(window.foo); // Returnerer 'bar'

// Du kunne såmænd også skrive:
window.alert(window.foo); // Returnerer stadig 'bar'

Globale funktioner og variabler siges at have namespace'et window.

Et statisk namespace kan oprettes som et statisk objekt. Et namespace kan i den forbindelse opfattes som kasser/skuffer, man opbevarer og ordner sine variabler og metoder i. Her opretter jeg således to namespaces ved hjælp af et par objekter:

foo = {
    methA: function(argA, argB){/* Gør noget */},
    methB: function(argA, argB){/* Gør noget andet */},
    propA: "En eller anden streng"
};

bar = {
    methA: function(argA, argB){/* Gør noget nyt */},
    methB: function(argA, argB){/* Gør noget helt fjerde */},
    propA: "En fuldstændig anden streng"
};

var fooBar = bar.propA;
var baz = foo.methB(fooBar, "bavl");
alert(baz);

Så kan du pludselig bruge ret korte og stadig sigende navne til variabler og funktioner, der gør næsten det samme - men i forskellig kontekst. Når man for alvor objektorienterer sin kode, bruger man dog ikke kun den statiske objekter, men hovedsagelig objekter instantieret med constructor funktioner (se link nedenfor).

Derudover er der en hel masse ting omkring arv fra klasse til instans - og hvordan nye klasser, der udvider bestående klasser arver fra disse, osv, osv.

En forsigtig begyndelse med lidt simple eksempler kunne være denne side.

Håber, dette lillebitte crash-kursus gav begyndelsen til forståelse af objekter og objektorientering  =)
Avatar billede olebole Juniormester
07. november 2011 - 22:47 #10
PS: Du bruger faktisk dagligt objektorienteret kode og namespaces, selvom du ikke ved det. I din egen kode bruger du dog som så mange andre JavaScripts objektbaserede kerne i en almindelig procedural kontekst - uden at udnytte objektorienteringens fordele.

Når vi f.eks. roder med DOM, er document det namespace, der indeholder de fleste funktioner (metoder på document objektet):

*) document.getElementById
*) document.getElementsByTagName
*) document.getElementsByName
*) document.createElement

- osv, osv
Avatar billede lone_a_p Praktikant
09. november 2011 - 18:52 #11
Hej Olebole,

Jeg må indrømme, at jeg har taget et javakursus for nogle år siden. Men det var bare teori, og jeg har aldrig lært at benytte det konkret.

Så jeg kan godt genkende det meste fra linket, men der er også mange grundlæggende ting, som jeg ikke forstår (tror jeg!).

Det kan godt ske, at jeg skal prøve mig frem med små forsigtige skridt. Jeg behøver jo ikke ændre al kode nu og her.

Vil du forklare mig, hvorledes jeg får fat på et enkelt objekt? Er det at give det et navn, og når en bruger så klikker på en bestemt madvare (på websiden), har varen et <div id=vareid> og på den måde tager jeg fat i javascripten? Er det rette vej?

Det er lidt det samme, som jeg allerede gør ... jeg opbygger blot i arrays, og sådan kan jeg forstå, at det objektorienterede javascript også fungerer? Blot på en mere smart måde, for der er godt nok lidt at holde styr på. Men så længe jeg har overblikket, ja, så har det ikke været et problem. Men programmet vokser jo, og dermed også behovet for overblik.

Mvh Lone
Avatar billede olebole Juniormester
09. november 2011 - 19:56 #12
Der er en del forskel på OOP i Java og i JavaScript. Java er baseret på klasser, mens JavaScript er baseret på prototyper, selvom man ofte bruger ordet 'klasse' i JS (hvilket ikke er helt korrekt, hvis man er 'hysterisk puritaner'). Objekter i JS kan skabes på baggrund af allerede bestående objekter, men det kan de ikke i Java.

Der er ganske mange måder at objektorientere en kode på - og i mange grader af hel eller delvis objektorientering. Det kommer helt an på applikationens opbygning og struktur, og den orker jeg ikke rigtig at sætte mig ind i. Derudover er det under alle omstændigheder en ret lang udredning, som kræver rigtig mange linjer med rigtig mange bogstaver  =)
Avatar billede lone_a_p Praktikant
09. november 2011 - 23:22 #13
ole>

Puha, det bliver hurtigt langhåret.

Jeg er nået til at oprette User, Product og fået tilknyttet et User.eatenList array indeholdende objekter Intake (fordi Intake både består af 1 object og 2 properties (feks Pasta, 30g, "2011-11-05")).

Lyder det nogenlunde rigtigt? Tænker specielt på om der er behov for at oprette object Intake, eller om det kan tilknyttes User.EatenList på anden vis?

Men så når jeg skal finde frem til de produkter som er spist den 5. november, så skal jeg jo køre hele User.eatenList igennem og finde dem der har parameteren date = "2011-11-05". Kan det passe, at det er den rigtige måde at gøre det på? (det kan jo være, at der er hentet mad for FLERE MÅNEDER, så vil der blive søgt i rigtig mange ligegyldige indtag for kun at finde få)

Eller skal jeg egentlig hellere oprette User.EatenList("2011-11-05") indeholdende arrays med de forskellige Intake objekter (med food og gram)?

Men ligeledes hvis jeg skal slette et Intake med id=63434, så skal User.EatenList jo også køres hele vejen igennem til id er fundet? Det må være en nemmere vej til at finde et bestemt objekt (eller instans af objekt??)

Har du flere gode javascript-læresider? Jeg synes jeg har nogenlunde styr på den side du sendte, men jeg har brug for nedarvning (jeg har f.eks. Produkt og Opskrift som begge er Mad).
Og så forstår jeg ikke prototype ... og sikkert også en masse andet, som jeg ikke aner det mindste om endnu.

Jeg har lavet nogle søgninger på nettet (og har også tidligere), men der er temmelig mange forklaringer, og de gør det bestemt ikke lige godt (=nemt). Så det kunne være rigtig godt, hvis du vil sende til par pletskud til en javascript OOP newbie, som tager tingene lidt i etaper :-)

Og vil du i øvrigt også lægge et svar til mig, det er ved at være langt ud over spørgsmålet :-/ Har dog fået en masse ud af det, så virkelig lækkert med hjælp! TAK Jeg har ofte tænkt at det er "noget jeg skal have lavet", men jeg har det også ofte sådan at når man bevæger sig ind på et nyt område i denne verden (læs: it/programmering) så tror man, at man åbner døren til et nyt rum ... men man åbner i virkeligheden til en ny verden i stedet for et rum! :) Så det er rigtig farligt at åbne døre :D og den med skiltet "js OOP" er derfor bare holdt lukket indtil nu. Men nu er den på klem og så har man jo fået et lille indblik i fordelene ... så bliver man jo nødt til at gå helt ind! :-O

ja, nu tog du jo billedsproget op tidligere ;)
Avatar billede lone_a_p Praktikant
09. november 2011 - 23:58 #14
Eller skal jeg egentlig hellere oprette User.EatenList("2011-11-05") indeholdende arrays med de forskellige Intake objekter (med food og gram)?

Nej... det kan ikke passe ... så er det lige svært at få fat i de enkelte objekter...

Jeg vil gerne kunne tilgå property id=4 fra User.EatenList direkte i stedet for at løbe hele User.EatenList igennem.
Avatar billede olebole Juniormester
10. november 2011 - 00:42 #15
Du skal ikke bruge arrays, men objekter. Så kalder du direkte ned i et hash table, hvilket går lynhurtigt! Her er et eksempel med et statisk objekt, der indeholder to constructor funktioner og en metode til at tilgå de enkelte objekter med:


var App = {
    idList: {},
    dateList: {},
   
    item: function(v) {
        if (typeof v=="number") return this.idList[""+v];
        else if (typeof v=="string") return this.dateList[v];
        return null;
    },
    Obj_A: function(id, date, argA, argB, argC) {
        this.a = argA;
        this.b = argB;
        this.c = argC;
        idList[""+id] = dateList[date] = this;
    },
    Obj_C: function(id, date, argA, argB, argC) {
        this.a = argA;
        this.b = argB;
        this.c = argC;
        idList[""+id] = dateList[date] = this;
    }
};

var obj_1 = new App.Obj_A(123, "2011-11-03", "foo", "bar", "baz");
var obj_2 = new App.Obj_B(234, "2011-11-05", "baz", "bar", "foo");

/* Så kan du altid kalde ned i listerne med */

alert(App.item(123).b);
alert(App.item("2011-11-05").c);


Det vil være en overdrivelse at kalde det OOP, men det giver en idé om, hvordan du kan bruge objekter til at strukturere din kode med - samt, hvordan du kan store dine instanser med hurtig og direkte adgang
Avatar billede olebole Juniormester
10. november 2011 - 00:45 #16
Hvis du ikke skal bruge objektet, når du instantierer det, behøver du selvfølgelig ikke variablerne obj_1 og obj_2. Så skriver du bare:


new App.Obj_A(123, "2011-11-03", "foo", "bar", "baz");
new App.Obj_B(234, "2011-11-05", "baz", "bar", "foo");

Avatar billede olebole Juniormester
10. november 2011 - 01:05 #17
Sorry! Man bør teste sin kode, før den postes ... alternativt bruge den glatte side af badehætten!  :D


var App = {
    idList: {},
    dateList: {},
   
    item: function(v) {
        if (typeof v=="number") return this.idList[""+v];
        else if (typeof v=="string") return this.dateList[v];
        return null;
    },
    create: function(sType) {
        var a = arguments;
        return new this[sType](a[1], a[2], a[3], a[4], a[5]);
    },
    Obj_A: function(id, date, argA, argB, argC) {
        this.a = argA;
        this.b = argB;
        this.c = argC;
        App.idList[""+id] = this;
        App.dateList[date] = this;
    },
    Obj_C: function(id, date, argA, argB, argC) {
        this.a = argA;
        this.b = argB;
        this.c = argC;
        App.idList[""+id] = this;
        App.dateList[date] = this;
    }
};

var obj_1 = App.create("Obj_A", 123, "2011-11-03", "foo", "bar", "baz");
var obj_2 = App.create("Obj_C", 234, "2011-11-05", "baz", "bar", "foo");

Avatar billede olebole Juniormester
10. november 2011 - 01:14 #18
Men du kan også lave en constructor for App objektet:


function AppClass() {
    this.idList = {};
    this.dateList = {};
   
    this.create = function(sType) {
        var a = arguments;
        var o = new this[sType](a[1], a[2], a[3], a[4], a[5]);
        this.idList[a[1]] = o;
        this.dateList[a[2]] = o;
        return o;
    };
    this.item = function(v) {
        if (typeof v=="number") return this.idList[""+v];
        else if (typeof v=="string") return this.dateList[v];
        return null;
    };
    this.Obj_A = function(id, date, argA, argB, argC) {
        this.a = argA;
        this.b = argB;
        this.c = argC;
    };
    this.Obj_C = function(id, date, argA, argB, argC) {
        this.a = argA;
        this.b = argB;
        this.c = argC;
    };
}

var App = new AppClass();

Avatar billede olebole Juniormester
10. november 2011 - 01:18 #19
Hvis vi i stedet bruger klassens prototype og lægger en masse over på denne, bliver den enkelte instans af klassen langt mindre. Den enkelte instans har adgang til alle klassens protypes metoder og properties - uden selv at slæbe rundt på dem. Det kunne se sådan ud:


function AppClass() {
    this.idList = {};
    this.dateList = {};
}

var p = AppClass.prototype;
p.create = function(sType) {
    var a = arguments;
    var o = new this[sType](a[1], a[2], a[3], a[4], a[5]);
    this.idList[a[1]] = o;
    this.dateList[a[2]] = o;
    return o;
};
p.item = function(v) {
    if (typeof v=="number") return this.idList[""+v];
    else if (typeof v=="string") return this.dateList[v];
    return null;
};
p.Obj_A = function(id, date, argA, argB, argC) {
    this.a = argA;
    this.b = argB;
    this.c = argC;
};
p.Obj_C = function(id, date, argA, argB, argC) {
    this.a = argA;
    this.b = argB;
    this.c = argC;
};

var App = new AppClass();

Avatar billede olebole Juniormester
10. november 2011 - 01:25 #20
- og et enkelt link til Mozillas forklaring af prototype

Her er i øvrigt hovedsiden for Mozillas JS reference ... det er en perle!  =)
Avatar billede lone_a_p Praktikant
10. november 2011 - 19:58 #21
Hej ole,

Er følgende arrays, eller skal det forstås som arrays?:

idList: {},
dateList: {},
...
this.idList[a[1]] = o;
this.dateList[a[2]] = o;

Og bliver den samme data ikke gemt to gange? Altså bliver o ikke både gemt i idList og dateList og dermed fylder dobbelt?
Avatar billede lone_a_p Praktikant
10. november 2011 - 20:16 #22
eller er det blot en form for pointere?
Avatar billede lone_a_p Praktikant
13. november 2011 - 17:54 #23
Hej ole,

Tak for hjælpen indtil nu, vil du lægge et svar, så jeg kan give point? :)

Mvh Lone
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Vi tilbyder markedets bedste kurser inden for webudvikling

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester