[{"data":1,"prerenderedAt":2595},["ShallowReactive",2],{"page-\u002Fvue\u002F14-dynamic-and-async-components":3},{"id":4,"title":5,"body":6,"description":2588,"extension":2589,"meta":2590,"navigation":118,"path":2591,"seo":2592,"stem":2593,"__hash__":2594},"content\u002Fvue\u002F14-dynamic-and-async-components.md","Vue 3 Engineering Reference — Dynamic & Async Components",{"type":7,"value":8,"toc":2576},"minimark",[9,14,23,396,401,519,523,904,908,1928,1932,2058,2062,2190,2194,2197,2354,2572],[10,11,13],"h1",{"id":12},"_14-dynamic-async-components","14 — Dynamic & Async Components",[15,16,18,19,22],"h2",{"id":17},"component-runtime-component-switching","component ",[20,21],"is",{}," — Runtime Component Switching",[24,25,28],"code-wrapper",{"filename":26,"language":27},"DynamicComponent.vue","vue",[29,30,34],"pre",{"className":31,"code":32,"language":27,"meta":33,"style":33},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup>\nimport { shallowRef, markRaw, ref, computed } from 'vue'\nimport HomeView from '.\u002FHomeView.vue'\nimport ProfileView from '.\u002FProfileView.vue'\nimport SettingsView from '.\u002FSettingsView.vue'\n\n\u002F\u002F ── shallowRef: hold component definitions without deep reactivity ──\n\u002F\u002F Components are complex objects — reactive() wrapping them is wasteful\n\u002F\u002F and can break Vue's internal compilation caches. Use shallowRef or markRaw.\nconst components = shallowRef({\n  home: markRaw(HomeView),\n  profile: markRaw(ProfileView),\n  settings: markRaw(SettingsView),\n})\n\nconst activeTab = ref('home')\nconst currentComponent = computed(() => components.value[activeTab.value])\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- ── :is binds to: component object | string name | async component ── -->\n  \u003C!-- Vue resolves the component and renders it. -->\n  \u003C!-- Switching :is unmounts the old component, mounts the new one. -->\n  \u003Ccomponent :is=\"currentComponent\" \u002F>\n\n  \u003Cnav>\n    \u003Cbutton v-for=\"(comp, name) in components\" :key=\"name\" @click=\"activeTab = name\">\n      {{ name }}\n    \u003C\u002Fbutton>\n  \u003C\u002Fnav>\n\u003C\u002Ftemplate>\n","",[35,36,37,57,74,87,100,113,120,127,133,139,158,170,181,192,198,203,225,247,257,262,272,278,284,290,311,316,326,361,367,377,387],"code",{"__ignoreMap":33},[38,39,42,46,50,54],"span",{"class":40,"line":41},"line",1,[38,43,45],{"class":44},"ssxIu","\u003C",[38,47,49],{"class":48},"sk71V","script",[38,51,53],{"class":52},"sIsaT"," setup",[38,55,56],{"class":44},">\n",[38,58,60,64,67,70],{"class":40,"line":59},2,[38,61,63],{"class":62},"svdQ7","import",[38,65,66],{"class":44}," { shallowRef, markRaw, ref, computed } ",[38,68,69],{"class":62},"from",[38,71,73],{"class":72},"sJ6F3"," 'vue'\n",[38,75,77,79,82,84],{"class":40,"line":76},3,[38,78,63],{"class":62},[38,80,81],{"class":44}," HomeView ",[38,83,69],{"class":62},[38,85,86],{"class":72}," '.\u002FHomeView.vue'\n",[38,88,90,92,95,97],{"class":40,"line":89},4,[38,91,63],{"class":62},[38,93,94],{"class":44}," ProfileView ",[38,96,69],{"class":62},[38,98,99],{"class":72}," '.\u002FProfileView.vue'\n",[38,101,103,105,108,110],{"class":40,"line":102},5,[38,104,63],{"class":62},[38,106,107],{"class":44}," SettingsView ",[38,109,69],{"class":62},[38,111,112],{"class":72}," '.\u002FSettingsView.vue'\n",[38,114,116],{"class":40,"line":115},6,[38,117,119],{"emptyLinePlaceholder":118},true,"\n",[38,121,123],{"class":40,"line":122},7,[38,124,126],{"class":125},"sdCPZ","\u002F\u002F ── shallowRef: hold component definitions without deep reactivity ──\n",[38,128,130],{"class":40,"line":129},8,[38,131,132],{"class":125},"\u002F\u002F Components are complex objects — reactive() wrapping them is wasteful\n",[38,134,136],{"class":40,"line":135},9,[38,137,138],{"class":125},"\u002F\u002F and can break Vue's internal compilation caches. Use shallowRef or markRaw.\n",[38,140,142,145,149,152,155],{"class":40,"line":141},10,[38,143,144],{"class":62},"const",[38,146,148],{"class":147},"snvgF"," components",[38,150,151],{"class":62}," =",[38,153,154],{"class":52}," shallowRef",[38,156,157],{"class":44},"({\n",[38,159,161,164,167],{"class":40,"line":160},11,[38,162,163],{"class":44},"  home: ",[38,165,166],{"class":52},"markRaw",[38,168,169],{"class":44},"(HomeView),\n",[38,171,173,176,178],{"class":40,"line":172},12,[38,174,175],{"class":44},"  profile: ",[38,177,166],{"class":52},[38,179,180],{"class":44},"(ProfileView),\n",[38,182,184,187,189],{"class":40,"line":183},13,[38,185,186],{"class":44},"  settings: ",[38,188,166],{"class":52},[38,190,191],{"class":44},"(SettingsView),\n",[38,193,195],{"class":40,"line":194},14,[38,196,197],{"class":44},"})\n",[38,199,201],{"class":40,"line":200},15,[38,202,119],{"emptyLinePlaceholder":118},[38,204,206,208,211,213,216,219,222],{"class":40,"line":205},16,[38,207,144],{"class":62},[38,209,210],{"class":147}," activeTab",[38,212,151],{"class":62},[38,214,215],{"class":52}," ref",[38,217,218],{"class":44},"(",[38,220,221],{"class":72},"'home'",[38,223,224],{"class":44},")\n",[38,226,228,230,233,235,238,241,244],{"class":40,"line":227},17,[38,229,144],{"class":62},[38,231,232],{"class":147}," currentComponent",[38,234,151],{"class":62},[38,236,237],{"class":52}," computed",[38,239,240],{"class":44},"(() ",[38,242,243],{"class":62},"=>",[38,245,246],{"class":44}," components.value[activeTab.value])\n",[38,248,250,253,255],{"class":40,"line":249},18,[38,251,252],{"class":44},"\u003C\u002F",[38,254,49],{"class":48},[38,256,56],{"class":44},[38,258,260],{"class":40,"line":259},19,[38,261,119],{"emptyLinePlaceholder":118},[38,263,265,267,270],{"class":40,"line":264},20,[38,266,45],{"class":44},[38,268,269],{"class":48},"template",[38,271,56],{"class":44},[38,273,275],{"class":40,"line":274},21,[38,276,277],{"class":125},"  \u003C!-- ── :is binds to: component object | string name | async component ── -->\n",[38,279,281],{"class":40,"line":280},22,[38,282,283],{"class":125},"  \u003C!-- Vue resolves the component and renders it. -->\n",[38,285,287],{"class":40,"line":286},23,[38,288,289],{"class":125},"  \u003C!-- Switching :is unmounts the old component, mounts the new one. -->\n",[38,291,293,296,299,302,305,308],{"class":40,"line":292},24,[38,294,295],{"class":44},"  \u003C",[38,297,298],{"class":48},"component",[38,300,301],{"class":52}," :is",[38,303,304],{"class":44},"=",[38,306,307],{"class":72},"\"currentComponent\"",[38,309,310],{"class":44}," \u002F>\n",[38,312,314],{"class":40,"line":313},25,[38,315,119],{"emptyLinePlaceholder":118},[38,317,319,321,324],{"class":40,"line":318},26,[38,320,295],{"class":44},[38,322,323],{"class":48},"nav",[38,325,56],{"class":44},[38,327,329,332,335,338,340,343,346,348,351,354,356,359],{"class":40,"line":328},27,[38,330,331],{"class":44},"    \u003C",[38,333,334],{"class":48},"button",[38,336,337],{"class":52}," v-for",[38,339,304],{"class":44},[38,341,342],{"class":72},"\"(comp, name) in components\"",[38,344,345],{"class":52}," :key",[38,347,304],{"class":44},[38,349,350],{"class":72},"\"name\"",[38,352,353],{"class":52}," @click",[38,355,304],{"class":44},[38,357,358],{"class":72},"\"activeTab = name\"",[38,360,56],{"class":44},[38,362,364],{"class":40,"line":363},28,[38,365,366],{"class":44},"      {{ name }}\n",[38,368,370,373,375],{"class":40,"line":369},29,[38,371,372],{"class":44},"    \u003C\u002F",[38,374,334],{"class":48},[38,376,56],{"class":44},[38,378,380,383,385],{"class":40,"line":379},30,[38,381,382],{"class":44},"  \u003C\u002F",[38,384,323],{"class":48},[38,386,56],{"class":44},[38,388,390,392,394],{"class":40,"line":389},31,[38,391,252],{"class":44},[38,393,269],{"class":48},[38,395,56],{"class":44},[397,398,400],"h3",{"id":399},"anti-pattern-reactive-on-component-definitions","Anti-Pattern: reactive() on Component Definitions",[24,402,405],{"filename":403,"language":404},"anti-pattern.ts","typescript",[29,406,409],{"className":407,"code":408,"language":404,"meta":33,"style":33},"language-typescript shiki shiki-themes github-light github-dark","import { reactive } from 'vue'\nimport HomeView from '.\u002FHomeView.vue'\n\n\u002F\u002F ❌ WRONG: reactive() wraps the component in a deep Proxy\n\u002F\u002F This breaks Vue's internal component caching and causes subtle render bugs.\nconst components = reactive({\n  home: HomeView,  \u002F\u002F Proxy-wrapped — internal compilation cache misses\n})\n\n\u002F\u002F ✅ CORRECT: shallowRef or markRaw\nimport { shallowRef, markRaw } from 'vue'\nconst components = shallowRef({\n  home: markRaw(HomeView),  \u002F\u002F raw — no Proxy wrapping\n})\n",[35,410,411,422,432,436,441,446,459,467,471,475,480,491,503,515],{"__ignoreMap":33},[38,412,413,415,418,420],{"class":40,"line":41},[38,414,63],{"class":62},[38,416,417],{"class":44}," { reactive } ",[38,419,69],{"class":62},[38,421,73],{"class":72},[38,423,424,426,428,430],{"class":40,"line":59},[38,425,63],{"class":62},[38,427,81],{"class":44},[38,429,69],{"class":62},[38,431,86],{"class":72},[38,433,434],{"class":40,"line":76},[38,435,119],{"emptyLinePlaceholder":118},[38,437,438],{"class":40,"line":89},[38,439,440],{"class":125},"\u002F\u002F ❌ WRONG: reactive() wraps the component in a deep Proxy\n",[38,442,443],{"class":40,"line":102},[38,444,445],{"class":125},"\u002F\u002F This breaks Vue's internal component caching and causes subtle render bugs.\n",[38,447,448,450,452,454,457],{"class":40,"line":115},[38,449,144],{"class":62},[38,451,148],{"class":147},[38,453,151],{"class":62},[38,455,456],{"class":52}," reactive",[38,458,157],{"class":44},[38,460,461,464],{"class":40,"line":122},[38,462,463],{"class":44},"  home: HomeView,  ",[38,465,466],{"class":125},"\u002F\u002F Proxy-wrapped — internal compilation cache misses\n",[38,468,469],{"class":40,"line":129},[38,470,197],{"class":44},[38,472,473],{"class":40,"line":135},[38,474,119],{"emptyLinePlaceholder":118},[38,476,477],{"class":40,"line":141},[38,478,479],{"class":125},"\u002F\u002F ✅ CORRECT: shallowRef or markRaw\n",[38,481,482,484,487,489],{"class":40,"line":160},[38,483,63],{"class":62},[38,485,486],{"class":44}," { shallowRef, markRaw } ",[38,488,69],{"class":62},[38,490,73],{"class":72},[38,492,493,495,497,499,501],{"class":40,"line":172},[38,494,144],{"class":62},[38,496,148],{"class":147},[38,498,151],{"class":62},[38,500,154],{"class":52},[38,502,157],{"class":44},[38,504,505,507,509,512],{"class":40,"line":183},[38,506,163],{"class":44},[38,508,166],{"class":52},[38,510,511],{"class":44},"(HomeView),  ",[38,513,514],{"class":125},"\u002F\u002F raw — no Proxy wrapping\n",[38,516,517],{"class":40,"line":194},[38,518,197],{"class":44},[15,520,522],{"id":521},"defineasynccomponent-code-splitting-with-states","defineAsyncComponent — Code Splitting with States",[24,524,526],{"filename":525,"language":404},"async-components.ts",[29,527,529],{"className":407,"code":528,"language":404,"meta":33,"style":33},"import { defineAsyncComponent } from 'vue'\nimport LoadingSpinner from '.\u002FLoadingSpinner.vue'\nimport ErrorDisplay from '.\u002FErrorDisplay.vue'\n\n\u002F\u002F ── Basic async component: loaded on first use ──\n\u002F\u002F Dynamic import creates a separate chunk (code splitting).\nconst AsyncDashboard = defineAsyncComponent(() => import('.\u002FDashboard.vue'))\n\n\u002F\u002F ── Full configuration: loading\u002Ferror states, delay, timeout ──\nconst AsyncChart = defineAsyncComponent({\n  \u002F\u002F loader: returns a promise that resolves to the component\n  loader: () => import('.\u002FChart.vue'),\n\n  \u002F\u002F loadingComponent: shown while the chunk downloads\n  loadingComponent: LoadingSpinner,\n\n  \u002F\u002F errorComponent: shown if the chunk fails to load\n  errorComponent: ErrorDisplay,\n\n  \u002F\u002F delay: ms before showing loadingComponent (avoid flicker for fast loads)\n  delay: 200,\n\n  \u002F\u002F timeout: ms before showing errorComponent (chunk took too long)\n  timeout: 10_000,\n\n  \u002F\u002F suspensible: defer to \u003CSuspense> boundary (true by default)\n  \u002F\u002F If false, shows loadingComponent instead of using Suspense fallback\n  suspensible: true,\n\n  \u002F\u002F ── onError: retry logic for failed loads ──\n  onError(error, retry, fail, attempts) {\n    \u002F\u002F error: the load error\n    \u002F\u002F retry(): call to retry loading\n    \u002F\u002F fail(): call to give up (show errorComponent)\n    \u002F\u002F attempts: number of attempts so far\n    if (error.message.includes('Network') && attempts \u003C= 3) {\n      setTimeout(retry, 1000 * attempts)  \u002F\u002F exponential-ish backoff\n    } else {\n      fail()  \u002F\u002F give up — show errorComponent\n    }\n  },\n})\n",[35,530,531,542,554,566,570,575,580,607,611,616,629,634,654,658,663,668,672,677,682,686,691,702,706,711,721,725,730,735,745,749,754,784,790,796,802,808,842,863,875,887,893,899],{"__ignoreMap":33},[38,532,533,535,538,540],{"class":40,"line":41},[38,534,63],{"class":62},[38,536,537],{"class":44}," { defineAsyncComponent } ",[38,539,69],{"class":62},[38,541,73],{"class":72},[38,543,544,546,549,551],{"class":40,"line":59},[38,545,63],{"class":62},[38,547,548],{"class":44}," LoadingSpinner ",[38,550,69],{"class":62},[38,552,553],{"class":72}," '.\u002FLoadingSpinner.vue'\n",[38,555,556,558,561,563],{"class":40,"line":76},[38,557,63],{"class":62},[38,559,560],{"class":44}," ErrorDisplay ",[38,562,69],{"class":62},[38,564,565],{"class":72}," '.\u002FErrorDisplay.vue'\n",[38,567,568],{"class":40,"line":89},[38,569,119],{"emptyLinePlaceholder":118},[38,571,572],{"class":40,"line":102},[38,573,574],{"class":125},"\u002F\u002F ── Basic async component: loaded on first use ──\n",[38,576,577],{"class":40,"line":115},[38,578,579],{"class":125},"\u002F\u002F Dynamic import creates a separate chunk (code splitting).\n",[38,581,582,584,587,589,592,594,596,599,601,604],{"class":40,"line":122},[38,583,144],{"class":62},[38,585,586],{"class":147}," AsyncDashboard",[38,588,151],{"class":62},[38,590,591],{"class":52}," defineAsyncComponent",[38,593,240],{"class":44},[38,595,243],{"class":62},[38,597,598],{"class":62}," import",[38,600,218],{"class":44},[38,602,603],{"class":72},"'.\u002FDashboard.vue'",[38,605,606],{"class":44},"))\n",[38,608,609],{"class":40,"line":129},[38,610,119],{"emptyLinePlaceholder":118},[38,612,613],{"class":40,"line":135},[38,614,615],{"class":125},"\u002F\u002F ── Full configuration: loading\u002Ferror states, delay, timeout ──\n",[38,617,618,620,623,625,627],{"class":40,"line":141},[38,619,144],{"class":62},[38,621,622],{"class":147}," AsyncChart",[38,624,151],{"class":62},[38,626,591],{"class":52},[38,628,157],{"class":44},[38,630,631],{"class":40,"line":160},[38,632,633],{"class":125},"  \u002F\u002F loader: returns a promise that resolves to the component\n",[38,635,636,639,642,644,646,648,651],{"class":40,"line":172},[38,637,638],{"class":52},"  loader",[38,640,641],{"class":44},": () ",[38,643,243],{"class":62},[38,645,598],{"class":62},[38,647,218],{"class":44},[38,649,650],{"class":72},"'.\u002FChart.vue'",[38,652,653],{"class":44},"),\n",[38,655,656],{"class":40,"line":183},[38,657,119],{"emptyLinePlaceholder":118},[38,659,660],{"class":40,"line":194},[38,661,662],{"class":125},"  \u002F\u002F loadingComponent: shown while the chunk downloads\n",[38,664,665],{"class":40,"line":200},[38,666,667],{"class":44},"  loadingComponent: LoadingSpinner,\n",[38,669,670],{"class":40,"line":205},[38,671,119],{"emptyLinePlaceholder":118},[38,673,674],{"class":40,"line":227},[38,675,676],{"class":125},"  \u002F\u002F errorComponent: shown if the chunk fails to load\n",[38,678,679],{"class":40,"line":249},[38,680,681],{"class":44},"  errorComponent: ErrorDisplay,\n",[38,683,684],{"class":40,"line":259},[38,685,119],{"emptyLinePlaceholder":118},[38,687,688],{"class":40,"line":264},[38,689,690],{"class":125},"  \u002F\u002F delay: ms before showing loadingComponent (avoid flicker for fast loads)\n",[38,692,693,696,699],{"class":40,"line":274},[38,694,695],{"class":44},"  delay: ",[38,697,698],{"class":147},"200",[38,700,701],{"class":44},",\n",[38,703,704],{"class":40,"line":280},[38,705,119],{"emptyLinePlaceholder":118},[38,707,708],{"class":40,"line":286},[38,709,710],{"class":125},"  \u002F\u002F timeout: ms before showing errorComponent (chunk took too long)\n",[38,712,713,716,719],{"class":40,"line":292},[38,714,715],{"class":44},"  timeout: ",[38,717,718],{"class":147},"10_000",[38,720,701],{"class":44},[38,722,723],{"class":40,"line":313},[38,724,119],{"emptyLinePlaceholder":118},[38,726,727],{"class":40,"line":318},[38,728,729],{"class":125},"  \u002F\u002F suspensible: defer to \u003CSuspense> boundary (true by default)\n",[38,731,732],{"class":40,"line":328},[38,733,734],{"class":125},"  \u002F\u002F If false, shows loadingComponent instead of using Suspense fallback\n",[38,736,737,740,743],{"class":40,"line":363},[38,738,739],{"class":44},"  suspensible: ",[38,741,742],{"class":147},"true",[38,744,701],{"class":44},[38,746,747],{"class":40,"line":369},[38,748,119],{"emptyLinePlaceholder":118},[38,750,751],{"class":40,"line":379},[38,752,753],{"class":125},"  \u002F\u002F ── onError: retry logic for failed loads ──\n",[38,755,756,759,761,765,768,771,773,776,778,781],{"class":40,"line":389},[38,757,758],{"class":52},"  onError",[38,760,218],{"class":44},[38,762,764],{"class":763},"sCrzJ","error",[38,766,767],{"class":44},", ",[38,769,770],{"class":763},"retry",[38,772,767],{"class":44},[38,774,775],{"class":763},"fail",[38,777,767],{"class":44},[38,779,780],{"class":763},"attempts",[38,782,783],{"class":44},") {\n",[38,785,787],{"class":40,"line":786},32,[38,788,789],{"class":125},"    \u002F\u002F error: the load error\n",[38,791,793],{"class":40,"line":792},33,[38,794,795],{"class":125},"    \u002F\u002F retry(): call to retry loading\n",[38,797,799],{"class":40,"line":798},34,[38,800,801],{"class":125},"    \u002F\u002F fail(): call to give up (show errorComponent)\n",[38,803,805],{"class":40,"line":804},35,[38,806,807],{"class":125},"    \u002F\u002F attempts: number of attempts so far\n",[38,809,811,814,817,820,822,825,828,831,834,837,840],{"class":40,"line":810},36,[38,812,813],{"class":62},"    if",[38,815,816],{"class":44}," (error.message.",[38,818,819],{"class":52},"includes",[38,821,218],{"class":44},[38,823,824],{"class":72},"'Network'",[38,826,827],{"class":44},") ",[38,829,830],{"class":62},"&&",[38,832,833],{"class":44}," attempts ",[38,835,836],{"class":62},"\u003C=",[38,838,839],{"class":147}," 3",[38,841,783],{"class":44},[38,843,845,848,851,854,857,860],{"class":40,"line":844},37,[38,846,847],{"class":52},"      setTimeout",[38,849,850],{"class":44},"(retry, ",[38,852,853],{"class":147},"1000",[38,855,856],{"class":62}," *",[38,858,859],{"class":44}," attempts)  ",[38,861,862],{"class":125},"\u002F\u002F exponential-ish backoff\n",[38,864,866,869,872],{"class":40,"line":865},38,[38,867,868],{"class":44},"    } ",[38,870,871],{"class":62},"else",[38,873,874],{"class":44}," {\n",[38,876,878,881,884],{"class":40,"line":877},39,[38,879,880],{"class":52},"      fail",[38,882,883],{"class":44},"()  ",[38,885,886],{"class":125},"\u002F\u002F give up — show errorComponent\n",[38,888,890],{"class":40,"line":889},40,[38,891,892],{"class":44},"    }\n",[38,894,896],{"class":40,"line":895},41,[38,897,898],{"class":44},"  },\n",[38,900,902],{"class":40,"line":901},42,[38,903,197],{"class":44},[15,905,907],{"id":906},"keepalive-lru-caching-of-component-instances","KeepAlive — LRU Caching of Component Instances",[24,909,911,1147,1368,1372],{"filename":910,"language":27},"KeepAlivePattern.vue",[29,912,914],{"className":31,"code":913,"language":27,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref, computed, KeepAlive } from 'vue'\nimport { useRoute } from 'vue-router'\n\nconst route = useRoute()\n\n\u002F\u002F ── KeepAlive caches component instances when they're toggled out ──\n\u002F\u002F Instead of destroying + recreating (losing state), the instance is\n\u002F\u002F detached from the DOM and cached in memory. Re-activation restores state.\n\u002F\u002F\n\u002F\u002F Hooks: onActivated (re-attached), onDeactivated (detached, not destroyed)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- ── include\u002Fexclude: cache only specific components by name ── -->\n  \u003C!-- Component name comes from: defineOptions({ name: 'X' }) or .vue filename -->\n  \u003CKeepAlive\n    :include=\"['UserList', 'SearchResults']\"\n    :exclude=\"['HeavyChart']\"\n    :max=\"5\"\n  >\n    \u003C!-- ── max: LRU eviction — keep at most 5 instances ── -->\n    \u003C!-- When 6th is cached, the least recently used is destroyed -->\n    \u003Ccomponent :is=\"route.meta.component\" \u002F>\n  \u003C\u002FKeepAlive>\n\u003C\u002Ftemplate>\n",[35,915,916,926,937,949,953,968,972,977,982,987,992,997,1005,1009,1017,1022,1027,1034,1064,1084,1100,1105,1110,1115,1130,1139],{"__ignoreMap":33},[38,917,918,920,922,924],{"class":40,"line":41},[38,919,45],{"class":44},[38,921,49],{"class":48},[38,923,53],{"class":52},[38,925,56],{"class":44},[38,927,928,930,933,935],{"class":40,"line":59},[38,929,63],{"class":62},[38,931,932],{"class":44}," { ref, computed, KeepAlive } ",[38,934,69],{"class":62},[38,936,73],{"class":72},[38,938,939,941,944,946],{"class":40,"line":76},[38,940,63],{"class":62},[38,942,943],{"class":44}," { useRoute } ",[38,945,69],{"class":62},[38,947,948],{"class":72}," 'vue-router'\n",[38,950,951],{"class":40,"line":89},[38,952,119],{"emptyLinePlaceholder":118},[38,954,955,957,960,962,965],{"class":40,"line":102},[38,956,144],{"class":62},[38,958,959],{"class":147}," route",[38,961,151],{"class":62},[38,963,964],{"class":52}," useRoute",[38,966,967],{"class":44},"()\n",[38,969,970],{"class":40,"line":115},[38,971,119],{"emptyLinePlaceholder":118},[38,973,974],{"class":40,"line":122},[38,975,976],{"class":125},"\u002F\u002F ── KeepAlive caches component instances when they're toggled out ──\n",[38,978,979],{"class":40,"line":129},[38,980,981],{"class":125},"\u002F\u002F Instead of destroying + recreating (losing state), the instance is\n",[38,983,984],{"class":40,"line":135},[38,985,986],{"class":125},"\u002F\u002F detached from the DOM and cached in memory. Re-activation restores state.\n",[38,988,989],{"class":40,"line":141},[38,990,991],{"class":125},"\u002F\u002F\n",[38,993,994],{"class":40,"line":160},[38,995,996],{"class":125},"\u002F\u002F Hooks: onActivated (re-attached), onDeactivated (detached, not destroyed)\n",[38,998,999,1001,1003],{"class":40,"line":172},[38,1000,252],{"class":44},[38,1002,49],{"class":48},[38,1004,56],{"class":44},[38,1006,1007],{"class":40,"line":183},[38,1008,119],{"emptyLinePlaceholder":118},[38,1010,1011,1013,1015],{"class":40,"line":194},[38,1012,45],{"class":44},[38,1014,269],{"class":48},[38,1016,56],{"class":44},[38,1018,1019],{"class":40,"line":200},[38,1020,1021],{"class":125},"  \u003C!-- ── include\u002Fexclude: cache only specific components by name ── -->\n",[38,1023,1024],{"class":40,"line":205},[38,1025,1026],{"class":125},"  \u003C!-- Component name comes from: defineOptions({ name: 'X' }) or .vue filename -->\n",[38,1028,1029,1031],{"class":40,"line":227},[38,1030,295],{"class":44},[38,1032,1033],{"class":48},"KeepAlive\n",[38,1035,1036,1039,1042,1044,1047,1050,1053,1055,1058,1061],{"class":40,"line":249},[38,1037,1038],{"class":44},"    :",[38,1040,1041],{"class":52},"include",[38,1043,304],{"class":44},[38,1045,1046],{"class":72},"\"",[38,1048,1049],{"class":44},"[",[38,1051,1052],{"class":72},"'UserList'",[38,1054,767],{"class":44},[38,1056,1057],{"class":72},"'SearchResults'",[38,1059,1060],{"class":44},"]",[38,1062,1063],{"class":72},"\"\n",[38,1065,1066,1068,1071,1073,1075,1077,1080,1082],{"class":40,"line":259},[38,1067,1038],{"class":44},[38,1069,1070],{"class":52},"exclude",[38,1072,304],{"class":44},[38,1074,1046],{"class":72},[38,1076,1049],{"class":44},[38,1078,1079],{"class":72},"'HeavyChart'",[38,1081,1060],{"class":44},[38,1083,1063],{"class":72},[38,1085,1086,1088,1091,1093,1095,1098],{"class":40,"line":264},[38,1087,1038],{"class":44},[38,1089,1090],{"class":52},"max",[38,1092,304],{"class":44},[38,1094,1046],{"class":72},[38,1096,1097],{"class":147},"5",[38,1099,1063],{"class":72},[38,1101,1102],{"class":40,"line":274},[38,1103,1104],{"class":44},"  >\n",[38,1106,1107],{"class":40,"line":280},[38,1108,1109],{"class":125},"    \u003C!-- ── max: LRU eviction — keep at most 5 instances ── -->\n",[38,1111,1112],{"class":40,"line":286},[38,1113,1114],{"class":125},"    \u003C!-- When 6th is cached, the least recently used is destroyed -->\n",[38,1116,1117,1119,1121,1123,1125,1128],{"class":40,"line":292},[38,1118,331],{"class":44},[38,1120,298],{"class":48},[38,1122,301],{"class":52},[38,1124,304],{"class":44},[38,1126,1127],{"class":72},"\"route.meta.component\"",[38,1129,310],{"class":44},[38,1131,1132,1134,1137],{"class":40,"line":313},[38,1133,382],{"class":44},[38,1135,1136],{"class":48},"KeepAlive",[38,1138,56],{"class":44},[38,1140,1141,1143,1145],{"class":40,"line":318},[38,1142,252],{"class":44},[38,1144,269],{"class":48},[38,1146,56],{"class":44},[24,1148,1150],{"filename":1149,"language":27},"KeepAliveTab.vue",[29,1151,1153],{"className":31,"code":1152,"language":27,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref, onMounted, onActivated, onDeactivated, onUnmounted, defineOptions } from 'vue'\n\ndefineOptions({ name: 'UserList' })  \u002F\u002F required for KeepAlive include\u002Fexclude\n\nconst searchQuery = ref('')\n\n\u002F\u002F ── onMounted fires ONCE even with KeepAlive ──\nonMounted(() => console.log('mounted — fires once'))\n\n\u002F\u002F ── onActivated fires on EVERY activation (including first mount) ──\nonActivated(() => {\n  \u002F\u002F ── Resume polling, re-attach event listeners, refresh data ──\n  \u002F\u002F This is the \"resume\" hook — component is visible again.\n  startPolling()\n})\n\n\u002F\u002F ── onDeactivated fires when cached (detached from DOM, NOT destroyed) ──\nonDeactivated(() => {\n  \u002F\u002F ── Pause polling, remove event listeners — component is hidden ──\n  \u002F\u002F State (searchQuery, scroll position) is PRESERVED for re-activation.\n  stopPolling()\n})\n\n\u002F\u002F ── onUnmounted fires only when KeepAlive evicts (max exceeded) or is removed ──\nonUnmounted(() => console.log('truly destroyed'))\n\u003C\u002Fscript>\n",[35,1154,1155,1165,1176,1180,1196,1200,1218,1222,1227,1249,1253,1258,1269,1274,1279,1286,1290,1294,1299,1310,1315,1320,1327,1331,1335,1340,1360],{"__ignoreMap":33},[38,1156,1157,1159,1161,1163],{"class":40,"line":41},[38,1158,45],{"class":44},[38,1160,49],{"class":48},[38,1162,53],{"class":52},[38,1164,56],{"class":44},[38,1166,1167,1169,1172,1174],{"class":40,"line":59},[38,1168,63],{"class":62},[38,1170,1171],{"class":44}," { ref, onMounted, onActivated, onDeactivated, onUnmounted, defineOptions } ",[38,1173,69],{"class":62},[38,1175,73],{"class":72},[38,1177,1178],{"class":40,"line":76},[38,1179,119],{"emptyLinePlaceholder":118},[38,1181,1182,1185,1188,1190,1193],{"class":40,"line":89},[38,1183,1184],{"class":52},"defineOptions",[38,1186,1187],{"class":44},"({ name: ",[38,1189,1052],{"class":72},[38,1191,1192],{"class":44}," })  ",[38,1194,1195],{"class":125},"\u002F\u002F required for KeepAlive include\u002Fexclude\n",[38,1197,1198],{"class":40,"line":102},[38,1199,119],{"emptyLinePlaceholder":118},[38,1201,1202,1204,1207,1209,1211,1213,1216],{"class":40,"line":115},[38,1203,144],{"class":62},[38,1205,1206],{"class":147}," searchQuery",[38,1208,151],{"class":62},[38,1210,215],{"class":52},[38,1212,218],{"class":44},[38,1214,1215],{"class":72},"''",[38,1217,224],{"class":44},[38,1219,1220],{"class":40,"line":122},[38,1221,119],{"emptyLinePlaceholder":118},[38,1223,1224],{"class":40,"line":129},[38,1225,1226],{"class":125},"\u002F\u002F ── onMounted fires ONCE even with KeepAlive ──\n",[38,1228,1229,1232,1234,1236,1239,1242,1244,1247],{"class":40,"line":135},[38,1230,1231],{"class":52},"onMounted",[38,1233,240],{"class":44},[38,1235,243],{"class":62},[38,1237,1238],{"class":44}," console.",[38,1240,1241],{"class":52},"log",[38,1243,218],{"class":44},[38,1245,1246],{"class":72},"'mounted — fires once'",[38,1248,606],{"class":44},[38,1250,1251],{"class":40,"line":141},[38,1252,119],{"emptyLinePlaceholder":118},[38,1254,1255],{"class":40,"line":160},[38,1256,1257],{"class":125},"\u002F\u002F ── onActivated fires on EVERY activation (including first mount) ──\n",[38,1259,1260,1263,1265,1267],{"class":40,"line":172},[38,1261,1262],{"class":52},"onActivated",[38,1264,240],{"class":44},[38,1266,243],{"class":62},[38,1268,874],{"class":44},[38,1270,1271],{"class":40,"line":183},[38,1272,1273],{"class":125},"  \u002F\u002F ── Resume polling, re-attach event listeners, refresh data ──\n",[38,1275,1276],{"class":40,"line":194},[38,1277,1278],{"class":125},"  \u002F\u002F This is the \"resume\" hook — component is visible again.\n",[38,1280,1281,1284],{"class":40,"line":200},[38,1282,1283],{"class":52},"  startPolling",[38,1285,967],{"class":44},[38,1287,1288],{"class":40,"line":205},[38,1289,197],{"class":44},[38,1291,1292],{"class":40,"line":227},[38,1293,119],{"emptyLinePlaceholder":118},[38,1295,1296],{"class":40,"line":249},[38,1297,1298],{"class":125},"\u002F\u002F ── onDeactivated fires when cached (detached from DOM, NOT destroyed) ──\n",[38,1300,1301,1304,1306,1308],{"class":40,"line":259},[38,1302,1303],{"class":52},"onDeactivated",[38,1305,240],{"class":44},[38,1307,243],{"class":62},[38,1309,874],{"class":44},[38,1311,1312],{"class":40,"line":264},[38,1313,1314],{"class":125},"  \u002F\u002F ── Pause polling, remove event listeners — component is hidden ──\n",[38,1316,1317],{"class":40,"line":274},[38,1318,1319],{"class":125},"  \u002F\u002F State (searchQuery, scroll position) is PRESERVED for re-activation.\n",[38,1321,1322,1325],{"class":40,"line":280},[38,1323,1324],{"class":52},"  stopPolling",[38,1326,967],{"class":44},[38,1328,1329],{"class":40,"line":286},[38,1330,197],{"class":44},[38,1332,1333],{"class":40,"line":292},[38,1334,119],{"emptyLinePlaceholder":118},[38,1336,1337],{"class":40,"line":313},[38,1338,1339],{"class":125},"\u002F\u002F ── onUnmounted fires only when KeepAlive evicts (max exceeded) or is removed ──\n",[38,1341,1342,1345,1347,1349,1351,1353,1355,1358],{"class":40,"line":318},[38,1343,1344],{"class":52},"onUnmounted",[38,1346,240],{"class":44},[38,1348,243],{"class":62},[38,1350,1238],{"class":44},[38,1352,1241],{"class":52},[38,1354,218],{"class":44},[38,1356,1357],{"class":72},"'truly destroyed'",[38,1359,606],{"class":44},[38,1361,1362,1364,1366],{"class":40,"line":328},[38,1363,252],{"class":44},[38,1365,49],{"class":48},[38,1367,56],{"class":44},[15,1369,1371],{"id":1370},"suspense-async-component-orchestration","Suspense — Async Component Orchestration",[24,1373,1375,1784],{"filename":1374,"language":27},"SuspensePattern.vue",[29,1376,1378],{"className":31,"code":1377,"language":27,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref, onErrorCaptured, defineAsyncComponent } from 'vue'\n\n\u002F\u002F ── Suspense: coordinates multiple async child components ──\n\u002F\u002F Shows fallback until ALL async children's setup() promises resolve.\nconst AsyncHeader = defineAsyncComponent(() => import('.\u002FAsyncHeader.vue'))\nconst AsyncMain = defineAsyncComponent(() => import('.\u002FAsyncMain.vue'))\nconst AsyncFooter = defineAsyncComponent(() => import('.\u002FAsyncFooter.vue'))\n\nconst error = ref(null)\n\n\u002F\u002F ── Error handling: Suspense doesn't handle errors itself ──\n\u002F\u002F Use onErrorCaptured in the parent to catch async setup errors.\nonErrorCaptured((err) => {\n  error.value = err\n  return false  \u002F\u002F prevent error from propagating further\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"error\" class=\"error\">\n    \u003Cp>Failed to load: {{ error.message }}\u003C\u002Fp>\n    \u003Cbutton @click=\"error = null\">Retry\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\n  \u003C!-- ── Suspense shows #fallback until ALL children resolve ── -->\n  \u003CSuspense v-else>\n    \u003Ctemplate #default>\n      \u003CAsyncHeader \u002F>\n      \u003CAsyncMain \u002F>\n      \u003CAsyncFooter \u002F>\n      \u003C!-- All three must resolve before #default renders -->\n    \u003C\u002Ftemplate>\n    \u003Ctemplate #fallback>\n      \u003Cdiv class=\"loading-skeleton\">Loading page…\u003C\u002Fdiv>\n    \u003C\u002Ftemplate>\n  \u003C\u002FSuspense>\n\u003C\u002Ftemplate>\n",[35,1379,1380,1390,1401,1405,1410,1415,1439,1463,1487,1491,1509,1513,1518,1523,1540,1550,1561,1565,1573,1577,1585,1609,1623,1643,1651,1655,1660,1672,1686,1696,1705,1714,1719,1727,1740,1760,1768,1776],{"__ignoreMap":33},[38,1381,1382,1384,1386,1388],{"class":40,"line":41},[38,1383,45],{"class":44},[38,1385,49],{"class":48},[38,1387,53],{"class":52},[38,1389,56],{"class":44},[38,1391,1392,1394,1397,1399],{"class":40,"line":59},[38,1393,63],{"class":62},[38,1395,1396],{"class":44}," { ref, onErrorCaptured, defineAsyncComponent } ",[38,1398,69],{"class":62},[38,1400,73],{"class":72},[38,1402,1403],{"class":40,"line":76},[38,1404,119],{"emptyLinePlaceholder":118},[38,1406,1407],{"class":40,"line":89},[38,1408,1409],{"class":125},"\u002F\u002F ── Suspense: coordinates multiple async child components ──\n",[38,1411,1412],{"class":40,"line":102},[38,1413,1414],{"class":125},"\u002F\u002F Shows fallback until ALL async children's setup() promises resolve.\n",[38,1416,1417,1419,1422,1424,1426,1428,1430,1432,1434,1437],{"class":40,"line":115},[38,1418,144],{"class":62},[38,1420,1421],{"class":147}," AsyncHeader",[38,1423,151],{"class":62},[38,1425,591],{"class":52},[38,1427,240],{"class":44},[38,1429,243],{"class":62},[38,1431,598],{"class":62},[38,1433,218],{"class":44},[38,1435,1436],{"class":72},"'.\u002FAsyncHeader.vue'",[38,1438,606],{"class":44},[38,1440,1441,1443,1446,1448,1450,1452,1454,1456,1458,1461],{"class":40,"line":122},[38,1442,144],{"class":62},[38,1444,1445],{"class":147}," AsyncMain",[38,1447,151],{"class":62},[38,1449,591],{"class":52},[38,1451,240],{"class":44},[38,1453,243],{"class":62},[38,1455,598],{"class":62},[38,1457,218],{"class":44},[38,1459,1460],{"class":72},"'.\u002FAsyncMain.vue'",[38,1462,606],{"class":44},[38,1464,1465,1467,1470,1472,1474,1476,1478,1480,1482,1485],{"class":40,"line":129},[38,1466,144],{"class":62},[38,1468,1469],{"class":147}," AsyncFooter",[38,1471,151],{"class":62},[38,1473,591],{"class":52},[38,1475,240],{"class":44},[38,1477,243],{"class":62},[38,1479,598],{"class":62},[38,1481,218],{"class":44},[38,1483,1484],{"class":72},"'.\u002FAsyncFooter.vue'",[38,1486,606],{"class":44},[38,1488,1489],{"class":40,"line":135},[38,1490,119],{"emptyLinePlaceholder":118},[38,1492,1493,1495,1498,1500,1502,1504,1507],{"class":40,"line":141},[38,1494,144],{"class":62},[38,1496,1497],{"class":147}," error",[38,1499,151],{"class":62},[38,1501,215],{"class":52},[38,1503,218],{"class":44},[38,1505,1506],{"class":147},"null",[38,1508,224],{"class":44},[38,1510,1511],{"class":40,"line":160},[38,1512,119],{"emptyLinePlaceholder":118},[38,1514,1515],{"class":40,"line":172},[38,1516,1517],{"class":125},"\u002F\u002F ── Error handling: Suspense doesn't handle errors itself ──\n",[38,1519,1520],{"class":40,"line":183},[38,1521,1522],{"class":125},"\u002F\u002F Use onErrorCaptured in the parent to catch async setup errors.\n",[38,1524,1525,1528,1531,1534,1536,1538],{"class":40,"line":194},[38,1526,1527],{"class":52},"onErrorCaptured",[38,1529,1530],{"class":44},"((",[38,1532,1533],{"class":763},"err",[38,1535,827],{"class":44},[38,1537,243],{"class":62},[38,1539,874],{"class":44},[38,1541,1542,1545,1547],{"class":40,"line":200},[38,1543,1544],{"class":44},"  error.value ",[38,1546,304],{"class":62},[38,1548,1549],{"class":44}," err\n",[38,1551,1552,1555,1558],{"class":40,"line":205},[38,1553,1554],{"class":62},"  return",[38,1556,1557],{"class":147}," false",[38,1559,1560],{"class":125},"  \u002F\u002F prevent error from propagating further\n",[38,1562,1563],{"class":40,"line":227},[38,1564,197],{"class":44},[38,1566,1567,1569,1571],{"class":40,"line":249},[38,1568,252],{"class":44},[38,1570,49],{"class":48},[38,1572,56],{"class":44},[38,1574,1575],{"class":40,"line":259},[38,1576,119],{"emptyLinePlaceholder":118},[38,1578,1579,1581,1583],{"class":40,"line":264},[38,1580,45],{"class":44},[38,1582,269],{"class":48},[38,1584,56],{"class":44},[38,1586,1587,1589,1592,1595,1597,1600,1603,1605,1607],{"class":40,"line":274},[38,1588,295],{"class":44},[38,1590,1591],{"class":48},"div",[38,1593,1594],{"class":52}," v-if",[38,1596,304],{"class":44},[38,1598,1599],{"class":72},"\"error\"",[38,1601,1602],{"class":52}," class",[38,1604,304],{"class":44},[38,1606,1599],{"class":72},[38,1608,56],{"class":44},[38,1610,1611,1613,1616,1619,1621],{"class":40,"line":280},[38,1612,331],{"class":44},[38,1614,1615],{"class":48},"p",[38,1617,1618],{"class":44},">Failed to load: {{ error.message }}\u003C\u002F",[38,1620,1615],{"class":48},[38,1622,56],{"class":44},[38,1624,1625,1627,1629,1631,1633,1636,1639,1641],{"class":40,"line":286},[38,1626,331],{"class":44},[38,1628,334],{"class":48},[38,1630,353],{"class":52},[38,1632,304],{"class":44},[38,1634,1635],{"class":72},"\"error = null\"",[38,1637,1638],{"class":44},">Retry\u003C\u002F",[38,1640,334],{"class":48},[38,1642,56],{"class":44},[38,1644,1645,1647,1649],{"class":40,"line":292},[38,1646,382],{"class":44},[38,1648,1591],{"class":48},[38,1650,56],{"class":44},[38,1652,1653],{"class":40,"line":313},[38,1654,119],{"emptyLinePlaceholder":118},[38,1656,1657],{"class":40,"line":318},[38,1658,1659],{"class":125},"  \u003C!-- ── Suspense shows #fallback until ALL children resolve ── -->\n",[38,1661,1662,1664,1667,1670],{"class":40,"line":328},[38,1663,295],{"class":44},[38,1665,1666],{"class":48},"Suspense",[38,1668,1669],{"class":62}," v-else",[38,1671,56],{"class":44},[38,1673,1674,1676,1678,1681,1684],{"class":40,"line":363},[38,1675,331],{"class":44},[38,1677,269],{"class":48},[38,1679,1680],{"class":44}," #",[38,1682,1683],{"class":52},"default",[38,1685,56],{"class":44},[38,1687,1688,1691,1694],{"class":40,"line":369},[38,1689,1690],{"class":44},"      \u003C",[38,1692,1693],{"class":48},"AsyncHeader",[38,1695,310],{"class":44},[38,1697,1698,1700,1703],{"class":40,"line":379},[38,1699,1690],{"class":44},[38,1701,1702],{"class":48},"AsyncMain",[38,1704,310],{"class":44},[38,1706,1707,1709,1712],{"class":40,"line":389},[38,1708,1690],{"class":44},[38,1710,1711],{"class":48},"AsyncFooter",[38,1713,310],{"class":44},[38,1715,1716],{"class":40,"line":786},[38,1717,1718],{"class":125},"      \u003C!-- All three must resolve before #default renders -->\n",[38,1720,1721,1723,1725],{"class":40,"line":792},[38,1722,372],{"class":44},[38,1724,269],{"class":48},[38,1726,56],{"class":44},[38,1728,1729,1731,1733,1735,1738],{"class":40,"line":798},[38,1730,331],{"class":44},[38,1732,269],{"class":48},[38,1734,1680],{"class":44},[38,1736,1737],{"class":52},"fallback",[38,1739,56],{"class":44},[38,1741,1742,1744,1746,1748,1750,1753,1756,1758],{"class":40,"line":804},[38,1743,1690],{"class":44},[38,1745,1591],{"class":48},[38,1747,1602],{"class":52},[38,1749,304],{"class":44},[38,1751,1752],{"class":72},"\"loading-skeleton\"",[38,1754,1755],{"class":44},">Loading page…\u003C\u002F",[38,1757,1591],{"class":48},[38,1759,56],{"class":44},[38,1761,1762,1764,1766],{"class":40,"line":810},[38,1763,372],{"class":44},[38,1765,269],{"class":48},[38,1767,56],{"class":44},[38,1769,1770,1772,1774],{"class":40,"line":844},[38,1771,382],{"class":44},[38,1773,1666],{"class":48},[38,1775,56],{"class":44},[38,1777,1778,1780,1782],{"class":40,"line":865},[38,1779,252],{"class":44},[38,1781,269],{"class":48},[38,1783,56],{"class":44},[24,1785,1787],{"filename":1786,"language":27},"AsyncChild.vue",[29,1788,1790],{"className":31,"code":1789,"language":27,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref } from 'vue'\n\nconst data = ref(null)\n\n\u002F\u002F ── Top-level await makes this an async component ──\n\u002F\u002F Suspense waits for this promise before rendering the child.\nconst res = await fetch('\u002Fapi\u002Fdata')\ndata.value = await res.json()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>{{ data }}\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[35,1791,1792,1802,1813,1817,1834,1838,1843,1848,1870,1887,1895,1899,1907,1920],{"__ignoreMap":33},[38,1793,1794,1796,1798,1800],{"class":40,"line":41},[38,1795,45],{"class":44},[38,1797,49],{"class":48},[38,1799,53],{"class":52},[38,1801,56],{"class":44},[38,1803,1804,1806,1809,1811],{"class":40,"line":59},[38,1805,63],{"class":62},[38,1807,1808],{"class":44}," { ref } ",[38,1810,69],{"class":62},[38,1812,73],{"class":72},[38,1814,1815],{"class":40,"line":76},[38,1816,119],{"emptyLinePlaceholder":118},[38,1818,1819,1821,1824,1826,1828,1830,1832],{"class":40,"line":89},[38,1820,144],{"class":62},[38,1822,1823],{"class":147}," data",[38,1825,151],{"class":62},[38,1827,215],{"class":52},[38,1829,218],{"class":44},[38,1831,1506],{"class":147},[38,1833,224],{"class":44},[38,1835,1836],{"class":40,"line":102},[38,1837,119],{"emptyLinePlaceholder":118},[38,1839,1840],{"class":40,"line":115},[38,1841,1842],{"class":125},"\u002F\u002F ── Top-level await makes this an async component ──\n",[38,1844,1845],{"class":40,"line":122},[38,1846,1847],{"class":125},"\u002F\u002F Suspense waits for this promise before rendering the child.\n",[38,1849,1850,1852,1855,1857,1860,1863,1865,1868],{"class":40,"line":129},[38,1851,144],{"class":62},[38,1853,1854],{"class":147}," res",[38,1856,151],{"class":62},[38,1858,1859],{"class":62}," await",[38,1861,1862],{"class":52}," fetch",[38,1864,218],{"class":44},[38,1866,1867],{"class":72},"'\u002Fapi\u002Fdata'",[38,1869,224],{"class":44},[38,1871,1872,1875,1877,1879,1882,1885],{"class":40,"line":135},[38,1873,1874],{"class":44},"data.value ",[38,1876,304],{"class":62},[38,1878,1859],{"class":62},[38,1880,1881],{"class":44}," res.",[38,1883,1884],{"class":52},"json",[38,1886,967],{"class":44},[38,1888,1889,1891,1893],{"class":40,"line":141},[38,1890,252],{"class":44},[38,1892,49],{"class":48},[38,1894,56],{"class":44},[38,1896,1897],{"class":40,"line":160},[38,1898,119],{"emptyLinePlaceholder":118},[38,1900,1901,1903,1905],{"class":40,"line":172},[38,1902,45],{"class":44},[38,1904,269],{"class":48},[38,1906,56],{"class":44},[38,1908,1909,1911,1913,1916,1918],{"class":40,"line":183},[38,1910,295],{"class":44},[38,1912,1591],{"class":48},[38,1914,1915],{"class":44},">{{ data }}\u003C\u002F",[38,1917,1591],{"class":48},[38,1919,56],{"class":44},[38,1921,1922,1924,1926],{"class":40,"line":194},[38,1923,252],{"class":44},[38,1925,269],{"class":48},[38,1927,56],{"class":44},[15,1929,1931],{"id":1930},"tips-tricks","💡 Tips & Tricks",[24,1933,1935],{"filename":1934,"language":404},"tips.ts",[29,1936,1938],{"className":407,"code":1937,"language":404,"meta":33,"style":33},"\u002F\u002F ── 1. markRaw vs shallowRef for component refs ──\n\u002F\u002F markRaw(comp): marks a single component as non-reactive\n\u002F\u002F shallowRef({ a: compA, b: compB }): the whole object is shallow (non-deep)\n\n\u002F\u002F ── 2. Async component + Suspense + error boundary ──\n\u002F\u002F Wrap \u003CSuspense> in a parent with onErrorCaptured for full error handling.\n\u002F\u002F Suspense handles loading, parent handles errors.\n\n\u002F\u002F ── 3. KeepAlive include can be a regex ──\n\u002F\u002F \u003CKeepAlive :include=\"\u002F^(User|Profile)\u002F\"> → caches any component starting with User or Profile\n\n\u002F\u002F ── 4. Prefetch async components on hover ──\n\u002F\u002F \u003CRouterLink @mouseover=\"() => import('.\u002FNextPage.vue')\">Next\u003C\u002FRouterLink>\n\u002F\u002F Triggers chunk download before navigation — faster perceived load.\n\n\u002F\u002F ── 5. defineAsyncComponent onError for CDN fallback ──\nonError(err, retry, fail) {\n  \u002F\u002F Try loading from a backup CDN if the primary fails\n  if (primaryCDNFailed) { retryWithBackupCDN() } else { fail() }\n}\n",[35,1939,1940,1945,1950,1955,1959,1964,1969,1974,1978,1983,1988,1992,1997,2002,2007,2011,2016,2024,2029,2053],{"__ignoreMap":33},[38,1941,1942],{"class":40,"line":41},[38,1943,1944],{"class":125},"\u002F\u002F ── 1. markRaw vs shallowRef for component refs ──\n",[38,1946,1947],{"class":40,"line":59},[38,1948,1949],{"class":125},"\u002F\u002F markRaw(comp): marks a single component as non-reactive\n",[38,1951,1952],{"class":40,"line":76},[38,1953,1954],{"class":125},"\u002F\u002F shallowRef({ a: compA, b: compB }): the whole object is shallow (non-deep)\n",[38,1956,1957],{"class":40,"line":89},[38,1958,119],{"emptyLinePlaceholder":118},[38,1960,1961],{"class":40,"line":102},[38,1962,1963],{"class":125},"\u002F\u002F ── 2. Async component + Suspense + error boundary ──\n",[38,1965,1966],{"class":40,"line":115},[38,1967,1968],{"class":125},"\u002F\u002F Wrap \u003CSuspense> in a parent with onErrorCaptured for full error handling.\n",[38,1970,1971],{"class":40,"line":122},[38,1972,1973],{"class":125},"\u002F\u002F Suspense handles loading, parent handles errors.\n",[38,1975,1976],{"class":40,"line":129},[38,1977,119],{"emptyLinePlaceholder":118},[38,1979,1980],{"class":40,"line":135},[38,1981,1982],{"class":125},"\u002F\u002F ── 3. KeepAlive include can be a regex ──\n",[38,1984,1985],{"class":40,"line":141},[38,1986,1987],{"class":125},"\u002F\u002F \u003CKeepAlive :include=\"\u002F^(User|Profile)\u002F\"> → caches any component starting with User or Profile\n",[38,1989,1990],{"class":40,"line":160},[38,1991,119],{"emptyLinePlaceholder":118},[38,1993,1994],{"class":40,"line":172},[38,1995,1996],{"class":125},"\u002F\u002F ── 4. Prefetch async components on hover ──\n",[38,1998,1999],{"class":40,"line":183},[38,2000,2001],{"class":125},"\u002F\u002F \u003CRouterLink @mouseover=\"() => import('.\u002FNextPage.vue')\">Next\u003C\u002FRouterLink>\n",[38,2003,2004],{"class":40,"line":194},[38,2005,2006],{"class":125},"\u002F\u002F Triggers chunk download before navigation — faster perceived load.\n",[38,2008,2009],{"class":40,"line":200},[38,2010,119],{"emptyLinePlaceholder":118},[38,2012,2013],{"class":40,"line":205},[38,2014,2015],{"class":125},"\u002F\u002F ── 5. defineAsyncComponent onError for CDN fallback ──\n",[38,2017,2018,2021],{"class":40,"line":227},[38,2019,2020],{"class":52},"onError",[38,2022,2023],{"class":44},"(err, retry, fail) {\n",[38,2025,2026],{"class":40,"line":249},[38,2027,2028],{"class":125},"  \u002F\u002F Try loading from a backup CDN if the primary fails\n",[38,2030,2031,2034,2037,2040,2043,2045,2048,2050],{"class":40,"line":259},[38,2032,2033],{"class":62},"  if",[38,2035,2036],{"class":44}," (primaryCDNFailed) { ",[38,2038,2039],{"class":52},"retryWithBackupCDN",[38,2041,2042],{"class":44},"() } ",[38,2044,871],{"class":62},[38,2046,2047],{"class":44}," { ",[38,2049,775],{"class":52},[38,2051,2052],{"class":44},"() }\n",[38,2054,2055],{"class":40,"line":264},[38,2056,2057],{"class":44},"}\n",[15,2059,2061],{"id":2060},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[24,2063,2065],{"filename":2064,"language":404},"edge-cases.ts",[29,2066,2068],{"className":407,"code":2067,"language":404,"meta":33,"style":33},"\u002F\u002F ── 1. :is with string name requires global registration ──\n\u002F\u002F \u003Ccomponent :is=\"'MyComponent'\" \u002F> — only works if MyComponent is globally\n\u002F\u002F registered via app.component(). With \u003Cscript setup>, import the component\n\u002F\u002F object directly: \u003Ccomponent :is=\"MyComponent\" \u002F>\n\n\u002F\u002F ── 2. KeepAlive + onMounted fires only once ──\n\u002F\u002F Common bug: event listeners in onMounted with KeepAlive.\n\u002F\u002F onDeactivated should remove them, onActivated should re-add.\n\u002F\u002F Putting cleanup in onUnmounted means listeners persist while cached.\n\n\u002F\u002F ── 3. Async component timeout doesn't cancel the import ──\n\u002F\u002F If timeout fires, errorComponent shows, but the import promise still resolves\n\u002F\u002F later. The component is cached — next render uses it without re-loading.\n\n\u002F\u002F ── 4. Suspense fallback shows for ALL async children ──\n\u002F\u002F If one child takes 5s and another takes 0.1s, the fast one isn't shown\n\u002F\u002F until the slow one resolves. Split into separate Suspense boundaries if needed.\n\n\u002F\u002F ── 5. :max on KeepAlive — eviction is LRU, not FIFO ──\n\u002F\u002F The least recently ACTIVATED (not created) component is evicted.\n\u002F\u002F A component created first but used most recently survives over newer ones.\n\n\u002F\u002F ── 6. Suspense can't catch errors in event handlers ──\n\u002F\u002F Only catches errors in: render, setup, lifecycle, watchers of async children.\n\u002F\u002F Event handler errors go to app.config.errorHandler.\n",[35,2069,2070,2075,2080,2085,2090,2094,2099,2104,2109,2114,2118,2123,2128,2133,2137,2142,2147,2152,2156,2161,2166,2171,2175,2180,2185],{"__ignoreMap":33},[38,2071,2072],{"class":40,"line":41},[38,2073,2074],{"class":125},"\u002F\u002F ── 1. :is with string name requires global registration ──\n",[38,2076,2077],{"class":40,"line":59},[38,2078,2079],{"class":125},"\u002F\u002F \u003Ccomponent :is=\"'MyComponent'\" \u002F> — only works if MyComponent is globally\n",[38,2081,2082],{"class":40,"line":76},[38,2083,2084],{"class":125},"\u002F\u002F registered via app.component(). With \u003Cscript setup>, import the component\n",[38,2086,2087],{"class":40,"line":89},[38,2088,2089],{"class":125},"\u002F\u002F object directly: \u003Ccomponent :is=\"MyComponent\" \u002F>\n",[38,2091,2092],{"class":40,"line":102},[38,2093,119],{"emptyLinePlaceholder":118},[38,2095,2096],{"class":40,"line":115},[38,2097,2098],{"class":125},"\u002F\u002F ── 2. KeepAlive + onMounted fires only once ──\n",[38,2100,2101],{"class":40,"line":122},[38,2102,2103],{"class":125},"\u002F\u002F Common bug: event listeners in onMounted with KeepAlive.\n",[38,2105,2106],{"class":40,"line":129},[38,2107,2108],{"class":125},"\u002F\u002F onDeactivated should remove them, onActivated should re-add.\n",[38,2110,2111],{"class":40,"line":135},[38,2112,2113],{"class":125},"\u002F\u002F Putting cleanup in onUnmounted means listeners persist while cached.\n",[38,2115,2116],{"class":40,"line":141},[38,2117,119],{"emptyLinePlaceholder":118},[38,2119,2120],{"class":40,"line":160},[38,2121,2122],{"class":125},"\u002F\u002F ── 3. Async component timeout doesn't cancel the import ──\n",[38,2124,2125],{"class":40,"line":172},[38,2126,2127],{"class":125},"\u002F\u002F If timeout fires, errorComponent shows, but the import promise still resolves\n",[38,2129,2130],{"class":40,"line":183},[38,2131,2132],{"class":125},"\u002F\u002F later. The component is cached — next render uses it without re-loading.\n",[38,2134,2135],{"class":40,"line":194},[38,2136,119],{"emptyLinePlaceholder":118},[38,2138,2139],{"class":40,"line":200},[38,2140,2141],{"class":125},"\u002F\u002F ── 4. Suspense fallback shows for ALL async children ──\n",[38,2143,2144],{"class":40,"line":205},[38,2145,2146],{"class":125},"\u002F\u002F If one child takes 5s and another takes 0.1s, the fast one isn't shown\n",[38,2148,2149],{"class":40,"line":227},[38,2150,2151],{"class":125},"\u002F\u002F until the slow one resolves. Split into separate Suspense boundaries if needed.\n",[38,2153,2154],{"class":40,"line":249},[38,2155,119],{"emptyLinePlaceholder":118},[38,2157,2158],{"class":40,"line":259},[38,2159,2160],{"class":125},"\u002F\u002F ── 5. :max on KeepAlive — eviction is LRU, not FIFO ──\n",[38,2162,2163],{"class":40,"line":264},[38,2164,2165],{"class":125},"\u002F\u002F The least recently ACTIVATED (not created) component is evicted.\n",[38,2167,2168],{"class":40,"line":274},[38,2169,2170],{"class":125},"\u002F\u002F A component created first but used most recently survives over newer ones.\n",[38,2172,2173],{"class":40,"line":280},[38,2174,119],{"emptyLinePlaceholder":118},[38,2176,2177],{"class":40,"line":286},[38,2178,2179],{"class":125},"\u002F\u002F ── 6. Suspense can't catch errors in event handlers ──\n",[38,2181,2182],{"class":40,"line":292},[38,2183,2184],{"class":125},"\u002F\u002F Only catches errors in: render, setup, lifecycle, watchers of async children.\n",[38,2186,2187],{"class":40,"line":313},[38,2188,2189],{"class":125},"\u002F\u002F Event handler errors go to app.config.errorHandler.\n",[15,2191,2193],{"id":2192},"spot-the-bug","🧠 Spot the Bug",[1615,2195,2196],{},"A KeepAlive tab's scroll position resets when the user returns to it.",[24,2198,2200],{"filename":2199,"language":27},"KeepAliveBug.vue",[29,2201,2203],{"className":31,"code":2202,"language":27,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref, onMounted, onUnmounted } from 'vue'\n\nconst scrollPos = ref(0)\n\nonMounted(() => {\n  window.addEventListener('scroll', () => { scrollPos.value = window.scrollY })\n})\n\nonUnmounted(() => {\n  \u002F\u002F ⚠️ Doesn't fire on tab switch — only on true destruction (eviction)\n  window.removeEventListener('scroll', handler)\n  window.scrollTo(0, scrollPos.value)  \u002F\u002F ← never runs on tab switch\n})\n\u003C\u002Fscript>\n",[35,2204,2205,2215,2226,2230,2248,2252,2262,2288,2292,2296,2306,2311,2325,2342,2346],{"__ignoreMap":33},[38,2206,2207,2209,2211,2213],{"class":40,"line":41},[38,2208,45],{"class":44},[38,2210,49],{"class":48},[38,2212,53],{"class":52},[38,2214,56],{"class":44},[38,2216,2217,2219,2222,2224],{"class":40,"line":59},[38,2218,63],{"class":62},[38,2220,2221],{"class":44}," { ref, onMounted, onUnmounted } ",[38,2223,69],{"class":62},[38,2225,73],{"class":72},[38,2227,2228],{"class":40,"line":76},[38,2229,119],{"emptyLinePlaceholder":118},[38,2231,2232,2234,2237,2239,2241,2243,2246],{"class":40,"line":89},[38,2233,144],{"class":62},[38,2235,2236],{"class":147}," scrollPos",[38,2238,151],{"class":62},[38,2240,215],{"class":52},[38,2242,218],{"class":44},[38,2244,2245],{"class":147},"0",[38,2247,224],{"class":44},[38,2249,2250],{"class":40,"line":102},[38,2251,119],{"emptyLinePlaceholder":118},[38,2253,2254,2256,2258,2260],{"class":40,"line":115},[38,2255,1231],{"class":52},[38,2257,240],{"class":44},[38,2259,243],{"class":62},[38,2261,874],{"class":44},[38,2263,2264,2267,2270,2272,2275,2278,2280,2283,2285],{"class":40,"line":122},[38,2265,2266],{"class":44},"  window.",[38,2268,2269],{"class":52},"addEventListener",[38,2271,218],{"class":44},[38,2273,2274],{"class":72},"'scroll'",[38,2276,2277],{"class":44},", () ",[38,2279,243],{"class":62},[38,2281,2282],{"class":44}," { scrollPos.value ",[38,2284,304],{"class":62},[38,2286,2287],{"class":44}," window.scrollY })\n",[38,2289,2290],{"class":40,"line":129},[38,2291,197],{"class":44},[38,2293,2294],{"class":40,"line":135},[38,2295,119],{"emptyLinePlaceholder":118},[38,2297,2298,2300,2302,2304],{"class":40,"line":141},[38,2299,1344],{"class":52},[38,2301,240],{"class":44},[38,2303,243],{"class":62},[38,2305,874],{"class":44},[38,2307,2308],{"class":40,"line":160},[38,2309,2310],{"class":125},"  \u002F\u002F ⚠️ Doesn't fire on tab switch — only on true destruction (eviction)\n",[38,2312,2313,2315,2318,2320,2322],{"class":40,"line":172},[38,2314,2266],{"class":44},[38,2316,2317],{"class":52},"removeEventListener",[38,2319,218],{"class":44},[38,2321,2274],{"class":72},[38,2323,2324],{"class":44},", handler)\n",[38,2326,2327,2329,2332,2334,2336,2339],{"class":40,"line":183},[38,2328,2266],{"class":44},[38,2330,2331],{"class":52},"scrollTo",[38,2333,218],{"class":44},[38,2335,2245],{"class":147},[38,2337,2338],{"class":44},", scrollPos.value)  ",[38,2340,2341],{"class":125},"\u002F\u002F ← never runs on tab switch\n",[38,2343,2344],{"class":40,"line":194},[38,2345,197],{"class":44},[38,2347,2348,2350,2352],{"class":40,"line":200},[38,2349,252],{"class":44},[38,2351,49],{"class":48},[38,2353,56],{"class":44},[2355,2356,2357,2361,2381,2394,2554],"details",{},[2358,2359,2360],"summary",{},"Answer",[1615,2362,2363,2364,2366,2367,2369,2370,2372,2373,2369,2375,2377,2378,2380],{},"With ",[35,2365,1136],{},", switching tabs fires ",[35,2368,1303],{}," (not ",[35,2371,1344],{},") and returning fires ",[35,2374,1262],{},[35,2376,1231],{},"). The scroll restoration code in ",[35,2379,1344],{}," never runs during tab switching — it only runs when the component is truly evicted from cache.",[1615,2382,2383,2387,2388,2390,2391,2393],{},[2384,2385,2386],"strong",{},"Fix"," — use ",[35,2389,1262],{},"\u002F",[35,2392,1303],{}," for KeepAlive lifecycle:",[24,2395,2397],{"filename":2396,"language":27},"KeepAliveFixed.vue",[29,2398,2400],{"className":31,"code":2399,"language":27,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref, onActivated, onDeactivated } from 'vue'\n\nconst scrollPos = ref(0)\n\nfunction handler() { scrollPos.value = window.scrollY }\n\nonActivated(() => {\n  \u002F\u002F Restore scroll position when tab is re-activated\n  window.scrollTo(0, scrollPos.value)\n  window.addEventListener('scroll', handler)\n})\n\nonDeactivated(() => {\n  \u002F\u002F Save scroll position when tab is deactivated (cached)\n  window.removeEventListener('scroll', handler)\n})\n\u003C\u002Fscript>\n",[35,2401,2402,2412,2423,2427,2443,2447,2463,2467,2477,2482,2495,2507,2511,2515,2525,2530,2542,2546],{"__ignoreMap":33},[38,2403,2404,2406,2408,2410],{"class":40,"line":41},[38,2405,45],{"class":44},[38,2407,49],{"class":48},[38,2409,53],{"class":52},[38,2411,56],{"class":44},[38,2413,2414,2416,2419,2421],{"class":40,"line":59},[38,2415,63],{"class":62},[38,2417,2418],{"class":44}," { ref, onActivated, onDeactivated } ",[38,2420,69],{"class":62},[38,2422,73],{"class":72},[38,2424,2425],{"class":40,"line":76},[38,2426,119],{"emptyLinePlaceholder":118},[38,2428,2429,2431,2433,2435,2437,2439,2441],{"class":40,"line":89},[38,2430,144],{"class":62},[38,2432,2236],{"class":147},[38,2434,151],{"class":62},[38,2436,215],{"class":52},[38,2438,218],{"class":44},[38,2440,2245],{"class":147},[38,2442,224],{"class":44},[38,2444,2445],{"class":40,"line":102},[38,2446,119],{"emptyLinePlaceholder":118},[38,2448,2449,2452,2455,2458,2460],{"class":40,"line":115},[38,2450,2451],{"class":62},"function",[38,2453,2454],{"class":52}," handler",[38,2456,2457],{"class":44},"() { scrollPos.value ",[38,2459,304],{"class":62},[38,2461,2462],{"class":44}," window.scrollY }\n",[38,2464,2465],{"class":40,"line":122},[38,2466,119],{"emptyLinePlaceholder":118},[38,2468,2469,2471,2473,2475],{"class":40,"line":129},[38,2470,1262],{"class":52},[38,2472,240],{"class":44},[38,2474,243],{"class":62},[38,2476,874],{"class":44},[38,2478,2479],{"class":40,"line":135},[38,2480,2481],{"class":125},"  \u002F\u002F Restore scroll position when tab is re-activated\n",[38,2483,2484,2486,2488,2490,2492],{"class":40,"line":141},[38,2485,2266],{"class":44},[38,2487,2331],{"class":52},[38,2489,218],{"class":44},[38,2491,2245],{"class":147},[38,2493,2494],{"class":44},", scrollPos.value)\n",[38,2496,2497,2499,2501,2503,2505],{"class":40,"line":160},[38,2498,2266],{"class":44},[38,2500,2269],{"class":52},[38,2502,218],{"class":44},[38,2504,2274],{"class":72},[38,2506,2324],{"class":44},[38,2508,2509],{"class":40,"line":172},[38,2510,197],{"class":44},[38,2512,2513],{"class":40,"line":183},[38,2514,119],{"emptyLinePlaceholder":118},[38,2516,2517,2519,2521,2523],{"class":40,"line":194},[38,2518,1303],{"class":52},[38,2520,240],{"class":44},[38,2522,243],{"class":62},[38,2524,874],{"class":44},[38,2526,2527],{"class":40,"line":200},[38,2528,2529],{"class":125},"  \u002F\u002F Save scroll position when tab is deactivated (cached)\n",[38,2531,2532,2534,2536,2538,2540],{"class":40,"line":205},[38,2533,2266],{"class":44},[38,2535,2317],{"class":52},[38,2537,218],{"class":44},[38,2539,2274],{"class":72},[38,2541,2324],{"class":44},[38,2543,2544],{"class":40,"line":227},[38,2545,197],{"class":44},[38,2547,2548,2550,2552],{"class":40,"line":249},[38,2549,252],{"class":44},[38,2551,49],{"class":48},[38,2553,56],{"class":44},[1615,2555,2556,2559,2560,767,2562,2390,2564,2566,2567,2390,2569,2571],{},[2384,2557,2558],{},"The lesson",": with ",[35,2561,1136],{},[35,2563,1231],{},[35,2565,1344],{}," fire only once. Use ",[35,2568,1262],{},[35,2570,1303],{}," for save\u002Frestore logic that should run on every tab switch.",[2573,2574,2575],"style",{},"html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":33,"searchDepth":59,"depth":59,"links":2577},[2578,2582,2583,2584,2585,2586,2587],{"id":17,"depth":59,"text":2579,"children":2580},"component  — Runtime Component Switching",[2581],{"id":399,"depth":76,"text":400},{"id":521,"depth":59,"text":522},{"id":906,"depth":59,"text":907},{"id":1370,"depth":59,"text":1371},{"id":1930,"depth":59,"text":1931},{"id":2060,"depth":59,"text":2061},{"id":2192,"depth":59,"text":2193},"component :is switching, defineAsyncComponent loading\u002Ferror\u002Ftimeout, KeepAlive LRU caching with include\u002Fexclude, Suspense orchestration, and shallowRef for component references.","md",{},"\u002Fvue\u002F14-dynamic-and-async-components",{"title":5,"description":2588},"vue\u002F14-dynamic-and-async-components","QNGcpl9QvoWghJF1-xGZVSknbPv7CJ4trZpumAnNnAc",1789924655661]