[{"data":1,"prerenderedAt":2643},["ShallowReactive",2],{"navigation":3,"blog-forbedre-din-kode-med-javascript-features":35,"blog-related-forbedre-din-kode-med-javascript-features":926},[4,11,17,23,29],{"fields":5,"sys":9},{"navigationTitle":6,"slug":7,"external":8},"Om Nicky","nicky-christensen",false,{"id":10},"nav-1",{"fields":12,"sys":15},{"navigationTitle":13,"slug":14,"external":8},"Udvalgte projekter","referencer",{"id":16},"nav-2",{"fields":18,"sys":21},{"navigationTitle":19,"slug":20,"external":8},"Blog","blog",{"id":22},"nav-blog",{"fields":24,"sys":27},{"navigationTitle":25,"slug":26,"external":8},"Hvad koster det?","projekt-estimator",{"id":28},"nav-estimator",{"fields":30,"sys":33},{"navigationTitle":31,"slug":32,"external":8},"Kontakt","kontakt",{"id":34},"nav-3",{"id":36,"title":37,"body":38,"date":911,"description":912,"extension":913,"meta":914,"navigation":915,"noindex":8,"path":916,"seo":917,"seoDescription":918,"seoTitle":37,"slug":919,"stem":920,"tags":921,"thumbnail":924,"updated":911,"__hash__":925},"blog\u002Fblog\u002Fforbedre-din-kode-med-javascript-features.md","Forbedre din kode: Prøv disse JavaScript features i dag",{"type":39,"value":40,"toc":903},"minimark",[41,45,48,53,56,181,226,262,291,294,298,301,363,371,377,383,392,396,399,429,433,436,439,585,592,596,599,684,687,725,771,777,784,788,791,858,887,893,899],[42,43,44],"p",{},"At følge med i de seneste nyheder inden for frontend-verdenen kan være svært. Tingene udvikler sig hurtigt. Nye frameworks, nye biblioteker, nye features og meget mere.",[42,46,47],{},"Jeg vil prøve at lave en kort opsamling af nogle af de super fede nye features JavaScript har i ærmet til frontend-udviklere.",[49,50,52],"h2",{"id":51},"optional-chaining","Optional Chaining",[42,54,55],{},"Optional chaining er en lille, men meget nyttig tilføjelse til sproget. Det gør den kode du skal skrive en smule kortere og renere. Det giver dig basalt set mulighed for at tjekke om værdier eksisterer i et objekt:",[57,58,63],"pre",{"className":59,"code":60,"language":61,"meta":62,"style":62},"language-javascript shiki shiki-themes github-dark","const someObject = {\n  profile: {\n   firstName: 'Nicky',\n    lastName: 'Christensen',\n    country: 'Denmark'\n  }\n}\n\u002F\u002F with optional chaining:\nif (someObject?.profile?.firstName){ \n console.log('Name is 1: ', someObject.profile.firstName)\n}\u002F\u002F navigate object graph safely\n","javascript","",[64,65,66,86,92,105,116,125,131,137,144,153,172],"code",{"__ignoreMap":62},[67,68,71,75,79,82],"span",{"class":69,"line":70},"line",1,[67,72,74],{"class":73},"snl16","const",[67,76,78],{"class":77},"sDLfK"," someObject",[67,80,81],{"class":73}," =",[67,83,85],{"class":84},"s95oV"," {\n",[67,87,89],{"class":69,"line":88},2,[67,90,91],{"class":84},"  profile: {\n",[67,93,95,98,102],{"class":69,"line":94},3,[67,96,97],{"class":84},"   firstName: ",[67,99,101],{"class":100},"sU2Wk","'Nicky'",[67,103,104],{"class":84},",\n",[67,106,108,111,114],{"class":69,"line":107},4,[67,109,110],{"class":84},"    lastName: ",[67,112,113],{"class":100},"'Christensen'",[67,115,104],{"class":84},[67,117,119,122],{"class":69,"line":118},5,[67,120,121],{"class":84},"    country: ",[67,123,124],{"class":100},"'Denmark'\n",[67,126,128],{"class":69,"line":127},6,[67,129,130],{"class":84},"  }\n",[67,132,134],{"class":69,"line":133},7,[67,135,136],{"class":84},"}\n",[67,138,140],{"class":69,"line":139},8,[67,141,143],{"class":142},"sAwPA","\u002F\u002F with optional chaining:\n",[67,145,147,150],{"class":69,"line":146},9,[67,148,149],{"class":73},"if",[67,151,152],{"class":84}," (someObject?.profile?.firstName){ \n",[67,154,156,159,163,166,169],{"class":69,"line":155},10,[67,157,158],{"class":84}," console.",[67,160,162],{"class":161},"svObZ","log",[67,164,165],{"class":84},"(",[67,167,168],{"class":100},"'Name is 1: '",[67,170,171],{"class":84},", someObject.profile.firstName)\n",[67,173,175,178],{"class":69,"line":174},11,[67,176,177],{"class":84},"}",[67,179,180],{"class":142},"\u002F\u002F navigate object graph safely\n",[57,182,184],{"className":59,"code":183,"language":61,"meta":62,"style":62},"\u002F\u002F old style without optional chaining:\nif (someObject && someObject.profile && someObject.profile.firstName){ \n console.log('Name is 2: ', someObject.profile.firstName)\n}\n",[64,185,186,191,209,222],{"__ignoreMap":62},[67,187,188],{"class":69,"line":70},[67,189,190],{"class":142},"\u002F\u002F old style without optional chaining:\n",[67,192,193,195,198,201,204,206],{"class":69,"line":88},[67,194,149],{"class":73},[67,196,197],{"class":84}," (someObject ",[67,199,200],{"class":73},"&&",[67,202,203],{"class":84}," someObject.profile ",[67,205,200],{"class":73},[67,207,208],{"class":84}," someObject.profile.firstName){ \n",[67,210,211,213,215,217,220],{"class":69,"line":94},[67,212,158],{"class":84},[67,214,162],{"class":161},[67,216,165],{"class":84},[67,218,219],{"class":100},"'Name is 2: '",[67,221,171],{"class":84},[67,223,224],{"class":69,"line":107},[67,225,136],{"class":84},[57,227,229],{"className":59,"code":228,"language":61,"meta":62,"style":62},"\u002F\u002F with optional chaining that fails as name doesnt exist:\nif (someObject?.profile?.name){ \n console.log('Name is 3: ', someObject.profile.firstName)\n}\u002F\u002F navigate object graph safely\n",[64,230,231,236,243,256],{"__ignoreMap":62},[67,232,233],{"class":69,"line":70},[67,234,235],{"class":142},"\u002F\u002F with optional chaining that fails as name doesnt exist:\n",[67,237,238,240],{"class":69,"line":88},[67,239,149],{"class":73},[67,241,242],{"class":84}," (someObject?.profile?.name){ \n",[67,244,245,247,249,251,254],{"class":69,"line":94},[67,246,158],{"class":84},[67,248,162],{"class":161},[67,250,165],{"class":84},[67,252,253],{"class":100},"'Name is 3: '",[67,255,171],{"class":84},[67,257,258,260],{"class":69,"line":107},[67,259,177],{"class":84},[67,261,180],{"class":142},[42,263,264,265,272,273,278,279,284,285,290],{},"Som du kan se, er de eneste to console.logs() der bliver printet ",[266,267,268],"em",{},[269,270,271],"strong",{},"\"Name is 1\""," og ",[266,274,275],{},[269,276,277],{},"\"Name is 2\"",". Den tredje bliver ikke printet, da ",[266,280,281],{},[269,282,283],{},"\"name\""," ikke eksisterer på ",[266,286,287],{},[269,288,289],{},"\"profile\"",".",[42,292,293],{},"Før optional chaining var vi nødt til at gøre som i eksempel 2, hvilket kan være noget besværligt og langt at skrive.",[49,295,297],{"id":296},"nullish-coalescing","Nullish coalescing",[42,299,300],{},"I den seneste udgave af ECMAScript kom en ny logisk operator som returnerer sin højresides operand når dens venstresides operand er null eller undefined. Du kan bruge nullish coalescing \"??\" når du skal sætte standardværdier. Ofte når vi gør dette har vi brugt \"||\" operatoren, som du selvfølgelig stadig kan bruge. \"??\" har nogle andre fordele som kan gavne dig når du vil undgå sideeffekter.",[57,302,304],{"className":59,"code":303,"language":61,"meta":62,"style":62},"const falsy = false;\nconst emptyString = '';\nconst nullish = null;\nconst uDefined = undefined;\n",[64,305,306,321,335,349],{"__ignoreMap":62},[67,307,308,310,313,315,318],{"class":69,"line":70},[67,309,74],{"class":73},[67,311,312],{"class":77}," falsy",[67,314,81],{"class":73},[67,316,317],{"class":77}," false",[67,319,320],{"class":84},";\n",[67,322,323,325,328,330,333],{"class":69,"line":88},[67,324,74],{"class":73},[67,326,327],{"class":77}," emptyString",[67,329,81],{"class":73},[67,331,332],{"class":100}," ''",[67,334,320],{"class":84},[67,336,337,339,342,344,347],{"class":69,"line":94},[67,338,74],{"class":73},[67,340,341],{"class":77}," nullish",[67,343,81],{"class":73},[67,345,346],{"class":77}," null",[67,348,320],{"class":84},[67,350,351,353,356,358,361],{"class":69,"line":107},[67,352,74],{"class":73},[67,354,355],{"class":77}," uDefined",[67,357,81],{"class":73},[67,359,360],{"class":77}," undefined",[67,362,320],{"class":84},[57,364,369],{"className":365,"code":367,"language":368},[366],"language-text","console.log('1', falsy ?? 'Some string');\nconsole.log('2', emptyString ?? 'Default string') \u002F\u002F\"\" (as the empty string is not null or undefined)\nconsole.log('3', nullish ?? 'Default string')\nconsole.log('4', uDefined ?? 'Default string')\n","text",[64,370,367],{"__ignoreMap":62},[57,372,375],{"className":373,"code":374,"language":368},[366],"console.log('-------');\n",[64,376,374],{"__ignoreMap":62},[57,378,381],{"className":379,"code":380,"language":368},[366],"console.log('1.1', falsy || 'Some string');\nconsole.log('2.2', emptyString || 'Default string')\nconsole.log('3.3', nullish || 'Default string')\nconsole.log('4.4', uDefined || 'Default string')\n",[64,382,380],{"__ignoreMap":62},[42,384,385,386],{},"Du kan prøve at lege med det i denne fiddle for at se forskellen mellem || og ?? operatoren: ",[387,388,389],"a",{"href":389,"rel":390},"https:\u002F\u002Fjsfiddle.net\u002F96b4zw71\u002F",[391],"nofollow",[49,393,395],{"id":394},"dynamic-imports","Dynamic imports",[42,397,398],{},"Den seneste version af ECMAScript introducerer dynamic imports på moduler. Det gør det muligt at indlæse moduler asynkront. Dette er også kendt som code splitting, noget vi har gjort via build tools i årevis.",[57,400,402],{"className":59,"code":401,"language":61,"meta":62,"style":62},"let someAsyncModule = await import('\u002Fmodules\u002Fmy-module.ts');\n",[64,403,404],{"__ignoreMap":62},[67,405,406,409,412,415,418,421,423,426],{"class":69,"line":70},[67,407,408],{"class":73},"let",[67,410,411],{"class":84}," someAsyncModule ",[67,413,414],{"class":73},"=",[67,416,417],{"class":73}," await",[67,419,420],{"class":73}," import",[67,422,165],{"class":84},[67,424,425],{"class":100},"'\u002Fmodules\u002Fmy-module.ts'",[67,427,428],{"class":84},");\n",[49,430,432],{"id":431},"promiseallsettled","Promise.allSettled()",[42,434,435],{},"I årevis har vi brugt Promise.all() til at vente på at alle promises bliver resolved. Problemet med Promise.all() har været at vi ikke vidste hvilke promises der faktisk blev resolved og hvilke der fejlede.",[42,437,438],{},"Metoden Promise.allSettled() giver dig mulighed for at observere resultaterne af et sæt promises, uanset om de er fulfilled eller rejected, hvilket kan være rigtig nyttigt.",[57,440,442],{"className":59,"code":441,"language":61,"meta":62,"style":62},"const promise1 = Promise.resolve(\"OK, I resolved\");\nconst promise2 = Promise.reject(\"OH no, I was rejected\");\nconst promise3 = Promise.resolve(\"After I was rejected\");\nPromise.allSettled([promise1, promise2, promise3])\n    .then((results) => console.log(results))\n    .catch((err) => console.log(\"error: \" + err));\n",[64,443,444,468,491,513,526,554],{"__ignoreMap":62},[67,445,446,448,451,453,456,458,461,463,466],{"class":69,"line":70},[67,447,74],{"class":73},[67,449,450],{"class":77}," promise1",[67,452,81],{"class":73},[67,454,455],{"class":77}," Promise",[67,457,290],{"class":84},[67,459,460],{"class":161},"resolve",[67,462,165],{"class":84},[67,464,465],{"class":100},"\"OK, I resolved\"",[67,467,428],{"class":84},[67,469,470,472,475,477,479,481,484,486,489],{"class":69,"line":88},[67,471,74],{"class":73},[67,473,474],{"class":77}," promise2",[67,476,81],{"class":73},[67,478,455],{"class":77},[67,480,290],{"class":84},[67,482,483],{"class":161},"reject",[67,485,165],{"class":84},[67,487,488],{"class":100},"\"OH no, I was rejected\"",[67,490,428],{"class":84},[67,492,493,495,498,500,502,504,506,508,511],{"class":69,"line":94},[67,494,74],{"class":73},[67,496,497],{"class":77}," promise3",[67,499,81],{"class":73},[67,501,455],{"class":77},[67,503,290],{"class":84},[67,505,460],{"class":161},[67,507,165],{"class":84},[67,509,510],{"class":100},"\"After I was rejected\"",[67,512,428],{"class":84},[67,514,515,518,520,523],{"class":69,"line":107},[67,516,517],{"class":77},"Promise",[67,519,290],{"class":84},[67,521,522],{"class":161},"allSettled",[67,524,525],{"class":84},"([promise1, promise2, promise3])\n",[67,527,528,531,534,537,541,544,547,549,551],{"class":69,"line":118},[67,529,530],{"class":84},"    .",[67,532,533],{"class":161},"then",[67,535,536],{"class":84},"((",[67,538,540],{"class":539},"s9osk","results",[67,542,543],{"class":84},") ",[67,545,546],{"class":73},"=>",[67,548,158],{"class":84},[67,550,162],{"class":161},[67,552,553],{"class":84},"(results))\n",[67,555,556,558,561,563,566,568,570,572,574,576,579,582],{"class":69,"line":127},[67,557,530],{"class":84},[67,559,560],{"class":161},"catch",[67,562,536],{"class":84},[67,564,565],{"class":539},"err",[67,567,543],{"class":84},[67,569,546],{"class":73},[67,571,158],{"class":84},[67,573,162],{"class":161},[67,575,165],{"class":84},[67,577,578],{"class":100},"\"error: \"",[67,580,581],{"class":73}," +",[67,583,584],{"class":84}," err));\n",[42,586,587,588],{},"Tjek denne fiddle for at se hvordan det bruges: ",[387,589,590],{"href":590,"rel":591},"https:\u002F\u002Fjsfiddle.net\u002Fx2h01z7p\u002F1\u002F",[391],[49,593,595],{"id":594},"spread-operators","Spread operators",[42,597,598],{},"I nogen tid har vi haft adgang til spread operators. Det er særligt nyttigt når du for eksempel vil flette arrays eller objekter sammen. Før spread operators kunne vi bruge array.concat til at flette arrays, nu kan vi gøre det endnu nemmere:",[57,600,602],{"className":59,"code":601,"language":61,"meta":62,"style":62},"const arr1 = [1,2,3];\nconst arr2 = [4,5,6];\nconst arr3 = [...arr1, ...arr2] \u002F\u002Farr3 ==> [1,2,3,4,5,6]\n",[64,603,604,633,659],{"__ignoreMap":62},[67,605,606,608,611,613,616,619,622,625,627,630],{"class":69,"line":70},[67,607,74],{"class":73},[67,609,610],{"class":77}," arr1",[67,612,81],{"class":73},[67,614,615],{"class":84}," [",[67,617,618],{"class":77},"1",[67,620,621],{"class":84},",",[67,623,624],{"class":77},"2",[67,626,621],{"class":84},[67,628,629],{"class":77},"3",[67,631,632],{"class":84},"];\n",[67,634,635,637,640,642,644,647,649,652,654,657],{"class":69,"line":88},[67,636,74],{"class":73},[67,638,639],{"class":77}," arr2",[67,641,81],{"class":73},[67,643,615],{"class":84},[67,645,646],{"class":77},"4",[67,648,621],{"class":84},[67,650,651],{"class":77},"5",[67,653,621],{"class":84},[67,655,656],{"class":77},"6",[67,658,632],{"class":84},[67,660,661,663,666,668,670,673,676,678,681],{"class":69,"line":94},[67,662,74],{"class":73},[67,664,665],{"class":77}," arr3",[67,667,81],{"class":73},[67,669,615],{"class":84},[67,671,672],{"class":73},"...",[67,674,675],{"class":84},"arr1, ",[67,677,672],{"class":73},[67,679,680],{"class":84},"arr2] ",[67,682,683],{"class":142},"\u002F\u002Farr3 ==> [1,2,3,4,5,6]\n",[42,685,686],{},"For objekter kan vi gøre:",[57,688,690],{"className":59,"code":689,"language":61,"meta":62,"style":62},"const basePerson = {\n name: 'Nicky C',\n  country: 'DK'\n}\n",[64,691,692,703,713,721],{"__ignoreMap":62},[67,693,694,696,699,701],{"class":69,"line":70},[67,695,74],{"class":73},[67,697,698],{"class":77}," basePerson",[67,700,81],{"class":73},[67,702,85],{"class":84},[67,704,705,708,711],{"class":69,"line":88},[67,706,707],{"class":84}," name: ",[67,709,710],{"class":100},"'Nicky C'",[67,712,104],{"class":84},[67,714,715,718],{"class":69,"line":94},[67,716,717],{"class":84},"  country: ",[67,719,720],{"class":100},"'DK'\n",[67,722,723],{"class":69,"line":107},[67,724,136],{"class":84},[57,726,728],{"className":59,"code":727,"language":61,"meta":62,"style":62},"const footballerPerson = {\n ...basePerson,\n  team: 'Man UTD',\n  shirtNumber: '11'\n}\n",[64,729,730,741,749,759,767],{"__ignoreMap":62},[67,731,732,734,737,739],{"class":69,"line":70},[67,733,74],{"class":73},[67,735,736],{"class":77}," footballerPerson",[67,738,81],{"class":73},[67,740,85],{"class":84},[67,742,743,746],{"class":69,"line":88},[67,744,745],{"class":73}," ...",[67,747,748],{"class":84},"basePerson,\n",[67,750,751,754,757],{"class":69,"line":94},[67,752,753],{"class":84},"  team: ",[67,755,756],{"class":100},"'Man UTD'",[67,758,104],{"class":84},[67,760,761,764],{"class":69,"line":107},[67,762,763],{"class":84},"  shirtNumber: ",[67,765,766],{"class":100},"'11'\n",[67,768,769],{"class":69,"line":118},[67,770,136],{"class":84},[57,772,775],{"className":773,"code":774,"language":368},[366],"console.log(footballerPerson) \u002F\u002FWill output a merge of the two objects\n",[64,776,774],{"__ignoreMap":62},[42,778,779,780],{},"Prøv det her: ",[387,781,782],{"href":782,"rel":783},"https:\u002F\u002Fjsfiddle.net\u002Fs86knh72\u002F",[391],[49,785,787],{"id":786},"object-destructuring","Object Destructuring",[42,789,790],{},"Ved at bruge object destructuring kan du nemt udpakke værdier fra et objekt:",[57,792,794],{"className":59,"code":793,"language":61,"meta":62,"style":62},"const basePerson = {\n name: 'Nicky C',\n  country: 'DK'\n}\nconst footballerPerson = {\n ...basePerson,\n  team: 'Man UTD',\n  shirtNumber: '11'\n}\n",[64,795,796,806,814,820,824,834,840,848,854],{"__ignoreMap":62},[67,797,798,800,802,804],{"class":69,"line":70},[67,799,74],{"class":73},[67,801,698],{"class":77},[67,803,81],{"class":73},[67,805,85],{"class":84},[67,807,808,810,812],{"class":69,"line":88},[67,809,707],{"class":84},[67,811,710],{"class":100},[67,813,104],{"class":84},[67,815,816,818],{"class":69,"line":94},[67,817,717],{"class":84},[67,819,720],{"class":100},[67,821,822],{"class":69,"line":107},[67,823,136],{"class":84},[67,825,826,828,830,832],{"class":69,"line":118},[67,827,74],{"class":73},[67,829,736],{"class":77},[67,831,81],{"class":73},[67,833,85],{"class":84},[67,835,836,838],{"class":69,"line":127},[67,837,745],{"class":73},[67,839,748],{"class":84},[67,841,842,844,846],{"class":69,"line":133},[67,843,753],{"class":84},[67,845,756],{"class":100},[67,847,104],{"class":84},[67,849,850,852],{"class":69,"line":139},[67,851,763],{"class":84},[67,853,766],{"class":100},[67,855,856],{"class":69,"line":146},[67,857,136],{"class":84},[57,859,861],{"className":59,"code":860,"language":61,"meta":62,"style":62},"const {team, shirtNumber} = footballerPerson;\n",[64,862,863],{"__ignoreMap":62},[67,864,865,867,870,873,876,879,882,884],{"class":69,"line":70},[67,866,74],{"class":73},[67,868,869],{"class":84}," {",[67,871,872],{"class":77},"team",[67,874,875],{"class":84},", ",[67,877,878],{"class":77},"shirtNumber",[67,880,881],{"class":84},"} ",[67,883,414],{"class":73},[67,885,886],{"class":84}," footballerPerson;\n",[57,888,891],{"className":889,"code":890,"language":368},[366],"console.log(team, shirtNumber); \u002F\u002FManUtd, 11\n",[64,892,890],{"__ignoreMap":62},[42,894,779,895],{},[387,896,897],{"href":897,"rel":898},"https:\u002F\u002Fjsfiddle.net\u002F2z3rp18v\u002F",[391],[900,901,902],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":62,"searchDepth":88,"depth":88,"links":904},[905,906,907,908,909,910],{"id":51,"depth":88,"text":52},{"id":296,"depth":88,"text":297},{"id":394,"depth":88,"text":395},{"id":431,"depth":88,"text":432},{"id":594,"depth":88,"text":595},{"id":786,"depth":88,"text":787},"2022-02-21","Prøv disse fede nye features og gør din kode endnu bedre. Fra optional chaining til spread operators - her er et overblik over nyttige JavaScript features.","md",{},true,"\u002Fblog\u002Fforbedre-din-kode-med-javascript-features",{"title":37,"description":912},"Prøv disse fede nye JavaScript features og gør din kode endnu bedre. Optional chaining, nullish coalescing, spread operators og mere.","forbedre-din-kode-med-javascript-features","blog\u002Fforbedre-din-kode-med-javascript-features",[61,922,923],"best-practices","frontend","https:\u002F\u002Fcdn-images-1.medium.com\u002Fmax\u002F800\u002F1*mmtXU2DtE3qMOuFqACJYEg.png","nKqYxevtIUUY7fxIcXLv4lTkeNP4nCKheWd07xdaMqA",[927,1640,2032],{"id":928,"title":929,"body":930,"date":1624,"description":1625,"extension":913,"meta":1626,"navigation":915,"noindex":8,"path":1627,"seo":1628,"seoDescription":1629,"seoTitle":1630,"slug":1631,"stem":1632,"tags":1633,"thumbnail":1638,"updated":1624,"__hash__":1639},"blog\u002Fblog\u002Ffrontend-arkitektur-for-store-projekter.md","Frontend arkitektur for store projekter — Sådan skalerer du din kodebase",{"type":39,"value":931,"toc":1613},[932,935,938,941,945,948,954,960,963,973,976,980,983,989,995,1148,1154,1157,1161,1164,1264,1267,1289,1292,1296,1299,1302,1340,1343,1347,1350,1356,1367,1373,1384,1387,1390,1394,1397,1403,1409,1497,1516,1522,1526,1529,1535,1541,1547,1550,1554,1557,1563,1569,1583,1589,1593,1596,1599,1602,1610],[42,933,934],{},"Når du arbejder på et frontend-projekt med 2-3 udviklere, kan du slippe afsted med mange ting. Mappestruktur? Det løser sig selv. State management? Props er fint. Kodekonventioner? Vi snakker bare sammen.",[42,936,937],{},"Men når teamet vokser til 10, 20 eller 50 udviklere, og kodebasen rammer hundredtusinder af linjer kode, så falder den tilgang fra hinanden. Pludselig er der konflikter i hver merge request, ingen kan finde noget, og performance er gået i stændig tilbagegang.",[42,939,940],{},"Jeg har arbejdet med frontend i enterprise-skala hos Grundfos, Vestas, Playable og Harness — og nu som Technical Director hos Checkmate.dk. Her er de vigtigste lærdomme om hvordan du arkitekterer frontend-applikationer der faktisk skalerer.",[49,942,944],{"id":943},"komponentarkitektur-og-mappestruktur","Komponentarkitektur og mappestruktur",[42,946,947],{},"Den første og mest grundlæggende beslutning er hvordan du organiserer dine komponenter. Jeg har set to primære tilgange:",[42,949,950,953],{},[269,951,952],{},"Feature-baseret struktur"," er næsten altid det rigtige valg for store projekter. I stedet for at gruppere efter type (alle buttons i en mappe, alle modals i en anden), grupperer du efter feature:",[57,955,958],{"className":956,"code":957,"language":368},[366],"components\u002F\n  Dashboard\u002F\n    DashboardView.vue\n    DashboardStats.vue\n    DashboardChart.vue\n    useDashboardData.ts\n  UserProfile\u002F\n    UserProfileView.vue\n    UserProfileForm.vue\n    useUserProfile.ts\n  UI\u002F\n    BaseButton.vue\n    BaseModal.vue\n    BaseInput.vue\n",[64,959,957],{"__ignoreMap":62},[42,961,962],{},"Grunden er simpel: når en udvikler arbejder på en feature, skal de kun røre filer i en mappe. Det reducerer merge-konflikter drastisk og gør det nemmere at forstå konteksten.",[42,964,965,968,969,972],{},[269,966,967],{},"UI-komponenter"," (buttons, inputs, modals) lever for sig selv i en delt mappe, fordi de bruges på tværs af features. Men feature-specifikke komponenter hører til i feature-mappen — ikke i en generisk ",[64,970,971],{},"components\u002F"," rod.",[42,974,975],{},"En tommelfingerregel jeg bruger: Hvis en komponent kun bruges et sted, hører den til i den features mappe. Først når den bruges to eller flere steder, flyttes den til en delt mappe.",[49,977,979],{"id":978},"state-management-pinia-vs-composables-vs-props","State management — Pinia vs composables vs props",[42,981,982],{},"Et af de mest debatterede emner i frontend-verdenen. I Vue-økosystemet har vi Pinia, composables og props. I React er det Redux\u002FZustand, hooks og props. Men principperne er de samme.",[42,984,985,988],{},[269,986,987],{},"Props"," er førstevalget. Altid. Data der flyder fra forælder til barn er det simpleste og mest forudsigelige mønster. Når folk klager over \"prop drilling\", er det ofte et tegn på at komponenthierarkiet er forkert — ikke at du mangler global state.",[42,990,991,994],{},[269,992,993],{},"Composables (eller custom hooks)"," er næste trin. Når logik skal deles mellem komponenter der ikke har et direkte forælder-barn-forhold, er en composable perfekt. Den indkapsler logik og state uden at gøre det globalt.",[57,996,1000],{"className":997,"code":998,"language":999,"meta":62,"style":62},"language-typescript shiki shiki-themes github-dark","\u002F\u002F useCurrentUser.ts\nexport function useCurrentUser() {\n  const user = ref\u003CUser | null>(null)\n  const isLoading = ref(false)\n\n  async function fetchUser() {\n    isLoading.value = true\n    user.value = await api.getCurrentUser()\n    isLoading.value = false\n  }\n\n  return { user, isLoading, fetchUser }\n}\n","typescript",[64,1001,1002,1007,1021,1054,1072,1077,1089,1099,1117,1126,1130,1134,1143],{"__ignoreMap":62},[67,1003,1004],{"class":69,"line":70},[67,1005,1006],{"class":142},"\u002F\u002F useCurrentUser.ts\n",[67,1008,1009,1012,1015,1018],{"class":69,"line":88},[67,1010,1011],{"class":73},"export",[67,1013,1014],{"class":73}," function",[67,1016,1017],{"class":161}," useCurrentUser",[67,1019,1020],{"class":84},"() {\n",[67,1022,1023,1026,1029,1031,1034,1037,1040,1043,1045,1048,1051],{"class":69,"line":94},[67,1024,1025],{"class":73},"  const",[67,1027,1028],{"class":77}," user",[67,1030,81],{"class":73},[67,1032,1033],{"class":161}," ref",[67,1035,1036],{"class":84},"\u003C",[67,1038,1039],{"class":161},"User",[67,1041,1042],{"class":73}," |",[67,1044,346],{"class":77},[67,1046,1047],{"class":84},">(",[67,1049,1050],{"class":77},"null",[67,1052,1053],{"class":84},")\n",[67,1055,1056,1058,1061,1063,1065,1067,1070],{"class":69,"line":107},[67,1057,1025],{"class":73},[67,1059,1060],{"class":77}," isLoading",[67,1062,81],{"class":73},[67,1064,1033],{"class":161},[67,1066,165],{"class":84},[67,1068,1069],{"class":77},"false",[67,1071,1053],{"class":84},[67,1073,1074],{"class":69,"line":118},[67,1075,1076],{"emptyLinePlaceholder":915},"\n",[67,1078,1079,1082,1084,1087],{"class":69,"line":127},[67,1080,1081],{"class":73},"  async",[67,1083,1014],{"class":73},[67,1085,1086],{"class":161}," fetchUser",[67,1088,1020],{"class":84},[67,1090,1091,1094,1096],{"class":69,"line":133},[67,1092,1093],{"class":84},"    isLoading.value ",[67,1095,414],{"class":73},[67,1097,1098],{"class":77}," true\n",[67,1100,1101,1104,1106,1108,1111,1114],{"class":69,"line":139},[67,1102,1103],{"class":84},"    user.value ",[67,1105,414],{"class":73},[67,1107,417],{"class":73},[67,1109,1110],{"class":84}," api.",[67,1112,1113],{"class":161},"getCurrentUser",[67,1115,1116],{"class":84},"()\n",[67,1118,1119,1121,1123],{"class":69,"line":146},[67,1120,1093],{"class":84},[67,1122,414],{"class":73},[67,1124,1125],{"class":77}," false\n",[67,1127,1128],{"class":69,"line":155},[67,1129,130],{"class":84},[67,1131,1132],{"class":69,"line":174},[67,1133,1076],{"emptyLinePlaceholder":915},[67,1135,1137,1140],{"class":69,"line":1136},12,[67,1138,1139],{"class":73},"  return",[67,1141,1142],{"class":84}," { user, isLoading, fetchUser }\n",[67,1144,1146],{"class":69,"line":1145},13,[67,1147,136],{"class":84},[42,1149,1150,1153],{},[269,1151,1152],{},"Pinia\u002FRedux"," er kun til ægte global state — ting som den aktuelle bruger, tema-indstillinger, notifikationer. Hvis du har mere end 10-15 stores, har du sandsynligvis lagt for meget i global state.",[42,1155,1156],{},"Min erfaring fra Harness, hvor vi havde en stor Vue-applikation: Teamet startede med at lægge alt i Vuex (forgængeren til Pinia). Resultatet var en massiv, uforudsigelig state-graf hvor ingen vidste hvad der påvirkede hvad. Da vi refaktorerede til composables for feature-specifik state og kun beholdt ægte global state i stores, blev kodebasen markant nemmere at arbejde med.",[49,1158,1160],{"id":1159},"code-splitting-og-lazy-loading","Code splitting og lazy loading",[42,1162,1163],{},"I en stor applikation er det afgørende at brugeren ikke skal downloade hele din kodebase for at se første side. Både Vue Router og React Router understøtter lazy loading af routes ud af boksen:",[57,1165,1167],{"className":997,"code":1166,"language":999,"meta":62,"style":62},"\u002F\u002F Vue Router\nconst routes = [\n  {\n    path: '\u002Fdashboard',\n    component: () => import('.\u002Fpages\u002FDashboard.vue')\n  },\n  {\n    path: '\u002Fsettings',\n    component: () => import('.\u002Fpages\u002FSettings.vue')\n  }\n]\n",[64,1168,1169,1174,1186,1191,1201,1220,1225,1229,1238,1255,1259],{"__ignoreMap":62},[67,1170,1171],{"class":69,"line":70},[67,1172,1173],{"class":142},"\u002F\u002F Vue Router\n",[67,1175,1176,1178,1181,1183],{"class":69,"line":88},[67,1177,74],{"class":73},[67,1179,1180],{"class":77}," routes",[67,1182,81],{"class":73},[67,1184,1185],{"class":84}," [\n",[67,1187,1188],{"class":69,"line":94},[67,1189,1190],{"class":84},"  {\n",[67,1192,1193,1196,1199],{"class":69,"line":107},[67,1194,1195],{"class":84},"    path: ",[67,1197,1198],{"class":100},"'\u002Fdashboard'",[67,1200,104],{"class":84},[67,1202,1203,1206,1209,1211,1213,1215,1218],{"class":69,"line":118},[67,1204,1205],{"class":161},"    component",[67,1207,1208],{"class":84},": () ",[67,1210,546],{"class":73},[67,1212,420],{"class":73},[67,1214,165],{"class":84},[67,1216,1217],{"class":100},"'.\u002Fpages\u002FDashboard.vue'",[67,1219,1053],{"class":84},[67,1221,1222],{"class":69,"line":127},[67,1223,1224],{"class":84},"  },\n",[67,1226,1227],{"class":69,"line":133},[67,1228,1190],{"class":84},[67,1230,1231,1233,1236],{"class":69,"line":139},[67,1232,1195],{"class":84},[67,1234,1235],{"class":100},"'\u002Fsettings'",[67,1237,104],{"class":84},[67,1239,1240,1242,1244,1246,1248,1250,1253],{"class":69,"line":146},[67,1241,1205],{"class":161},[67,1243,1208],{"class":84},[67,1245,546],{"class":73},[67,1247,420],{"class":73},[67,1249,165],{"class":84},[67,1251,1252],{"class":100},"'.\u002Fpages\u002FSettings.vue'",[67,1254,1053],{"class":84},[67,1256,1257],{"class":69,"line":155},[67,1258,130],{"class":84},[67,1260,1261],{"class":69,"line":174},[67,1262,1263],{"class":84},"]\n",[42,1265,1266],{},"Men routes er bare begyndelsen. I store projekter bør du også overveje:",[1268,1269,1270,1277,1283],"ul",{},[1271,1272,1273,1276],"li",{},[269,1274,1275],{},"Lazy loading af tunge komponenter"," — Diagrammer, editorer, og lignende bør loades on-demand",[1271,1278,1279,1282],{},[269,1280,1281],{},"Prefetching af sandsynlige næste sider"," — Hvis 80% af brugerne går fra dashboard til rapporter, så prefetch rapporter-chunken",[1271,1284,1285,1288],{},[269,1286,1287],{},"Dynamiske imports for tredjepartsbiblioteker"," — Lad være med at bundle moment.js eller lodash ind i din hovedbundle hvis det kun bruges et sted",[42,1290,1291],{},"Den største fejl jeg ser er at teams optimerer for tidligt. Start med route-baseret code splitting. Mål din bundle-størrelse. Optimer derefter de største chunks først.",[49,1293,1295],{"id":1294},"design-system-integration","Design system integration",[42,1297,1298],{},"Når du har mere end 3-4 frontend-udviklere, har du brug for et design system. Ikke et fancy Storybook-setup med 200 komponenter — men en fælles forståelse af de basale byggeklodser.",[42,1300,1301],{},"Et effektivt design system i praksis:",[1303,1304,1305,1311,1317],"ol",{},[1271,1306,1307,1310],{},[269,1308,1309],{},"Tokens først"," — Definer farver, spacing, typografi og breakpoints som design tokens (CSS custom properties eller en theme-fil). Alt andet bygger på disse.",[1271,1312,1313,1316],{},[269,1314,1315],{},"Primitive komponenter"," — Button, Input, Select, Modal, Card. Maks 15-20 komponenter. De skal være veltestede og veldokumenterede.",[1271,1318,1319,1322,1323,1326,1327,875,1330,875,1333,875,1336,1339],{},[269,1320,1321],{},"Kompositionsmønstret"," — Byg komplekse komponenter ved at kombinere primitive. Lad være med at lave en ",[64,1324,1325],{},"SuperTable"," der kan alt — lav i stedet ",[64,1328,1329],{},"Table",[64,1331,1332],{},"TableHeader",[64,1334,1335],{},"TableRow",[64,1337,1338],{},"TableCell"," der kan sammensættes.",[42,1341,1342],{},"Det vigtigste er at design systemet lever tæt på koden. Jeg har set for mange teams hvor design systemet er et separat projekt der altid er bagud. Når det lever i en delt pakke i din monorepo (eller som en npm-pakke med hurtig release-cyklus), bliver det faktisk brugt.",[49,1344,1346],{"id":1345},"monorepo-vs-polyrepo","Monorepo vs polyrepo",[42,1348,1349],{},"Det her er en beslutning der har store konsekvenser, og der er ikke et universelt svar.",[42,1351,1352,1355],{},[269,1353,1354],{},"Monorepo"," (alle pakker i et repository) er bedst når:",[1268,1357,1358,1361,1364],{},[1271,1359,1360],{},"Teams deler meget kode (design system, utilities, typer)",[1271,1362,1363],{},"Du vil have atomare ændringer på tværs af pakker",[1271,1365,1366],{},"Du har værktøj til det (Nx, Turborepo, eller Lerna)",[42,1368,1369,1372],{},[269,1370,1371],{},"Polyrepo"," (separate repositories) er bedst når:",[1268,1374,1375,1378,1381],{},[1271,1376,1377],{},"Teams er meget autonome og sjældent deler kode",[1271,1379,1380],{},"Du har strikse deployment-boundaries",[1271,1382,1383],{},"Teams bruger forskellige tech stacks",[42,1385,1386],{},"Min erfaring: For de fleste frontend-teams er en monorepo det rigtige valg. Værktøjerne (særligt Nx og Turborepo) er blevet så modne at overhead er minimal, og fordelen ved at kunne dele typer, konfiguration og design system-komponenter er enorm.",[42,1388,1389],{},"Hos Vestas arbejdede vi med en polyrepo-tilgang der betød at vi konstant kæmpede med at holde delte biblioteker i sync. Hos Playable skiftede vi til monorepo, og det var en game-changer for developer experience.",[49,1391,1393],{"id":1392},"typescript-som-skaleringsværktøj","TypeScript som skaleringsværktøj",[42,1395,1396],{},"TypeScript er ikke bare \"JavaScript med typer.\" I store projekter er det et arkitekturværktøj.",[42,1398,1399,1402],{},[269,1400,1401],{},"Interfaces som kontrakter."," Når team A bygger en API og team B bruger den, fungerer TypeScript-interfaces som en levende kontrakt. Hvis API'en ændrer sig, får team B en kompileringsfejl — ikke en runtime-fejl i produktion kl. 3 om natten.",[42,1404,1405,1408],{},[269,1406,1407],{},"Strenge typer for domæneobjekter."," Definer dine domæneobjekter som TypeScript-typer og brug dem konsekvent:",[57,1410,1412],{"className":997,"code":1411,"language":999,"meta":62,"style":62},"interface Order {\n  id: string\n  status: 'pending' | 'confirmed' | 'shipped' | 'delivered'\n  items: OrderItem[]\n  customer: Customer\n  createdAt: Date\n}\n",[64,1413,1414,1424,1435,1460,1473,1483,1493],{"__ignoreMap":62},[67,1415,1416,1419,1422],{"class":69,"line":70},[67,1417,1418],{"class":73},"interface",[67,1420,1421],{"class":161}," Order",[67,1423,85],{"class":84},[67,1425,1426,1429,1432],{"class":69,"line":88},[67,1427,1428],{"class":539},"  id",[67,1430,1431],{"class":73},":",[67,1433,1434],{"class":77}," string\n",[67,1436,1437,1440,1442,1445,1447,1450,1452,1455,1457],{"class":69,"line":94},[67,1438,1439],{"class":539},"  status",[67,1441,1431],{"class":73},[67,1443,1444],{"class":100}," 'pending'",[67,1446,1042],{"class":73},[67,1448,1449],{"class":100}," 'confirmed'",[67,1451,1042],{"class":73},[67,1453,1454],{"class":100}," 'shipped'",[67,1456,1042],{"class":73},[67,1458,1459],{"class":100}," 'delivered'\n",[67,1461,1462,1465,1467,1470],{"class":69,"line":107},[67,1463,1464],{"class":539},"  items",[67,1466,1431],{"class":73},[67,1468,1469],{"class":161}," OrderItem",[67,1471,1472],{"class":84},"[]\n",[67,1474,1475,1478,1480],{"class":69,"line":118},[67,1476,1477],{"class":539},"  customer",[67,1479,1431],{"class":73},[67,1481,1482],{"class":161}," Customer\n",[67,1484,1485,1488,1490],{"class":69,"line":127},[67,1486,1487],{"class":539},"  createdAt",[67,1489,1431],{"class":73},[67,1491,1492],{"class":161}," Date\n",[67,1494,1495],{"class":69,"line":133},[67,1496,136],{"class":84},[42,1498,1499,1500,1503,1504,1507,1508,1511,1512,1515],{},"Når ",[64,1501,1502],{},"status"," er en union type i stedet for en ",[64,1505,1506],{},"string",", kan du ikke ved et uheld sætte den til ",[64,1509,1510],{},"\"PENDING\""," eller ",[64,1513,1514],{},"\"Shipped\"",". Det lyder som en lille ting, men i en kodebase med 50 udviklere forhindrer det hundredevis af bugs.",[42,1517,1518,1521],{},[269,1519,1520],{},"Generics for genbrugelig kode."," Composables og utility-funktioner der bruger generics er mærkbart nemmere at genbruge korrekt i store teams.",[49,1523,1525],{"id":1524},"teststrategi-i-skala","Teststrategi i skala",[42,1527,1528],{},"Store projekter har brug for en strategi — ikke bare \"vi skriver tests.\" Her er den pyramide jeg anbefaler:",[42,1530,1531,1534],{},[269,1532,1533],{},"Unit tests (70%)"," — Test composables, utility-funktioner og forretningslogik. De er hurtige, stabile og giver mest værdi per tidsenhed.",[42,1536,1537,1540],{},[269,1538,1539],{},"Komponent-tests (20%)"," — Test at komponenter renderer korrekt med forskellige props og håndterer brugerinteraktion. Brug Vue Test Utils eller React Testing Library.",[42,1542,1543,1546],{},[269,1544,1545],{},"End-to-end tests (10%)"," — Test kritiske brugerflows (login, checkout, osv.) med Playwright eller Cypress. Hold antallet lavt — de er langsomme og skrøbelige.",[42,1548,1549],{},"Den største fejl er at starte med E2E-tests. De er dyre at vedligeholde og giver falsk tryghed. Start med unit tests af din forretningslogik, og tilføj E2E-tests for de mest kritiske flows.",[49,1551,1553],{"id":1552},"teamkonventioner-og-code-review","Teamkonventioner og code review",[42,1555,1556],{},"Værktøj og arkitektur er kun halvdelen. Den anden halvdel er mennesker og processer.",[42,1558,1559,1562],{},[269,1560,1561],{},"Automatiser hvad der kan automatiseres."," ESLint, Prettier, husky pre-commit hooks. Diskuter ikke formatering i code reviews — lad værktøjerne håndtere det.",[42,1564,1565,1568],{},[269,1566,1567],{},"Brug ADR'er (Architecture Decision Records)."," Når teamet træffer en større beslutning (f.eks. \"vi bruger Pinia i stedet for composables til global state\"), skriv det ned med kontekst og begrundelse. Nye teammedlemmer vil takke dig.",[42,1570,1571,1574,1575,1578,1579,1582],{},[269,1572,1573],{},"Code reviews er læring, ikke gatekeeping."," De bedste teams jeg har arbejdet med bruger code reviews som en mulighed for videndeling. Forklar ",[266,1576,1577],{},"hvorfor"," du foreslår en ændring, ikke bare ",[266,1580,1581],{},"hvad",". Og vær åben for at lære noget nyt fra den der har skrevet koden.",[42,1584,1585,1588],{},[269,1586,1587],{},"Definer \"done\" for komponenter."," En komponent er ikke færdig når den virker. Den er færdig når den har typer, er testet, er dokumenteret (mindst en kommentar med et eksempel), og er reviewet.",[49,1590,1592],{"id":1591},"afslutningstanker","Afslutningstanker",[42,1594,1595],{},"God frontend-arkitektur handler ikke om at vælge det rigtige framework eller det nyeste værktøj. Det handler om at skabe struktur der gør det muligt for mange mennesker at arbejde effektivt på den samme kodebase over lang tid.",[42,1597,1598],{},"Start simpelt. Tilføj kompleksitet når du har bevist at du har brug for det. Og husk: den bedste arkitektur er den dit team faktisk forstår og følger.",[1600,1601],"hr",{},[42,1603,1604,1605,1609],{},"Har du brug for hjælp med at skalere din frontend-arkitektur? Jeg arbejder til dagligt med disse udfordringer som Technical Director hos Checkmate.dk, og jeg deler regelmæssigt mine erfaringer her på bloggen. ",[387,1606,1608],{"href":1607},"\u002Fkontakt","Kontakt mig"," hvis du vil diskutere dit projekt, eller følg med her for flere artikler om frontend i enterprise-skala.",[900,1611,1612],{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":62,"searchDepth":88,"depth":88,"links":1614},[1615,1616,1617,1618,1619,1620,1621,1622,1623],{"id":943,"depth":88,"text":944},{"id":978,"depth":88,"text":979},{"id":1159,"depth":88,"text":1160},{"id":1294,"depth":88,"text":1295},{"id":1345,"depth":88,"text":1346},{"id":1392,"depth":88,"text":1393},{"id":1524,"depth":88,"text":1525},{"id":1552,"depth":88,"text":1553},{"id":1591,"depth":88,"text":1592},"2026-04-03","Hvordan arkitekterer du frontend-applikationer der skalerer? Her er mine erfaringer fra 19+ år med enterprise-projekter hos Grundfos, Vestas, Playable og Harness.",{},"\u002Fblog\u002Ffrontend-arkitektur-for-store-projekter",{"title":929,"description":1625},"Lær at arkitektere frontend-applikationer der skalerer. Praktiske tips om komponentarkitektur, state management, TypeScript og teamkonventioner.","Frontend Arkitektur for Store Projekter — Skalering, Patterns og Best Practices","frontend-arkitektur-for-store-projekter","blog\u002Ffrontend-arkitektur-for-store-projekter",[923,1634,999,1635,1636,1637],"arkitektur","vue","react","enterprise","\u002Fimages\u002Fcontentful\u002Fblog-typescript.png","iudtKHtProPWl3tsX982-Uk__W7bE_DaSy3xhLasrGU",{"id":1641,"title":1642,"body":1643,"date":1624,"description":2021,"extension":913,"meta":2022,"navigation":915,"noindex":8,"path":2023,"seo":2024,"seoDescription":2025,"seoTitle":2026,"slug":2027,"stem":2028,"tags":2029,"thumbnail":1638,"updated":1624,"__hash__":2031},"blog\u002Fblog\u002Freact-vs-vue-i-2026.md","React vs Vue i 2026 — En erfaren udviklers perspektiv",{"type":39,"value":1644,"toc":1996},[1645,1649,1652,1655,1658,1662,1788,1792,1797,1800,1803,1807,1810,1814,1817,1821,1824,1828,1832,1835,1838,1842,1845,1849,1852,1856,1872,1876,1908,1912,1944,1948,1952,1955,1959,1962,1966,1969,1973,1980,1983,1987,1990,1993],[49,1646,1648],{"id":1647},"jeg-har-brugt-begge-i-mange-år","Jeg har brugt begge — i mange år",[42,1650,1651],{},"Dette er ikke en af de artikler der er skrevet af nogen der har læst dokumentationen og sammenlignet bullet points. Jeg har brugt både React og Vue professionelt i adskillige år — Vue hos Playable og i mine egne projekter, React hos Grundfos, Vestas og Harness.",[42,1653,1654],{},"Jeg har bygget SaaS-platforme, enterprise-løsninger og websites med begge frameworks. Og mit svar på \"hvad er bedst?\" er det samme som altid: det kommer an på.",[42,1656,1657],{},"Men lad mig være mere specifik end det.",[49,1659,1661],{"id":1660},"hurtigt-overblik","Hurtigt overblik",[1663,1664,1665,1680],"table",{},[1666,1667,1668],"thead",{},[1669,1670,1671,1674,1677],"tr",{},[1672,1673],"th",{},[1672,1675,1676],{},"React",[1672,1678,1679],{},"Vue",[1681,1682,1683,1697,1710,1723,1736,1749,1762,1775],"tbody",{},[1669,1684,1685,1691,1694],{},[1686,1687,1688],"td",{},[269,1689,1690],{},"Skabt af",[1686,1692,1693],{},"Meta (Facebook)",[1686,1695,1696],{},"Evan You \u002F community",[1669,1698,1699,1704,1707],{},[1686,1700,1701],{},[269,1702,1703],{},"Første release",[1686,1705,1706],{},"2013",[1686,1708,1709],{},"2014",[1669,1711,1712,1717,1720],{},[1686,1713,1714],{},[269,1715,1716],{},"Popularitet",[1686,1718,1719],{},"Størst community og jobmarked",[1686,1721,1722],{},"Større i Asien, voksende i Europa",[1669,1724,1725,1730,1733],{},[1686,1726,1727],{},[269,1728,1729],{},"Læringskurve",[1686,1731,1732],{},"Stejlere",[1686,1734,1735],{},"Blødere",[1669,1737,1738,1743,1746],{},[1686,1739,1740],{},[269,1741,1742],{},"Rendering",[1686,1744,1745],{},"JSX (JavaScript + HTML)",[1686,1747,1748],{},"Templates (HTML + directives)",[1669,1750,1751,1756,1759],{},[1686,1752,1753],{},[269,1754,1755],{},"State management",[1686,1757,1758],{},"Mange valg (Redux, Zustand, Jotai)",[1686,1760,1761],{},"Pinia (officiel)",[1669,1763,1764,1769,1772],{},[1686,1765,1766],{},[269,1767,1768],{},"Meta-framework",[1686,1770,1771],{},"Next.js",[1686,1773,1774],{},"Nuxt",[1669,1776,1777,1782,1785],{},[1686,1778,1779],{},[269,1780,1781],{},"TypeScript",[1686,1783,1784],{},"Førsteklasses support",[1686,1786,1787],{},"Førsteklasses support (fra Vue 3)",[49,1789,1791],{"id":1790},"hvor-react-udmærker-sig","Hvor React udmærker sig",[1793,1794,1796],"h3",{"id":1795},"større-økosystem-og-jobmarked","Større økosystem og jobmarked",[42,1798,1799],{},"React har det største økosystem i frontend-verdenen. Der er flere biblioteker, flere tutorials, flere svar på Stack Overflow, og langt flere jobopslag. Hvis du optimerer for karrieremuligheder, er React det sikre valg.",[42,1801,1802],{},"I Danmark er React stadig det mest efterspurgte frontend-framework på jobmarkedet — særligt i enterprise-segmentet.",[1793,1804,1806],{"id":1805},"fleksibilitet","Fleksibilitet",[42,1808,1809],{},"React er et bibliotek, ikke et framework. Det giver dig frihed til at vælge din egen state management, routing, og arkitektur. For erfarne teams der ved hvad de vil, er den fleksibilitet en styrke.",[1793,1811,1813],{"id":1812},"react-server-components-og-server-actions","React Server Components og Server Actions",[42,1815,1816],{},"React har med Server Components taget et stort skridt mod server-side rendering der føles naturligt. Det er en arkitektonisk innovation der giver mulighed for at blande server- og klient-kode på en måde der er unik for React.",[1793,1818,1820],{"id":1819},"react-native","React Native",[42,1822,1823],{},"Hvis du har brug for at bygge mobile apps, er React Native det største cross-platform framework. At dele kode mellem web og mobil med samme team er en reel fordel.",[49,1825,1827],{"id":1826},"hvor-vue-udmærker-sig","Hvor Vue udmærker sig",[1793,1829,1831],{"id":1830},"læringskurven-er-kortere","Læringskurven er kortere",[42,1833,1834],{},"Vue er lettere at lære for både nye og erfarne udviklere. Template-syntaksen er mere intuitiv end JSX, og Vue's Composition API giver den samme kraft som React Hooks, men med en blødere læringskurve.",[42,1836,1837],{},"Jeg har onboardet mange udviklere til begge frameworks, og Vue-teams er konsekvent produktive hurtigere.",[1793,1839,1841],{"id":1840},"bedre-batteries-included","Bedre \"batteries included\"",[42,1843,1844],{},"Vue har officielle løsninger for de fleste behov: Pinia til state management, Vue Router til routing, og Nuxt som meta-framework. Du slipper for at evaluere og vælge mellem 10 forskellige state management-biblioteker.",[1793,1846,1848],{"id":1847},"composition-api-er-elegant","Composition API er elegant",[42,1850,1851],{},"Vue 3's Composition API er efter min mening den reneste måde at skrive reaktiv logik på. Composables (Vue's svar på custom hooks) er nemmere at læse, teste og genbruge end React hooks, primært fordi du slipper for at tænke på dependency arrays og re-renders.",[1793,1853,1855],{"id":1854},"reaktivitetssystem","Reaktivitetssystem",[42,1857,1858,1859,1861,1862,875,1865,272,1868,1871],{},"Vue's fine-grained reaktivitet er smartere end React's re-rendering model. Vue ved præcis ",[266,1860,1581],{}," der har ændret sig og opdaterer kun det nødvendige. React re-renderer hele komponenten og dens børn, hvilket kræver at du tænker over ",[64,1863,1864],{},"useMemo",[64,1866,1867],{},"useCallback",[64,1869,1870],{},"React.memo"," for at undgå performance-problemer.",[49,1873,1875],{"id":1874},"hvornår-skal-du-vælge-react","Hvornår skal du vælge React?",[1268,1877,1878,1884,1890,1896,1902],{},[1271,1879,1880,1883],{},[269,1881,1882],{},"Dit team kender allerede React"," — Det vigtigste argument. Skift ikke framework for sjov",[1271,1885,1886,1889],{},[269,1887,1888],{},"Du har brug for React Native"," — Hvis mobil er en del af planen",[1271,1891,1892,1895],{},[269,1893,1894],{},"Du ansætter i et stort marked"," — Der er flere React-udviklere at vælge imellem",[1271,1897,1898,1901],{},[269,1899,1900],{},"Du bygger på et eksisterende React-økosystem"," — Mange virksomheder har interne React-biblioteker og tooling",[1271,1903,1904,1907],{},[269,1905,1906],{},"Enterprise med mange teams"," — React's fleksibilitet giver teams mulighed for at vælge deres egne patterns",[49,1909,1911],{"id":1910},"hvornår-skal-du-vælge-vue","Hvornår skal du vælge Vue?",[1268,1913,1914,1920,1926,1932,1938],{},[1271,1915,1916,1919],{},[269,1917,1918],{},"Du starter et nyt projekt"," — Vue's opinionated tilgang giver hurtigere time-to-market",[1271,1921,1922,1925],{},[269,1923,1924],{},"Dit team har blandet erfaring"," — Vue er lettere at lære for juniors og backendudviklere",[1271,1927,1928,1931],{},[269,1929,1930],{},"Du bygger et content-site eller marketing-site"," — Nuxt's statiske generering og SEO-support er fremragende",[1271,1933,1934,1937],{},[269,1935,1936],{},"Performance er kritisk"," — Vue's reaktivitetssystem giver bedre out-of-the-box performance",[1271,1939,1940,1943],{},[269,1941,1942],{},"Du vil have en sammenhængende stack"," — Vue + Pinia + Nuxt er en tight integration uden compatibility-sorger",[49,1945,1947],{"id":1946},"hvad-med-andre-frameworks","Hvad med andre frameworks?",[1793,1949,1951],{"id":1950},"sveltesveltekit","Svelte\u002FSvelteKit",[42,1953,1954],{},"Svelte er et fantastisk framework der kompilerer væk sit eget runtime. Det giver ekstremt let output og en behagelig udvikleroplevelse. Men økosystemet er stadig lille sammenlignet med React og Vue, og jobmarkedet er begrænset.",[1793,1956,1958],{"id":1957},"angular","Angular",[42,1960,1961],{},"Angular er stadig stort i enterprise-verdenen, særligt i backend-tunge organisationer. Men i den bredere frontend-verden er det blevet mindre relevant.",[1793,1963,1965],{"id":1964},"solid-qwik-og-andre","Solid, Qwik og andre",[42,1967,1968],{},"Spændende teknologier der løser reelle problemer, men for de fleste projekter i 2026 er React eller Vue stadig det pragmatiske valg.",[49,1970,1972],{"id":1971},"min-personlige-præference","Min personlige præference",[42,1974,1975,1976,1979],{},"Hvis jeg starter et nyt projekt i dag og har frit valg, vælger jeg ",[269,1977,1978],{},"Vue 3 med Nuxt",". Det er det jeg kender bedst, og Composition API + Nuxt's DX er efter min mening den bedste udvikleroplevelse i frontend-verdenen.",[42,1981,1982],{},"Men hvis et team allerede bruger React, eller hvis projektet kræver React Native, ville jeg aldrig foreslå at skifte til Vue. Det vigtigste er ikke hvilket framework du vælger — det er at du bruger det godt.",[49,1984,1986],{"id":1985},"konklusion","Konklusion",[42,1988,1989],{},"Både React og Vue er modne, velunderstøttede frameworks der kan bygge hvad som helst. I 2026 er forskellen mellem dem mindre end nogensinde — begge har TypeScript-support, server-side rendering, og store økosystemer.",[42,1991,1992],{},"Vælg det der passer til dit team, dit projekt og dine krav. Ikke det der er mest hypet på Twitter.",[42,1994,1995],{},"Har du spørgsmål om valg af framework til dit næste projekt? Skriv til mig — jeg rådgiver gerne baseret på dine specifikke behov.",{"title":62,"searchDepth":88,"depth":88,"links":1997},[1998,1999,2000,2006,2012,2013,2014,2019,2020],{"id":1647,"depth":88,"text":1648},{"id":1660,"depth":88,"text":1661},{"id":1790,"depth":88,"text":1791,"children":2001},[2002,2003,2004,2005],{"id":1795,"depth":94,"text":1796},{"id":1805,"depth":94,"text":1806},{"id":1812,"depth":94,"text":1813},{"id":1819,"depth":94,"text":1820},{"id":1826,"depth":88,"text":1827,"children":2007},[2008,2009,2010,2011],{"id":1830,"depth":94,"text":1831},{"id":1840,"depth":94,"text":1841},{"id":1847,"depth":94,"text":1848},{"id":1854,"depth":94,"text":1855},{"id":1874,"depth":88,"text":1875},{"id":1910,"depth":88,"text":1911},{"id":1946,"depth":88,"text":1947,"children":2015},[2016,2017,2018],{"id":1950,"depth":94,"text":1951},{"id":1957,"depth":94,"text":1958},{"id":1964,"depth":94,"text":1965},{"id":1971,"depth":88,"text":1972},{"id":1985,"depth":88,"text":1986},"Jeg har brugt både React og Vue professionelt i mange år. Her er min ærlige sammenligning i 2026 — uden fanboy-bias.",{},"\u002Fblog\u002Freact-vs-vue-i-2026",{"title":1642,"description":2021},"React vs Vue sammenligning fra en udvikler med 19+ års erfaring i begge. Lær forskelle, fordele og ulemper, og hvornår du skal vælge hvad i 2026.","React vs Vue i 2026 — Hvilket framework skal du vælge?","react-vs-vue-i-2026","blog\u002Freact-vs-vue-i-2026",[1636,1635,61,923,2030],"framework","H2b6Ass22LWn2bRwdep2gTqn61AIsH5pFcDxpAYYhvQ",{"id":2033,"title":2034,"body":2035,"date":1624,"description":2630,"extension":913,"meta":2631,"navigation":915,"noindex":8,"path":2632,"seo":2633,"seoDescription":2634,"seoTitle":2635,"slug":2636,"stem":2637,"tags":2638,"thumbnail":1638,"updated":1624,"__hash__":2642},"blog\u002Fblog\u002Fsaadan-vaelger-du-den-rigtige-tech-stack.md","Sådan vælger du den rigtige tech stack i 2026",{"type":39,"value":2036,"toc":2603},[2037,2041,2044,2047,2079,2082,2086,2089,2093,2096,2099,2104,2115,2119,2126,2129,2133,2150,2154,2157,2161,2175,2179,2182,2186,2197,2201,2205,2212,2216,2219,2223,2226,2230,2233,2237,2240,2244,2247,2251,2277,2280,2284,2308,2312,2342,2345,2349,2372,2375,2379,2386,2391,2405,2410,2424,2429,2440,2445,2456,2460,2463,2469,2475,2481,2487,2491,2494,2500,2506,2512,2515,2519,2522,2577,2580,2584,2587,2593,2595],[49,2038,2040],{"id":2039},"hvorfor-tech-stack-valget-er-vigtigere-end-du-tror","Hvorfor tech stack-valget er vigtigere end du tror",[42,2042,2043],{},"At vælge en tech stack er ikke bare et teknisk valg — det er en forretningsbeslutning. Jeg har set virksomheder spilde hundredtusindvis af kroner fordi de valgte den forkerte teknologi. Og jeg har set små teams levere hurtigere end store teams, fordi de valgte klogt.",[42,2045,2046],{},"Her er hvad der står på spil:",[1268,2048,2049,2055,2061,2067,2073],{},[1271,2050,2051,2054],{},[269,2052,2053],{},"Udviklingsomkostninger"," — Den forkerte stack kan fordoble din udviklingstid",[1271,2056,2057,2060],{},[269,2058,2059],{},"Hastighed til markedet"," — Rigtig teknologi betyder hurtigere launch",[1271,2062,2063,2066],{},[269,2064,2065],{},"Rekruttering"," — Kan du finde udviklere der kender stakken? Og hvad koster de?",[1271,2068,2069,2072],{},[269,2070,2071],{},"Skalerbarhed"," — Kan teknologien vokse med din forretning?",[1271,2074,2075,2078],{},[269,2076,2077],{},"Vedligeholdelse"," — Hvad koster det at holde systemet kørende om 3-5 år?",[42,2080,2081],{},"I min karriere — fra Grundfos og Vestas til Playable og Harness, og nu som Technical Director hos Checkmate.dk — har jeg truffet denne beslutning mange gange. Her er det framework jeg bruger.",[49,2083,2085],{"id":2084},"mit-framework-til-at-evaluere-tech-stack","Mit framework til at evaluere tech stack",[42,2087,2088],{},"Før du overhovedet tænker på specifikke teknologier, skal du stille de rigtige spørgsmål. Her er min tjekliste:",[1793,2090,2092],{"id":2091},"_1-hvad-kan-dit-team","1. Hvad kan dit team?",[42,2094,2095],{},"Det vigtigste spørgsmål. Punkt. Teknologi dit team allerede kender vil næsten altid slå \"den bedste\" teknologi dit team ikke kender.",[42,2097,2098],{},"Hos Grundfos arvede vi et projekt bygget i et framework teamet ikke havde erfaring med. Resultatet? 6 måneders forsinkelse og en komplet omskrivning. Havde nogen spurgt \"hvad kan teamet?\", var det aldrig sket.",[42,2100,2101],{},[269,2102,2103],{},"Spørgsmål du skal stille:",[1268,2105,2106,2109,2112],{},[1271,2107,2108],{},"Hvad har teamet erfaring med?",[1271,2110,2111],{},"Hvad kan de lære realistisk inden for projektets tidsramme?",[1271,2113,2114],{},"Er der nogen i teamet der kan drive den tekniske retning?",[1793,2116,2118],{"id":2117},"_2-hvad-kræver-projektet-reelt","2. Hvad kræver projektet reelt?",[42,2120,2121,2122,2125],{},"Ikke hvad det ",[266,2123,2124],{},"måske"," kræver om 3 år. Hvad kræver det nu?",[42,2127,2128],{},"Mange teams over-engineerer fordi de forbereder sig på skaleringsudfordringer de aldrig får. Byg til dine næste 12 måneder, ikke til et hypotetisk scenarie.",[42,2130,2131],{},[269,2132,2103],{},[1268,2134,2135,2138,2141,2144,2147],{},[1271,2136,2137],{},"Hvor mange brugere skal systemet håndtere?",[1271,2139,2140],{},"Hvor kompleks er forretningslogikken?",[1271,2142,2143],{},"Er der real-time krav?",[1271,2145,2146],{},"Skal det integreres med eksisterende systemer?",[1271,2148,2149],{},"Hvad er tidsplanen?",[1793,2151,2153],{"id":2152},"_3-hvor-modent-er-økosystemet","3. Hvor modent er økosystemet?",[42,2155,2156],{},"Et framework kan være teknisk overlegent, men hvis der ikke er gode biblioteker, dokumentation og community support, betaler du prisen i udvikling.",[42,2158,2159],{},[269,2160,2103],{},[1268,2162,2163,2166,2169,2172],{},[1271,2164,2165],{},"Hvor gammel er teknologien? (Under 2 år = risiko)",[1271,2167,2168],{},"Er der aktiv udvikling og regelmæssige releases?",[1271,2170,2171],{},"Hvor stort er communityet?",[1271,2173,2174],{},"Er der gode biblioteker til dine kernebehov?",[1793,2176,2178],{"id":2177},"_4-kan-du-ansætte-folk","4. Kan du ansætte folk?",[42,2180,2181],{},"I Danmark er der stor forskel på hvor mange udviklere der kan de forskellige teknologier. Det skal du tænke ind fra start.",[42,2183,2184],{},[269,2185,2103],{},[1268,2187,2188,2191,2194],{},[1271,2189,2190],{},"Hvor mange ledige stillinger bruger denne teknologi i dit område?",[1271,2192,2193],{},"Hvad er lønniveauet for udviklere med denne kompetence?",[1271,2195,2196],{},"Kan du træne eksisterende medarbejdere?",[49,2198,2200],{"id":2199},"de-5-mest-almindelige-fejl","De 5 mest almindelige fejl",[1793,2202,2204],{"id":2203},"fejl-1-at-vælge-baseret-på-hype","Fejl 1: At vælge baseret på hype",[42,2206,2207,2208,2211],{},"\"Vi skal bruge ",[67,2209,2210],{},"nyeste framework"," fordi alle taler om det.\" Nej. Medmindre der er en reel teknisk grund, er popularitet på Twitter ikke et argument.",[1793,2213,2215],{"id":2214},"fejl-2-over-engineering-fra-dag-1","Fejl 2: Over-engineering fra dag 1",[42,2217,2218],{},"Du bygger en marketing-side og vælger microservices med Kubernetes? Stop. Start simpelt, tilføj kompleksitet når du har brug for det.",[1793,2220,2222],{"id":2221},"fejl-3-at-ignorere-teamets-kompetencer","Fejl 3: At ignorere teamets kompetencer",[42,2224,2225],{},"Jeg gentager det gerne: dit teams erfaring trækker mere end noget frameworks features. En middelmådig stack i hænderne på et godt team slår en perfekt stack i hænderne på et team der fumler.",[1793,2227,2229],{"id":2228},"fejl-4-at-lade-en-enkelt-udvikler-diktere-valget","Fejl 4: At lade en enkelt udvikler diktere valget",[42,2231,2232],{},"Undgå \"resume-driven development\" — hvor nogen vælger en teknologi fordi de gerne vil have den på deres CV, ikke fordi den er rigtig for projektet.",[1793,2234,2236],{"id":2235},"fejl-5-at-glemme-exit-strategien","Fejl 5: At glemme exit-strategien",[42,2238,2239],{},"Hvad sker der hvis frameworket dør, eller den primære maintainer stopper? Kan du migrere? Hvor låst er du inde?",[49,2241,2243],{"id":2242},"konkrete-anbefalinger-efter-projekttype","Konkrete anbefalinger efter projekttype",[42,2245,2246],{},"Her er mine anbefalinger baseret på det jeg ser virke i 2026. Husk: disse er udgangspunkter, ikke dogmer.",[1793,2248,2250],{"id":2249},"marketing-site-portfolio","Marketing-site \u002F portfolio",[1268,2252,2253,2259,2265,2271],{},[1271,2254,2255,2258],{},[269,2256,2257],{},"Frontend:"," Nuxt 3 (Vue) eller Next.js (React) med statisk generering",[1271,2260,2261,2264],{},[269,2262,2263],{},"CMS:"," Headless CMS som Storyblok, Contentful eller Sanity",[1271,2266,2267,2270],{},[269,2268,2269],{},"Hosting:"," Netlify eller Vercel",[1271,2272,2273,2276],{},[269,2274,2275],{},"Hvorfor:"," Hurtig udvikling, god SEO ud af boksen, billig hosting, ingen server at vedligeholde",[42,2278,2279],{},"Dette site (nickychristensen.dk) er bygget præcis sådan — Nuxt 3, statisk generering, deployeret på Netlify. Det er hurtigt, billigt at drive og nemt at vedligeholde.",[1793,2281,2283],{"id":2282},"e-commerce-webshop","E-commerce \u002F webshop",[1268,2285,2286,2292,2297,2303],{},[1271,2287,2288,2291],{},[269,2289,2290],{},"Platform:"," Shopify (simpel) eller headless Shopify \u002F Medusa med custom frontend",[1271,2293,2294,2296],{},[269,2295,2257],{}," Nuxt eller Next.js",[1271,2298,2299,2302],{},[269,2300,2301],{},"Betalinger:"," Stripe eller Adyen",[1271,2304,2305,2307],{},[269,2306,2275],{}," E-commerce er et løst problem. Brug en platform der håndterer det tunge (betaling, lager, ordre), og byg custom UI ovenpå",[1793,2309,2311],{"id":2310},"saas-produkt","SaaS-produkt",[1268,2313,2314,2319,2325,2331,2337],{},[1271,2315,2316,2318],{},[269,2317,2257],{}," Vue 3 + Pinia eller React + Zustand\u002FJotai",[1271,2320,2321,2324],{},[269,2322,2323],{},"Backend:"," Node.js (Express\u002FFastify) eller Go for høj performance",[1271,2326,2327,2330],{},[269,2328,2329],{},"Database:"," PostgreSQL med Prisma eller Drizzle ORM",[1271,2332,2333,2336],{},[269,2334,2335],{},"Auth:"," Auth0, Clerk eller Supabase Auth",[1271,2338,2339,2341],{},[269,2340,2275],{}," Her skal du tænke langsigtet. Vælg det dit team er stærkest i, og prioriter god arkitektur fra start",[42,2343,2344],{},"Hos Playable byggede vi SaaS-produktet i Vue med en Node-backend. Det fungerede fremragende fordi teamet kendte stakken.",[1793,2346,2348],{"id":2347},"enterprise-applikation","Enterprise-applikation",[1268,2350,2351,2356,2361,2367],{},[1271,2352,2353,2355],{},[269,2354,2257],{}," React (størst talentpool) eller Vue 3 med TypeScript",[1271,2357,2358,2360],{},[269,2359,2323],{}," .NET, Java Spring eller Node.js — afhængig af organisationens kompetencer",[1271,2362,2363,2366],{},[269,2364,2365],{},"Infrastruktur:"," Cloud (AWS\u002FAzure\u002FGCP) med CI\u002FCD",[1271,2368,2369,2371],{},[269,2370,2275],{}," Her handler det om stabilitet, skalerbarhed og muligheden for at ansætte. Vælg modne teknologier med lang track record",[42,2373,2374],{},"Hos Vestas og Grundfos oplevede jeg værdien af at vælge kedeligt og gennemprøvet. Nye og spændende teknologier har en højere pris når hundredevis af udviklere skal arbejde med dem.",[49,2376,2378],{"id":2377},"vue-vs-react-vs-andre-frameworks-i-2026","Vue vs React vs andre frameworks i 2026",[42,2380,2381,2382,2385],{},"Jeg har skrevet en hel artikel om ",[387,2383,2384],{"href":2023},"React vs Vue i 2026",", men her er den korte version:",[42,2387,2388],{},[269,2389,2390],{},"Vælg Vue hvis:",[1268,2392,2393,2396,2399,2402],{},[1271,2394,2395],{},"Dit team er mindre (2-10 udviklere)",[1271,2397,2398],{},"Du vil have en blødere læringskurve",[1271,2400,2401],{},"Du foretrækker convention over configuration",[1271,2403,2404],{},"Du bygger med Nuxt og vil have \"batteries included\"",[42,2406,2407],{},[269,2408,2409],{},"Vælg React hvis:",[1268,2411,2412,2415,2418,2421],{},[1271,2413,2414],{},"Du ansætter i stor skala og har brug for den største talentpool",[1271,2416,2417],{},"Du har brug for React Native til mobil",[1271,2419,2420],{},"Dit team allerede kender React",[1271,2422,2423],{},"Du arbejder i et enterprise-miljø med eksisterende React-infrastruktur",[42,2425,2426],{},[269,2427,2428],{},"Overvej Svelte\u002FSvelteKit hvis:",[1268,2430,2431,2434,2437],{},[1271,2432,2433],{},"Du bygger et mindre projekt med fokus på performance",[1271,2435,2436],{},"Dit team er nysgerrige og villige til at lære noget nyt",[1271,2438,2439],{},"Du ikke har brug for et kæmpe økosystem af tredjepartsbiblioteker",[42,2441,2442],{},[269,2443,2444],{},"Angular er stadig relevant hvis:",[1268,2446,2447,2450,2453],{},[1271,2448,2449],{},"Du arbejder i et stort enterprise-miljø (særligt .NET-shops)",[1271,2451,2452],{},"Dit team kender det allerede",[1271,2454,2455],{},"Du værdsætter streng struktur og opinionated arkitektur",[49,2457,2459],{"id":2458},"backend-overvejelser","Backend-overvejelser",[42,2461,2462],{},"Frontend får al opmærksomheden, men backend-valget er mindst lige så vigtigt.",[42,2464,2465,2468],{},[269,2466,2467],{},"Node.js"," er det oplagte valg hvis dit team allerede skriver JavaScript\u002FTypeScript. Du får kodedeling mellem frontend og backend, og der er et enormt økosystem.",[42,2470,2471,2474],{},[269,2472,2473],{},"Headless CMS"," (Storyblok, Contentful, Sanity) er rigtige for content-drevne sites. Lad redaktørerne arbejde i en editor de forstår, og byg en custom frontend.",[42,2476,2477,2480],{},[269,2478,2479],{},"Backend-as-a-Service"," (Supabase, Firebase) er fantastisk til prototyper og mindre SaaS-produkter. Du får database, auth og API uden at bygge det selv. Men vær opmærksom på vendor lock-in.",[42,2482,2483,2486],{},[269,2484,2485],{},"Go eller Rust"," giver mening hvis performance er kritisk og dit team har kompetencen. For de fleste projekter er Node.js rigeligt.",[49,2488,2490],{"id":2489},"ai-værktøjers-rolle-i-tech-stack-valget-2026-perspektiv","AI-værktøjers rolle i tech stack-valget (2026-perspektiv)",[42,2492,2493],{},"I 2026 har AI-assistenter som GitHub Copilot, Cursor og Claude fundamentalt ændret hvordan vi skriver kode. Det påvirker også tech stack-valget:",[42,2495,2496,2499],{},[269,2497,2498],{},"Større økosystem = bedre AI-support."," AI-modeller er trænet på offentlig kode. React og Vue har mere træningsdata end nicheframeworks, hvilket betyder bedre autokomplettering og fejlfinding.",[42,2501,2502,2505],{},[269,2503,2504],{},"AI reducerer læringskurven."," Det er nemmere at komme i gang med et nyt framework når AI kan forklare kode, generere boilerplate og finde fejl. Men det erstatter ikke dyb forståelse — du skal stadig vide hvad du laver.",[42,2507,2508,2511],{},[269,2509,2510],{},"Vælg ikke kun baseret på AI-support."," Det er fristende at vælge den teknologi AI er bedst til at generere. Men AI er et værktøj, ikke en arkitekt. De fundamentale kriterier (team, krav, økosystem, rekruttering) er stadig vigtigere.",[42,2513,2514],{},"Mit råd: Brug AI som accelerator, men lad det ikke diktere dine teknologiske valg.",[49,2516,2518],{"id":2517},"din-tjekliste","Din tjekliste",[42,2520,2521],{},"Før du træffer den næste tech stack-beslutning, gå denne liste igennem:",[1268,2523,2526,2535,2541,2547,2553,2559,2565,2571],{"className":2524},[2525],"contains-task-list",[1271,2527,2530,2534],{"className":2528},[2529],"task-list-item",[2531,2532],"input",{"disabled":915,"type":2533},"checkbox"," Hvad kan dit team allerede?",[1271,2536,2538,2540],{"className":2537},[2529],[2531,2539],{"disabled":915,"type":2533}," Hvad kræver projektet reelt (ikke hypotetisk)?",[1271,2542,2544,2546],{"className":2543},[2529],[2531,2545],{"disabled":915,"type":2533}," Er økosystemet modent nok?",[1271,2548,2550,2552],{"className":2549},[2529],[2531,2551],{"disabled":915,"type":2533}," Kan du ansætte folk med denne kompetence?",[1271,2554,2556,2558],{"className":2555},[2529],[2531,2557],{"disabled":915,"type":2533}," Har du overvejet vedligeholdelsesomkostninger?",[1271,2560,2562,2564],{"className":2561},[2529],[2531,2563],{"disabled":915,"type":2533}," Er du sikker på du ikke over-engineerer?",[1271,2566,2568,2570],{"className":2567},[2529],[2531,2569],{"disabled":915,"type":2533}," Har du en exit-strategi?",[1271,2572,2574,2576],{"className":2573},[2529],[2531,2575],{"disabled":915,"type":2533}," Har du spurgt teamet (og ikke kun den mest højlydte udvikler)?",[42,2578,2579],{},"Hvis du kan svare ja til de fleste af disse, er du på rette vej.",[49,2581,2583],{"id":2582},"afsluttende-tanker","Afsluttende tanker",[42,2585,2586],{},"I 19+ år som udvikler og nu som Technical Director har jeg lært en ting: den bedste tech stack er den der lader dit team levere værdi hurtigt og pålideligt. Det lyder kedeligt. Det er det også. Men det virker.",[42,2588,2589,2590,290],{},"Stop med at lede efter den perfekte teknologi. Find den rigtige teknologi ",[266,2591,2592],{},"for dig, dit team og dit projekt",[1600,2594],{},[42,2596,2597],{},[266,2598,2599,2600,2602],{},"Har du brug for sparring på dit næste teknologivalg? Jeg hjælper virksomheder med at træffe de rigtige tekniske beslutninger. ",[387,2601,1608],{"href":1607}," for en uforpligtende snak.",{"title":62,"searchDepth":88,"depth":88,"links":2604},[2605,2606,2612,2619,2625,2626,2627,2628,2629],{"id":2039,"depth":88,"text":2040},{"id":2084,"depth":88,"text":2085,"children":2607},[2608,2609,2610,2611],{"id":2091,"depth":94,"text":2092},{"id":2117,"depth":94,"text":2118},{"id":2152,"depth":94,"text":2153},{"id":2177,"depth":94,"text":2178},{"id":2199,"depth":88,"text":2200,"children":2613},[2614,2615,2616,2617,2618],{"id":2203,"depth":94,"text":2204},{"id":2214,"depth":94,"text":2215},{"id":2221,"depth":94,"text":2222},{"id":2228,"depth":94,"text":2229},{"id":2235,"depth":94,"text":2236},{"id":2242,"depth":88,"text":2243,"children":2620},[2621,2622,2623,2624],{"id":2249,"depth":94,"text":2250},{"id":2282,"depth":94,"text":2283},{"id":2310,"depth":94,"text":2311},{"id":2347,"depth":94,"text":2348},{"id":2377,"depth":88,"text":2378},{"id":2458,"depth":88,"text":2459},{"id":2489,"depth":88,"text":2490},{"id":2517,"depth":88,"text":2518},{"id":2582,"depth":88,"text":2583},"En praktisk guide til at vælge den rigtige teknologi til dit næste projekt. Baseret på 19+ års erfaring med alt fra enterprise-løsninger til startups.",{},"\u002Fblog\u002Fsaadan-vaelger-du-den-rigtige-tech-stack",{"title":2034,"description":2630},"Lær at vælge den rigtige tech stack i 2026. Praktisk framework med konkrete anbefalinger til marketing sites, webshops, SaaS og enterprise.","Sådan vælger du den rigtige tech stack i 2026 — Praktisk guide","saadan-vaelger-du-den-rigtige-tech-stack","blog\u002Fsaadan-vaelger-du-den-rigtige-tech-stack",[2639,2640,923,1635,1636,2641],"tech-stack","strategi","webudvikling","YEW7CxxBTR1_9m_6gqtGIaVBwqx6qvpwQQFhlWkwHs8",1775218039726]