[{"data":1,"prerenderedAt":2777},["ShallowReactive",2],{"post:\u002Fblog\u002Fvuex-kilka-przemyslen-po-roku-kodowania":3,"surround:\u002Fblog\u002Fvuex-kilka-przemyslen-po-roku-kodowania":1965,"related:\u002Fblog\u002Fvuex-kilka-przemyslen-po-roku-kodowania":1976,"categories":2009,"post-author:maciek-walczak":2077,"tags":2091},{"id":4,"title":5,"author":6,"body":7,"categories":1941,"comments":595,"cover":1944,"coverAlt":1945,"date":1946,"description":1947,"draft":1948,"extension":1949,"hidden":1948,"meta":1950,"navigation":595,"path":1951,"readingTime":922,"redirectFrom":1952,"seo":1953,"sitemap":1954,"slug":1957,"stem":1958,"tags":1959,"updated":1962,"wpId":1963,"__hash__":1964},"blog\u002Fblog\u002F2019-08-28-vuex-kilka-przemyslen-po-roku-kodowania.md","VUEX – kilka przemyśleń po roku kodowania","maciek-walczak",{"type":8,"value":9,"toc":1925},"minimark",[10,15,24,28,31,34,39,48,51,55,58,66,69,81,94,97,100,238,241,311,315,318,326,337,343,354,384,387,471,475,485,547,550,564,567,645,668,689,695,698,762,775,839,846,935,939,955,978,1043,1051,1166,1170,1173,1176,1268,1281,1285,1288,1296,1304,1307,1310,1339,1343,1354,1362,1365,1369,1372,1378,1384,1499,1510,1513,1789,1805,1809,1818,1821,1869,1872,1879,1886,1892,1899,1902,1908,1912,1918,1921],[11,12,14],"h2",{"id":13},"co-to-jest-vue-i-vuex","Co to jest Vue i Vuex?",[16,17,18,19],"p",{},"Jeśli nie wiesz co to jest Vue lub Vuex – to zapraszam najpierw do obejrzenia nagrania z mojej prezentacji w ramach Tech3Camp’a: ",[20,21,23],"a",{"href":22},"\u002Fblog\u002Ftech-3camp-jest-juz-nagranie-video","https:\u002F\u002Fnowoczesny-frontend.pl\u002Ftech-3camp-jest-juz-nagranie-video",[11,25,27],{"id":26},"kilka-przemyśleń","Kilka przemyśleń…",[16,29,30],{},"Mniej więcej od roku w różnych projektach firmowych używamy tandemu Vue.js + Vuex i muszę powiedzieć, że im dalej w las tym ciekawiej 🙂 A dokładniej, im więcej używam zarządzania stanem aplikacji poprzez Vuex, tym lepiej cały ten flow układa się w głowie i odkrywam coraz wydajniejsze sposoby na stworzenie re-używalnych i bardzo prostych w swojej konstrukcji komponentów.",[16,32,33],{},"W trakcie tego roku obejrzałem całkiem sporo filmów z różnych prezentacji, przeczytałem też wiele artykułów o użyciu Vuexa, jednak nic nie zastąpi praktyki i własnych doświadczeń podczas rozwiązywania bieżących problemów w kodzie. Na tej podstawie postanowiłem stworzyć dość luźną listę własnych wniosków i przemyśleń odnośnie użycia Vuexa w projekcie. No to lecimy…",[35,36,38],"h3",{"id":37},"czy-na-pewno-potrzebuję-vuexa","Czy na pewno potrzebuję Vuex’a?",[40,41,42,45],"blockquote",{},[16,43,44],{},"Flux libraries are like glasses: you’ll know when you need them.",[16,46,47],{},"–Dan Abramov",[16,49,50],{},"O ile użycie Vuexa w przypadku ekstremalnie małego projektu oczywiście nie ma większego sensu, to jednak jeśli mamy choć kilka osobnych komponentów, zastosowanie biblioteki do zarządzania stanem aplikacji jest jak najbardziej wskazane. To jest decyzja bardziej na poziomie architektury – jeśli użyjemy Vuexa, możemy w dużej mierze pozbyć się bezpośredniej komunikacji między komponentami za pomocą emitowanych eventów. Vuex – dzięki wsparciu reaktywności – zapewni automatyczne przerenderowanie korzystających z niego komponentów.",[35,52,54],{"id":53},"moduły","Moduły!",[16,56,57],{},"Dokumentacja i przykłady bardzo często prezentują użycie pojedynczego store’a, ale w prawdziwej aplikacji takie podejście jest mało efektywne. Dobrą praktyką jest dzielenie projektu na moduły, dlaczego więc nie zastosować tego do samego Vuexa?",[16,59,60,61],{},"Podział na moduły całkiem dobrze opisuje dokumentacja: ",[20,62,63],{"href":63,"rel":64},"https:\u002F\u002Fvuex.vuejs.org\u002Fguide\u002Fmodules.html#modules",[65],"nofollow",[16,67,68],{},"W praktyce podział store’a na moduły znacząco zwiększa czytelność i ogranicza objętość kodu. Takie podejście ułatwia też pracę w teamie – każdy programista może się skupić na swoim fragmencie aplikacji, bez tworzenia punktów ewentualnych kolizji.",[16,70,71,72,76,77],{},"W tym miejscu trzeba jednak pamiętać o jednym – taki podział na moduły jest dość umowny – w praktyce wszystkie akcje, mutacje i gettery są rejestrowane we wspólnej przestrzeni (tylko ",[73,74,75],"code",{},"state"," jest prywatny dla każdego modułu) – co wymusza stosowanie dla nich unikalnych nazw. Można tego uniknąć wprowadzając dodatkowo przestrzenie nazw (namespace) – zagadnienie to jest opisane tu: ",[20,78,79],{"href":79,"rel":80},"https:\u002F\u002Fvuex.vuejs.org\u002Fguide\u002Fmodules.html#namespacing",[65],[16,82,83,84,86,87,89,90,93],{},"Warto też pamiętać, że czasem może zajść potrzeba komunikacji pomiędzy modułami Vuexa – gdy potrzebujemy z poziomu jednego modułu pobrać zawartość ",[73,85,75],{}," z innego modułu lub z głównego ",[73,88,75],{}," – w tym wypadku można to zrobić za pomocą ",[73,91,92],{},"rootState",".",[16,95,96],{},"Spójrzmy na przykłady z dokumentacji.",[16,98,99],{},"Akcja:",[101,102,107],"pre",{"className":103,"code":104,"language":105,"meta":106,"style":106},"language-js shiki shiki-themes github-light-default github-light-default github-dark-default","const moduleA = {\n  \u002F\u002F ...\n  actions: {\n    incrementIfOddOnRootSum ({ state, commit, rootState }) {\n      if ((state.count + rootState.count) % 2 === 1) {\n        commit('increment')\n      }\n    }\n  }\n}\n","js","",[73,108,109,129,136,142,168,198,214,220,226,232],{"__ignoreMap":106},[110,111,114,118,122,125],"span",{"class":112,"line":113},"line",1,[110,115,117],{"class":116},"s5nWI","const",[110,119,121],{"class":120},"sDkKp"," moduleA",[110,123,124],{"class":116}," =",[110,126,128],{"class":127},"s_bOF"," {\n",[110,130,132],{"class":112,"line":131},2,[110,133,135],{"class":134},"s9BHP","  \u002F\u002F ...\n",[110,137,139],{"class":112,"line":138},3,[110,140,141],{"class":127},"  actions: {\n",[110,143,145,149,152,155,158,161,163,165],{"class":112,"line":144},4,[110,146,148],{"class":147},"sd69k","    incrementIfOddOnRootSum",[110,150,151],{"class":127}," ({ ",[110,153,75],{"class":154},"sQJ0q",[110,156,157],{"class":127},", ",[110,159,160],{"class":154},"commit",[110,162,157],{"class":127},[110,164,92],{"class":154},[110,166,167],{"class":127}," }) {\n",[110,169,171,174,177,180,183,186,189,192,195],{"class":112,"line":170},5,[110,172,173],{"class":116},"      if",[110,175,176],{"class":127}," ((state.count ",[110,178,179],{"class":116},"+",[110,181,182],{"class":127}," rootState.count) ",[110,184,185],{"class":116},"%",[110,187,188],{"class":120}," 2",[110,190,191],{"class":116}," ===",[110,193,194],{"class":120}," 1",[110,196,197],{"class":127},") {\n",[110,199,201,204,207,211],{"class":112,"line":200},6,[110,202,203],{"class":147},"        commit",[110,205,206],{"class":127},"(",[110,208,210],{"class":209},"s-BGO","'increment'",[110,212,213],{"class":127},")\n",[110,215,217],{"class":112,"line":216},7,[110,218,219],{"class":127},"      }\n",[110,221,223],{"class":112,"line":222},8,[110,224,225],{"class":127},"    }\n",[110,227,229],{"class":112,"line":228},9,[110,230,231],{"class":127},"  }\n",[110,233,235],{"class":112,"line":234},10,[110,236,237],{"class":127},"}\n",[16,239,240],{},"Getter:",[101,242,244],{"className":103,"code":243,"language":105,"meta":106,"style":106},"const moduleA = {\n  \u002F\u002F ...\n  getters: {\n    sumWithRootCount (state, getters, rootState) {\n      return state.count + rootState.count\n    }\n  }\n}\n",[73,245,246,256,260,265,286,299,303,307],{"__ignoreMap":106},[110,247,248,250,252,254],{"class":112,"line":113},[110,249,117],{"class":116},[110,251,121],{"class":120},[110,253,124],{"class":116},[110,255,128],{"class":127},[110,257,258],{"class":112,"line":131},[110,259,135],{"class":134},[110,261,262],{"class":112,"line":138},[110,263,264],{"class":127},"  getters: {\n",[110,266,267,270,273,275,277,280,282,284],{"class":112,"line":144},[110,268,269],{"class":147},"    sumWithRootCount",[110,271,272],{"class":127}," (",[110,274,75],{"class":154},[110,276,157],{"class":127},[110,278,279],{"class":154},"getters",[110,281,157],{"class":127},[110,283,92],{"class":154},[110,285,197],{"class":127},[110,287,288,291,294,296],{"class":112,"line":170},[110,289,290],{"class":116},"      return",[110,292,293],{"class":127}," state.count ",[110,295,179],{"class":116},[110,297,298],{"class":127}," rootState.count\n",[110,300,301],{"class":112,"line":200},[110,302,225],{"class":127},[110,304,305],{"class":112,"line":216},[110,306,231],{"class":127},[110,308,309],{"class":112,"line":222},[110,310,237],{"class":127},[35,312,314],{"id":313},"destrukturyzacja-obiektu-kontekstu","Destrukturyzacja obiektu kontekstu",[16,316,317],{},"W powyższym przykładzie akcji widzimy taki fragment kodu:",[101,319,324],{"className":320,"code":322,"language":323},[321],"language-text","incrementIfOddOnRootSum ({ state, commit, rootState }) ...\n","text",[73,325,322],{"__ignoreMap":106},[16,327,328,329,332,333,336],{},"Generalnie akcja Vuex’owa przyjmuje 2 parametry: ",[73,330,331],{},"context"," i ",[73,334,335],{},"data",":",[101,338,341],{"className":339,"code":340,"language":323},[321],"incrementIfOddOnRootSum (context, data) ...\n",[73,342,340],{"__ignoreMap":106},[16,344,345,346,348,349,353],{},"W tym przypadku ",[73,347,331],{}," jest obiektem, który można zdestrukturyzować (",[20,350,351],{"href":351,"rel":352},"https:\u002F\u002Fgithub.com\u002Flukehoban\u002Fes6features#destructuring",[65],") i użyć z niego tylko te elementy, których potrzebujemy:",[355,356,357,363,369,374,379],"ul",{},[358,359,360,362],"li",{},[73,361,160],{}," – uruchomienie mutacji",[358,364,365,368],{},[73,366,367],{},"dispatch"," – uruchomienie akcji",[358,370,371,373],{},[73,372,279],{}," – wywołanie gettera",[358,375,376,378],{},[73,377,75],{}," – dostęp do zawartości state’a",[358,380,381,383],{},[73,382,92],{}," – dostęp do głównego state’a lub dalszych modułów",[16,385,386],{},"Bez użycia destrukturyzacji poprzedni kod wyglądałby tak:",[101,388,390],{"className":103,"code":389,"language":105,"meta":106,"style":106},"const moduleA = {\n  \u002F\u002F ...\n  actions: {\n    incrementIfOddOnRootSum (context) {\n      if ((context.state.count + context.rootState.count) % 2 === 1) {\n        context.commit('increment')\n      }\n    }\n  }\n}\n",[73,391,392,402,406,410,420,442,455,459,463,467],{"__ignoreMap":106},[110,393,394,396,398,400],{"class":112,"line":113},[110,395,117],{"class":116},[110,397,121],{"class":120},[110,399,124],{"class":116},[110,401,128],{"class":127},[110,403,404],{"class":112,"line":131},[110,405,135],{"class":134},[110,407,408],{"class":112,"line":138},[110,409,141],{"class":127},[110,411,412,414,416,418],{"class":112,"line":144},[110,413,148],{"class":147},[110,415,272],{"class":127},[110,417,331],{"class":154},[110,419,197],{"class":127},[110,421,422,424,427,429,432,434,436,438,440],{"class":112,"line":170},[110,423,173],{"class":116},[110,425,426],{"class":127}," ((context.state.count ",[110,428,179],{"class":116},[110,430,431],{"class":127}," context.rootState.count) ",[110,433,185],{"class":116},[110,435,188],{"class":120},[110,437,191],{"class":116},[110,439,194],{"class":120},[110,441,197],{"class":127},[110,443,444,447,449,451,453],{"class":112,"line":200},[110,445,446],{"class":127},"        context.",[110,448,160],{"class":147},[110,450,206],{"class":127},[110,452,210],{"class":209},[110,454,213],{"class":127},[110,456,457],{"class":112,"line":216},[110,458,219],{"class":127},[110,460,461],{"class":112,"line":222},[110,462,225],{"class":127},[110,464,465],{"class":112,"line":228},[110,466,231],{"class":127},[110,468,469],{"class":112,"line":234},[110,470,237],{"class":127},[35,472,474],{"id":473},"mapowanie-vuex-w-komponentach","Mapowanie VUEX w komponentach",[16,476,477,478,481,482,484],{},"Podłączając bibliotekę VUEX do Vue.js, otrzymujemy w efekcie dostępny globalnie w projekcie wskaźnik ",[73,479,480],{},"this.$store",". Dzięki niemu możemy w komponentach odwołać się do dowolnego ",[73,483,75],{},", wywołać akcję, mutację czy gettera.",[101,486,488],{"className":103,"code":487,"language":105,"meta":106,"style":106},"const Counter = {\n  template: `\u003Cdiv>{{ count }}\u003C\u002Fdiv>`,\n  computed: {\n    count () {\n      return this.$store.state.count\n    }\n  }\n}\n",[73,489,490,501,512,517,525,535,539,543],{"__ignoreMap":106},[110,491,492,494,497,499],{"class":112,"line":113},[110,493,117],{"class":116},[110,495,496],{"class":120}," Counter",[110,498,124],{"class":116},[110,500,128],{"class":127},[110,502,503,506,509],{"class":112,"line":131},[110,504,505],{"class":127},"  template: ",[110,507,508],{"class":209},"`\u003Cdiv>{{ count }}\u003C\u002Fdiv>`",[110,510,511],{"class":127},",\n",[110,513,514],{"class":112,"line":138},[110,515,516],{"class":127},"  computed: {\n",[110,518,519,522],{"class":112,"line":144},[110,520,521],{"class":147},"    count",[110,523,524],{"class":127}," () {\n",[110,526,527,529,532],{"class":112,"line":170},[110,528,290],{"class":116},[110,530,531],{"class":120}," this",[110,533,534],{"class":127},".$store.state.count\n",[110,536,537],{"class":112,"line":200},[110,538,225],{"class":127},[110,540,541],{"class":112,"line":216},[110,542,231],{"class":127},[110,544,545],{"class":112,"line":222},[110,546,237],{"class":127},[16,548,549],{},"Jednak na dłuższą metę takie użycie dostępu do stanu vuexowego może być niewygodne, dlatego warto skorzystać z kilku funkcji udostępnionych przez tą bibliotekę:",[355,551,552,555,558,561],{},[358,553,554],{},"mapState",[358,556,557],{},"mapMutations",[358,559,560],{},"mapActions",[358,562,563],{},"mapGetters",[16,565,566],{},"Kilka przykładów użycia:",[101,568,570],{"className":103,"code":569,"language":105,"meta":106,"style":106},"\u002F\u002F in full builds helpers are exposed as Vuex.mapState\nimport { mapState } from 'vuex'\n\nexport default {\n  \u002F\u002F ...\n  computed: mapState({\n    count: state => state.count\n  })\n}\n",[73,571,572,577,591,597,607,611,621,636,641],{"__ignoreMap":106},[110,573,574],{"class":112,"line":113},[110,575,576],{"class":134},"\u002F\u002F in full builds helpers are exposed as Vuex.mapState\n",[110,578,579,582,585,588],{"class":112,"line":131},[110,580,581],{"class":116},"import",[110,583,584],{"class":127}," { mapState } ",[110,586,587],{"class":116},"from",[110,589,590],{"class":209}," 'vuex'\n",[110,592,593],{"class":112,"line":138},[110,594,596],{"emptyLinePlaceholder":595},true,"\n",[110,598,599,602,605],{"class":112,"line":144},[110,600,601],{"class":116},"export",[110,603,604],{"class":116}," default",[110,606,128],{"class":154},[110,608,609],{"class":112,"line":170},[110,610,135],{"class":134},[110,612,613,616,618],{"class":112,"line":200},[110,614,615],{"class":127},"  computed: ",[110,617,554],{"class":147},[110,619,620],{"class":127},"({\n",[110,622,623,625,628,630,633],{"class":112,"line":216},[110,624,521],{"class":147},[110,626,627],{"class":127},": ",[110,629,75],{"class":154},[110,631,632],{"class":116}," =>",[110,634,635],{"class":127}," state.count\n",[110,637,638],{"class":112,"line":222},[110,639,640],{"class":127},"  })\n",[110,642,643],{"class":112,"line":228},[110,644,237],{"class":154},[16,646,647,648,650,651,654,655,657,658,661,662,664,665,667],{},"W powyższym przykładzie możemy zauważyć kilka rzeczy. Przede wszystkim, mapowanie ",[73,649,75],{}," (oraz getterów) wykonujemy w sekcji ",[73,652,653],{},"computed"," komponentu. Jest to o tyle istotne, że każda zmiana zawartości ",[73,656,75],{}," w ",[73,659,660],{},"store"," VUEXa – dzięki reaktywności – automatycznie powoduje przerenderowanie wszystkich komponentów korzystających z tego ",[73,663,75],{},". Tym samym zadeklarowane w ",[73,666,653],{}," wartości (będące wynikiem wykonania odpowiednich funkcji) zostaną przeliczone i uaktualnione, a efekty zobaczymy od razu na ekranie.",[16,669,670,671,674,675,677,678,681,682,685,686,688],{},"Funkcja ",[73,672,673],{},"mapState()"," może przyjąć jako parametr zarówno obiekt jak i tablicę. W powyższym przykładzie w lokalnym ",[73,676,75],{}," komponentu zostanie utworzone pole o nazwie ",[73,679,680],{},"count",", będące wynikiem wykonania funkcji zwracającej zawartość ",[73,683,684],{},"state.count"," ze ",[73,687,660],{}," VUEX. Przy zastosowaniu takiej składni, lokalne pole komponentu może się nazywać dowolnie. Jeśli jednak nie chcemy zmieniać jego nazwy, możemy użyć krótszego zapisu za pomocą tablicy:",[101,690,693],{"className":691,"code":692,"language":323},[321],"computed: mapState([\n  \u002F\u002F map this.count to store.state.count\n  'count'\n])\n",[73,694,692],{"__ignoreMap":106},[16,696,697],{},"Warto jest świadomie korzystać z obu formatów zapisu – bo o ile w przypadku getterów przypisanie wyniku wykonania gettera do lokalnego pola ze zmianą nazwy ma duży sens, o tyle zmiana nazw akcji czy mutacji już niekoniecznie. Może mały przykład:",[101,699,701],{"className":103,"code":700,"language":105,"meta":106,"style":106},"import { mapGetters } from 'vuex'\n\nexport default {\n  \u002F\u002F ...\n  computed: {\n    ...mapGetters([\n      'getDoneTodosCount'\n    ])\n  }\n}\n",[73,702,703,714,718,726,730,734,744,749,754,758],{"__ignoreMap":106},[110,704,705,707,710,712],{"class":112,"line":113},[110,706,581],{"class":116},[110,708,709],{"class":127}," { mapGetters } ",[110,711,587],{"class":116},[110,713,590],{"class":209},[110,715,716],{"class":112,"line":131},[110,717,596],{"emptyLinePlaceholder":595},[110,719,720,722,724],{"class":112,"line":138},[110,721,601],{"class":116},[110,723,604],{"class":116},[110,725,128],{"class":154},[110,727,728],{"class":112,"line":144},[110,729,135],{"class":134},[110,731,732],{"class":112,"line":170},[110,733,516],{"class":127},[110,735,736,739,741],{"class":112,"line":200},[110,737,738],{"class":116},"    ...",[110,740,563],{"class":147},[110,742,743],{"class":127},"([\n",[110,745,746],{"class":112,"line":216},[110,747,748],{"class":209},"      'getDoneTodosCount'\n",[110,750,751],{"class":112,"line":222},[110,752,753],{"class":127},"    ])\n",[110,755,756],{"class":112,"line":228},[110,757,231],{"class":127},[110,759,760],{"class":112,"line":234},[110,761,237],{"class":154},[16,763,764,765,677,767,770,771,774],{},"W tym przypadku w lokalnym ",[73,766,75],{},[73,768,769],{},"getDoneTodosCount"," – co będzie mylące, ponieważ pole to zawiera wartość zwróconą przez wykonanie funkcji gettera, a nie definicję tej funkcji. Widząc taką nazwę w kodzie kusi nas by jej użyć jako wywołanie funkcji, np. ",[73,772,773],{},"this.getDoneTodosCount()"," – co skończy się błędem, bo to nie jest funkcja. Warto więc skorzystać z przypisania wyniku wykonania gettera do samodzielnie nazwanego pola. Moim zdaniem poniższy kod jest dużo bardziej czytelny i intuicyjny:",[101,776,778],{"className":103,"code":777,"language":105,"meta":106,"style":106},"import { mapGetters } from 'vuex'\n\nexport default {\n  \u002F\u002F ...\n  computed: {\n    ...mapGetters({\n      doneTodosCount: 'getDoneTodosCount'\n    })\n  }\n}\n",[73,779,780,790,794,802,806,810,818,826,831,835],{"__ignoreMap":106},[110,781,782,784,786,788],{"class":112,"line":113},[110,783,581],{"class":116},[110,785,709],{"class":127},[110,787,587],{"class":116},[110,789,590],{"class":209},[110,791,792],{"class":112,"line":131},[110,793,596],{"emptyLinePlaceholder":595},[110,795,796,798,800],{"class":112,"line":138},[110,797,601],{"class":116},[110,799,604],{"class":116},[110,801,128],{"class":154},[110,803,804],{"class":112,"line":144},[110,805,135],{"class":134},[110,807,808],{"class":112,"line":170},[110,809,516],{"class":127},[110,811,812,814,816],{"class":112,"line":200},[110,813,738],{"class":116},[110,815,563],{"class":147},[110,817,620],{"class":127},[110,819,820,823],{"class":112,"line":216},[110,821,822],{"class":127},"      doneTodosCount: ",[110,824,825],{"class":209},"'getDoneTodosCount'\n",[110,827,828],{"class":112,"line":222},[110,829,830],{"class":127},"    })\n",[110,832,833],{"class":112,"line":228},[110,834,231],{"class":127},[110,836,837],{"class":112,"line":234},[110,838,237],{"class":154},[16,840,841,842,845],{},"Co innego użycie akcji i mutacji – mapujemy je do sekcji ",[73,843,844],{},"methods"," komponentu – jako funkcje – dlatego jak najbardziej możemy użyć następującej formy:",[101,847,849],{"className":103,"code":848,"language":105,"meta":106,"style":106},"import { mapMutations, mapActions } from 'vuex'\n\nexport default {\n  \u002F\u002F ...\n  methods: {\n    ...mapMutations([\n      'increment' \u002F\u002F map `this.increment()` to `this.$store.commit('increment')`\n    ]),\n    ...mapActions([\n      'addOrder' \u002F\u002F map `this.addOrder()` to `this.$store.dispatch('addOrder')`\n    ])\n  }\n}\n",[73,850,851,862,866,874,878,883,891,899,904,912,920,925,930],{"__ignoreMap":106},[110,852,853,855,858,860],{"class":112,"line":113},[110,854,581],{"class":116},[110,856,857],{"class":127}," { mapMutations, mapActions } ",[110,859,587],{"class":116},[110,861,590],{"class":209},[110,863,864],{"class":112,"line":131},[110,865,596],{"emptyLinePlaceholder":595},[110,867,868,870,872],{"class":112,"line":138},[110,869,601],{"class":116},[110,871,604],{"class":116},[110,873,128],{"class":154},[110,875,876],{"class":112,"line":144},[110,877,135],{"class":134},[110,879,880],{"class":112,"line":170},[110,881,882],{"class":127},"  methods: {\n",[110,884,885,887,889],{"class":112,"line":200},[110,886,738],{"class":116},[110,888,557],{"class":147},[110,890,743],{"class":127},[110,892,893,896],{"class":112,"line":216},[110,894,895],{"class":209},"      'increment'",[110,897,898],{"class":134}," \u002F\u002F map `this.increment()` to `this.$store.commit('increment')`\n",[110,900,901],{"class":112,"line":222},[110,902,903],{"class":127},"    ]),\n",[110,905,906,908,910],{"class":112,"line":228},[110,907,738],{"class":116},[110,909,560],{"class":147},[110,911,743],{"class":127},[110,913,914,917],{"class":112,"line":234},[110,915,916],{"class":209},"      'addOrder'",[110,918,919],{"class":134}," \u002F\u002F map `this.addOrder()` to `this.$store.dispatch('addOrder')`\n",[110,921,923],{"class":112,"line":922},11,[110,924,753],{"class":127},[110,926,928],{"class":112,"line":927},12,[110,929,231],{"class":127},[110,931,933],{"class":112,"line":932},13,[110,934,237],{"class":154},[35,936,938],{"id":937},"kiedy-użyć-getterów","Kiedy użyć getterów?",[16,940,941,942,944,945,947,948,950,951,954],{},"Zawartość naszego ",[73,943,660],{},"’a, możemy pobrać na kilka sposobów – nawiązując do przykładów wyżej – w sekcji ",[73,946,653],{}," komponentu możemy użyć ",[73,949,673],{}," lub ",[73,952,953],{},"mapGetters()",". Kiedy jednak użyć którego sposobu?",[16,956,957,958,960,961,964,965,657,967,969,970,973,974,977],{},"Użycie ",[73,959,673],{}," pozwala na pobranie i przypisanie do lokalnego ",[73,962,963],{},"scope"," wartości bezpośrednio z sekcji ",[73,966,75],{},[73,968,660],{},". W ten sposób otrzymujemy czyste dane – bez żadnych modyfikacji. Ma to duży sens, jeśli są to typy proste – jakaś flaga będąca wartością boolean (",[73,971,972],{},"true","\u002F",[73,975,976],{},"false","), np. flaga „loading” itp.",[101,979,981],{"className":103,"code":980,"language":105,"meta":106,"style":106},"import { mapState } from 'vuex'\nexport default {\n  \u002F\u002F ...\n  computed: {\n    ...mapState({\n      loading: state => state.loading\n    })\n  }\n}\n",[73,982,983,993,1001,1005,1009,1017,1031,1035,1039],{"__ignoreMap":106},[110,984,985,987,989,991],{"class":112,"line":113},[110,986,581],{"class":116},[110,988,584],{"class":127},[110,990,587],{"class":116},[110,992,590],{"class":209},[110,994,995,997,999],{"class":112,"line":131},[110,996,601],{"class":116},[110,998,604],{"class":116},[110,1000,128],{"class":154},[110,1002,1003],{"class":112,"line":138},[110,1004,135],{"class":134},[110,1006,1007],{"class":112,"line":144},[110,1008,516],{"class":127},[110,1010,1011,1013,1015],{"class":112,"line":170},[110,1012,738],{"class":116},[110,1014,554],{"class":147},[110,1016,620],{"class":127},[110,1018,1019,1022,1024,1026,1028],{"class":112,"line":200},[110,1020,1021],{"class":147},"      loading",[110,1023,627],{"class":127},[110,1025,75],{"class":154},[110,1027,632],{"class":116},[110,1029,1030],{"class":127}," state.loading\n",[110,1032,1033],{"class":112,"line":216},[110,1034,830],{"class":127},[110,1036,1037],{"class":112,"line":222},[110,1038,231],{"class":127},[110,1040,1041],{"class":112,"line":228},[110,1042,237],{"class":154},[16,1044,1045,1046,1048,1049,93],{},"W przypadku typów złożonych, np. tablicy lub obiektu z danymi, warto pobrać dane za pomocą gettera – czyli funkcji mającej dostęp do danych ze ",[73,1047,75],{}," i zawierającej logikę, która przetwarza wstępnie dane. Możemy takim getterem np. zwracać przefiltrowaną tablicę z danymi lub wynik wykonania jakiejś innej operacji na danych ze ",[73,1050,75],{},[101,1052,1054],{"className":103,"code":1053,"language":105,"meta":106,"style":106},"getters: {\n  \u002F\u002F ...\n  doneTodosCount: (state, getters) => {\n    return getters.doneTodos.length\n  }\n}\n\nimport { mapGetters } from 'vuex'\nexport default {\n  \u002F\u002F ...\n  computed: {\n    ...mapGetters([\n      'doneTodosCount'\n    ])\n  }\n}\n",[73,1055,1056,1063,1067,1089,1100,1104,1108,1112,1122,1130,1134,1138,1146,1151,1156,1161],{"__ignoreMap":106},[110,1057,1058,1060],{"class":112,"line":113},[110,1059,279],{"class":154},[110,1061,1062],{"class":127},": {\n",[110,1064,1065],{"class":112,"line":131},[110,1066,135],{"class":134},[110,1068,1069,1072,1075,1077,1079,1081,1084,1087],{"class":112,"line":138},[110,1070,1071],{"class":154},"  doneTodosCount",[110,1073,1074],{"class":127},": (",[110,1076,75],{"class":154},[110,1078,157],{"class":127},[110,1080,279],{"class":154},[110,1082,1083],{"class":127},") ",[110,1085,1086],{"class":116},"=>",[110,1088,128],{"class":127},[110,1090,1091,1094,1097],{"class":112,"line":144},[110,1092,1093],{"class":116},"    return",[110,1095,1096],{"class":127}," getters.doneTodos.",[110,1098,1099],{"class":120},"length\n",[110,1101,1102],{"class":112,"line":170},[110,1103,231],{"class":127},[110,1105,1106],{"class":112,"line":200},[110,1107,237],{"class":127},[110,1109,1110],{"class":112,"line":216},[110,1111,596],{"emptyLinePlaceholder":595},[110,1113,1114,1116,1118,1120],{"class":112,"line":222},[110,1115,581],{"class":116},[110,1117,709],{"class":127},[110,1119,587],{"class":116},[110,1121,590],{"class":209},[110,1123,1124,1126,1128],{"class":112,"line":228},[110,1125,601],{"class":116},[110,1127,604],{"class":116},[110,1129,128],{"class":154},[110,1131,1132],{"class":112,"line":234},[110,1133,135],{"class":134},[110,1135,1136],{"class":112,"line":922},[110,1137,516],{"class":127},[110,1139,1140,1142,1144],{"class":112,"line":927},[110,1141,738],{"class":116},[110,1143,563],{"class":147},[110,1145,743],{"class":127},[110,1147,1148],{"class":112,"line":932},[110,1149,1150],{"class":209},"      'doneTodosCount'\n",[110,1152,1154],{"class":112,"line":1153},14,[110,1155,753],{"class":127},[110,1157,1159],{"class":112,"line":1158},15,[110,1160,231],{"class":127},[110,1162,1164],{"class":112,"line":1163},16,[110,1165,237],{"class":154},[35,1167,1169],{"id":1168},"jak-użyć-gettera-z-parametrami","Jak użyć gettera z parametrami?",[16,1171,1172],{},"Ponieważ getter jest funkcją, która w założeniu ma jakoś przetwarzać zwracane dane, może wystąpić potrzeba przekazania do tej funkcji jakiegoś parametru wejściowego.",[16,1174,1175],{},"Można to zrobić za pomocą następującej konstrukcji:",[101,1177,1179],{"className":103,"code":1178,"language":105,"meta":106,"style":106},"getters: {\n  \u002F\u002F ...\n  getTodoById: (state) => (id) => {\n    return state.todos.find(todo => todo.id === id)\n  }\n}\n\nstore.getters.getTodoById(2)\n",[73,1180,1181,1187,1191,1215,1241,1245,1249,1253],{"__ignoreMap":106},[110,1182,1183,1185],{"class":112,"line":113},[110,1184,279],{"class":154},[110,1186,1062],{"class":127},[110,1188,1189],{"class":112,"line":131},[110,1190,135],{"class":134},[110,1192,1193,1196,1198,1200,1202,1204,1206,1209,1211,1213],{"class":112,"line":138},[110,1194,1195],{"class":154},"  getTodoById",[110,1197,1074],{"class":127},[110,1199,75],{"class":154},[110,1201,1083],{"class":127},[110,1203,1086],{"class":116},[110,1205,272],{"class":127},[110,1207,1208],{"class":154},"id",[110,1210,1083],{"class":127},[110,1212,1086],{"class":116},[110,1214,128],{"class":127},[110,1216,1217,1219,1222,1225,1227,1230,1232,1235,1238],{"class":112,"line":144},[110,1218,1093],{"class":116},[110,1220,1221],{"class":127}," state.todos.",[110,1223,1224],{"class":147},"find",[110,1226,206],{"class":127},[110,1228,1229],{"class":154},"todo",[110,1231,632],{"class":116},[110,1233,1234],{"class":127}," todo.id ",[110,1236,1237],{"class":116},"===",[110,1239,1240],{"class":127}," id)\n",[110,1242,1243],{"class":112,"line":170},[110,1244,231],{"class":127},[110,1246,1247],{"class":112,"line":200},[110,1248,237],{"class":127},[110,1250,1251],{"class":112,"line":216},[110,1252,596],{"emptyLinePlaceholder":595},[110,1254,1255,1258,1261,1263,1266],{"class":112,"line":222},[110,1256,1257],{"class":127},"store.getters.",[110,1259,1260],{"class":147},"getTodoById",[110,1262,206],{"class":127},[110,1264,1265],{"class":120},"2",[110,1267,213],{"class":127},[16,1269,1270,1271,1274,1275,1278,1279,93],{},"W powyższym przykładzie getter ",[73,1272,1273],{},"getTodoById(id)"," zwraca wynik przefiltrowania tablicy za pomocą metody ",[73,1276,1277],{},".find()",", do której przekazujemy parametr ",[73,1280,1208],{},[35,1282,1284],{"id":1283},"akcja-czy-mutacja","Akcja czy mutacja?",[16,1286,1287],{},"W dokumentacji VUEXa możemy znaleźć taki schemat działania:",[16,1289,1290],{},[1291,1292],"img",{"alt":106,"height":1293,"src":1294,"width":1295},551,"https:\u002F\u002F5icqmonbzxnrw2bn.public.blob.vercel-storage.com\u002Fwp\u002F2019\u002F08\u002Fvuex-1.png",701,[16,1297,1298,1299,1303],{},"Wynika z niego (jak i z całego wzorca Flux, na którym Vuex jest oparty), że komponent zawsze wywołuje akcję (która jest rozpatrywana przez ",[1300,1301,1302],"em",{},"dispatcher","), w wyniku działania akcji wywoływane są mutacje na stanie store’a, które to zmiany dzięki magii reaktywności Vue.js są automatycznie odzwierciedlane w komponencie i jego widoku.",[16,1305,1306],{},"Warto jednak wiedzieć, że nie zawsze musimy wywoływać akcję – jeśli akcja miałaby tylko wywołać mutację, to bardziej celowe będzie bezpośrednie wywołanie mutacji.",[16,1308,1309],{},"W akcjach i mutacjach widzę 2 podstawowe różnice:",[1311,1312,1313,1320],"ol",{},[358,1314,1315,1319],{},[1316,1317,1318],"strong",{},"akcje są wywoływane asynchronicznie"," (a mutacje synchronicznie), co oznacza, że wywołanie akcji nie blokuje wątku JavaScriptu. Taka asynchroniczna akcja doskonale nadaje się do przetworzenia logiki – wywołania backendu requestem ajaxowym, przetworzenia wyników, wywołania różnych mutacji. Mutacja powinna obsługiwać tylko modyfikację stanu store’a – bez większej logiki (ewentualnie jakaś walidacja przesłanej wartości).",[358,1321,1322,1325,1326,1331,1332,1334,1335,657,1337,93],{},[1316,1323,1324],{},"akcje"," (w przeciwieństwie do mutacji) ",[1316,1327,1328,1329],{},"mają dostęp do całej instancji ",[73,1330,660],{}," i pozwalają na dostęp zarówno do ",[73,1333,75],{}," (najlepiej w celu odczytania wartości) jak i do innych akcji i mutacji, które to można z poziomu danej akcji wywołać i uruchomić. Mutacja ma dostęp tylko do samego ",[73,1336,75],{},[73,1338,660],{},[35,1340,1342],{"id":1341},"komunikacja-pomiędzy-komponentami","Komunikacja pomiędzy komponentami",[16,1344,1345,1346,1349,1350,1353],{},"Użycie w projekcie biblioteki Vuex pozwala na zmianę sposobu komunikacji pomiędzy komponentami. O ile często będziemy używać ",[73,1347,1348],{},"props"," do zasilenia jakiegoś komponentu danymi, o tyle możemy w dużej mierze zrezygnować z użycia ",[73,1351,1352],{},"$emit"," do sygnalizowania zmian do komponentów nadrzędnych.",[16,1355,1356,1357,950,1359,1361],{},"Wystarczy, że wszystkie komponenty korzystające ze wspólnych danych będą miały je zamapowane za pomocą ",[73,1358,673],{},[73,1360,953],{}," (lub poprzez bezpośrednie odwołanie do store VUEXa), by każda mutacja w module VUEXa została reaktywnie odwzorowana na instancji komponentu.",[16,1363,1364],{},"Takie podejście pozwala na łatwą komunikację pomiędzy równoległymi komponentami, lub też takimi, które znajdują się daleko od siebie w drzewiastej strukturze komponentów naszej aplikacji. Po prostu korzystamy ze wspólnego, wyodrębnionego i reaktywnego stanu aplikacji.",[35,1366,1368],{"id":1367},"użycie-storea-vuex-jako-modelu-dla-formularza","Użycie store’a Vuex jako modelu dla formularza",[16,1370,1371],{},"Formularze są nieodłączną częścią każdej aplikacji – czy będzie to filtr w panelu administracyjnym, czy formularz kontaktowy lub zamówieniowy – prędzej czy później użycie tego mechanizmu będzie konieczne. W Vue.js formularze zwykle korzystają z dwukierunkowej komunikacji ze swoim modelem – zwykle zlokalizowanym bezpośrednio w komponencie.",[16,1373,1374,1375,1377],{},"W przypadku użycia VUEXa można model formularza przenieść bezpośrednio do ",[73,1376,660],{},". Nie zawsze jest to konieczne, ale są sytuacje, gdy takie podejście jest korzystne. W moim wypadku dobrze sprawdza się to na ekranach CRUDowych – gdzie wyświetlam dane z bazy, zapewniam możliwość ich filtrowania (formularz), a także dodania\u002Fedycji\u002Fusunięcia.",[16,1379,1380,1381,1383],{},"Przykładowy ",[73,1382,75],{}," dla takiego ekranu może wyglądać np. tak:",[101,1385,1387],{"className":103,"code":1386,"language":105,"meta":106,"style":106},"const state = {\n  filters: {\n    status: null,\n    operationType: null,\n    id: null,\n    page: 1,\n    pageSize: 20\n  },\n  loading: false,\n  items: [],\n  pagination: {\n    page: null,\n    totalPages: null\n  }\n}\n",[73,1388,1389,1400,1405,1415,1424,1433,1443,1451,1456,1465,1470,1475,1483,1491,1495],{"__ignoreMap":106},[110,1390,1391,1393,1396,1398],{"class":112,"line":113},[110,1392,117],{"class":116},[110,1394,1395],{"class":120}," state",[110,1397,124],{"class":116},[110,1399,128],{"class":127},[110,1401,1402],{"class":112,"line":131},[110,1403,1404],{"class":127},"  filters: {\n",[110,1406,1407,1410,1413],{"class":112,"line":138},[110,1408,1409],{"class":127},"    status: ",[110,1411,1412],{"class":120},"null",[110,1414,511],{"class":127},[110,1416,1417,1420,1422],{"class":112,"line":144},[110,1418,1419],{"class":127},"    operationType: ",[110,1421,1412],{"class":120},[110,1423,511],{"class":127},[110,1425,1426,1429,1431],{"class":112,"line":170},[110,1427,1428],{"class":127},"    id: ",[110,1430,1412],{"class":120},[110,1432,511],{"class":127},[110,1434,1435,1438,1441],{"class":112,"line":200},[110,1436,1437],{"class":127},"    page: ",[110,1439,1440],{"class":120},"1",[110,1442,511],{"class":127},[110,1444,1445,1448],{"class":112,"line":216},[110,1446,1447],{"class":127},"    pageSize: ",[110,1449,1450],{"class":120},"20\n",[110,1452,1453],{"class":112,"line":222},[110,1454,1455],{"class":127},"  },\n",[110,1457,1458,1461,1463],{"class":112,"line":228},[110,1459,1460],{"class":127},"  loading: ",[110,1462,976],{"class":120},[110,1464,511],{"class":127},[110,1466,1467],{"class":112,"line":234},[110,1468,1469],{"class":127},"  items: [],\n",[110,1471,1472],{"class":112,"line":922},[110,1473,1474],{"class":127},"  pagination: {\n",[110,1476,1477,1479,1481],{"class":112,"line":927},[110,1478,1437],{"class":127},[110,1480,1412],{"class":120},[110,1482,511],{"class":127},[110,1484,1485,1488],{"class":112,"line":932},[110,1486,1487],{"class":127},"    totalPages: ",[110,1489,1490],{"class":120},"null\n",[110,1492,1493],{"class":112,"line":1153},[110,1494,231],{"class":127},[110,1496,1497],{"class":112,"line":1158},[110,1498,237],{"class":127},[16,1500,1501,1502,157,1505,332,1508,93],{},"Jak widać na przykładzie, stete Vuexa zawiera między innymi zawartość filtra – w tym przypadku pola ",[73,1503,1504],{},"status",[73,1506,1507],{},"operationType",[73,1509,1208],{},[16,1511,1512],{},"Aby zapewnić sobie prawidłową dwukierunkową komunikację między formularzem a modelem w Vuexie, stosujemy w komponencie filtra następującą konstrukcję:",[101,1514,1516],{"className":103,"code":1515,"language":105,"meta":106,"style":106},"import { mapMutations } from 'vuex'\nexport default {\n  ...\n  computed: {\n    status: {\n      get () {\n        return this.$store.state.documents.filters.status\n      },\n      set (value) {\n        this.setDocumentsFilterStatus(value)\n      }\n    },\n    operationType: {\n      get () {\n        return this.$store.state.documents.filters.operationType\n      },\n      set (value) {\n        this.setDocumentsFilterOperationType(value)\n      }\n    },\n    id: {\n      get () {\n        return this.$store.state.documents.filters.id\n      },\n      set (value) {\n        this.setDocumentsFilterId(value)\n      }\n    }\n  },\n  methods: {\n    ...mapMutations([\n      'setDocumentsFilterStatus',\n      'setDocumentsFilterOperationType',\n      'setDocumentsFilterId'\n    ])\n  ...\n  }\n}\n",[73,1517,1518,1529,1537,1542,1546,1551,1558,1568,1573,1585,1598,1602,1607,1612,1618,1627,1631,1642,1654,1659,1664,1670,1677,1687,1692,1703,1715,1720,1725,1733,1738,1747,1755,1763,1769,1774,1779,1784],{"__ignoreMap":106},[110,1519,1520,1522,1525,1527],{"class":112,"line":113},[110,1521,581],{"class":116},[110,1523,1524],{"class":127}," { mapMutations } ",[110,1526,587],{"class":116},[110,1528,590],{"class":209},[110,1530,1531,1533,1535],{"class":112,"line":131},[110,1532,601],{"class":116},[110,1534,604],{"class":116},[110,1536,128],{"class":154},[110,1538,1539],{"class":112,"line":138},[110,1540,1541],{"class":116},"  ...\n",[110,1543,1544],{"class":112,"line":144},[110,1545,516],{"class":127},[110,1547,1548],{"class":112,"line":170},[110,1549,1550],{"class":127},"    status: {\n",[110,1552,1553,1556],{"class":112,"line":200},[110,1554,1555],{"class":147},"      get",[110,1557,524],{"class":127},[110,1559,1560,1563,1565],{"class":112,"line":216},[110,1561,1562],{"class":116},"        return",[110,1564,531],{"class":120},[110,1566,1567],{"class":127},".$store.state.documents.filters.status\n",[110,1569,1570],{"class":112,"line":222},[110,1571,1572],{"class":127},"      },\n",[110,1574,1575,1578,1580,1583],{"class":112,"line":228},[110,1576,1577],{"class":147},"      set",[110,1579,272],{"class":127},[110,1581,1582],{"class":154},"value",[110,1584,197],{"class":127},[110,1586,1587,1590,1592,1595],{"class":112,"line":234},[110,1588,1589],{"class":120},"        this",[110,1591,93],{"class":127},[110,1593,1594],{"class":147},"setDocumentsFilterStatus",[110,1596,1597],{"class":127},"(value)\n",[110,1599,1600],{"class":112,"line":922},[110,1601,219],{"class":127},[110,1603,1604],{"class":112,"line":927},[110,1605,1606],{"class":127},"    },\n",[110,1608,1609],{"class":112,"line":932},[110,1610,1611],{"class":127},"    operationType: {\n",[110,1613,1614,1616],{"class":112,"line":1153},[110,1615,1555],{"class":147},[110,1617,524],{"class":127},[110,1619,1620,1622,1624],{"class":112,"line":1158},[110,1621,1562],{"class":116},[110,1623,531],{"class":120},[110,1625,1626],{"class":127},".$store.state.documents.filters.operationType\n",[110,1628,1629],{"class":112,"line":1163},[110,1630,1572],{"class":127},[110,1632,1634,1636,1638,1640],{"class":112,"line":1633},17,[110,1635,1577],{"class":147},[110,1637,272],{"class":127},[110,1639,1582],{"class":154},[110,1641,197],{"class":127},[110,1643,1645,1647,1649,1652],{"class":112,"line":1644},18,[110,1646,1589],{"class":120},[110,1648,93],{"class":127},[110,1650,1651],{"class":147},"setDocumentsFilterOperationType",[110,1653,1597],{"class":127},[110,1655,1657],{"class":112,"line":1656},19,[110,1658,219],{"class":127},[110,1660,1662],{"class":112,"line":1661},20,[110,1663,1606],{"class":127},[110,1665,1667],{"class":112,"line":1666},21,[110,1668,1669],{"class":127},"    id: {\n",[110,1671,1673,1675],{"class":112,"line":1672},22,[110,1674,1555],{"class":147},[110,1676,524],{"class":127},[110,1678,1680,1682,1684],{"class":112,"line":1679},23,[110,1681,1562],{"class":116},[110,1683,531],{"class":120},[110,1685,1686],{"class":127},".$store.state.documents.filters.id\n",[110,1688,1690],{"class":112,"line":1689},24,[110,1691,1572],{"class":127},[110,1693,1695,1697,1699,1701],{"class":112,"line":1694},25,[110,1696,1577],{"class":147},[110,1698,272],{"class":127},[110,1700,1582],{"class":154},[110,1702,197],{"class":127},[110,1704,1706,1708,1710,1713],{"class":112,"line":1705},26,[110,1707,1589],{"class":120},[110,1709,93],{"class":127},[110,1711,1712],{"class":147},"setDocumentsFilterId",[110,1714,1597],{"class":127},[110,1716,1718],{"class":112,"line":1717},27,[110,1719,219],{"class":127},[110,1721,1723],{"class":112,"line":1722},28,[110,1724,225],{"class":127},[110,1726,1728,1731],{"class":112,"line":1727},29,[110,1729,1730],{"class":127},"  }",[110,1732,511],{"class":154},[110,1734,1736],{"class":112,"line":1735},30,[110,1737,882],{"class":127},[110,1739,1741,1743,1745],{"class":112,"line":1740},31,[110,1742,738],{"class":116},[110,1744,557],{"class":147},[110,1746,743],{"class":127},[110,1748,1750,1753],{"class":112,"line":1749},32,[110,1751,1752],{"class":209},"      'setDocumentsFilterStatus'",[110,1754,511],{"class":127},[110,1756,1758,1761],{"class":112,"line":1757},33,[110,1759,1760],{"class":209},"      'setDocumentsFilterOperationType'",[110,1762,511],{"class":127},[110,1764,1766],{"class":112,"line":1765},34,[110,1767,1768],{"class":209},"      'setDocumentsFilterId'\n",[110,1770,1772],{"class":112,"line":1771},35,[110,1773,753],{"class":127},[110,1775,1777],{"class":112,"line":1776},36,[110,1778,1541],{"class":116},[110,1780,1782],{"class":112,"line":1781},37,[110,1783,231],{"class":127},[110,1785,1787],{"class":112,"line":1786},38,[110,1788,237],{"class":154},[16,1790,1791,1792,332,1795,1798,1799,1801,1802,1804],{},"To oczywiście wyrwany a kontekstu fragment kodu, ale pokazuje on, jak w sekcji computed komponentu Vue stworzyć pola będące modelem dla formularza. Wystarczy każde takie pole zdefiniować przy pomocy zadeklarowania funkcji ",[73,1793,1794],{},"get()",[73,1796,1797],{},"set()"," – gdzie ",[73,1800,1794],{}," pobiera i zwraca wartość bezpośrednio ze state Vuexa, a ",[73,1803,1797],{}," wykonuje mutację Vuexową.",[35,1806,1808],{"id":1807},"to-be-strict-or-not-to-be-czyli-mutowanie-danych-poza-vuex","To be STRICT, or not to be… – czyli mutowanie danych poza Vuex",[16,1810,1811,1814,1815,1817],{},[1316,1812,1813],{},"Strict Mode"," – to taki tryb działania biblioteki Vuex, w którym szczególnie jest pilnowany sposób modyfikacji ",[73,1816,75],{}," – dozwolona jest jego modyfikacja tylko za pośrednictwem mutacji.",[16,1819,1820],{},"Ze względów wydajnościowych dokumentacja zaleca włączenie tego trybu tylko w wersji developerskiej:",[101,1822,1824],{"className":103,"code":1823,"language":105,"meta":106,"style":106},"const store = new Vuex.Store({\n  \u002F\u002F ...\n  strict: process.env.NODE_ENV !== 'production'\n})\n",[73,1825,1826,1846,1850,1864],{"__ignoreMap":106},[110,1827,1828,1830,1833,1835,1838,1841,1844],{"class":112,"line":113},[110,1829,117],{"class":116},[110,1831,1832],{"class":120}," store",[110,1834,124],{"class":116},[110,1836,1837],{"class":116}," new",[110,1839,1840],{"class":127}," Vuex.",[110,1842,1843],{"class":147},"Store",[110,1845,620],{"class":127},[110,1847,1848],{"class":112,"line":131},[110,1849,135],{"class":134},[110,1851,1852,1855,1858,1861],{"class":112,"line":138},[110,1853,1854],{"class":127},"  strict: process.env.",[110,1856,1857],{"class":120},"NODE_ENV",[110,1859,1860],{"class":116}," !==",[110,1862,1863],{"class":209}," 'production'\n",[110,1865,1866],{"class":112,"line":144},[110,1867,1868],{"class":127},"})\n",[16,1870,1871],{},"Tryb strict jest tu kluczowy, ponieważ podczas początków pracy z Vuexem bardzo często spotkamy się z takim komunikatem błędu:",[40,1873,1874],{},[16,1875,1876],{},[73,1877,1878],{},"Error: [vuex] Do not mutate vuex store state outside mutation handlers.",[16,1880,1881,1882,1885],{},"W ten sposób Vuex upomina nas, żeby nie modyfikować state bezpośrednio. Taka sytuacja często się zdarza w przypadku typów złożonych takich jak obiekty i tablice (tak wiem, tablica to też obiekt…), które to w JavaScript są przekazywane poprzez ",[1300,1883,1884],{},"referencję",", a nie przez skopiowanie wartości.",[16,1887,1888,1889,1891],{},"Jeśli pobraliśmy z bazy tablicę obiektów, którą to przechowujemy w ",[73,1890,660],{}," Vuexa i wyświetlamy ją jako jakąś listę lub tabelę z danymi – to bardzo często przekazujemy pojedynczy element\u002Fobiekt z takiej listy do komponentu z jakimś formularzem edycji. Ponieważ obiekt ten jest jedynie referencją do oryginalnego obiektu w store, próba bezpośredniej modyfikacji wartości jego pól skończy się nieuchronnie powyższym błędem, a na produkcji dość nieprzewidzianym działaniem całej aplikacji.",[16,1893,1894,1895],{},"Sposobem na ominięcie tego problemu jest wykonanie tzw. „głębokiej kopii” (deep copy\u002Fdeep clone). Można to zrobić na kilka sposobów, omówiono je np. tu: ",[20,1896,1897],{"href":1897,"rel":1898},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F122102\u002Fwhat-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript",[65],[16,1900,1901],{},"Ja w niektórych projektach używam podłączonej pod Vue biblioteki Lodash, która to udostępnia stosowną metodę.",[16,1903,1904,1905,1907],{},"Taka „głęboka kopia” jest już odłączona od swojego oryginału w ",[73,1906,75],{}," Vuexa, dzięki czemu możemy ją dowolnie zmodyfikować, wysłać do backendu jako np. update rekordu, a następnie uaktualnić sobie zawartość oryginalnej listy – czy to zaciągając ją ponownie w tle czy modyfikując ją tym naszym nowym obiektem – tu już metoda dowolna, byle było to wykonane odpowiednią akcją i mutacją.",[11,1909,1911],{"id":1910},"podsumowanie","Podsumowanie",[16,1913,1914,1915,1917],{},"Z mojej perspektywy użycie Vuexa w aplikacjach opartych o Vue.js było strzałem w 10 – sprawiło że komponenty „schudły”, część logiki została wydzielona w dedykowane moduły Vuexa, a komunikacja w całej aplikacji została bardzo uproszczona. Powiązanie scentralizowanego ",[73,1916,660],{}," z reaktywnością Vue sprawiło, że zmiany w stanie aplikacji oddziałują automatycznie na wszystkie powiązane z tym stanem komponenty i widoki, dzięki czemu sam kod staje się prostszy, bardziej czysty i przyjazny.",[16,1919,1920],{},"Zdaję sobie sprawę, że te kilka przemyśleń i własnych doświadczeń na pewno nie wyczerpują tematu – jest to też spojrzenie bardzo subiektywne i wynikające z mojej praktyki – jeśli ktoś ma odmienne zdanie lub chciałby coś uzupełnić – zapraszam do komentowania – bardzo chętnie skonfrontuję swoje spojrzenie z innym 🙂",[1922,1923,1924],"style",{},"html pre.shiki code .s5nWI, html code.shiki .s5nWI{--shiki-light:#CF222E;--shiki-default:#CF222E;--shiki-dark:#FF7B72}html pre.shiki code .sDkKp, html code.shiki .sDkKp{--shiki-light:#0550AE;--shiki-default:#0550AE;--shiki-dark:#79C0FF}html pre.shiki code .s_bOF, html code.shiki .s_bOF{--shiki-light:#1F2328;--shiki-default:#1F2328;--shiki-dark:#E6EDF3}html pre.shiki code .s9BHP, html code.shiki .s9BHP{--shiki-light:#6E7781;--shiki-default:#6E7781;--shiki-dark:#8B949E}html pre.shiki code .sd69k, html code.shiki .sd69k{--shiki-light:#8250DF;--shiki-default:#8250DF;--shiki-dark:#D2A8FF}html pre.shiki code .sQJ0q, html code.shiki .sQJ0q{--shiki-light:#953800;--shiki-default:#953800;--shiki-dark:#FFA657}html pre.shiki code .s-BGO, html code.shiki .s-BGO{--shiki-light:#0A3069;--shiki-default:#0A3069;--shiki-dark:#A5D6FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":106,"searchDepth":131,"depth":131,"links":1926},[1927,1928,1940],{"id":13,"depth":131,"text":14},{"id":26,"depth":131,"text":27,"children":1929},[1930,1931,1932,1933,1934,1935,1936,1937,1938,1939],{"id":37,"depth":138,"text":38},{"id":53,"depth":138,"text":54},{"id":313,"depth":138,"text":314},{"id":473,"depth":138,"text":474},{"id":937,"depth":138,"text":938},{"id":1168,"depth":138,"text":1169},{"id":1283,"depth":138,"text":1284},{"id":1341,"depth":138,"text":1342},{"id":1367,"depth":138,"text":1368},{"id":1807,"depth":138,"text":1808},{"id":1910,"depth":131,"text":1911},[1942,1943],"artykuly","vue-js","https:\u002F\u002F5icqmonbzxnrw2bn.public.blob.vercel-storage.com\u002Fwp\u002F2019\u002F08\u002Fvuex.png","VUEX","2019-08-28","Co to jest Vue i Vuex? Jeśli nie wiesz co to jest Vue lub Vuex – to zapraszam najpierw do obejrzenia nagrania z mojej prezentacji…",false,"md",{},"\u002Fblog\u002Fvuex-kilka-przemyslen-po-roku-kodowania",[],{"title":5,"description":1947},{"loc":1951,"images":1955},[1956],{"loc":1294},"vuex-kilka-przemyslen-po-roku-kodowania","blog\u002F2019-08-28-vuex-kilka-przemyslen-po-roku-kodowania",[1960,1943,1961],"store-management","vuex",null,322,"DnxaQG78BTlaPxos__rL63tWV22qGADkrUYfr-G7uY8",[1966,1971],{"title":1967,"path":1968,"stem":1969,"description":1970,"children":-1},"Czy da się zakup gamepada od XBOXa wrzucić w koszty firmy?","\u002Fblog\u002Fczy-da-sie-zakup-gamepada-od-xboxa-wrzucic-w-koszty-firmy","blog\u002F2019-03-13-czy-da-sie-zakup-gamepada-od-xboxa-wrzucic-w-koszty-firmy","Chyba każdy programista pracujący w trybie B2B posiadając własną działalność gospodarczą zastanawia się, co jeszcze ze sprzętu komputerowego da się wrzucić w…",{"title":1972,"path":1973,"stem":1974,"description":1975,"children":-1},"Zaproszenie na ConFrontJS 2019 – 7 grudnia 2019 – Warszawa (25% zniżki!)","\u002Fblog\u002Fzaproszenie-na-confrontjs-2019-7-grudnia-2019-warszawa-25-znizki","blog\u002F2019-11-25-zaproszenie-na-confrontjs-2019-7-grudnia-2019-warszawa-25-znizki","Ostatnimi czasami obrodziło nam w różne konferencje i meet’upy front-endowe – naprawdę jest w czym wybierać. Od organizatorów jednej z konferencji – a…",[1977,1988,1997],{"path":22,"title":1978,"description":1979,"date":1980,"categories":1981,"tags":1983,"readingTime":113,"draft":1948,"cover":1987},"Tech.3camp – Zarządzanie stanem aplikacji na przykładzie Vue i Vuex – jest już nagranie video :)","Na kanale Tech.3amp’a na Vimeo pojawiły się filmy z nagraniami z ostatniego spotkania, w tym z mojej prezentacji. Jeśli ktoś jest ciekawy jak i o…","2019-01-28",[1982,1943],"prezentacje",[1984,1985,1986,1961],"prezentacja","tech3camp","vue","https:\u002F\u002F5icqmonbzxnrw2bn.public.blob.vercel-storage.com\u002Fwp\u002F2019\u002F01\u002Ftech3camp.png",{"path":1989,"title":1990,"description":1991,"date":1992,"categories":1993,"tags":1994,"readingTime":113,"draft":1948,"cover":1987},"\u002Fblog\u002Ftech-3camp-zapraszam","Tech.3camp – zapraszam!","Już jutro, czyli 15 stycznia 2019 o godzinie 18:00 w O4 Coworking (Olivia Business Centre), odbędzie się 51 spotkanie z serii Tech.3camp. Program zapowiada się…","2019-01-14",[1982],[1995,1996,1985,1961],"frontend","redux",{"path":1998,"title":1999,"description":2000,"date":2001,"categories":2002,"tags":2004,"readingTime":131,"draft":1948,"cover":2008},"\u002Fblog\u002Fvue-js-toolbox-niezbednik-kazdego-vue-developera","VUE.js Toolbox – niezbędnik każdego VUE-developera","2 lata temu… VUE – ta nazwa tkwiła w mojej podświadomości jak drzazga. Niby obca a jednak znajoma. „Wyguglałem” i wyskoczyło mi Vue.js – czyli…","2018-09-14",[2003,1943],"polecane",[1986,2005,2006,2007,1961],"vuejs","vuenews","vuetoolbox","https:\u002F\u002F5icqmonbzxnrw2bn.public.blob.vercel-storage.com\u002Fwp\u002F2018\u002F09\u002Fvuejs-tutorial_2d2a853c-aa2f-44b0-80df-933b495f77f8.png",[2010,2017,2024,2031,2038,2045,2052,2058,2065,2071],{"id":2011,"description":1962,"extension":2012,"meta":2013,"name":2014,"slug":1942,"stem":2015,"__hash__":2016},"categories\u002Fcategories\u002Fartykuly.yml","yml",{},"Artykuły","categories\u002Fartykuly","aw37OCuccDz54Q07CDyESiid8VqHzN1nGf8cMOk_mI0",{"id":2018,"description":1962,"extension":2012,"meta":2019,"name":2020,"slug":2021,"stem":2022,"__hash__":2023},"categories\u002Fcategories\u002Fdostepnosc-cyfrowa.yml",{},"Dostępność cyfrowa","dostepnosc-cyfrowa","categories\u002Fdostepnosc-cyfrowa","6m7Ag8ZZ1EzfdgLuQnAUAMmRcjkgb2tpFM9n1qxANOM",{"id":2025,"description":1962,"extension":2012,"meta":2026,"name":2027,"slug":2028,"stem":2029,"__hash__":2030},"categories\u002Fcategories\u002Fkonferencje.yml",{},"Konferencje","konferencje","categories\u002Fkonferencje","dQl4MW7163iEG-b7Cv6FJ2gUxLqnpaiMOLixCXvhHTw",{"id":2032,"description":1962,"extension":2012,"meta":2033,"name":2034,"slug":2035,"stem":2036,"__hash__":2037},"categories\u002Fcategories\u002Fkonfiguracja.yml",{},"Konfiguracja","konfiguracja","categories\u002Fkonfiguracja","ESTOPlJnBPnwuuqgOm7yqGAwgllsRJfSPVk22K_nQe0",{"id":2039,"description":1962,"extension":2012,"meta":2040,"name":2041,"slug":2042,"stem":2043,"__hash__":2044},"categories\u002Fcategories\u002Fnauka.yml",{},"Nauka","nauka","categories\u002Fnauka","3hQRPpuXqoNblW09Gj46arWFgH-3YfsMcgDyd4xLkN4",{"id":2046,"description":1962,"extension":2012,"meta":2047,"name":2048,"slug":2049,"stem":2050,"__hash__":2051},"categories\u002Fcategories\u002Fosx.yml",{},"OSX","osx","categories\u002Fosx","WW8-0hUIqcivgAUl8WQuAvJ3-xVLRLjLkmP00PP4eXY",{"id":2053,"description":1962,"extension":2012,"meta":2054,"name":2055,"slug":2003,"stem":2056,"__hash__":2057},"categories\u002Fcategories\u002Fpolecane.yml",{},"Polecane","categories\u002Fpolecane","ckLXqMEBUHLxZavR7FGvpQjhxc7ycP_sQ7myEw64R9I",{"id":2059,"description":1962,"extension":2012,"meta":2060,"name":2061,"slug":2062,"stem":2063,"__hash__":2064},"categories\u002Fcategories\u002Fpomoce.yml",{},"Pomoce","pomoce","categories\u002Fpomoce","3oKSrG-VgrihP1nofQwuUItxUsyzVfgEfTMF2vq7EIw",{"id":2066,"description":1962,"extension":2012,"meta":2067,"name":2068,"slug":1982,"stem":2069,"__hash__":2070},"categories\u002Fcategories\u002Fprezentacje.yml",{},"Prezentacje","categories\u002Fprezentacje","4L5LUH8zxl0qXRvdymYTwtrkuj1tQun8R7yYz_OfndE",{"id":2072,"description":1962,"extension":2012,"meta":2073,"name":2074,"slug":1943,"stem":2075,"__hash__":2076},"categories\u002Fcategories\u002Fvue-js.yml",{},"Vue.js","categories\u002Fvue-js","wOIuiF3jWQ-Zph8v7hYGNuLdJbCx0VguVg0loyh-Mwk",{"id":2078,"avatar":2079,"bio":2080,"extension":2012,"intro":2081,"links":2082,"meta":2087,"name":2088,"slug":6,"stem":2089,"__hash__":2090},"authors\u002Fauthors\u002Fmaciek-walczak.yml","\u002Fmaciek.png","Front-End Developer, Team Leader, trener i wykładowca na kursach programowania.","Cześć! Nazywam się Maciek i od 2005 roku pracuję jako programista – najpierw Full Stack, a obecnie jako Front-End Developer i Team Leader. Udzielam się też jako trener – prowadzę zajęcia w ramach kursów dla osób, które chcą rozpocząć swoją przygodę z programowaniem. Miałem też okazję występować jako prelegent na spotkaniach z serii tech3camp. Zapraszam do lektury mojego bloga, gdzie staram się publikować swoje przemyślenia w tematach mniej lub bardziej związanych z front-endem.",[2083],{"label":2084,"url":2085,"icon":2086},"YouTube","https:\u002F\u002Fwww.youtube.com\u002Fchannel\u002FUCMG_PvHPcSe0TCmnIXvzB1Q","i-lucide-youtube",{},"Maciej Walczak","authors\u002Fmaciek-walczak","6aJnd9c5TZ6nsZd_ycEjnlIJ4stHJREQCAgigfsc_Hk",[2092,2098,2105,2112,2119,2126,2132,2139,2146,2153,2160,2167,2174,2181,2188,2195,2201,2208,2215,2221,2227,2233,2239,2245,2251,2257,2264,2269,2275,2281,2287,2294,2300,2307,2314,2320,2327,2334,2340,2345,2350,2356,2363,2370,2376,2383,2390,2395,2402,2409,2416,2422,2429,2434,2441,2448,2454,2461,2467,2473,2480,2486,2493,2498,2504,2510,2516,2523,2529,2536,2541,2547,2554,2561,2568,2575,2581,2587,2594,2601,2608,2614,2620,2627,2634,2640,2647,2653,2660,2667,2672,2678,2684,2691,2697,2703,2710,2717,2723,2729,2734,2739,2744,2749,2756,2763,2770],{"id":2093,"extension":2012,"meta":2094,"name":2095,"slug":2095,"stem":2096,"__hash__":2097},"tags\u002Ftags\u002Faccessibility.yml",{},"accessibility","tags\u002Faccessibility","luflFOwd8bzxVK2qeBv80Pzxhje6x-hbJYaDUihuwOk",{"id":2099,"extension":2012,"meta":2100,"name":2101,"slug":2102,"stem":2103,"__hash__":2104},"tags\u002Ftags\u002Fangular.yml",{},"Angular","angular","tags\u002Fangular","z6SH0xA8FvB2X9kVmW-gtOdfx__mVrgIuA7HVCzfLjo",{"id":2106,"extension":2012,"meta":2107,"name":2108,"slug":2109,"stem":2110,"__hash__":2111},"tags\u002Ftags\u002Fapache2.yml",{},"Apache2","apache2","tags\u002Fapache2","unCvGoDYof2-UiVE0lnN8eq5ru5roRg5_2mAo4TdEHk",{"id":2113,"extension":2012,"meta":2114,"name":2115,"slug":2116,"stem":2117,"__hash__":2118},"tags\u002Ftags\u002Faplikacje-i-uslugi-internetowe.yml",{},"aplikacje i usługi internetowe","aplikacje-i-uslugi-internetowe","tags\u002Faplikacje-i-uslugi-internetowe","G2Vojqp3Zd7QgL3zFleglcNCFkjzkAAeDQCScU6P8bc",{"id":2120,"extension":2012,"meta":2121,"name":2122,"slug":2123,"stem":2124,"__hash__":2125},"tags\u002Ftags\u002Fasynchronicznosc.yml",{},"asynchroniczność","asynchronicznosc","tags\u002Fasynchronicznosc","9RMRbHed9wQxDzVpEjO6Ay9zxWDCI0e4hE8F4zqGgj0",{"id":2127,"extension":2012,"meta":2128,"name":2129,"slug":2129,"stem":2130,"__hash__":2131},"tags\u002Ftags\u002Fautomatyzacja.yml",{},"automatyzacja","tags\u002Fautomatyzacja","vfUpLN8opVGvKUQgjGwEYxKfsZsLo3jxneMF9YMMVD0",{"id":2133,"extension":2012,"meta":2134,"name":2135,"slug":2136,"stem":2137,"__hash__":2138},"tags\u002Ftags\u002Fblokowanie-reklam.yml",{},"blokowanie reklam","blokowanie-reklam","tags\u002Fblokowanie-reklam","lq-tfiZ_uB_SerIlaIQFMOXvGIF3QoVVVyPxjwrEsR4",{"id":2140,"extension":2012,"meta":2141,"name":2142,"slug":2143,"stem":2144,"__hash__":2145},"tags\u002Ftags\u002Fblue-media.yml",{},"Blue Media","blue-media","tags\u002Fblue-media","z8G577CxZ7uSy-oNOkWtn3XgbsbjeKhuNby5Q6BCRuM",{"id":2147,"extension":2012,"meta":2148,"name":2149,"slug":2150,"stem":2151,"__hash__":2152},"tags\u002Ftags\u002Fchrome-extensions.yml",{},"chrome extensions","chrome-extensions","tags\u002Fchrome-extensions","Ip0fOTD9TMJiHcbXwgvAB2mFf_D1rlP5ZfkJG3GbCL0",{"id":2154,"extension":2012,"meta":2155,"name":2156,"slug":2157,"stem":2158,"__hash__":2159},"tags\u002Ftags\u002Fcmr.yml",{},"CMR","cmr","tags\u002Fcmr","eNv_ncSDXVsAFBCM8A4uJqAJItqSOP29I1Kabpm-l3Y",{"id":2161,"extension":2012,"meta":2162,"name":2163,"slug":2164,"stem":2165,"__hash__":2166},"tags\u002Ftags\u002Fcodersday.yml",{},"CodersDay","codersday","tags\u002Fcodersday","jLWUVGvGvi2irKFpFs0Ea6hkW71mLoiroOaGDcv1KWw",{"id":2168,"extension":2012,"meta":2169,"name":2170,"slug":2171,"stem":2172,"__hash__":2173},"tags\u002Ftags\u002Fcoderslab.yml",{},"CodersLab","coderslab","tags\u002Fcoderslab","NQEy1mD_tk9h71yZtjSoC4LhXGE54DGYaYgj3JOoUqY",{"id":2175,"extension":2012,"meta":2176,"name":2177,"slug":2178,"stem":2179,"__hash__":2180},"tags\u002Ftags\u002Fcolor-contrast-analyzer.yml",{},"Color Contrast Analyzer","color-contrast-analyzer","tags\u002Fcolor-contrast-analyzer","hGu3gO_owf9Tn4p5-FssJGVt4ToSsRfgjMjWdX2YgEI",{"id":2182,"extension":2012,"meta":2183,"name":2184,"slug":2185,"stem":2186,"__hash__":2187},"tags\u002Ftags\u002Fcolor-contrast-checker.yml",{},"Color Contrast Checker","color-contrast-checker","tags\u002Fcolor-contrast-checker","w8QKUWUH691wTFtUWDJMjbCvC6zlFZo6j4UDDSg1NDY",{"id":2189,"extension":2012,"meta":2190,"name":2191,"slug":2192,"stem":2193,"__hash__":2194},"tags\u002Ftags\u002Fconfrontjs.yml",{},"ConFrontJS","confrontjs","tags\u002Fconfrontjs","G4ePmrCbuLVA_ccxs7rcKSveQ3Kvn7y6-Ie9-H_5gpc",{"id":2196,"extension":2012,"meta":2197,"name":2198,"slug":2198,"stem":2199,"__hash__":2200},"tags\u002Ftags\u002Fcss3.yml",{},"css3","tags\u002Fcss3","f5Gtp9yzvmyMSZ-IN--19c7OR-v5LXzdvhNaEmiOsuw",{"id":2202,"extension":2012,"meta":2203,"name":2204,"slug":2205,"stem":2206,"__hash__":2207},"tags\u002Ftags\u002Fdesi-2020.yml",{},"DESI 2020","desi-2020","tags\u002Fdesi-2020","KeSFSj6VcXN0OciAUSTR8C1kiGUE3nLzGR1Pi5h_USE",{"id":2209,"extension":2012,"meta":2210,"name":2211,"slug":2212,"stem":2213,"__hash__":2214},"tags\u002Ftags\u002Fdns.yml",{},"DNS","dns","tags\u002Fdns","_TzsfpRL_ROm8MT5opDE1gg4GnuukbCyKY87W-IFl68",{"id":2216,"extension":2012,"meta":2217,"name":2218,"slug":2021,"stem":2219,"__hash__":2220},"tags\u002Ftags\u002Fdostepnosc-cyfrowa.yml",{},"dostępność cyfrowa","tags\u002Fdostepnosc-cyfrowa","8xZ6WRsys1qnr-vWH_Y4Ne8ac2RBONWyLrrm31u00xI",{"id":2222,"extension":2012,"meta":2223,"name":2224,"slug":2224,"stem":2225,"__hash__":2226},"tags\u002Ftags\u002Febook.yml",{},"ebook","tags\u002Febook","2Eqjl28-mK0fxpryAfGFMCel8MSByJVuQRsvy_S_cgY",{"id":2228,"extension":2012,"meta":2229,"name":2230,"slug":2230,"stem":2231,"__hash__":2232},"tags\u002Ftags\u002Felgato.yml",{},"elgato","tags\u002Felgato","ys3dC5VhSFe1JP_z6vIBB8W8WSJmrbB2C3gwTjSkINM",{"id":2234,"extension":2012,"meta":2235,"name":2236,"slug":2236,"stem":2237,"__hash__":2238},"tags\u002Ftags\u002Fevent.yml",{},"event","tags\u002Fevent","xcUCN7zoFrW8LCakRgx4s2r8YTGvycTdYQEyx_Yg7Fk",{"id":2240,"extension":2012,"meta":2241,"name":2242,"slug":2242,"stem":2243,"__hash__":2244},"tags\u002Ftags\u002Ffacebook.yml",{},"facebook","tags\u002Ffacebook","xk1FSKdOP155PdsTUy6lbefdoi6pOrwzZtVO8B52vMo",{"id":2246,"extension":2012,"meta":2247,"name":2248,"slug":2248,"stem":2249,"__hash__":2250},"tags\u002Ftags\u002Ffanpage.yml",{},"fanpage","tags\u002Ffanpage","RoMU1ceix1opnNG2LPoGzfOjVZE128Gmk12ZtDAQxrM",{"id":2252,"extension":2012,"meta":2253,"name":2254,"slug":2254,"stem":2255,"__hash__":2256},"tags\u002Ftags\u002Ffront-end.yml",{},"front-end","tags\u002Ffront-end","xKzeWb2B7yr2l4WBGtpyUzD1T_sBK5i-sb21-MEDOWw",{"id":2258,"extension":2012,"meta":2259,"name":2260,"slug":2261,"stem":2262,"__hash__":2263},"tags\u002Ftags\u002Ffront-end-developer.yml",{},"front-end developer","front-end-developer","tags\u002Ffront-end-developer","rHwMfWcbpd8wXqIVD1HzPWxBheHq8V_9TdHCHkz4Sp8",{"id":2265,"extension":2012,"meta":2266,"name":1995,"slug":1995,"stem":2267,"__hash__":2268},"tags\u002Ftags\u002Ffrontend.yml",{},"tags\u002Ffrontend","CxnBlT8roa7DydGYGxgRVXKKTvJrkjMMQrvHZQMxukA",{"id":2270,"extension":2012,"meta":2271,"name":2272,"slug":2272,"stem":2273,"__hash__":2274},"tags\u002Ftags\u002Fgamepad.yml",{},"gamepad","tags\u002Fgamepad","Gsi3HzAfw68AJR6qCt8qeuS7Euw22ZMSpnaKNw0QV-A",{"id":2276,"extension":2012,"meta":2277,"name":2278,"slug":2278,"stem":2279,"__hash__":2280},"tags\u002Ftags\u002Fgoalkicker.yml",{},"goalkicker","tags\u002Fgoalkicker","L_zNfxJfO6lZOUiLaE5m_ggQUX0komBXXth0SrszFUk",{"id":2282,"extension":2012,"meta":2283,"name":2284,"slug":2284,"stem":2285,"__hash__":2286},"tags\u002Ftags\u002Fhandbook.yml",{},"handbook","tags\u002Fhandbook","D0-VmrV-_WQQnlVvaePXWciWrJ8zuWNJoJULW_rOpmI",{"id":2288,"extension":2012,"meta":2289,"name":2290,"slug":2291,"stem":2292,"__hash__":2293},"tags\u002Ftags\u002Fhtml-api.yml",{},"HTML API","html-api","tags\u002Fhtml-api","DBZv8DJ2hZR64GhJALF_-YzUG4mZWG9m2VZE76eM_wY",{"id":2295,"extension":2012,"meta":2296,"name":2297,"slug":2297,"stem":2298,"__hash__":2299},"tags\u002Ftags\u002Fhtml5.yml",{},"html5","tags\u002Fhtml5","fcqPnoul30jPoEk6tO8K9WWwU8XyFz3N7-GAse2uwsQ",{"id":2301,"extension":2012,"meta":2302,"name":2303,"slug":2304,"stem":2305,"__hash__":2306},"tags\u002Ftags\u002Finteligentny-dom.yml",{},"inteligentny dom","inteligentny-dom","tags\u002Finteligentny-dom","LwWCH_MYDfKSgfl9wyRWrWegvCgb2hAnXMlON3OQ0dk",{"id":2308,"extension":2012,"meta":2309,"name":2310,"slug":2311,"stem":2312,"__hash__":2313},"tags\u002Ftags\u002Fjaws.yml",{},"Jaws","jaws","tags\u002Fjaws","aklZIj6z2_fzRQ_h9RtAfvXgDhHH9v02f224m6u8B7s",{"id":2315,"extension":2012,"meta":2316,"name":2317,"slug":2317,"stem":2318,"__hash__":2319},"tags\u002Ftags\u002Fjunior.yml",{},"junior","tags\u002Fjunior","ega8Cjq-Z5swNxexQG-ROW8hvi0eaDCJj-l4ILDW-4U",{"id":2321,"extension":2012,"meta":2322,"name":2323,"slug":2324,"stem":2325,"__hash__":2326},"tags\u002Ftags\u002Fkariera-w-it.yml",{},"kariera w IT","kariera-w-it","tags\u002Fkariera-w-it","tyqsMUw9eQaVgTnsZy9DZAHFQXZNe0sDZavdgJ0KTcA",{"id":2328,"extension":2012,"meta":2329,"name":2330,"slug":2331,"stem":2332,"__hash__":2333},"tags\u002Ftags\u002Fkobiety-w-it.yml",{},"kobiety w IT","kobiety-w-it","tags\u002Fkobiety-w-it","Gm0D6QX7IUT1CDThH6LuhQtWOOWFop09QiDZwX_Tm7c",{"id":2335,"extension":2012,"meta":2336,"name":2337,"slug":2337,"stem":2338,"__hash__":2339},"tags\u002Ftags\u002Fkonferencja.yml",{},"konferencja","tags\u002Fkonferencja","-n95F1XvVC7rR9O9sOxyxnKIJBm4i5o3Md_Q-Z72occ",{"id":2341,"extension":2012,"meta":2342,"name":2028,"slug":2028,"stem":2343,"__hash__":2344},"tags\u002Ftags\u002Fkonferencje.yml",{},"tags\u002Fkonferencje","Qf_9S1xyR0mqWi1CzdU_iGIwcU8Z0ET_3JBEIOWhaxA",{"id":2346,"extension":2012,"meta":2347,"name":2035,"slug":2035,"stem":2348,"__hash__":2349},"tags\u002Ftags\u002Fkonfiguracja.yml",{},"tags\u002Fkonfiguracja","r2NNwL_ZY7xvPy0pUN2IPK8U6qBXSfay_MZbt1FpC2I",{"id":2351,"extension":2012,"meta":2352,"name":2353,"slug":2353,"stem":2354,"__hash__":2355},"tags\u002Ftags\u002Fkoronawirus.yml",{},"koronawirus","tags\u002Fkoronawirus","8DgCLCZ1uSF_kl0dmgiEVVM-KkZFxGpXJus3fmMH4sE",{"id":2357,"extension":2012,"meta":2358,"name":2359,"slug":2360,"stem":2361,"__hash__":2362},"tags\u002Ftags\u002Fksiazki-programistyczne.yml",{},"książki programistyczne","ksiazki-programistyczne","tags\u002Fksiazki-programistyczne","DF1l0MrjODlaeQ0evw8JedIJJb3v4MpesV0Fi6HpViQ",{"id":2364,"extension":2012,"meta":2365,"name":2366,"slug":2367,"stem":2368,"__hash__":2369},"tags\u002Ftags\u002Flenovo-m710q.yml",{},"lenovo m710q","lenovo-m710q","tags\u002Flenovo-m710q","H2DSlpm_FUSkpZXj5AZxrXjTzdGXcfWhIFXE8MAjoJw",{"id":2371,"extension":2012,"meta":2372,"name":2373,"slug":2373,"stem":2374,"__hash__":2375},"tags\u002Ftags\u002Fmid.yml",{},"mid","tags\u002Fmid","61US_hzDYC5fw5yPUWya7ZNjc9fOddRUJyOJU3oVKaM",{"id":2377,"extension":2012,"meta":2378,"name":2379,"slug":2380,"stem":2381,"__hash__":2382},"tags\u002Ftags\u002Fmini-pc.yml",{},"mini pc","mini-pc","tags\u002Fmini-pc","zCEZkcdMTwLbaNGmGiT8qGCtnECxEafkiJ-glDrozw0",{"id":2384,"extension":2012,"meta":2385,"name":2386,"slug":2387,"stem":2388,"__hash__":2389},"tags\u002Ftags\u002Fnas.yml",{},"NAS","nas","tags\u002Fnas","RsN64SVV6BmbaR7e4pVVvGgbxQ0q8ihj5J4b_1oPH2k",{"id":2391,"extension":2012,"meta":2392,"name":2042,"slug":2042,"stem":2393,"__hash__":2394},"tags\u002Ftags\u002Fnauka.yml",{},"tags\u002Fnauka","bMIyGRMcT5SRj0bxdrER6sibTNLAHr1enmElQI67jTo",{"id":2396,"extension":2012,"meta":2397,"name":2398,"slug":2399,"stem":2400,"__hash__":2401},"tags\u002Ftags\u002Fnauka-programowania.yml",{},"nauka programowania","nauka-programowania","tags\u002Fnauka-programowania","M04c5RtH0Fu4ikOuW9fLy2-g2YBIAX0Yj9SHKEwOuUg",{"id":2403,"extension":2012,"meta":2404,"name":2405,"slug":2406,"stem":2407,"__hash__":2408},"tags\u002Ftags\u002Fnvda.yml",{},"NVDA","nvda","tags\u002Fnvda","AS3XsW73sA9-MAI4JVv4y7luo2oGkpXrGptc51MKLQY",{"id":2410,"extension":2012,"meta":2411,"name":2412,"slug":2413,"stem":2414,"__hash__":2415},"tags\u002Ftags\u002Fochrona-rodzicielska.yml",{},"ochrona rodzicielska","ochrona-rodzicielska","tags\u002Fochrona-rodzicielska","DZHsS1aBzb6OmeQFhCXy3Qu3QUMHsrbbN62bXmyBB0Q",{"id":2417,"extension":2012,"meta":2418,"name":2419,"slug":2419,"stem":2420,"__hash__":2421},"tags\u002Ftags\u002Foptimization.yml",{},"optimization","tags\u002Foptimization","fJedkuv-GIoYXYs-z7AFgjvCxunavWHOWZDesN1EgFU",{"id":2423,"extension":2012,"meta":2424,"name":2425,"slug":2426,"stem":2427,"__hash__":2428},"tags\u002Ftags\u002Forganizacja-pracy.yml",{},"organizacja pracy","organizacja-pracy","tags\u002Forganizacja-pracy","IJWHAuqRIlzOIgQbp6B0dYJLpApXOyvDii-k8n_CUCQ",{"id":2430,"extension":2012,"meta":2431,"name":2048,"slug":2049,"stem":2432,"__hash__":2433},"tags\u002Ftags\u002Fosx.yml",{},"tags\u002Fosx","QymfouNjzTcNrsPkrZeaPTZdKpMsdYilpn7HRI1FxQQ",{"id":2435,"extension":2012,"meta":2436,"name":2437,"slug":2438,"stem":2439,"__hash__":2440},"tags\u002Ftags\u002Fpanel-macro.yml",{},"panel macro","panel-macro","tags\u002Fpanel-macro","WrhWywY5PTfhm7n_TuxTZEWwebeWKfYiQf6n6nh19t4",{"id":2442,"extension":2012,"meta":2443,"name":2444,"slug":2445,"stem":2446,"__hash__":2447},"tags\u002Ftags\u002Fpassword-manager.yml",{},"password manager","password-manager","tags\u002Fpassword-manager","viAfKlsGbzjczJJrb8pwaB4tInjCfLzynbSm-GRQz4Y",{"id":2449,"extension":2012,"meta":2450,"name":2451,"slug":2451,"stem":2452,"__hash__":2453},"tags\u002Ftags\u002Fpdf.yml",{},"pdf","tags\u002Fpdf","UTB02hK82S5LzDB9kqZWrDRFb8SYzHfFRgCsqFwdBcE",{"id":2455,"extension":2012,"meta":2456,"name":2457,"slug":2458,"stem":2459,"__hash__":2460},"tags\u002Ftags\u002Fphp.yml",{},"PHP","php","tags\u002Fphp","Id59psmnOEw6xZ04LGSXqaCSXElgAZVfZdIWk4TTMmU",{"id":2462,"extension":2012,"meta":2463,"name":2464,"slug":2464,"stem":2465,"__hash__":2466},"tags\u002Ftags\u002Fpi-hole.yml",{},"pi-hole","tags\u002Fpi-hole","1YJ1tr1Tr23ItFg6rYXw6iKjvPJCdYb2MB_5W1FdZNw",{"id":2468,"extension":2012,"meta":2469,"name":2470,"slug":2470,"stem":2471,"__hash__":2472},"tags\u002Ftags\u002Fpixel-mania.yml",{},"pixel-mania","tags\u002Fpixel-mania","N9WX_K8mFmAndjRdRzZX1PonLHyneP4XCJe9kQlGOpY",{"id":2474,"extension":2012,"meta":2475,"name":2476,"slug":2477,"stem":2478,"__hash__":2479},"tags\u002Ftags\u002Fplaywright.yml",{},"PlayWright","playwright","tags\u002Fplaywright","fZAbd5k3k85yeGire7iJzvnRk00Wfww8OTu1h6rEuZM",{"id":2481,"extension":2012,"meta":2482,"name":2483,"slug":2483,"stem":2484,"__hash__":2485},"tags\u002Ftags\u002Fpraca.yml",{},"praca","tags\u002Fpraca","ra3_bHnZqDrux1PiJ_pW6eqfX7l-uIFUPUhPqaq5cf4",{"id":2487,"extension":2012,"meta":2488,"name":2489,"slug":2490,"stem":2491,"__hash__":2492},"tags\u002Ftags\u002Fpraca-zdalna.yml",{},"praca zdalna","praca-zdalna","tags\u002Fpraca-zdalna","HD5EOgzs-b3t5KIBRxlWQr0FuQVJ4N7eLQJV77YWcqc",{"id":2494,"extension":2012,"meta":2495,"name":1984,"slug":1984,"stem":2496,"__hash__":2497},"tags\u002Ftags\u002Fprezentacja.yml",{},"tags\u002Fprezentacja","eiP5x-l400TtOy-gxO3TyOqYwcD4LP65jJ--7H-jyng",{"id":2499,"extension":2012,"meta":2500,"name":2501,"slug":2501,"stem":2502,"__hash__":2503},"tags\u002Ftags\u002Fprivacy.yml",{},"privacy","tags\u002Fprivacy","Ln1AAZQ3fPOzuMJB0BWwUsjwaWTI7cFtzI6TaPrvKXc",{"id":2505,"extension":2012,"meta":2506,"name":2507,"slug":2507,"stem":2508,"__hash__":2509},"tags\u002Ftags\u002Fprogramowanie.yml",{},"programowanie","tags\u002Fprogramowanie","EUO8owu6V8cJivzRtVvD12KgW4ojOCp-N7aI4euHEa0",{"id":2511,"extension":2012,"meta":2512,"name":2513,"slug":2513,"stem":2514,"__hash__":2515},"tags\u002Ftags\u002Fproxmox.yml",{},"proxmox","tags\u002Fproxmox","2AZfZmPZffxT3YuTVGHbsGvqKzpNRfot-OjFS6kkSYs",{"id":2517,"extension":2012,"meta":2518,"name":2519,"slug":2520,"stem":2521,"__hash__":2522},"tags\u002Ftags\u002Fprywatny-dns.yml",{},"prywatny DNS","prywatny-dns","tags\u002Fprywatny-dns","kq-27-SNzP2YitFANg5iLgu0kdzyufoiOCLYApAehUc",{"id":2524,"extension":2012,"meta":2525,"name":2526,"slug":2526,"stem":2527,"__hash__":2528},"tags\u002Ftags\u002Fprzekwalifikowanie.yml",{},"przekwalifikowanie","tags\u002Fprzekwalifikowanie","TqQNM6H8dfkRMTjNT8ZWyzynSMGeFjxseCt7-fZq0nA",{"id":2530,"extension":2012,"meta":2531,"name":2532,"slug":2533,"stem":2534,"__hash__":2535},"tags\u002Ftags\u002Freact.yml",{},"React","react","tags\u002Freact","nXIcvF19iFz3UIXk2jbbJKRrr1_S9NLNYH3PUX_a2C4",{"id":2537,"extension":2012,"meta":2538,"name":1996,"slug":1996,"stem":2539,"__hash__":2540},"tags\u002Ftags\u002Fredux.yml",{},"tags\u002Fredux","CYtiFQKJae6MZE4Z_2qWwt7OGctPAutMzfD9f35a6-A",{"id":2542,"extension":2012,"meta":2543,"name":2544,"slug":2544,"stem":2545,"__hash__":2546},"tags\u002Ftags\u002Fretro.yml",{},"retro","tags\u002Fretro","5AjMkMU44tDn8TV2FSTzbrvxoE7rplMmkLej7E-RIYM",{"id":2548,"extension":2012,"meta":2549,"name":2550,"slug":2551,"stem":2552,"__hash__":2553},"tags\u002Ftags\u002Fretropi.yml",{},"RetroPi","retropi","tags\u002Fretropi","v_z0VjrAHiqYkzENB2DRMmo-V9VwE-8D2V_pRT-EBxw",{"id":2555,"extension":2012,"meta":2556,"name":2557,"slug":2558,"stem":2559,"__hash__":2560},"tags\u002Ftags\u002Frozwoj.yml",{},"rozwój","rozwoj","tags\u002Frozwoj","RkL4It2FQnfcyHt1gNPjQ7cLqxrpgMy9ewFnPkF5sms",{"id":2562,"extension":2012,"meta":2563,"name":2564,"slug":2565,"stem":2566,"__hash__":2567},"tags\u002Ftags\u002Frzemieslnik.yml",{},"rzemieślnik","rzemieslnik","tags\u002Frzemieslnik","fLVgsq47mga12X6gNKQ6QM5tpWdwICnLJwfSDwk1xEE",{"id":2569,"extension":2012,"meta":2570,"name":2571,"slug":2572,"stem":2573,"__hash__":2574},"tags\u002Ftags\u002Fsamodzielnosc.yml",{},"samodzielność","samodzielnosc","tags\u002Fsamodzielnosc","mu6DqJVN-dhRiXCnV4A5ewTRx_ndkWH_pu7Fdf7n6XI",{"id":2576,"extension":2012,"meta":2577,"name":2578,"slug":2578,"stem":2579,"__hash__":2580},"tags\u002Ftags\u002Fselfhosting.yml",{},"selfhosting","tags\u002Fselfhosting","ScAMu2i5h-QpOOGEuIvBN9fQn0ZmfyYfvD-561cUzKs",{"id":2582,"extension":2012,"meta":2583,"name":2584,"slug":2584,"stem":2585,"__hash__":2586},"tags\u002Ftags\u002Fsenior.yml",{},"senior","tags\u002Fsenior","DFUFihqxLSI2g34_DFYLVSXQnP79966itG8NnBs44TE",{"id":2588,"extension":2012,"meta":2589,"name":2590,"slug":2591,"stem":2592,"__hash__":2593},"tags\u002Ftags\u002Fsieci-komputerowe.yml",{},"sieci komputerowe","sieci-komputerowe","tags\u002Fsieci-komputerowe","lziU51DXijHvzSYVgbK5r_aRCBS9KLdgU5FWjE3pD4s",{"id":2595,"extension":2012,"meta":2596,"name":2597,"slug":2598,"stem":2599,"__hash__":2600},"tags\u002Ftags\u002Fsmart-home.yml",{},"smart home","smart-home","tags\u002Fsmart-home","wjGS16vdTWjBvPIfyZ7Jjv2rSAUfgKxYQTfVmfE_oNc",{"id":2602,"extension":2012,"meta":2603,"name":2604,"slug":2605,"stem":2606,"__hash__":2607},"tags\u002Ftags\u002Fsmr.yml",{},"SMR","smr","tags\u002Fsmr","hd_86HRNRQ5vp9GFDgvFR3iXcRs57XNvESz05e-vh-o",{"id":2609,"extension":2012,"meta":2610,"name":2611,"slug":2611,"stem":2612,"__hash__":2613},"tags\u002Ftags\u002Fspotkanie.yml",{},"spotkanie","tags\u002Fspotkanie","G-4xZsmDWDaZnA1t7fWrsGY0CvA2PWBRch_ISxgV3eY",{"id":2615,"extension":2012,"meta":2616,"name":2617,"slug":1960,"stem":2618,"__hash__":2619},"tags\u002Ftags\u002Fstore-management.yml",{},"store management","tags\u002Fstore-management","BDlb8jliEvzP4ByR_nwh5YcPAphegyjsvxnwheppvwM",{"id":2621,"extension":2012,"meta":2622,"name":2623,"slug":2624,"stem":2625,"__hash__":2626},"tags\u002Ftags\u002Fstream-deck.yml",{},"stream deck","stream-deck","tags\u002Fstream-deck","ETKaTcPMSGRXiF8fX9HaCChrkWvjK1Qhc8DCtKNRLFo",{"id":2628,"extension":2012,"meta":2629,"name":2630,"slug":2631,"stem":2632,"__hash__":2633},"tags\u002Ftags\u002Fstudia-podyplomowe.yml",{},"studia podyplomowe","studia-podyplomowe","tags\u002Fstudia-podyplomowe","CllbIMrpHwk7nHpdgNS5EJlfoY37AYtu6D28xcL7B8Y",{"id":2635,"extension":2012,"meta":2636,"name":2637,"slug":2637,"stem":2638,"__hash__":2639},"tags\u002Ftags\u002Fswitch.yml",{},"switch","tags\u002Fswitch","PqeuQm8ai89DcVFTdIBBYnrs6IVq0oystkF3JdV2cbQ",{"id":2641,"extension":2012,"meta":2642,"name":2643,"slug":2644,"stem":2645,"__hash__":2646},"tags\u002Ftags\u002Fszafa-rack.yml",{},"szafa rack","szafa-rack","tags\u002Fszafa-rack","Tdq-0TqjtHfOvyhnHIwrDsPzQYMI6Rkb3iJBoqgudAc",{"id":2648,"extension":2012,"meta":2649,"name":2650,"slug":2650,"stem":2651,"__hash__":2652},"tags\u002Ftags\u002Fszkolenia.yml",{},"szkolenia","tags\u002Fszkolenia","Ty9KLofCdboQngGvYkJOH1YZpBX-E3_Y1SLrdGucptY",{"id":2654,"extension":2012,"meta":2655,"name":2656,"slug":2657,"stem":2658,"__hash__":2659},"tags\u002Ftags\u002Fszkolenie-front-end.yml",{},"szkolenie front-end","szkolenie-front-end","tags\u002Fszkolenie-front-end","yv2XwBbhwF8m1vceHXglBt_lax32HDj4iqYS8ymfIpM",{"id":2661,"extension":2012,"meta":2662,"name":2663,"slug":2664,"stem":2665,"__hash__":2666},"tags\u002Ftags\u002Fszkolnictwo-zawodowe.yml",{},"szkolnictwo zawodowe","szkolnictwo-zawodowe","tags\u002Fszkolnictwo-zawodowe","wHy70cvPzW-iXNTesa8dnmsUN3GZ6RQ_08rvp2jcr-o",{"id":2668,"extension":2012,"meta":2669,"name":1985,"slug":1985,"stem":2670,"__hash__":2671},"tags\u002Ftags\u002Ftech3camp.yml",{},"tags\u002Ftech3camp","2xN4prsqldeVcTsCibyLj-T84k_q27SekfsQ9H3ml6w",{"id":2673,"extension":2012,"meta":2674,"name":2675,"slug":2675,"stem":2676,"__hash__":2677},"tags\u002Ftags\u002Ftestowanie.yml",{},"testowanie","tags\u002Ftestowanie","bQ7J7HLrZe6c9Me3bn_3VCbshav5tsqNTBIwd0xkRaQ",{"id":2679,"extension":2012,"meta":2680,"name":2681,"slug":2681,"stem":2682,"__hash__":2683},"tags\u002Ftags\u002Ftesty.yml",{},"testy","tags\u002Ftesty","JDNtVkIDLY5e-th779qHOQFRrQ6Ywoq8us2HXlBz9PY",{"id":2685,"extension":2012,"meta":2686,"name":2687,"slug":2688,"stem":2689,"__hash__":2690},"tags\u002Ftags\u002Ftruenas.yml",{},"TrueNas","truenas","tags\u002Ftruenas","jGviBDrcOLcc4LIDAMUINs_clt6cqQIbnmCsGjlBoEU",{"id":2692,"extension":2012,"meta":2693,"name":2694,"slug":2694,"stem":2695,"__hash__":2696},"tags\u002Ftags\u002Ftuya.yml",{},"tuya","tags\u002Ftuya","MwpPoYlxeAiebaRVNdxytmFq9k0BpiTEbiEvwh4pfq4",{"id":2698,"extension":2012,"meta":2699,"name":2700,"slug":2700,"stem":2701,"__hash__":2702},"tags\u002Ftags\u002Fublock.yml",{},"ublock","tags\u002Fublock","EPnKEvKkfWB2hbpkolTIytNQ9c12jSrnctf8CWEVbUM",{"id":2704,"extension":2012,"meta":2705,"name":2706,"slug":2707,"stem":2708,"__hash__":2709},"tags\u002Ftags\u002Fvirtual-host.yml",{},"virtual host","virtual-host","tags\u002Fvirtual-host","Ru2r4wU7txKcWDbL3hq2QGx7v8gKwTJE25BItrzjqCQ",{"id":2711,"extension":2012,"meta":2712,"name":2713,"slug":2714,"stem":2715,"__hash__":2716},"tags\u002Ftags\u002Fvoiceover.yml",{},"VoiceOver","voiceover","tags\u002Fvoiceover","7_jZqkfOk163vezQCATCpkIhdxmxfXe5dsLjgORGRWc",{"id":2718,"extension":2012,"meta":2719,"name":2720,"slug":1986,"stem":2721,"__hash__":2722},"tags\u002Ftags\u002Fvue.yml",{},"Vue","tags\u002Fvue","9ztytvQnoFLSGIXCwVz0HDl1yEBgdxBtwfIb4xxED8w",{"id":2724,"extension":2012,"meta":2725,"name":2726,"slug":1943,"stem":2727,"__hash__":2728},"tags\u002Ftags\u002Fvue-js.yml",{},"vue.js","tags\u002Fvue-js","ZEglZJ4b32MxGRUCWg3ij0_Sz-YkJDI60yOFUG3Dip8",{"id":2730,"extension":2012,"meta":2731,"name":2005,"slug":2005,"stem":2732,"__hash__":2733},"tags\u002Ftags\u002Fvuejs.yml",{},"tags\u002Fvuejs","Qz3030ujqXE_33tL5hbF8n1MGIh1jpRtdzXVrQT79jA",{"id":2735,"extension":2012,"meta":2736,"name":2006,"slug":2006,"stem":2737,"__hash__":2738},"tags\u002Ftags\u002Fvuenews.yml",{},"tags\u002Fvuenews","5I7T-cG9aZzeR62I-tWYDruQvjkSbRxtAeDI9otWrR8",{"id":2740,"extension":2012,"meta":2741,"name":2007,"slug":2007,"stem":2742,"__hash__":2743},"tags\u002Ftags\u002Fvuetoolbox.yml",{},"tags\u002Fvuetoolbox","y5NXiZfQviI16DIkNjTxSwnLQIyrXs7OBZjVY1IyNmk",{"id":2745,"extension":2012,"meta":2746,"name":1961,"slug":1961,"stem":2747,"__hash__":2748},"tags\u002Ftags\u002Fvuex.yml",{},"tags\u002Fvuex","ksF82HLoN49mJn2cJ0DhYz_Dt6mlrJDT82Ufu6QKmKQ",{"id":2750,"extension":2012,"meta":2751,"name":2752,"slug":2753,"stem":2754,"__hash__":2755},"tags\u002Ftags\u002Fweb-development.yml",{},"web development","web-development","tags\u002Fweb-development","144N0DLmtEIX98zdSuOWIsCZYMHyxStp8_ZksWK2M7E",{"id":2757,"extension":2012,"meta":2758,"name":2759,"slug":2760,"stem":2761,"__hash__":2762},"tags\u002Ftags\u002Fwladyslawowo.yml",{},"Władysławowo","wladyslawowo","tags\u002Fwladyslawowo","LgrtfAzosbyNoEeZ6_GWMrueP29fN7fPL3N5Kp5Wm4w",{"id":2764,"extension":2012,"meta":2765,"name":2766,"slug":2767,"stem":2768,"__hash__":2769},"tags\u002Ftags\u002Fwork-life-balance.yml",{},"work-life balance","work-life-balance","tags\u002Fwork-life-balance","hXw9EMtMUVm71LSUBz1o6Ttz7xlyrsnMo-VOEfMJwr0",{"id":2771,"extension":2012,"meta":2772,"name":2773,"slug":2774,"stem":2775,"__hash__":2776},"tags\u002Ftags\u002Fxbox.yml",{},"XBOX","xbox","tags\u002Fxbox","tDsqBFEx88ZSQxV8Q78dFlkSdjNdQOwFU4jn0D2wcYQ",1790284820643]