[{"data":1,"prerenderedAt":1819},["ShallowReactive",2],{"\u002Fblog\u002Fvue3-tips-tricks":3},{"id":4,"title":5,"body":6,"date":1809,"description":1810,"extension":1811,"meta":1812,"navigation":134,"path":1813,"seo":1814,"stem":1815,"tags":1816,"__hash__":1818},"blog\u002Fblog\u002Fvue3-tips-tricks.md","7 Vue3 tips & tricks I guarantee you didn’t know",{"type":7,"value":8,"toc":1799},"minimark",[9,13,18,21,56,59,507,516,520,523,541,546,672,677,769,773,776,951,961,965,968,1044,1047,1166,1172,1175,1181,1218,1223,1337,1340,1343,1347,1357,1371,1380,1384,1391,1463,1466,1487,1496,1499,1506,1509,1558,1562,1565,1568,1753,1773,1781,1785,1788,1796],[10,11,12],"p",{},"Vue 3 has a lot of hidden gems. Here are seven tips and tricks that most developers don't know about — but definitely should.",[14,15,17],"h2",{"id":16},"_1-vnode-hooks-v33","1. VNode hooks (v3.3+)",[10,19,20],{},"On any component or HTML tag, we can use some special hooks as event listeners. The hooks are:",[22,23,24,31,36,41,46,51],"ul",{},[25,26,27],"li",{},[28,29,30],"code",{},"@vue:beforeMount",[25,32,33],{},[28,34,35],{},"@vue:mounted",[25,37,38],{},[28,39,40],{},"@vue:beforeUpdate",[25,42,43],{},[28,44,45],{},"@vue:updated",[25,47,48],{},[28,49,50],{},"@vue:beforeUnmount",[25,52,53],{},[28,54,55],{},"@vue:unmounted",[10,57,58],{},"Beaware that these hooks are not documented in the official Vue 3 docs which means that they are not officially supported. So use them at your own risk.",[60,61,66],"pre",{"className":62,"code":63,"language":64,"meta":65,"style":65},"language-vue shiki shiki-themes night-owl","\u003Cscript setup>\nimport { ref } from 'vue'\nimport Comp from '.\u002FComp.vue'\n\nconst count = ref(0)\nconst cmp = ref(true)\n\nfunction onMyComponentMounted() { console.log('component mounted') }\nfunction onMyComponentUnMounted() { console.log('component unmounted') }\nfunction divThatDisplaysCountWasUpdated() { console.log('div was updated') }\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>Check the console in devtools\u003C\u002Fp>\n  \u003Chr \u002F>\n  \u003CComp v-if=\"cmp\" @vue:mounted=\"onMyComponentMounted\" @vue:unmounted=\"onMyComponentUnMounted\" \u002F>\n  \u003Cdiv @vue:updated=\"divThatDisplaysCountWasUpdated\">{{ count }}\u003C\u002Fdiv>\n\n  \u003Cbutton @click=\"count++\">+\u003C\u002Fbutton>\n  \u003Cbutton @click=\"cmp = !cmp\">Mount\u002FUnmount component\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n","vue","",[28,67,68,88,112,129,136,163,183,188,226,255,284,294,299,309,329,340,402,433,438,469,498],{"__ignoreMap":65},[69,70,73,77,81,85],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"s4DUm","\u003C",[69,78,80],{"class":79},"sOKB6","script",[69,82,84],{"class":83},"stjdN"," setup",[69,86,87],{"class":75},">\n",[69,89,91,95,99,102,106,109],{"class":71,"line":90},2,[69,92,94],{"class":93},"sVyDW","import",[69,96,98],{"class":97},"sJeqr"," { ref } ",[69,100,101],{"class":93},"from",[69,103,105],{"class":104},"stNC8"," '",[69,107,64],{"class":108},"sNQAM",[69,110,111],{"class":104},"'\n",[69,113,115,117,120,122,124,127],{"class":71,"line":114},3,[69,116,94],{"class":93},[69,118,119],{"class":97}," Comp ",[69,121,101],{"class":93},[69,123,105],{"class":104},[69,125,126],{"class":108},".\u002FComp.vue",[69,128,111],{"class":104},[69,130,132],{"class":71,"line":131},4,[69,133,135],{"emptyLinePlaceholder":134},true,"\n",[69,137,139,143,147,150,153,156,160],{"class":71,"line":138},5,[69,140,142],{"class":141},"sJ14y","const",[69,144,146],{"class":145},"sywZT"," count",[69,148,149],{"class":141}," =",[69,151,152],{"class":145}," ref",[69,154,155],{"class":97},"(",[69,157,159],{"class":158},"sx098","0",[69,161,162],{"class":97},")\n",[69,164,166,168,171,173,175,177,181],{"class":71,"line":165},6,[69,167,142],{"class":141},[69,169,170],{"class":145}," cmp",[69,172,149],{"class":141},[69,174,152],{"class":145},[69,176,155],{"class":97},[69,178,180],{"class":179},"sIpC3","true",[69,182,162],{"class":97},[69,184,186],{"class":71,"line":185},7,[69,187,135],{"emptyLinePlaceholder":134},[69,189,191,194,197,200,203,207,210,213,215,218,221,223],{"class":71,"line":190},8,[69,192,193],{"class":141},"function",[69,195,196],{"class":145}," onMyComponentMounted",[69,198,199],{"class":104},"()",[69,201,202],{"class":97}," { ",[69,204,206],{"class":205},"slldn","console",[69,208,209],{"class":93},".",[69,211,212],{"class":145},"log",[69,214,155],{"class":97},[69,216,217],{"class":104},"'",[69,219,220],{"class":108},"component mounted",[69,222,217],{"class":104},[69,224,225],{"class":97},") }\n",[69,227,229,231,234,236,238,240,242,244,246,248,251,253],{"class":71,"line":228},9,[69,230,193],{"class":141},[69,232,233],{"class":145}," onMyComponentUnMounted",[69,235,199],{"class":104},[69,237,202],{"class":97},[69,239,206],{"class":205},[69,241,209],{"class":93},[69,243,212],{"class":145},[69,245,155],{"class":97},[69,247,217],{"class":104},[69,249,250],{"class":108},"component unmounted",[69,252,217],{"class":104},[69,254,225],{"class":97},[69,256,258,260,263,265,267,269,271,273,275,277,280,282],{"class":71,"line":257},10,[69,259,193],{"class":141},[69,261,262],{"class":145}," divThatDisplaysCountWasUpdated",[69,264,199],{"class":104},[69,266,202],{"class":97},[69,268,206],{"class":205},[69,270,209],{"class":93},[69,272,212],{"class":145},[69,274,155],{"class":97},[69,276,217],{"class":104},[69,278,279],{"class":108},"div was updated",[69,281,217],{"class":104},[69,283,225],{"class":97},[69,285,287,290,292],{"class":71,"line":286},11,[69,288,289],{"class":75},"\u003C\u002F",[69,291,80],{"class":79},[69,293,87],{"class":75},[69,295,297],{"class":71,"line":296},12,[69,298,135],{"emptyLinePlaceholder":134},[69,300,302,304,307],{"class":71,"line":301},13,[69,303,76],{"class":75},[69,305,306],{"class":79},"template",[69,308,87],{"class":75},[69,310,312,315,317,320,323,325,327],{"class":71,"line":311},14,[69,313,314],{"class":75},"  \u003C",[69,316,10],{"class":79},[69,318,319],{"class":75},">",[69,321,322],{"class":97},"Check the console in devtools",[69,324,289],{"class":75},[69,326,10],{"class":79},[69,328,87],{"class":75},[69,330,332,334,337],{"class":71,"line":331},15,[69,333,314],{"class":75},[69,335,336],{"class":79},"hr",[69,338,339],{"class":75}," \u002F>\n",[69,341,343,345,348,351,354,357,360,362,365,367,370,373,375,377,380,382,384,386,388,391,393,395,398,400],{"class":71,"line":342},16,[69,344,314],{"class":75},[69,346,347],{"class":79},"Comp",[69,349,350],{"class":93}," v-if",[69,352,353],{"class":75},"=",[69,355,356],{"class":104},"\"",[69,358,359],{"class":97},"cmp",[69,361,356],{"class":104},[69,363,364],{"class":75}," @",[69,366,64],{"class":83},[69,368,369],{"class":75},":",[69,371,372],{"class":83},"mounted",[69,374,353],{"class":75},[69,376,356],{"class":104},[69,378,379],{"class":97},"onMyComponentMounted",[69,381,356],{"class":104},[69,383,364],{"class":75},[69,385,64],{"class":83},[69,387,369],{"class":75},[69,389,390],{"class":83},"unmounted",[69,392,353],{"class":75},[69,394,356],{"class":104},[69,396,397],{"class":97},"onMyComponentUnMounted",[69,399,356],{"class":104},[69,401,339],{"class":75},[69,403,405,407,410,413,415,417,420,422,424,427,429,431],{"class":71,"line":404},17,[69,406,314],{"class":75},[69,408,409],{"class":79},"div",[69,411,412],{"class":83}," @vue:updated",[69,414,353],{"class":75},[69,416,356],{"class":104},[69,418,419],{"class":108},"divThatDisplaysCountWasUpdated",[69,421,356],{"class":104},[69,423,319],{"class":75},[69,425,426],{"class":97},"{{ count }}",[69,428,289],{"class":75},[69,430,409],{"class":79},[69,432,87],{"class":75},[69,434,436],{"class":71,"line":435},18,[69,437,135],{"emptyLinePlaceholder":134},[69,439,441,443,446,449,451,453,456,458,460,463,465,467],{"class":71,"line":440},19,[69,442,314],{"class":75},[69,444,445],{"class":79},"button",[69,447,448],{"class":83}," @click",[69,450,353],{"class":75},[69,452,356],{"class":104},[69,454,455],{"class":108},"count++",[69,457,356],{"class":104},[69,459,319],{"class":75},[69,461,462],{"class":97},"+",[69,464,289],{"class":75},[69,466,445],{"class":79},[69,468,87],{"class":75},[69,470,472,474,476,478,480,482,485,487,489,492,494,496],{"class":71,"line":471},20,[69,473,314],{"class":75},[69,475,445],{"class":79},[69,477,448],{"class":83},[69,479,353],{"class":75},[69,481,356],{"class":104},[69,483,484],{"class":108},"cmp = !cmp",[69,486,356],{"class":104},[69,488,319],{"class":75},[69,490,491],{"class":97},"Mount\u002FUnmount component",[69,493,289],{"class":75},[69,495,445],{"class":79},[69,497,87],{"class":75},[69,499,501,503,505],{"class":71,"line":500},21,[69,502,289],{"class":75},[69,504,306],{"class":79},[69,506,87],{"class":75},[10,508,509,510,512,513,515],{},"It should be noted that these hooks pass arguments to the callback function. They pass only one argument — the current VNode — except for ",[28,511,40],{}," and ",[28,514,45],{},", which pass two arguments: the current VNode and the previous VNode.",[14,517,519],{"id":518},"_2-debugging-hooks","2. Debugging hooks",[10,521,522],{},"We all know the lifecycle hooks that Vue provides us. But did you know that Vue 3 gives us two hooks we can use for debugging purposes? They are:",[22,524,525,534],{},[25,526,527],{},[528,529,533],"a",{"href":530,"rel":531},"https:\u002F\u002Fvuejs.org\u002Fapi\u002Fcomposition-api-lifecycle.html#onrendertracked",[532],"nofollow","onRenderTracked",[25,535,536],{},[528,537,540],{"href":538,"rel":539},"https:\u002F\u002Fvuejs.org\u002Fapi\u002Fcomposition-api-lifecycle.html#onrendertriggered",[532],"onRenderTriggered",[10,542,543,545],{},[28,544,533],{}," gets called for every reactive dependency that has been tracked.",[60,547,549],{"className":62,"code":548,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { ref, onRenderTracked } from 'vue'\n\nconst count = ref(0)\nconst count2 = ref(0)\n\n\u002F\u002F It will be called for every reactive dependency that has been tracked\nonRenderTracked((event) => {\n  console.log(event)\n})\n\u003C\u002Fscript>\n",[28,550,551,561,576,580,596,613,617,623,644,659,664],{"__ignoreMap":65},[69,552,553,555,557,559],{"class":71,"line":72},[69,554,76],{"class":75},[69,556,80],{"class":79},[69,558,84],{"class":83},[69,560,87],{"class":75},[69,562,563,565,568,570,572,574],{"class":71,"line":90},[69,564,94],{"class":93},[69,566,567],{"class":97}," { ref, onRenderTracked } ",[69,569,101],{"class":93},[69,571,105],{"class":104},[69,573,64],{"class":108},[69,575,111],{"class":104},[69,577,578],{"class":71,"line":114},[69,579,135],{"emptyLinePlaceholder":134},[69,581,582,584,586,588,590,592,594],{"class":71,"line":131},[69,583,142],{"class":141},[69,585,146],{"class":145},[69,587,149],{"class":141},[69,589,152],{"class":145},[69,591,155],{"class":97},[69,593,159],{"class":158},[69,595,162],{"class":97},[69,597,598,600,603,605,607,609,611],{"class":71,"line":138},[69,599,142],{"class":141},[69,601,602],{"class":145}," count2",[69,604,149],{"class":141},[69,606,152],{"class":145},[69,608,155],{"class":97},[69,610,159],{"class":158},[69,612,162],{"class":97},[69,614,615],{"class":71,"line":165},[69,616,135],{"emptyLinePlaceholder":134},[69,618,619],{"class":71,"line":185},[69,620,622],{"class":621},"s8J1n","\u002F\u002F It will be called for every reactive dependency that has been tracked\n",[69,624,625,627,629,631,635,638,641],{"class":71,"line":190},[69,626,533],{"class":145},[69,628,155],{"class":97},[69,630,155],{"class":104},[69,632,634],{"class":633},"sasy7","event",[69,636,637],{"class":104},")",[69,639,640],{"class":141}," =>",[69,642,643],{"class":97}," {\n",[69,645,646,649,651,653,655,657],{"class":71,"line":228},[69,647,648],{"class":205},"  console",[69,650,209],{"class":93},[69,652,212],{"class":145},[69,654,155],{"class":97},[69,656,634],{"class":633},[69,658,162],{"class":97},[69,660,661],{"class":71,"line":257},[69,662,663],{"class":97},"})\n",[69,665,666,668,670],{"class":71,"line":286},[69,667,289],{"class":75},[69,669,80],{"class":79},[69,671,87],{"class":75},[10,673,674,676],{},[28,675,540],{}," gets called when we trigger a reactivity update, or as the docs say: \"when a reactive dependency triggers the component's render effect to be re-run\".",[60,678,680],{"className":62,"code":679,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { ref, onRenderTriggered } from 'vue'\n\nconst count = ref(0)\n\n\u002F\u002F It will be called when we update the reactive dependency\nonRenderTriggered((event) => {\n  debugger\n})\n\u003C\u002Fscript>\n",[28,681,682,692,707,711,727,731,736,752,757,761],{"__ignoreMap":65},[69,683,684,686,688,690],{"class":71,"line":72},[69,685,76],{"class":75},[69,687,80],{"class":79},[69,689,84],{"class":83},[69,691,87],{"class":75},[69,693,694,696,699,701,703,705],{"class":71,"line":90},[69,695,94],{"class":93},[69,697,698],{"class":97}," { ref, onRenderTriggered } ",[69,700,101],{"class":93},[69,702,105],{"class":104},[69,704,64],{"class":108},[69,706,111],{"class":104},[69,708,709],{"class":71,"line":114},[69,710,135],{"emptyLinePlaceholder":134},[69,712,713,715,717,719,721,723,725],{"class":71,"line":131},[69,714,142],{"class":141},[69,716,146],{"class":145},[69,718,149],{"class":141},[69,720,152],{"class":145},[69,722,155],{"class":97},[69,724,159],{"class":158},[69,726,162],{"class":97},[69,728,729],{"class":71,"line":138},[69,730,135],{"emptyLinePlaceholder":134},[69,732,733],{"class":71,"line":165},[69,734,735],{"class":621},"\u002F\u002F It will be called when we update the reactive dependency\n",[69,737,738,740,742,744,746,748,750],{"class":71,"line":185},[69,739,540],{"class":145},[69,741,155],{"class":97},[69,743,155],{"class":104},[69,745,634],{"class":633},[69,747,637],{"class":104},[69,749,640],{"class":141},[69,751,643],{"class":97},[69,753,754],{"class":71,"line":190},[69,755,756],{"class":75},"  debugger\n",[69,758,759],{"class":71,"line":228},[69,760,663],{"class":97},[69,762,763,765,767],{"class":71,"line":257},[69,764,289],{"class":75},[69,766,80],{"class":79},[69,768,87],{"class":75},[14,770,772],{"id":771},"_3-expose-slots-from-a-child-component","3. Expose slots from a child component",[10,774,775],{},"If you use a third-party component, chances are that you wrap its implementation in your own \"wrapper\" component. This is a good practice and a scalable solution, but in that way the slots of the third-party component get lost, and we should find a way to expose them to the parent component:",[60,777,779],{"className":62,"code":778,"language":64,"meta":65,"style":65},"\u003C!-- WrapperComponent.vue -->\n\u003Ctemplate>\n  \u003Cdiv class=\"wrapper-of-third-party-component\">\n    \u003CThirdPartyComponent v-bind=\"$attrs\">\n      \u003C!-- Expose the slots of the third-party component -->\n      \u003Ctemplate v-for=\"(_, name) in $slots\" #[name]=\"slotData\">\n        \u003Cslot :name=\"name\" v-bind=\"slotData || {}\">\u003C\u002Fslot>\n      \u003C\u002Ftemplate>\n    \u003C\u002FThirdPartyComponent>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[28,780,781,786,794,814,836,841,878,916,925,934,943],{"__ignoreMap":65},[69,782,783],{"class":71,"line":72},[69,784,785],{"class":621},"\u003C!-- WrapperComponent.vue -->\n",[69,787,788,790,792],{"class":71,"line":90},[69,789,76],{"class":75},[69,791,306],{"class":79},[69,793,87],{"class":75},[69,795,796,798,800,803,805,807,810,812],{"class":71,"line":114},[69,797,314],{"class":75},[69,799,409],{"class":79},[69,801,802],{"class":83}," class",[69,804,353],{"class":75},[69,806,356],{"class":104},[69,808,809],{"class":108},"wrapper-of-third-party-component",[69,811,356],{"class":104},[69,813,87],{"class":75},[69,815,816,819,822,825,827,829,832,834],{"class":71,"line":131},[69,817,818],{"class":75},"    \u003C",[69,820,821],{"class":79},"ThirdPartyComponent",[69,823,824],{"class":83}," v-bind",[69,826,353],{"class":75},[69,828,356],{"class":104},[69,830,831],{"class":97},"$attrs",[69,833,356],{"class":104},[69,835,87],{"class":75},[69,837,838],{"class":71,"line":138},[69,839,840],{"class":621},"      \u003C!-- Expose the slots of the third-party component -->\n",[69,842,843,846,848,851,853,855,858,861,864,866,869,871,874,876],{"class":71,"line":165},[69,844,845],{"class":75},"      \u003C",[69,847,306],{"class":79},[69,849,850],{"class":93}," v-for",[69,852,353],{"class":97},[69,854,356],{"class":104},[69,856,857],{"class":97},"(_, name) ",[69,859,860],{"class":75},"in",[69,862,863],{"class":97}," $slots",[69,865,356],{"class":104},[69,867,868],{"class":97}," #[name]=",[69,870,356],{"class":104},[69,872,873],{"class":97},"slotData",[69,875,356],{"class":104},[69,877,87],{"class":75},[69,879,880,883,886,889,891,893,896,898,900,902,904,907,909,912,914],{"class":71,"line":185},[69,881,882],{"class":75},"        \u003C",[69,884,885],{"class":79},"slot",[69,887,888],{"class":83}," :name",[69,890,353],{"class":75},[69,892,356],{"class":104},[69,894,895],{"class":108},"name",[69,897,356],{"class":104},[69,899,824],{"class":83},[69,901,353],{"class":75},[69,903,356],{"class":104},[69,905,906],{"class":108},"slotData || {}",[69,908,356],{"class":104},[69,910,911],{"class":75},">\u003C\u002F",[69,913,885],{"class":79},[69,915,87],{"class":75},[69,917,918,921,923],{"class":71,"line":190},[69,919,920],{"class":75},"      \u003C\u002F",[69,922,306],{"class":79},[69,924,87],{"class":75},[69,926,927,930,932],{"class":71,"line":228},[69,928,929],{"class":75},"    \u003C\u002F",[69,931,821],{"class":79},[69,933,87],{"class":75},[69,935,936,939,941],{"class":71,"line":257},[69,937,938],{"class":75},"  \u003C\u002F",[69,940,409],{"class":79},[69,942,87],{"class":75},[69,944,945,947,949],{"class":71,"line":286},[69,946,289],{"class":75},[69,948,306],{"class":79},[69,950,87],{"class":75},[10,952,953,954,957,958,960],{},"Now every component that uses ",[28,955,956],{},"WrapperComponent"," can use the slots of ",[28,959,821],{}," 🎉.",[14,962,964],{"id":963},"_4-scoped-styles-and-multi-root-nodes-dont-work-well-together","4. Scoped styles and multi-root nodes don't work well together",[10,966,967],{},"In Vue 3 we can finally have more than one root node in a component. That is great, but personally I fall into a design limitation when doing that. Imagine we have a child component:",[60,969,971],{"className":62,"code":970,"language":64,"meta":65,"style":65},"\u003Ctemplate>\n  \u003Cp class=\"my-p\">First p\u003C\u002Fp>\n  \u003Cp class=\"my-p\">Second p\u003C\u002Fp>\n\u003C\u002Ftemplate>\n",[28,972,973,981,1009,1036],{"__ignoreMap":65},[69,974,975,977,979],{"class":71,"line":72},[69,976,76],{"class":75},[69,978,306],{"class":79},[69,980,87],{"class":75},[69,982,983,985,987,989,991,993,996,998,1000,1003,1005,1007],{"class":71,"line":90},[69,984,314],{"class":75},[69,986,10],{"class":79},[69,988,802],{"class":83},[69,990,353],{"class":75},[69,992,356],{"class":104},[69,994,995],{"class":108},"my-p",[69,997,356],{"class":104},[69,999,319],{"class":75},[69,1001,1002],{"class":97},"First p",[69,1004,289],{"class":75},[69,1006,10],{"class":79},[69,1008,87],{"class":75},[69,1010,1011,1013,1015,1017,1019,1021,1023,1025,1027,1030,1032,1034],{"class":71,"line":114},[69,1012,314],{"class":75},[69,1014,10],{"class":79},[69,1016,802],{"class":83},[69,1018,353],{"class":75},[69,1020,356],{"class":104},[69,1022,995],{"class":108},[69,1024,356],{"class":104},[69,1026,319],{"class":75},[69,1028,1029],{"class":97},"Second p",[69,1031,289],{"class":75},[69,1033,10],{"class":79},[69,1035,87],{"class":75},[69,1037,1038,1040,1042],{"class":71,"line":131},[69,1039,289],{"class":75},[69,1041,306],{"class":79},[69,1043,87],{"class":75},[10,1045,1046],{},"And a parent component:",[60,1048,1050],{"className":62,"code":1049,"language":64,"meta":65,"style":65},"\u003Ctemplate>\n  \u003Ch1>My awesome component\u003C\u002Fh1>\n  \u003CMyChildComponent \u002F>\n\u003C\u002Ftemplate>\n\n\u003Cstyle scoped>\n\u002F* There is no way to style the p tags of MyChildComponent *\u002F\n.my-p { color: red; }\n:deep(.my-p) { color: red; }\n\u003C\u002Fstyle>\n",[28,1051,1052,1060,1078,1087,1095,1099,1111,1116,1138,1158],{"__ignoreMap":65},[69,1053,1054,1056,1058],{"class":71,"line":72},[69,1055,76],{"class":75},[69,1057,306],{"class":79},[69,1059,87],{"class":75},[69,1061,1062,1064,1067,1069,1072,1074,1076],{"class":71,"line":90},[69,1063,314],{"class":75},[69,1065,1066],{"class":79},"h1",[69,1068,319],{"class":75},[69,1070,1071],{"class":97},"My awesome component",[69,1073,289],{"class":75},[69,1075,1066],{"class":79},[69,1077,87],{"class":75},[69,1079,1080,1082,1085],{"class":71,"line":114},[69,1081,314],{"class":75},[69,1083,1084],{"class":79},"MyChildComponent",[69,1086,339],{"class":75},[69,1088,1089,1091,1093],{"class":71,"line":131},[69,1090,289],{"class":75},[69,1092,306],{"class":79},[69,1094,87],{"class":75},[69,1096,1097],{"class":71,"line":138},[69,1098,135],{"emptyLinePlaceholder":134},[69,1100,1101,1103,1106,1109],{"class":71,"line":165},[69,1102,76],{"class":75},[69,1104,1105],{"class":79},"style",[69,1107,1108],{"class":83}," scoped",[69,1110,87],{"class":75},[69,1112,1113],{"class":71,"line":185},[69,1114,1115],{"class":621},"\u002F* There is no way to style the p tags of MyChildComponent *\u002F\n",[69,1117,1118,1121,1123,1125,1129,1132,1135],{"class":71,"line":190},[69,1119,209],{"class":1120},"s34Dl",[69,1122,995],{"class":83},[69,1124,202],{"class":97},[69,1126,1128],{"class":1127},"sz1_M","color",[69,1130,1131],{"class":97},": ",[69,1133,1134],{"class":75},"red",[69,1136,1137],{"class":97},"; }\n",[69,1139,1140,1143,1145,1147,1150,1152,1154,1156],{"class":71,"line":228},[69,1141,1142],{"class":93},":deep(",[69,1144,209],{"class":1120},[69,1146,995],{"class":83},[69,1148,1149],{"class":97},") { ",[69,1151,1128],{"class":1127},[69,1153,1131],{"class":97},[69,1155,1134],{"class":75},[69,1157,1137],{"class":97},[69,1159,1160,1162,1164],{"class":71,"line":257},[69,1161,289],{"class":75},[69,1163,1105],{"class":79},[69,1165,87],{"class":75},[10,1167,1168,1169,1171],{},"There is no way from the scoped styling of the multi-root parent component to style the child component's ",[28,1170,10],{}," tags. In short: a multi-root component can't target a multi-root child component's styles with scoped styles.",[10,1173,1174],{},"The best way to fix that would be to wrap the parent or child component (or both) so we have only one root element. But if you absolutely need both to have multi-root nodes, you can:",[10,1176,1177],{},[1178,1179,1180],"strong",{},"Use a non-scoped style",[60,1182,1184],{"className":62,"code":1183,"language":64,"meta":65,"style":65},"\u003Cstyle>\n.my-p { color: red; }\n\u003C\u002Fstyle>\n",[28,1185,1186,1194,1210],{"__ignoreMap":65},[69,1187,1188,1190,1192],{"class":71,"line":72},[69,1189,76],{"class":75},[69,1191,1105],{"class":79},[69,1193,87],{"class":75},[69,1195,1196,1198,1200,1202,1204,1206,1208],{"class":71,"line":90},[69,1197,209],{"class":1120},[69,1199,995],{"class":83},[69,1201,202],{"class":97},[69,1203,1128],{"class":1127},[69,1205,1131],{"class":97},[69,1207,1134],{"class":75},[69,1209,1137],{"class":97},[69,1211,1212,1214,1216],{"class":71,"line":114},[69,1213,289],{"class":75},[69,1215,1105],{"class":79},[69,1217,87],{"class":75},[10,1219,1220],{},[1178,1221,1222],{},"Use CSS Modules",[60,1224,1226],{"className":62,"code":1225,"language":64,"meta":65,"style":65},"\u003Ctemplate>\n  \u003Ch1>My awesome component\u003C\u002Fh1>\n  \u003CMyChildComponent :class=\"$style.trick\" \u002F>\n\u003C\u002Ftemplate>\n\n\u003Cstyle module>\n.trick {\n  color: red;\n}\n\u003C\u002Fstyle>\n",[28,1227,1228,1236,1252,1281,1289,1293,1304,1312,1324,1329],{"__ignoreMap":65},[69,1229,1230,1232,1234],{"class":71,"line":72},[69,1231,76],{"class":75},[69,1233,306],{"class":79},[69,1235,87],{"class":75},[69,1237,1238,1240,1242,1244,1246,1248,1250],{"class":71,"line":90},[69,1239,314],{"class":75},[69,1241,1066],{"class":79},[69,1243,319],{"class":75},[69,1245,1071],{"class":97},[69,1247,289],{"class":75},[69,1249,1066],{"class":79},[69,1251,87],{"class":75},[69,1253,1254,1256,1258,1261,1264,1266,1268,1271,1273,1277,1279],{"class":71,"line":114},[69,1255,314],{"class":75},[69,1257,1084],{"class":79},[69,1259,1260],{"class":75}," :",[69,1262,1263],{"class":83},"class",[69,1265,353],{"class":75},[69,1267,356],{"class":104},[69,1269,1270],{"class":97},"$style",[69,1272,209],{"class":93},[69,1274,1276],{"class":1275},"sddte","trick",[69,1278,356],{"class":104},[69,1280,339],{"class":75},[69,1282,1283,1285,1287],{"class":71,"line":131},[69,1284,289],{"class":75},[69,1286,306],{"class":79},[69,1288,87],{"class":75},[69,1290,1291],{"class":71,"line":138},[69,1292,135],{"emptyLinePlaceholder":134},[69,1294,1295,1297,1299,1302],{"class":71,"line":165},[69,1296,76],{"class":75},[69,1298,1105],{"class":79},[69,1300,1301],{"class":83}," module",[69,1303,87],{"class":75},[69,1305,1306,1308,1310],{"class":71,"line":185},[69,1307,209],{"class":1120},[69,1309,1276],{"class":83},[69,1311,643],{"class":97},[69,1313,1314,1317,1319,1321],{"class":71,"line":190},[69,1315,1316],{"class":1127},"  color",[69,1318,1131],{"class":97},[69,1320,1134],{"class":75},[69,1322,1323],{"class":97},";\n",[69,1325,1326],{"class":71,"line":228},[69,1327,1328],{"class":97},"}\n",[69,1330,1331,1333,1335],{"class":71,"line":257},[69,1332,289],{"class":75},[69,1334,1105],{"class":79},[69,1336,87],{"class":75},[10,1338,1339],{},"Since we are specifying a class here, the multi-root child component has to explicitly specify the attribute fallthrough behavior.",[10,1341,1342],{},"If you want my opinion: unless you absolutely need a multi-root node component, go with a single root node and don't deal with this design limitation at all.",[14,1344,1346],{"id":1345},"_5-be-careful-when-using-css-selectors","5. Be careful when using CSS selectors",[10,1348,1349,1352,1353,1356],{},[28,1350,1351],{},"#main-nav > li {}"," will be many times slower compared to ",[28,1354,1355],{},".my-li { color: red }",". From the docs:",[1358,1359,1360],"blockquote",{},[10,1361,1362,1363,1366,1367,1370],{},"Due to the way browsers render various CSS selectors, ",[28,1364,1365],{},"p { color: red }"," will be many times slower when scoped (i.e. when combined with an attribute selector). If you use classes or ids instead, such as in ",[28,1368,1369],{},".example { color: red }",", then you virtually eliminate that performance hit.",[10,1372,1373,1374,1379],{},"I highly recommend you read ",[528,1375,1378],{"href":1376,"rel":1377},"https:\u002F\u002Fcss-tricks.com\u002Fefficiently-rendering-css\u002F",[532],"Efficiently Rendering CSS"," if you want to dive deeper into this topic.",[14,1381,1383],{"id":1382},"_6-boolean-casting","6. Boolean casting",[10,1385,1386,1387,1390],{},"In Vue 2 or early versions of Vue 3, for props with ",[28,1388,1389],{},"Boolean"," types we had different behavior depending on the order:",[60,1392,1396],{"className":1393,"code":1394,"language":1395,"meta":65,"style":65},"language-js shiki shiki-themes night-owl","\u002F\u002F 1st case\nprops: {\n  hoverColor: [String, Boolean] \u002F\u002F \u003C- defaults to ''\n}\n\n\u002F\u002F 2nd case\nprops: {\n  hoverColor: [Boolean, String] \u002F\u002F \u003C- defaults to false\n}\n","js",[28,1397,1398,1403,1408,1427,1431,1435,1440,1444,1459],{"__ignoreMap":65},[69,1399,1400],{"class":71,"line":72},[69,1401,1402],{"class":621},"\u002F\u002F 1st case\n",[69,1404,1405],{"class":71,"line":90},[69,1406,1407],{"class":97},"props: {\n",[69,1409,1410,1413,1416,1419,1421,1424],{"class":71,"line":114},[69,1411,1412],{"class":97},"  hoverColor: [",[69,1414,1415],{"class":633},"String",[69,1417,1418],{"class":97},", ",[69,1420,1389],{"class":633},[69,1422,1423],{"class":97},"] ",[69,1425,1426],{"class":621},"\u002F\u002F \u003C- defaults to ''\n",[69,1428,1429],{"class":71,"line":131},[69,1430,1328],{"class":97},[69,1432,1433],{"class":71,"line":138},[69,1434,135],{"emptyLinePlaceholder":134},[69,1436,1437],{"class":71,"line":165},[69,1438,1439],{"class":621},"\u002F\u002F 2nd case\n",[69,1441,1442],{"class":71,"line":185},[69,1443,1407],{"class":97},[69,1445,1446,1448,1450,1452,1454,1456],{"class":71,"line":190},[69,1447,1412],{"class":97},[69,1449,1389],{"class":633},[69,1451,1418],{"class":97},[69,1453,1415],{"class":633},[69,1455,1423],{"class":97},[69,1457,1458],{"class":621},"\u002F\u002F \u003C- defaults to false\n",[69,1460,1461],{"class":71,"line":228},[69,1462,1328],{"class":97},[10,1464,1465],{},"Not only that, but if you pass the prop like this:",[60,1467,1469],{"className":62,"code":1468,"language":64,"meta":65,"style":65},"\u003Cmy-component hover-color>\u003C\u002Fmy-component>\n",[28,1470,1471],{"__ignoreMap":65},[69,1472,1473,1475,1478,1481,1483,1485],{"class":71,"line":72},[69,1474,76],{"class":75},[69,1476,1477],{"class":79},"my-component",[69,1479,1480],{"class":83}," hover-color",[69,1482,911],{"class":75},[69,1484,1477],{"class":79},[69,1486,87],{"class":75},[10,1488,1489,1490,1493,1494,209],{},"In the first case it would be an empty string ",[28,1491,1492],{},"''",". In the second case, it would be ",[28,1495,180],{},[10,1497,1498],{},"As you can see, this was a bit confusing and inconsistent. Fortunately, in Vue 3 we have a new behavior that is consistent and predictable:",[1358,1500,1501],{},[10,1502,1503,1505],{},[28,1504,1389],{}," behavior will apply regardless of type appearance order.",[10,1507,1508],{},"So:",[60,1510,1512],{"className":1393,"code":1511,"language":1395,"meta":65,"style":65},"hoverColor: [String, Boolean] \u002F\u002F \u003C- defaults to false\nhoverColor: [Boolean, String] \u002F\u002F \u003C- defaults to false\nhoverColor: [Boolean, Number] \u002F\u002F \u003C- defaults to false\n",[28,1513,1514,1529,1543],{"__ignoreMap":65},[69,1515,1516,1519,1521,1523,1525,1527],{"class":71,"line":72},[69,1517,1518],{"class":97},"hoverColor: [",[69,1520,1415],{"class":633},[69,1522,1418],{"class":97},[69,1524,1389],{"class":633},[69,1526,1423],{"class":97},[69,1528,1458],{"class":621},[69,1530,1531,1533,1535,1537,1539,1541],{"class":71,"line":90},[69,1532,1518],{"class":97},[69,1534,1389],{"class":633},[69,1536,1418],{"class":97},[69,1538,1415],{"class":633},[69,1540,1423],{"class":97},[69,1542,1458],{"class":621},[69,1544,1545,1547,1549,1551,1554,1556],{"class":71,"line":114},[69,1546,1518],{"class":97},[69,1548,1389],{"class":633},[69,1550,1418],{"class":97},[69,1552,1553],{"class":633},"Number",[69,1555,1423],{"class":97},[69,1557,1458],{"class":621},[14,1559,1561],{"id":1560},"_7-template-refs-with-v-for-order-is-not-guaranteed","7. Template refs with v-for — order is not guaranteed",[10,1563,1564],{},"Remember this one so you don't lose hours of debugging trying to figure out what is going on.",[10,1566,1567],{},"In the code below:",[60,1569,1571],{"className":62,"code":1570,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { ref } from 'vue'\n\nconst list = ref([1, 2, 3])\nconst itemRefs = useTemplateRef('items')\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cul>\n    \u003Cli v-for=\"item in list\" ref=\"items\" :key=\"item\">\n      {{ item }}\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Ftemplate>\n",[28,1572,1573,1583,1597,1601,1632,1655,1663,1667,1675,1683,1724,1729,1737,1745],{"__ignoreMap":65},[69,1574,1575,1577,1579,1581],{"class":71,"line":72},[69,1576,76],{"class":75},[69,1578,80],{"class":79},[69,1580,84],{"class":83},[69,1582,87],{"class":75},[69,1584,1585,1587,1589,1591,1593,1595],{"class":71,"line":90},[69,1586,94],{"class":93},[69,1588,98],{"class":97},[69,1590,101],{"class":93},[69,1592,105],{"class":104},[69,1594,64],{"class":108},[69,1596,111],{"class":104},[69,1598,1599],{"class":71,"line":114},[69,1600,135],{"emptyLinePlaceholder":134},[69,1602,1603,1605,1608,1610,1612,1615,1618,1621,1624,1626,1629],{"class":71,"line":131},[69,1604,142],{"class":141},[69,1606,1607],{"class":145}," list",[69,1609,149],{"class":141},[69,1611,152],{"class":145},[69,1613,1614],{"class":97},"([",[69,1616,1617],{"class":158},"1",[69,1619,1620],{"class":97},",",[69,1622,1623],{"class":158}," 2",[69,1625,1620],{"class":97},[69,1627,1628],{"class":158}," 3",[69,1630,1631],{"class":97},"])\n",[69,1633,1634,1636,1639,1641,1644,1646,1648,1651,1653],{"class":71,"line":138},[69,1635,142],{"class":141},[69,1637,1638],{"class":145}," itemRefs",[69,1640,149],{"class":141},[69,1642,1643],{"class":145}," useTemplateRef",[69,1645,155],{"class":97},[69,1647,217],{"class":104},[69,1649,1650],{"class":108},"items",[69,1652,217],{"class":104},[69,1654,162],{"class":97},[69,1656,1657,1659,1661],{"class":71,"line":165},[69,1658,289],{"class":75},[69,1660,80],{"class":79},[69,1662,87],{"class":75},[69,1664,1665],{"class":71,"line":185},[69,1666,135],{"emptyLinePlaceholder":134},[69,1668,1669,1671,1673],{"class":71,"line":190},[69,1670,76],{"class":75},[69,1672,306],{"class":79},[69,1674,87],{"class":75},[69,1676,1677,1679,1681],{"class":71,"line":228},[69,1678,314],{"class":75},[69,1680,22],{"class":79},[69,1682,87],{"class":75},[69,1684,1685,1687,1689,1691,1693,1695,1698,1700,1702,1704,1706,1708,1710,1713,1715,1717,1720,1722],{"class":71,"line":257},[69,1686,818],{"class":75},[69,1688,25],{"class":79},[69,1690,850],{"class":83},[69,1692,353],{"class":75},[69,1694,356],{"class":104},[69,1696,1697],{"class":108},"item in list",[69,1699,356],{"class":104},[69,1701,152],{"class":83},[69,1703,353],{"class":75},[69,1705,356],{"class":104},[69,1707,1650],{"class":108},[69,1709,356],{"class":104},[69,1711,1712],{"class":83}," :key",[69,1714,353],{"class":75},[69,1716,356],{"class":104},[69,1718,1719],{"class":108},"item",[69,1721,356],{"class":104},[69,1723,87],{"class":75},[69,1725,1726],{"class":71,"line":286},[69,1727,1728],{"class":97},"      {{ item }}\n",[69,1730,1731,1733,1735],{"class":71,"line":296},[69,1732,929],{"class":75},[69,1734,25],{"class":79},[69,1736,87],{"class":75},[69,1738,1739,1741,1743],{"class":71,"line":301},[69,1740,938],{"class":75},[69,1742,22],{"class":79},[69,1744,87],{"class":75},[69,1746,1747,1749,1751],{"class":71,"line":311},[69,1748,289],{"class":75},[69,1750,306],{"class":79},[69,1752,87],{"class":75},[10,1754,1755,1756,1759,1760,1763,1764],{},"we are looping over the ",[28,1757,1758],{},"list"," array and creating the ",[28,1761,1762],{},"itemRefs"," array. ",[1178,1765,1766,1767,1769,1770,1772],{},"The ",[28,1768,1762],{}," array is not guaranteed to have the same order as the ",[28,1771,1758],{}," array.",[10,1774,1775,1776,209],{},"If you want to find out more about this, you can read ",[528,1777,1780],{"href":1778,"rel":1779},"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fcore\u002Fissues\u002F4010",[532],"this issue",[14,1782,1784],{"id":1783},"wrapping-up","Wrapping up",[10,1786,1787],{},"Thank you all for reading. Please share this post with your friends and colleagues if you found it useful.",[10,1789,1790,1791],{},"If you haven't already, follow me on ",[528,1792,1795],{"href":1793,"rel":1794},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Froland-doda\u002F",[532],"Linkedin",[1105,1797,1798],{},"html pre.shiki code .s4DUm, html code.shiki .s4DUm{--shiki-default:#7FDBCA}html pre.shiki code .sOKB6, html code.shiki .sOKB6{--shiki-default:#CAECE6}html pre.shiki code .stjdN, html code.shiki .stjdN{--shiki-default:#C5E478;--shiki-default-font-style:italic}html pre.shiki code .sVyDW, html code.shiki .sVyDW{--shiki-default:#C792EA;--shiki-default-font-style:italic}html pre.shiki code .sJeqr, html code.shiki .sJeqr{--shiki-default:#D6DEEB}html pre.shiki code .stNC8, html code.shiki .stNC8{--shiki-default:#D9F5DD}html pre.shiki code .sNQAM, html code.shiki .sNQAM{--shiki-default:#ECC48D}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sywZT, html code.shiki .sywZT{--shiki-default:#82AAFF;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sIpC3, html code.shiki .sIpC3{--shiki-default:#FF5874;--shiki-default-font-style:italic}html pre.shiki code .slldn, html code.shiki .slldn{--shiki-default:#7FDBCA;--shiki-default-font-style:italic}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 .s8J1n, html code.shiki .s8J1n{--shiki-default:#637777;--shiki-default-font-style:italic}html pre.shiki code .sasy7, html code.shiki .sasy7{--shiki-default:#D7DBE0}html pre.shiki code .s34Dl, html code.shiki .s34Dl{--shiki-default:#C5E478}html pre.shiki code .sz1_M, html code.shiki .sz1_M{--shiki-default:#80CBC4}html pre.shiki code .sddte, html code.shiki .sddte{--shiki-default:#BAEBE2}",{"title":65,"searchDepth":90,"depth":90,"links":1800},[1801,1802,1803,1804,1805,1806,1807,1808],{"id":16,"depth":90,"text":17},{"id":518,"depth":90,"text":519},{"id":771,"depth":90,"text":772},{"id":963,"depth":90,"text":964},{"id":1345,"depth":90,"text":1346},{"id":1382,"depth":90,"text":1383},{"id":1560,"depth":90,"text":1561},{"id":1783,"depth":90,"text":1784},"2026-08-24","Vue3 has a lot of hidden gems. Here are seven tips and tricks that most developers don't know about — but definitely should.","md",{},"\u002Fblog\u002Fvue3-tips-tricks",{"title":5,"description":1810},"blog\u002Fvue3-tips-tricks",[64,1817],"typescript","bGCIHWQg4bntJz20duIdoqModGgzRzoacYOlUrvf_5U",1790011947764]