[{"data":1,"prerenderedAt":2814},["ShallowReactive",2],{"page-\u002Fvue\u002F07-composition-api-in-depth":3},{"id":4,"title":5,"body":6,"description":2807,"extension":2808,"meta":2809,"navigation":71,"path":2810,"seo":2811,"stem":2812,"__hash__":2813},"content\u002Fvue\u002F07-composition-api-in-depth.md","Vue 3 Engineering Reference — Composition API In Depth",{"type":7,"value":8,"toc":2797},"minimark",[9,14,19,1980,1984,2793],[10,11,13],"h1",{"id":12},"_07-composition-api-in-depth","07 — Composition API In Depth",[15,16,18],"h2",{"id":17},"setup-vs-script-setup-compilation-difference","setup() vs script setup — Compilation Difference",[20,21,24,353,574,578,1200,1204,1463,1467],"code-wrapper",{"filename":22,"language":23},"SetupFunction.vue","vue",[25,26,30],"pre",{"className":27,"code":28,"language":23,"meta":29,"style":29},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript>\nimport { ref, computed, onMounted } from 'vue'\n\n\u002F\u002F ── setup() function form: the original Composition API ──\n\u002F\u002F Runs BEFORE the component is created (before data(), before created hook).\n\u002F\u002F Must explicitly return everything the template needs.\n\u002F\u002F `this` is NOT available — no access to component instance.\nexport default {\n  props: { userId: Number },\n  setup(props) {\n    \u002F\u002F props is reactive here — use props.userId, not this.userId\n    const count = ref(0)\n    const doubled = computed(() => count.value * 2)\n\n    function increment() { count.value++ }\n\n    onMounted(() => {\n      console.log('mounted, count is', count.value)\n    })\n\n    \u002F\u002F ── Must return an object — template accesses these as top-level ──\n    \u002F\u002F Anything not returned is invisible to the template.\n    return { count, doubled, increment }\n  }\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton @click=\"increment\">{{ count }} ({{ doubled }})\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n","",[31,32,33,49,66,73,80,86,92,98,110,116,133,139,163,193,198,216,221,233,250,256,261,267,273,282,288,294,304,309,319,344],"code",{"__ignoreMap":29},[34,35,38,42,46],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"ssxIu","\u003C",[34,43,45],{"class":44},"sk71V","script",[34,47,48],{"class":40},">\n",[34,50,52,56,59,62],{"class":36,"line":51},2,[34,53,55],{"class":54},"svdQ7","import",[34,57,58],{"class":40}," { ref, computed, onMounted } ",[34,60,61],{"class":54},"from",[34,63,65],{"class":64},"sJ6F3"," 'vue'\n",[34,67,69],{"class":36,"line":68},3,[34,70,72],{"emptyLinePlaceholder":71},true,"\n",[34,74,76],{"class":36,"line":75},4,[34,77,79],{"class":78},"sdCPZ","\u002F\u002F ── setup() function form: the original Composition API ──\n",[34,81,83],{"class":36,"line":82},5,[34,84,85],{"class":78},"\u002F\u002F Runs BEFORE the component is created (before data(), before created hook).\n",[34,87,89],{"class":36,"line":88},6,[34,90,91],{"class":78},"\u002F\u002F Must explicitly return everything the template needs.\n",[34,93,95],{"class":36,"line":94},7,[34,96,97],{"class":78},"\u002F\u002F `this` is NOT available — no access to component instance.\n",[34,99,101,104,107],{"class":36,"line":100},8,[34,102,103],{"class":54},"export",[34,105,106],{"class":54}," default",[34,108,109],{"class":40}," {\n",[34,111,113],{"class":36,"line":112},9,[34,114,115],{"class":40},"  props: { userId: Number },\n",[34,117,119,123,126,130],{"class":36,"line":118},10,[34,120,122],{"class":121},"sIsaT","  setup",[34,124,125],{"class":40},"(",[34,127,129],{"class":128},"sCrzJ","props",[34,131,132],{"class":40},") {\n",[34,134,136],{"class":36,"line":135},11,[34,137,138],{"class":78},"    \u002F\u002F props is reactive here — use props.userId, not this.userId\n",[34,140,142,145,149,152,155,157,160],{"class":36,"line":141},12,[34,143,144],{"class":54},"    const",[34,146,148],{"class":147},"snvgF"," count",[34,150,151],{"class":54}," =",[34,153,154],{"class":121}," ref",[34,156,125],{"class":40},[34,158,159],{"class":147},"0",[34,161,162],{"class":40},")\n",[34,164,166,168,171,173,176,179,182,185,188,191],{"class":36,"line":165},13,[34,167,144],{"class":54},[34,169,170],{"class":147}," doubled",[34,172,151],{"class":54},[34,174,175],{"class":121}," computed",[34,177,178],{"class":40},"(() ",[34,180,181],{"class":54},"=>",[34,183,184],{"class":40}," count.value ",[34,186,187],{"class":54},"*",[34,189,190],{"class":147}," 2",[34,192,162],{"class":40},[34,194,196],{"class":36,"line":195},14,[34,197,72],{"emptyLinePlaceholder":71},[34,199,201,204,207,210,213],{"class":36,"line":200},15,[34,202,203],{"class":54},"    function",[34,205,206],{"class":121}," increment",[34,208,209],{"class":40},"() { count.value",[34,211,212],{"class":54},"++",[34,214,215],{"class":40}," }\n",[34,217,219],{"class":36,"line":218},16,[34,220,72],{"emptyLinePlaceholder":71},[34,222,224,227,229,231],{"class":36,"line":223},17,[34,225,226],{"class":121},"    onMounted",[34,228,178],{"class":40},[34,230,181],{"class":54},[34,232,109],{"class":40},[34,234,236,239,242,244,247],{"class":36,"line":235},18,[34,237,238],{"class":40},"      console.",[34,240,241],{"class":121},"log",[34,243,125],{"class":40},[34,245,246],{"class":64},"'mounted, count is'",[34,248,249],{"class":40},", count.value)\n",[34,251,253],{"class":36,"line":252},19,[34,254,255],{"class":40},"    })\n",[34,257,259],{"class":36,"line":258},20,[34,260,72],{"emptyLinePlaceholder":71},[34,262,264],{"class":36,"line":263},21,[34,265,266],{"class":78},"    \u002F\u002F ── Must return an object — template accesses these as top-level ──\n",[34,268,270],{"class":36,"line":269},22,[34,271,272],{"class":78},"    \u002F\u002F Anything not returned is invisible to the template.\n",[34,274,276,279],{"class":36,"line":275},23,[34,277,278],{"class":54},"    return",[34,280,281],{"class":40}," { count, doubled, increment }\n",[34,283,285],{"class":36,"line":284},24,[34,286,287],{"class":40},"  }\n",[34,289,291],{"class":36,"line":290},25,[34,292,293],{"class":40},"}\n",[34,295,297,300,302],{"class":36,"line":296},26,[34,298,299],{"class":40},"\u003C\u002F",[34,301,45],{"class":44},[34,303,48],{"class":40},[34,305,307],{"class":36,"line":306},27,[34,308,72],{"emptyLinePlaceholder":71},[34,310,312,314,317],{"class":36,"line":311},28,[34,313,41],{"class":40},[34,315,316],{"class":44},"template",[34,318,48],{"class":40},[34,320,322,325,328,331,334,337,340,342],{"class":36,"line":321},29,[34,323,324],{"class":40},"  \u003C",[34,326,327],{"class":44},"button",[34,329,330],{"class":121}," @click",[34,332,333],{"class":40},"=",[34,335,336],{"class":64},"\"increment\"",[34,338,339],{"class":40},">{{ count }} ({{ doubled }})\u003C\u002F",[34,341,327],{"class":44},[34,343,48],{"class":40},[34,345,347,349,351],{"class":36,"line":346},30,[34,348,299],{"class":40},[34,350,316],{"class":44},[34,352,48],{"class":40},[20,354,356],{"filename":355,"language":23},"ScriptSetup.vue",[25,357,359],{"className":27,"code":358,"language":23,"meta":29,"style":29},"\u003Cscript setup>\nimport { ref, computed, onMounted } from 'vue'\n\n\u002F\u002F ── \u003Cscript setup> is syntactic sugar over setup() ──\n\u002F\u002F The compiler auto-returns all top-level bindings to the template.\n\u002F\u002F defineProps\u002FdefineEmits are compiler macros (not real imports).\nconst props = defineProps\u003C{ userId: number }>()\n\nconst count = ref(0)\nconst doubled = computed(() => count.value * 2)\n\nfunction increment() { count.value++ }\n\nonMounted(() => console.log('mounted'))\n\n\u002F\u002F No return needed — count, doubled, increment are auto-exposed.\n\u002F\u002F Only things explicitly imported remain private if not used in template.\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton @click=\"increment\">{{ count }} ({{ doubled }})\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[31,360,361,372,382,386,391,396,401,429,433,449,471,475,488,492,514,518,523,528,536,540,548,566],{"__ignoreMap":29},[34,362,363,365,367,370],{"class":36,"line":37},[34,364,41],{"class":40},[34,366,45],{"class":44},[34,368,369],{"class":121}," setup",[34,371,48],{"class":40},[34,373,374,376,378,380],{"class":36,"line":51},[34,375,55],{"class":54},[34,377,58],{"class":40},[34,379,61],{"class":54},[34,381,65],{"class":64},[34,383,384],{"class":36,"line":68},[34,385,72],{"emptyLinePlaceholder":71},[34,387,388],{"class":36,"line":75},[34,389,390],{"class":78},"\u002F\u002F ── \u003Cscript setup> is syntactic sugar over setup() ──\n",[34,392,393],{"class":36,"line":82},[34,394,395],{"class":78},"\u002F\u002F The compiler auto-returns all top-level bindings to the template.\n",[34,397,398],{"class":36,"line":88},[34,399,400],{"class":78},"\u002F\u002F defineProps\u002FdefineEmits are compiler macros (not real imports).\n",[34,402,403,406,409,411,414,417,420,423,426],{"class":36,"line":94},[34,404,405],{"class":54},"const",[34,407,408],{"class":147}," props",[34,410,151],{"class":54},[34,412,413],{"class":121}," defineProps",[34,415,416],{"class":40},"\u003C{ ",[34,418,419],{"class":128},"userId",[34,421,422],{"class":54},":",[34,424,425],{"class":147}," number",[34,427,428],{"class":40}," }>()\n",[34,430,431],{"class":36,"line":100},[34,432,72],{"emptyLinePlaceholder":71},[34,434,435,437,439,441,443,445,447],{"class":36,"line":112},[34,436,405],{"class":54},[34,438,148],{"class":147},[34,440,151],{"class":54},[34,442,154],{"class":121},[34,444,125],{"class":40},[34,446,159],{"class":147},[34,448,162],{"class":40},[34,450,451,453,455,457,459,461,463,465,467,469],{"class":36,"line":118},[34,452,405],{"class":54},[34,454,170],{"class":147},[34,456,151],{"class":54},[34,458,175],{"class":121},[34,460,178],{"class":40},[34,462,181],{"class":54},[34,464,184],{"class":40},[34,466,187],{"class":54},[34,468,190],{"class":147},[34,470,162],{"class":40},[34,472,473],{"class":36,"line":135},[34,474,72],{"emptyLinePlaceholder":71},[34,476,477,480,482,484,486],{"class":36,"line":141},[34,478,479],{"class":54},"function",[34,481,206],{"class":121},[34,483,209],{"class":40},[34,485,212],{"class":54},[34,487,215],{"class":40},[34,489,490],{"class":36,"line":165},[34,491,72],{"emptyLinePlaceholder":71},[34,493,494,497,499,501,504,506,508,511],{"class":36,"line":195},[34,495,496],{"class":121},"onMounted",[34,498,178],{"class":40},[34,500,181],{"class":54},[34,502,503],{"class":40}," console.",[34,505,241],{"class":121},[34,507,125],{"class":40},[34,509,510],{"class":64},"'mounted'",[34,512,513],{"class":40},"))\n",[34,515,516],{"class":36,"line":200},[34,517,72],{"emptyLinePlaceholder":71},[34,519,520],{"class":36,"line":218},[34,521,522],{"class":78},"\u002F\u002F No return needed — count, doubled, increment are auto-exposed.\n",[34,524,525],{"class":36,"line":223},[34,526,527],{"class":78},"\u002F\u002F Only things explicitly imported remain private if not used in template.\n",[34,529,530,532,534],{"class":36,"line":235},[34,531,299],{"class":40},[34,533,45],{"class":44},[34,535,48],{"class":40},[34,537,538],{"class":36,"line":252},[34,539,72],{"emptyLinePlaceholder":71},[34,541,542,544,546],{"class":36,"line":258},[34,543,41],{"class":40},[34,545,316],{"class":44},[34,547,48],{"class":40},[34,549,550,552,554,556,558,560,562,564],{"class":36,"line":263},[34,551,324],{"class":40},[34,553,327],{"class":44},[34,555,330],{"class":121},[34,557,333],{"class":40},[34,559,336],{"class":64},[34,561,339],{"class":40},[34,563,327],{"class":44},[34,565,48],{"class":40},[34,567,568,570,572],{"class":36,"line":269},[34,569,299],{"class":40},[34,571,316],{"class":44},[34,573,48],{"class":40},[15,575,577],{"id":576},"composable-usefetch-with-race-condition-prevention","Composable — useFetch with Race Condition Prevention",[20,579,582],{"filename":580,"language":581},"useFetch.ts","typescript",[25,583,586],{"className":584,"code":585,"language":581,"meta":29,"style":29},"language-typescript shiki shiki-themes github-light github-dark","import { ref, watchEffect, isRef, toValue, type MaybeRefOrGetter } from 'vue'\n\n\u002F\u002F ── Production-grade useFetch: handles race conditions, abort, SSR ──\n\u002F\u002F Accepts ref, getter, or plain string as URL source.\n\ninterface FetchState\u003CT> {\n  data: Ref\u003CT | null>\n  error: Ref\u003CError | null>\n  loading: Ref\u003Cboolean>\n}\n\nexport function useFetch\u003CT>(\n  source: MaybeRefOrGetter\u003Cstring>,\n  options?: RequestInit\n): FetchState\u003CT> {\n  const data = ref\u003CT | null>(null) as Ref\u003CT | null>\n  const error = ref\u003CError | null>(null)\n  const loading = ref(false)\n\n  \u002F\u002F watchEffect auto-tracks any reactive source read via toValue()\n  watchEffect(async (onCleanup) => {\n    const url = toValue(source)  \u002F\u002F unwrap ref\u002Fgetter\u002Fplain\n    if (!url) return\n\n    const controller = new AbortController()\n    loading.value = true\n    error.value = null\n\n    \u002F\u002F ── Race condition prevention: abort previous fetch on URL change ──\n    onCleanup(() => controller.abort())\n\n    try {\n      const res = await fetch(url, { ...options, signal: controller.signal })\n      if (!res.ok) throw new Error(`HTTP ${res.status}`)\n      data.value = await res.json() as T\n    } catch (e) {\n      if (e instanceof DOMException && e.name === 'AbortError') return\n      error.value = e as Error\n    } finally {\n      loading.value = false\n    }\n  })\n\n  return { data, error, loading }\n}\n",[31,587,588,605,609,614,619,623,639,661,681,697,701,705,722,740,751,766,810,835,853,857,862,884,902,918,922,940,950,960,964,969,987,992,1000,1026,1066,1090,1102,1132,1148,1158,1169,1175,1181,1186,1195],{"__ignoreMap":29},[34,589,590,592,595,598,601,603],{"class":36,"line":37},[34,591,55],{"class":54},[34,593,594],{"class":40}," { ref, watchEffect, isRef, toValue, ",[34,596,597],{"class":54},"type",[34,599,600],{"class":40}," MaybeRefOrGetter } ",[34,602,61],{"class":54},[34,604,65],{"class":64},[34,606,607],{"class":36,"line":51},[34,608,72],{"emptyLinePlaceholder":71},[34,610,611],{"class":36,"line":68},[34,612,613],{"class":78},"\u002F\u002F ── Production-grade useFetch: handles race conditions, abort, SSR ──\n",[34,615,616],{"class":36,"line":75},[34,617,618],{"class":78},"\u002F\u002F Accepts ref, getter, or plain string as URL source.\n",[34,620,621],{"class":36,"line":82},[34,622,72],{"emptyLinePlaceholder":71},[34,624,625,628,631,633,636],{"class":36,"line":88},[34,626,627],{"class":54},"interface",[34,629,630],{"class":121}," FetchState",[34,632,41],{"class":40},[34,634,635],{"class":121},"T",[34,637,638],{"class":40},"> {\n",[34,640,641,644,646,649,651,653,656,659],{"class":36,"line":94},[34,642,643],{"class":128},"  data",[34,645,422],{"class":54},[34,647,648],{"class":121}," Ref",[34,650,41],{"class":40},[34,652,635],{"class":121},[34,654,655],{"class":54}," |",[34,657,658],{"class":147}," null",[34,660,48],{"class":40},[34,662,663,666,668,670,672,675,677,679],{"class":36,"line":100},[34,664,665],{"class":128},"  error",[34,667,422],{"class":54},[34,669,648],{"class":121},[34,671,41],{"class":40},[34,673,674],{"class":121},"Error",[34,676,655],{"class":54},[34,678,658],{"class":147},[34,680,48],{"class":40},[34,682,683,686,688,690,692,695],{"class":36,"line":112},[34,684,685],{"class":128},"  loading",[34,687,422],{"class":54},[34,689,648],{"class":121},[34,691,41],{"class":40},[34,693,694],{"class":147},"boolean",[34,696,48],{"class":40},[34,698,699],{"class":36,"line":118},[34,700,293],{"class":40},[34,702,703],{"class":36,"line":135},[34,704,72],{"emptyLinePlaceholder":71},[34,706,707,709,712,715,717,719],{"class":36,"line":141},[34,708,103],{"class":54},[34,710,711],{"class":54}," function",[34,713,714],{"class":121}," useFetch",[34,716,41],{"class":40},[34,718,635],{"class":121},[34,720,721],{"class":40},">(\n",[34,723,724,727,729,732,734,737],{"class":36,"line":165},[34,725,726],{"class":128},"  source",[34,728,422],{"class":54},[34,730,731],{"class":121}," MaybeRefOrGetter",[34,733,41],{"class":40},[34,735,736],{"class":147},"string",[34,738,739],{"class":40},">,\n",[34,741,742,745,748],{"class":36,"line":195},[34,743,744],{"class":128},"  options",[34,746,747],{"class":54},"?:",[34,749,750],{"class":121}," RequestInit\n",[34,752,753,756,758,760,762,764],{"class":36,"line":200},[34,754,755],{"class":40},")",[34,757,422],{"class":54},[34,759,630],{"class":121},[34,761,41],{"class":40},[34,763,635],{"class":121},[34,765,638],{"class":40},[34,767,768,771,774,776,778,780,782,784,786,789,792,795,798,800,802,804,806,808],{"class":36,"line":218},[34,769,770],{"class":54},"  const",[34,772,773],{"class":147}," data",[34,775,151],{"class":54},[34,777,154],{"class":121},[34,779,41],{"class":40},[34,781,635],{"class":121},[34,783,655],{"class":54},[34,785,658],{"class":147},[34,787,788],{"class":40},">(",[34,790,791],{"class":147},"null",[34,793,794],{"class":40},") ",[34,796,797],{"class":54},"as",[34,799,648],{"class":121},[34,801,41],{"class":40},[34,803,635],{"class":121},[34,805,655],{"class":54},[34,807,658],{"class":147},[34,809,48],{"class":40},[34,811,812,814,817,819,821,823,825,827,829,831,833],{"class":36,"line":223},[34,813,770],{"class":54},[34,815,816],{"class":147}," error",[34,818,151],{"class":54},[34,820,154],{"class":121},[34,822,41],{"class":40},[34,824,674],{"class":121},[34,826,655],{"class":54},[34,828,658],{"class":147},[34,830,788],{"class":40},[34,832,791],{"class":147},[34,834,162],{"class":40},[34,836,837,839,842,844,846,848,851],{"class":36,"line":235},[34,838,770],{"class":54},[34,840,841],{"class":147}," loading",[34,843,151],{"class":54},[34,845,154],{"class":121},[34,847,125],{"class":40},[34,849,850],{"class":147},"false",[34,852,162],{"class":40},[34,854,855],{"class":36,"line":252},[34,856,72],{"emptyLinePlaceholder":71},[34,858,859],{"class":36,"line":258},[34,860,861],{"class":78},"  \u002F\u002F watchEffect auto-tracks any reactive source read via toValue()\n",[34,863,864,867,869,872,875,878,880,882],{"class":36,"line":263},[34,865,866],{"class":121},"  watchEffect",[34,868,125],{"class":40},[34,870,871],{"class":54},"async",[34,873,874],{"class":40}," (",[34,876,877],{"class":128},"onCleanup",[34,879,794],{"class":40},[34,881,181],{"class":54},[34,883,109],{"class":40},[34,885,886,888,891,893,896,899],{"class":36,"line":269},[34,887,144],{"class":54},[34,889,890],{"class":147}," url",[34,892,151],{"class":54},[34,894,895],{"class":121}," toValue",[34,897,898],{"class":40},"(source)  ",[34,900,901],{"class":78},"\u002F\u002F unwrap ref\u002Fgetter\u002Fplain\n",[34,903,904,907,909,912,915],{"class":36,"line":275},[34,905,906],{"class":54},"    if",[34,908,874],{"class":40},[34,910,911],{"class":54},"!",[34,913,914],{"class":40},"url) ",[34,916,917],{"class":54},"return\n",[34,919,920],{"class":36,"line":284},[34,921,72],{"emptyLinePlaceholder":71},[34,923,924,926,929,931,934,937],{"class":36,"line":290},[34,925,144],{"class":54},[34,927,928],{"class":147}," controller",[34,930,151],{"class":54},[34,932,933],{"class":54}," new",[34,935,936],{"class":121}," AbortController",[34,938,939],{"class":40},"()\n",[34,941,942,945,947],{"class":36,"line":296},[34,943,944],{"class":40},"    loading.value ",[34,946,333],{"class":54},[34,948,949],{"class":147}," true\n",[34,951,952,955,957],{"class":36,"line":306},[34,953,954],{"class":40},"    error.value ",[34,956,333],{"class":54},[34,958,959],{"class":147}," null\n",[34,961,962],{"class":36,"line":311},[34,963,72],{"emptyLinePlaceholder":71},[34,965,966],{"class":36,"line":321},[34,967,968],{"class":78},"    \u002F\u002F ── Race condition prevention: abort previous fetch on URL change ──\n",[34,970,971,974,976,978,981,984],{"class":36,"line":346},[34,972,973],{"class":121},"    onCleanup",[34,975,178],{"class":40},[34,977,181],{"class":54},[34,979,980],{"class":40}," controller.",[34,982,983],{"class":121},"abort",[34,985,986],{"class":40},"())\n",[34,988,990],{"class":36,"line":989},31,[34,991,72],{"emptyLinePlaceholder":71},[34,993,995,998],{"class":36,"line":994},32,[34,996,997],{"class":54},"    try",[34,999,109],{"class":40},[34,1001,1003,1006,1009,1011,1014,1017,1020,1023],{"class":36,"line":1002},33,[34,1004,1005],{"class":54},"      const",[34,1007,1008],{"class":147}," res",[34,1010,151],{"class":54},[34,1012,1013],{"class":54}," await",[34,1015,1016],{"class":121}," fetch",[34,1018,1019],{"class":40},"(url, { ",[34,1021,1022],{"class":54},"...",[34,1024,1025],{"class":40},"options, signal: controller.signal })\n",[34,1027,1029,1032,1034,1036,1039,1042,1044,1047,1049,1052,1055,1058,1061,1064],{"class":36,"line":1028},34,[34,1030,1031],{"class":54},"      if",[34,1033,874],{"class":40},[34,1035,911],{"class":54},[34,1037,1038],{"class":40},"res.ok) ",[34,1040,1041],{"class":54},"throw",[34,1043,933],{"class":54},[34,1045,1046],{"class":121}," Error",[34,1048,125],{"class":40},[34,1050,1051],{"class":64},"`HTTP ${",[34,1053,1054],{"class":40},"res",[34,1056,1057],{"class":64},".",[34,1059,1060],{"class":40},"status",[34,1062,1063],{"class":64},"}`",[34,1065,162],{"class":40},[34,1067,1069,1072,1074,1076,1079,1082,1085,1087],{"class":36,"line":1068},35,[34,1070,1071],{"class":40},"      data.value ",[34,1073,333],{"class":54},[34,1075,1013],{"class":54},[34,1077,1078],{"class":40}," res.",[34,1080,1081],{"class":121},"json",[34,1083,1084],{"class":40},"() ",[34,1086,797],{"class":54},[34,1088,1089],{"class":121}," T\n",[34,1091,1093,1096,1099],{"class":36,"line":1092},36,[34,1094,1095],{"class":40},"    } ",[34,1097,1098],{"class":54},"catch",[34,1100,1101],{"class":40}," (e) {\n",[34,1103,1105,1107,1110,1113,1116,1119,1122,1125,1128,1130],{"class":36,"line":1104},37,[34,1106,1031],{"class":54},[34,1108,1109],{"class":40}," (e ",[34,1111,1112],{"class":54},"instanceof",[34,1114,1115],{"class":121}," DOMException",[34,1117,1118],{"class":54}," &&",[34,1120,1121],{"class":40}," e.name ",[34,1123,1124],{"class":54},"===",[34,1126,1127],{"class":64}," 'AbortError'",[34,1129,794],{"class":40},[34,1131,917],{"class":54},[34,1133,1135,1138,1140,1143,1145],{"class":36,"line":1134},38,[34,1136,1137],{"class":40},"      error.value ",[34,1139,333],{"class":54},[34,1141,1142],{"class":40}," e ",[34,1144,797],{"class":54},[34,1146,1147],{"class":121}," Error\n",[34,1149,1151,1153,1156],{"class":36,"line":1150},39,[34,1152,1095],{"class":40},[34,1154,1155],{"class":54},"finally",[34,1157,109],{"class":40},[34,1159,1161,1164,1166],{"class":36,"line":1160},40,[34,1162,1163],{"class":40},"      loading.value ",[34,1165,333],{"class":54},[34,1167,1168],{"class":147}," false\n",[34,1170,1172],{"class":36,"line":1171},41,[34,1173,1174],{"class":40},"    }\n",[34,1176,1178],{"class":36,"line":1177},42,[34,1179,1180],{"class":40},"  })\n",[34,1182,1184],{"class":36,"line":1183},43,[34,1185,72],{"emptyLinePlaceholder":71},[34,1187,1189,1192],{"class":36,"line":1188},44,[34,1190,1191],{"class":54},"  return",[34,1193,1194],{"class":40}," { data, error, loading }\n",[34,1196,1198],{"class":36,"line":1197},45,[34,1199,293],{"class":40},[15,1201,1203],{"id":1202},"shared-state-module-scoped-singleton","Shared State — Module-Scoped Singleton",[20,1205,1207],{"filename":1206,"language":581},"useSharedCounter.ts",[25,1208,1210],{"className":584,"code":1209,"language":581,"meta":29,"style":29},"import { ref, computed, readonly, type Ref } from 'vue'\n\n\u002F\u002F ── Module-scoped ref = singleton shared across ALL importers ──\n\u002F\u002F The ref is created ONCE at module evaluation, not per composable call.\n\u002F\u002F Every component that calls useSharedCounter() gets the same ref.\n\nconst count = ref(0)  \u002F\u002F module scope — ONE instance for the whole app\nconst doubled = computed(() => count.value * 2)\n\nexport function useSharedCounter() {\n  function increment() { count.value++ }\n  function decrement() { count.value-- }\n  function reset() { count.value = 0 }\n\n  return {\n    count: readonly(count),  \u002F\u002F expose read-only — prevent external mutation\n    doubled: readonly(doubled),\n    increment,\n    decrement,\n    reset,\n  }\n}\n\n\u002F\u002F ── Why this works: ────────────────────────────────────\n\u002F\u002F ES modules are singletons — the module body runs once.\n\u002F\u002F `count` lives in module scope, shared by all callers.\n\u002F\u002F This is a lightweight alternative to Pinia for simple global state.\n\u002F\u002F For complex state (multiple stores, devtools, persistence), use Pinia.\n\n\u002F\u002F ── ⚠️ SSR caveat: ──────────────────────────────────────\n\u002F\u002F On the server, module state is shared across ALL requests.\n\u002F\u002F Request A's count leaks into Request B — a security\u002Fcorrectness bug.\n\u002F\u002F Fix: use Pinia (request-scoped stores) or reset module state in server entry.\n",[31,1211,1212,1228,1232,1237,1242,1247,1251,1271,1293,1297,1309,1322,1336,1353,1357,1363,1377,1387,1392,1397,1402,1406,1410,1414,1419,1424,1429,1434,1439,1443,1448,1453,1458],{"__ignoreMap":29},[34,1213,1214,1216,1219,1221,1224,1226],{"class":36,"line":37},[34,1215,55],{"class":54},[34,1217,1218],{"class":40}," { ref, computed, readonly, ",[34,1220,597],{"class":54},[34,1222,1223],{"class":40}," Ref } ",[34,1225,61],{"class":54},[34,1227,65],{"class":64},[34,1229,1230],{"class":36,"line":51},[34,1231,72],{"emptyLinePlaceholder":71},[34,1233,1234],{"class":36,"line":68},[34,1235,1236],{"class":78},"\u002F\u002F ── Module-scoped ref = singleton shared across ALL importers ──\n",[34,1238,1239],{"class":36,"line":75},[34,1240,1241],{"class":78},"\u002F\u002F The ref is created ONCE at module evaluation, not per composable call.\n",[34,1243,1244],{"class":36,"line":82},[34,1245,1246],{"class":78},"\u002F\u002F Every component that calls useSharedCounter() gets the same ref.\n",[34,1248,1249],{"class":36,"line":88},[34,1250,72],{"emptyLinePlaceholder":71},[34,1252,1253,1255,1257,1259,1261,1263,1265,1268],{"class":36,"line":94},[34,1254,405],{"class":54},[34,1256,148],{"class":147},[34,1258,151],{"class":54},[34,1260,154],{"class":121},[34,1262,125],{"class":40},[34,1264,159],{"class":147},[34,1266,1267],{"class":40},")  ",[34,1269,1270],{"class":78},"\u002F\u002F module scope — ONE instance for the whole app\n",[34,1272,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291],{"class":36,"line":100},[34,1274,405],{"class":54},[34,1276,170],{"class":147},[34,1278,151],{"class":54},[34,1280,175],{"class":121},[34,1282,178],{"class":40},[34,1284,181],{"class":54},[34,1286,184],{"class":40},[34,1288,187],{"class":54},[34,1290,190],{"class":147},[34,1292,162],{"class":40},[34,1294,1295],{"class":36,"line":112},[34,1296,72],{"emptyLinePlaceholder":71},[34,1298,1299,1301,1303,1306],{"class":36,"line":118},[34,1300,103],{"class":54},[34,1302,711],{"class":54},[34,1304,1305],{"class":121}," useSharedCounter",[34,1307,1308],{"class":40},"() {\n",[34,1310,1311,1314,1316,1318,1320],{"class":36,"line":135},[34,1312,1313],{"class":54},"  function",[34,1315,206],{"class":121},[34,1317,209],{"class":40},[34,1319,212],{"class":54},[34,1321,215],{"class":40},[34,1323,1324,1326,1329,1331,1334],{"class":36,"line":141},[34,1325,1313],{"class":54},[34,1327,1328],{"class":121}," decrement",[34,1330,209],{"class":40},[34,1332,1333],{"class":54},"--",[34,1335,215],{"class":40},[34,1337,1338,1340,1343,1346,1348,1351],{"class":36,"line":165},[34,1339,1313],{"class":54},[34,1341,1342],{"class":121}," reset",[34,1344,1345],{"class":40},"() { count.value ",[34,1347,333],{"class":54},[34,1349,1350],{"class":147}," 0",[34,1352,215],{"class":40},[34,1354,1355],{"class":36,"line":195},[34,1356,72],{"emptyLinePlaceholder":71},[34,1358,1359,1361],{"class":36,"line":200},[34,1360,1191],{"class":54},[34,1362,109],{"class":40},[34,1364,1365,1368,1371,1374],{"class":36,"line":218},[34,1366,1367],{"class":40},"    count: ",[34,1369,1370],{"class":121},"readonly",[34,1372,1373],{"class":40},"(count),  ",[34,1375,1376],{"class":78},"\u002F\u002F expose read-only — prevent external mutation\n",[34,1378,1379,1382,1384],{"class":36,"line":223},[34,1380,1381],{"class":40},"    doubled: ",[34,1383,1370],{"class":121},[34,1385,1386],{"class":40},"(doubled),\n",[34,1388,1389],{"class":36,"line":235},[34,1390,1391],{"class":40},"    increment,\n",[34,1393,1394],{"class":36,"line":252},[34,1395,1396],{"class":40},"    decrement,\n",[34,1398,1399],{"class":36,"line":258},[34,1400,1401],{"class":40},"    reset,\n",[34,1403,1404],{"class":36,"line":263},[34,1405,287],{"class":40},[34,1407,1408],{"class":36,"line":269},[34,1409,293],{"class":40},[34,1411,1412],{"class":36,"line":275},[34,1413,72],{"emptyLinePlaceholder":71},[34,1415,1416],{"class":36,"line":284},[34,1417,1418],{"class":78},"\u002F\u002F ── Why this works: ────────────────────────────────────\n",[34,1420,1421],{"class":36,"line":290},[34,1422,1423],{"class":78},"\u002F\u002F ES modules are singletons — the module body runs once.\n",[34,1425,1426],{"class":36,"line":296},[34,1427,1428],{"class":78},"\u002F\u002F `count` lives in module scope, shared by all callers.\n",[34,1430,1431],{"class":36,"line":306},[34,1432,1433],{"class":78},"\u002F\u002F This is a lightweight alternative to Pinia for simple global state.\n",[34,1435,1436],{"class":36,"line":311},[34,1437,1438],{"class":78},"\u002F\u002F For complex state (multiple stores, devtools, persistence), use Pinia.\n",[34,1440,1441],{"class":36,"line":321},[34,1442,72],{"emptyLinePlaceholder":71},[34,1444,1445],{"class":36,"line":346},[34,1446,1447],{"class":78},"\u002F\u002F ── ⚠️ SSR caveat: ──────────────────────────────────────\n",[34,1449,1450],{"class":36,"line":989},[34,1451,1452],{"class":78},"\u002F\u002F On the server, module state is shared across ALL requests.\n",[34,1454,1455],{"class":36,"line":994},[34,1456,1457],{"class":78},"\u002F\u002F Request A's count leaks into Request B — a security\u002Fcorrectness bug.\n",[34,1459,1460],{"class":36,"line":1002},[34,1461,1462],{"class":78},"\u002F\u002F Fix: use Pinia (request-scoped stores) or reset module state in server entry.\n",[15,1464,1466],{"id":1465},"effect-scope-cleanup-composable-teardown","Effect Scope Cleanup — Composable Teardown",[20,1468,1470,1696],{"filename":1469,"language":581},"useEventListener.ts",[25,1471,1473],{"className":584,"code":1472,"language":581,"meta":29,"style":29},"import { onScopeDispose, ref, type Ref } from 'vue'\n\n\u002F\u002F ── onScopeDispose: runs cleanup when the hosting scope stops ──\n\u002F\u002F In a component, the scope stops on unmount.\n\u002F\u002F In a composable called from setup(), the scope is the component's scope.\n\u002F\u002F This replaces manual onUnmounted cleanup for composables.\n\nexport function useEventListener(\n  target: EventTarget | Ref\u003CEventTarget>,\n  event: string,\n  handler: (e: Event) => void\n) {\n  \u002F\u002F Get the target — handle both raw EventTarget and Ref\u003CEventTarget>\n  const el = toValue(target)\n\n  el.addEventListener(event, handler)\n\n  \u002F\u002F ── Cleanup registered declaratively — runs on scope dispose ──\n  \u002F\u002F No need to return a cleanup function or call onUnmounted manually.\n  onScopeDispose(() => {\n    el.removeEventListener(event, handler)\n  })\n}\n\n\u002F\u002F ── Usage in a component: ──────────────────────────────\n\u002F\u002F setup() creates a scope; onScopeDispose callbacks fire on unmount.\n\u002F\u002F const { x, y } = useMouse()  \u002F\u002F internally calls useEventListener\n\u002F\u002F Component unmounts → scope disposes → event listeners removed automatically.\n",[31,1474,1475,1490,1494,1499,1504,1509,1514,1518,1530,1551,1564,1588,1592,1597,1611,1615,1626,1630,1635,1640,1651,1661,1665,1669,1673,1678,1683,1691],{"__ignoreMap":29},[34,1476,1477,1479,1482,1484,1486,1488],{"class":36,"line":37},[34,1478,55],{"class":54},[34,1480,1481],{"class":40}," { onScopeDispose, ref, ",[34,1483,597],{"class":54},[34,1485,1223],{"class":40},[34,1487,61],{"class":54},[34,1489,65],{"class":64},[34,1491,1492],{"class":36,"line":51},[34,1493,72],{"emptyLinePlaceholder":71},[34,1495,1496],{"class":36,"line":68},[34,1497,1498],{"class":78},"\u002F\u002F ── onScopeDispose: runs cleanup when the hosting scope stops ──\n",[34,1500,1501],{"class":36,"line":75},[34,1502,1503],{"class":78},"\u002F\u002F In a component, the scope stops on unmount.\n",[34,1505,1506],{"class":36,"line":82},[34,1507,1508],{"class":78},"\u002F\u002F In a composable called from setup(), the scope is the component's scope.\n",[34,1510,1511],{"class":36,"line":88},[34,1512,1513],{"class":78},"\u002F\u002F This replaces manual onUnmounted cleanup for composables.\n",[34,1515,1516],{"class":36,"line":94},[34,1517,72],{"emptyLinePlaceholder":71},[34,1519,1520,1522,1524,1527],{"class":36,"line":100},[34,1521,103],{"class":54},[34,1523,711],{"class":54},[34,1525,1526],{"class":121}," useEventListener",[34,1528,1529],{"class":40},"(\n",[34,1531,1532,1535,1537,1540,1542,1544,1546,1549],{"class":36,"line":112},[34,1533,1534],{"class":128},"  target",[34,1536,422],{"class":54},[34,1538,1539],{"class":121}," EventTarget",[34,1541,655],{"class":54},[34,1543,648],{"class":121},[34,1545,41],{"class":40},[34,1547,1548],{"class":121},"EventTarget",[34,1550,739],{"class":40},[34,1552,1553,1556,1558,1561],{"class":36,"line":118},[34,1554,1555],{"class":128},"  event",[34,1557,422],{"class":54},[34,1559,1560],{"class":147}," string",[34,1562,1563],{"class":40},",\n",[34,1565,1566,1569,1571,1573,1576,1578,1581,1583,1585],{"class":36,"line":135},[34,1567,1568],{"class":121},"  handler",[34,1570,422],{"class":54},[34,1572,874],{"class":40},[34,1574,1575],{"class":128},"e",[34,1577,422],{"class":54},[34,1579,1580],{"class":121}," Event",[34,1582,794],{"class":40},[34,1584,181],{"class":54},[34,1586,1587],{"class":147}," void\n",[34,1589,1590],{"class":36,"line":141},[34,1591,132],{"class":40},[34,1593,1594],{"class":36,"line":165},[34,1595,1596],{"class":78},"  \u002F\u002F Get the target — handle both raw EventTarget and Ref\u003CEventTarget>\n",[34,1598,1599,1601,1604,1606,1608],{"class":36,"line":195},[34,1600,770],{"class":54},[34,1602,1603],{"class":147}," el",[34,1605,151],{"class":54},[34,1607,895],{"class":121},[34,1609,1610],{"class":40},"(target)\n",[34,1612,1613],{"class":36,"line":200},[34,1614,72],{"emptyLinePlaceholder":71},[34,1616,1617,1620,1623],{"class":36,"line":218},[34,1618,1619],{"class":40},"  el.",[34,1621,1622],{"class":121},"addEventListener",[34,1624,1625],{"class":40},"(event, handler)\n",[34,1627,1628],{"class":36,"line":223},[34,1629,72],{"emptyLinePlaceholder":71},[34,1631,1632],{"class":36,"line":235},[34,1633,1634],{"class":78},"  \u002F\u002F ── Cleanup registered declaratively — runs on scope dispose ──\n",[34,1636,1637],{"class":36,"line":252},[34,1638,1639],{"class":78},"  \u002F\u002F No need to return a cleanup function or call onUnmounted manually.\n",[34,1641,1642,1645,1647,1649],{"class":36,"line":258},[34,1643,1644],{"class":121},"  onScopeDispose",[34,1646,178],{"class":40},[34,1648,181],{"class":54},[34,1650,109],{"class":40},[34,1652,1653,1656,1659],{"class":36,"line":263},[34,1654,1655],{"class":40},"    el.",[34,1657,1658],{"class":121},"removeEventListener",[34,1660,1625],{"class":40},[34,1662,1663],{"class":36,"line":269},[34,1664,1180],{"class":40},[34,1666,1667],{"class":36,"line":275},[34,1668,293],{"class":40},[34,1670,1671],{"class":36,"line":284},[34,1672,72],{"emptyLinePlaceholder":71},[34,1674,1675],{"class":36,"line":290},[34,1676,1677],{"class":78},"\u002F\u002F ── Usage in a component: ──────────────────────────────\n",[34,1679,1680],{"class":36,"line":296},[34,1681,1682],{"class":78},"\u002F\u002F setup() creates a scope; onScopeDispose callbacks fire on unmount.\n",[34,1684,1685,1688],{"class":36,"line":306},[34,1686,1687],{"class":78},"\u002F\u002F const { x, y } = useMouse()",[34,1689,1690],{"class":78},"  \u002F\u002F internally calls useEventListener\n",[34,1692,1693],{"class":36,"line":311},[34,1694,1695],{"class":78},"\u002F\u002F Component unmounts → scope disposes → event listeners removed automatically.\n",[20,1697,1699],{"filename":1698,"language":581},"useMouse.ts",[25,1700,1702],{"className":584,"code":1701,"language":581,"meta":29,"style":29},"import { ref, onScopeDispose } from 'vue'\n\nexport function useMouse() {\n  const x = ref(0)\n  const y = ref(0)\n\n  function handler(e: MouseEvent) {\n    x.value = e.clientX\n    y.value = e.clientY\n  }\n\n  window.addEventListener('mousemove', handler)\n\n  \u002F\u002F ── Declarative cleanup — fires when the hosting component unmounts ──\n  onScopeDispose(() => {\n    window.removeEventListener('mousemove', handler)\n  })\n\n  return { x, y }\n}\n\n\u002F\u002F ── Manual scope (outside component): ──────────────────\nimport { effectScope } from 'vue'\n\nconst scope = effectScope()\nscope.run(() => {\n  const { x, y } = useMouse()\n  \u002F\u002F listeners are active\n})\nscope.stop()  \u002F\u002F stops the scope → onScopeDispose fires → listeners removed\n",[31,1703,1704,1715,1719,1730,1747,1764,1768,1786,1796,1806,1810,1814,1829,1833,1838,1848,1861,1865,1869,1876,1880,1884,1889,1900,1904,1918,1932,1957,1962,1967],{"__ignoreMap":29},[34,1705,1706,1708,1711,1713],{"class":36,"line":37},[34,1707,55],{"class":54},[34,1709,1710],{"class":40}," { ref, onScopeDispose } ",[34,1712,61],{"class":54},[34,1714,65],{"class":64},[34,1716,1717],{"class":36,"line":51},[34,1718,72],{"emptyLinePlaceholder":71},[34,1720,1721,1723,1725,1728],{"class":36,"line":68},[34,1722,103],{"class":54},[34,1724,711],{"class":54},[34,1726,1727],{"class":121}," useMouse",[34,1729,1308],{"class":40},[34,1731,1732,1734,1737,1739,1741,1743,1745],{"class":36,"line":75},[34,1733,770],{"class":54},[34,1735,1736],{"class":147}," x",[34,1738,151],{"class":54},[34,1740,154],{"class":121},[34,1742,125],{"class":40},[34,1744,159],{"class":147},[34,1746,162],{"class":40},[34,1748,1749,1751,1754,1756,1758,1760,1762],{"class":36,"line":82},[34,1750,770],{"class":54},[34,1752,1753],{"class":147}," y",[34,1755,151],{"class":54},[34,1757,154],{"class":121},[34,1759,125],{"class":40},[34,1761,159],{"class":147},[34,1763,162],{"class":40},[34,1765,1766],{"class":36,"line":88},[34,1767,72],{"emptyLinePlaceholder":71},[34,1769,1770,1772,1775,1777,1779,1781,1784],{"class":36,"line":94},[34,1771,1313],{"class":54},[34,1773,1774],{"class":121}," handler",[34,1776,125],{"class":40},[34,1778,1575],{"class":128},[34,1780,422],{"class":54},[34,1782,1783],{"class":121}," MouseEvent",[34,1785,132],{"class":40},[34,1787,1788,1791,1793],{"class":36,"line":100},[34,1789,1790],{"class":40},"    x.value ",[34,1792,333],{"class":54},[34,1794,1795],{"class":40}," e.clientX\n",[34,1797,1798,1801,1803],{"class":36,"line":112},[34,1799,1800],{"class":40},"    y.value ",[34,1802,333],{"class":54},[34,1804,1805],{"class":40}," e.clientY\n",[34,1807,1808],{"class":36,"line":118},[34,1809,287],{"class":40},[34,1811,1812],{"class":36,"line":135},[34,1813,72],{"emptyLinePlaceholder":71},[34,1815,1816,1819,1821,1823,1826],{"class":36,"line":141},[34,1817,1818],{"class":40},"  window.",[34,1820,1622],{"class":121},[34,1822,125],{"class":40},[34,1824,1825],{"class":64},"'mousemove'",[34,1827,1828],{"class":40},", handler)\n",[34,1830,1831],{"class":36,"line":165},[34,1832,72],{"emptyLinePlaceholder":71},[34,1834,1835],{"class":36,"line":195},[34,1836,1837],{"class":78},"  \u002F\u002F ── Declarative cleanup — fires when the hosting component unmounts ──\n",[34,1839,1840,1842,1844,1846],{"class":36,"line":200},[34,1841,1644],{"class":121},[34,1843,178],{"class":40},[34,1845,181],{"class":54},[34,1847,109],{"class":40},[34,1849,1850,1853,1855,1857,1859],{"class":36,"line":218},[34,1851,1852],{"class":40},"    window.",[34,1854,1658],{"class":121},[34,1856,125],{"class":40},[34,1858,1825],{"class":64},[34,1860,1828],{"class":40},[34,1862,1863],{"class":36,"line":223},[34,1864,1180],{"class":40},[34,1866,1867],{"class":36,"line":235},[34,1868,72],{"emptyLinePlaceholder":71},[34,1870,1871,1873],{"class":36,"line":252},[34,1872,1191],{"class":54},[34,1874,1875],{"class":40}," { x, y }\n",[34,1877,1878],{"class":36,"line":258},[34,1879,293],{"class":40},[34,1881,1882],{"class":36,"line":263},[34,1883,72],{"emptyLinePlaceholder":71},[34,1885,1886],{"class":36,"line":269},[34,1887,1888],{"class":78},"\u002F\u002F ── Manual scope (outside component): ──────────────────\n",[34,1890,1891,1893,1896,1898],{"class":36,"line":275},[34,1892,55],{"class":54},[34,1894,1895],{"class":40}," { effectScope } ",[34,1897,61],{"class":54},[34,1899,65],{"class":64},[34,1901,1902],{"class":36,"line":284},[34,1903,72],{"emptyLinePlaceholder":71},[34,1905,1906,1908,1911,1913,1916],{"class":36,"line":290},[34,1907,405],{"class":54},[34,1909,1910],{"class":147}," scope",[34,1912,151],{"class":54},[34,1914,1915],{"class":121}," effectScope",[34,1917,939],{"class":40},[34,1919,1920,1923,1926,1928,1930],{"class":36,"line":296},[34,1921,1922],{"class":40},"scope.",[34,1924,1925],{"class":121},"run",[34,1927,178],{"class":40},[34,1929,181],{"class":54},[34,1931,109],{"class":40},[34,1933,1934,1936,1939,1942,1945,1948,1951,1953,1955],{"class":36,"line":306},[34,1935,770],{"class":54},[34,1937,1938],{"class":40}," { ",[34,1940,1941],{"class":147},"x",[34,1943,1944],{"class":40},", ",[34,1946,1947],{"class":147},"y",[34,1949,1950],{"class":40}," } ",[34,1952,333],{"class":54},[34,1954,1727],{"class":121},[34,1956,939],{"class":40},[34,1958,1959],{"class":36,"line":311},[34,1960,1961],{"class":78},"  \u002F\u002F listeners are active\n",[34,1963,1964],{"class":36,"line":321},[34,1965,1966],{"class":40},"})\n",[34,1968,1969,1971,1974,1977],{"class":36,"line":346},[34,1970,1922],{"class":40},[34,1972,1973],{"class":121},"stop",[34,1975,1976],{"class":40},"()  ",[34,1978,1979],{"class":78},"\u002F\u002F stops the scope → onScopeDispose fires → listeners removed\n",[15,1981,1983],{"id":1982},"async-setup-suspense-integration","Async Setup — Suspense Integration",[20,1985,1987,2142,2200,2204,2333,2337,2455,2459,2463,2604],{"filename":1986,"language":23},"AsyncDataLoader.vue",[25,1988,1990],{"className":27,"code":1989,"language":23,"meta":29,"style":29},"\u003Cscript setup>\nimport { ref } from 'vue'\n\n\u002F\u002F ── async setup() with top-level await ──\n\u002F\u002F Requires a \u003CSuspense> boundary in the parent.\n\u002F\u002F While awaiting, the parent shows the fallback slot.\n\nconst data = ref(null)\n\n\u002F\u002F Top-level await pauses setup() — the component doesn't render until resolved.\n\u002F\u002F Parent: \u003CSuspense>\u003CAsyncDataLoader \u002F>\u003Ctemplate #fallback>Loading…\u003C\u002Ftemplate>\u003C\u002FSuspense>\nconst res = await fetch('\u002Fapi\u002Finitial-data')\ndata.value = await res.json()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>{{ data }}\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[31,1991,1992,2002,2013,2017,2022,2027,2032,2036,2052,2056,2061,2066,2085,2100,2108,2112,2120,2134],{"__ignoreMap":29},[34,1993,1994,1996,1998,2000],{"class":36,"line":37},[34,1995,41],{"class":40},[34,1997,45],{"class":44},[34,1999,369],{"class":121},[34,2001,48],{"class":40},[34,2003,2004,2006,2009,2011],{"class":36,"line":51},[34,2005,55],{"class":54},[34,2007,2008],{"class":40}," { ref } ",[34,2010,61],{"class":54},[34,2012,65],{"class":64},[34,2014,2015],{"class":36,"line":68},[34,2016,72],{"emptyLinePlaceholder":71},[34,2018,2019],{"class":36,"line":75},[34,2020,2021],{"class":78},"\u002F\u002F ── async setup() with top-level await ──\n",[34,2023,2024],{"class":36,"line":82},[34,2025,2026],{"class":78},"\u002F\u002F Requires a \u003CSuspense> boundary in the parent.\n",[34,2028,2029],{"class":36,"line":88},[34,2030,2031],{"class":78},"\u002F\u002F While awaiting, the parent shows the fallback slot.\n",[34,2033,2034],{"class":36,"line":94},[34,2035,72],{"emptyLinePlaceholder":71},[34,2037,2038,2040,2042,2044,2046,2048,2050],{"class":36,"line":100},[34,2039,405],{"class":54},[34,2041,773],{"class":147},[34,2043,151],{"class":54},[34,2045,154],{"class":121},[34,2047,125],{"class":40},[34,2049,791],{"class":147},[34,2051,162],{"class":40},[34,2053,2054],{"class":36,"line":112},[34,2055,72],{"emptyLinePlaceholder":71},[34,2057,2058],{"class":36,"line":118},[34,2059,2060],{"class":78},"\u002F\u002F Top-level await pauses setup() — the component doesn't render until resolved.\n",[34,2062,2063],{"class":36,"line":135},[34,2064,2065],{"class":78},"\u002F\u002F Parent: \u003CSuspense>\u003CAsyncDataLoader \u002F>\u003Ctemplate #fallback>Loading…\u003C\u002Ftemplate>\u003C\u002FSuspense>\n",[34,2067,2068,2070,2072,2074,2076,2078,2080,2083],{"class":36,"line":141},[34,2069,405],{"class":54},[34,2071,1008],{"class":147},[34,2073,151],{"class":54},[34,2075,1013],{"class":54},[34,2077,1016],{"class":121},[34,2079,125],{"class":40},[34,2081,2082],{"class":64},"'\u002Fapi\u002Finitial-data'",[34,2084,162],{"class":40},[34,2086,2087,2090,2092,2094,2096,2098],{"class":36,"line":165},[34,2088,2089],{"class":40},"data.value ",[34,2091,333],{"class":54},[34,2093,1013],{"class":54},[34,2095,1078],{"class":40},[34,2097,1081],{"class":121},[34,2099,939],{"class":40},[34,2101,2102,2104,2106],{"class":36,"line":195},[34,2103,299],{"class":40},[34,2105,45],{"class":44},[34,2107,48],{"class":40},[34,2109,2110],{"class":36,"line":200},[34,2111,72],{"emptyLinePlaceholder":71},[34,2113,2114,2116,2118],{"class":36,"line":218},[34,2115,41],{"class":40},[34,2117,316],{"class":44},[34,2119,48],{"class":40},[34,2121,2122,2124,2127,2130,2132],{"class":36,"line":223},[34,2123,324],{"class":40},[34,2125,2126],{"class":44},"div",[34,2128,2129],{"class":40},">{{ data }}\u003C\u002F",[34,2131,2126],{"class":44},[34,2133,48],{"class":40},[34,2135,2136,2138,2140],{"class":36,"line":235},[34,2137,299],{"class":40},[34,2139,316],{"class":44},[34,2141,48],{"class":40},[20,2143,2145],{"filename":2144,"language":581},"async-caveat.ts",[25,2146,2148],{"className":584,"code":2147,"language":581,"meta":29,"style":29},"\u002F\u002F ── Async setup caveats: ────────────────────────────────\n\u002F\u002F 1. Top-level await in \u003Cscript setup> makes the component async.\n\u002F\u002F    It MUST be wrapped in \u003CSuspense> or it throws.\n\u002F\u002F 2. Watchers and lifecycle hooks registered BEFORE the await\n\u002F\u002F    still fire, but after the await they may not if the component\n\u002F\u002F    is unmounted while still awaiting.\n\u002F\u002F 3. Error handling: use \u003CSuspense> with error boundary, or\n\u002F\u002F    onErrorCaptured in the parent.\n\u002F\u002F 4. For most cases, prefer useFetch() composable (non-blocking)\n\u002F\u002F    over async setup — it's more flexible and doesn't require Suspense.\n",[31,2149,2150,2155,2160,2165,2170,2175,2180,2185,2190,2195],{"__ignoreMap":29},[34,2151,2152],{"class":36,"line":37},[34,2153,2154],{"class":78},"\u002F\u002F ── Async setup caveats: ────────────────────────────────\n",[34,2156,2157],{"class":36,"line":51},[34,2158,2159],{"class":78},"\u002F\u002F 1. Top-level await in \u003Cscript setup> makes the component async.\n",[34,2161,2162],{"class":36,"line":68},[34,2163,2164],{"class":78},"\u002F\u002F    It MUST be wrapped in \u003CSuspense> or it throws.\n",[34,2166,2167],{"class":36,"line":75},[34,2168,2169],{"class":78},"\u002F\u002F 2. Watchers and lifecycle hooks registered BEFORE the await\n",[34,2171,2172],{"class":36,"line":82},[34,2173,2174],{"class":78},"\u002F\u002F    still fire, but after the await they may not if the component\n",[34,2176,2177],{"class":36,"line":88},[34,2178,2179],{"class":78},"\u002F\u002F    is unmounted while still awaiting.\n",[34,2181,2182],{"class":36,"line":94},[34,2183,2184],{"class":78},"\u002F\u002F 3. Error handling: use \u003CSuspense> with error boundary, or\n",[34,2186,2187],{"class":36,"line":100},[34,2188,2189],{"class":78},"\u002F\u002F    onErrorCaptured in the parent.\n",[34,2191,2192],{"class":36,"line":112},[34,2193,2194],{"class":78},"\u002F\u002F 4. For most cases, prefer useFetch() composable (non-blocking)\n",[34,2196,2197],{"class":36,"line":118},[34,2198,2199],{"class":78},"\u002F\u002F    over async setup — it's more flexible and doesn't require Suspense.\n",[15,2201,2203],{"id":2202},"tips-tricks","💡 Tips & Tricks",[20,2205,2207],{"filename":2206,"language":581},"tips.ts",[25,2208,2210],{"className":584,"code":2209,"language":581,"meta":29,"style":29},"\u002F\u002F ── 1. Composable naming convention: useXxx ──\n\u002F\u002F Enables auto-import in some setups, signals \"this is a composable.\"\n\n\u002F\u002F ── 2. Always return refs, not values, from composables ──\n\u002F\u002F ✅ return { count: readonly(count) }  — stays reactive in consumer\n\u002F\u002F ❌ return { count: count.value }       — snapshot, not reactive\n\n\u002F\u002F ── 3. Accept MaybeRefOrGetter for flexibility ──\nfunction useTimer(duration: MaybeRefOrGetter\u003Cnumber>) {\n  const ms = toValue(duration)  \u002F\u002F unwrap at call site, not definition\n  \u002F\u002F Caller can pass: ref(5000), () => store.timeout, or 5000\n}\n\n\u002F\u002F ── 4. Use shallowRef for large data inside composables ──\n\u002F\u002F Avoids deep reactivity proxy overhead for big arrays\u002Fobjects.\n\n\u002F\u002F ── 5. Composables can use provide\u002Finject internally ──\n\u002F\u002F Pattern: parent calls provideX(), descendants call useX() which injects.\n\u002F\u002F Decouples creation from consumption — clean DI.\n",[31,2211,2212,2217,2222,2226,2231,2236,2241,2245,2250,2274,2291,2296,2300,2304,2309,2314,2318,2323,2328],{"__ignoreMap":29},[34,2213,2214],{"class":36,"line":37},[34,2215,2216],{"class":78},"\u002F\u002F ── 1. Composable naming convention: useXxx ──\n",[34,2218,2219],{"class":36,"line":51},[34,2220,2221],{"class":78},"\u002F\u002F Enables auto-import in some setups, signals \"this is a composable.\"\n",[34,2223,2224],{"class":36,"line":68},[34,2225,72],{"emptyLinePlaceholder":71},[34,2227,2228],{"class":36,"line":75},[34,2229,2230],{"class":78},"\u002F\u002F ── 2. Always return refs, not values, from composables ──\n",[34,2232,2233],{"class":36,"line":82},[34,2234,2235],{"class":78},"\u002F\u002F ✅ return { count: readonly(count) }  — stays reactive in consumer\n",[34,2237,2238],{"class":36,"line":88},[34,2239,2240],{"class":78},"\u002F\u002F ❌ return { count: count.value }       — snapshot, not reactive\n",[34,2242,2243],{"class":36,"line":94},[34,2244,72],{"emptyLinePlaceholder":71},[34,2246,2247],{"class":36,"line":100},[34,2248,2249],{"class":78},"\u002F\u002F ── 3. Accept MaybeRefOrGetter for flexibility ──\n",[34,2251,2252,2254,2257,2259,2262,2264,2266,2268,2271],{"class":36,"line":112},[34,2253,479],{"class":54},[34,2255,2256],{"class":121}," useTimer",[34,2258,125],{"class":40},[34,2260,2261],{"class":128},"duration",[34,2263,422],{"class":54},[34,2265,731],{"class":121},[34,2267,41],{"class":40},[34,2269,2270],{"class":147},"number",[34,2272,2273],{"class":40},">) {\n",[34,2275,2276,2278,2281,2283,2285,2288],{"class":36,"line":118},[34,2277,770],{"class":54},[34,2279,2280],{"class":147}," ms",[34,2282,151],{"class":54},[34,2284,895],{"class":121},[34,2286,2287],{"class":40},"(duration)  ",[34,2289,2290],{"class":78},"\u002F\u002F unwrap at call site, not definition\n",[34,2292,2293],{"class":36,"line":135},[34,2294,2295],{"class":78},"  \u002F\u002F Caller can pass: ref(5000), () => store.timeout, or 5000\n",[34,2297,2298],{"class":36,"line":141},[34,2299,293],{"class":40},[34,2301,2302],{"class":36,"line":165},[34,2303,72],{"emptyLinePlaceholder":71},[34,2305,2306],{"class":36,"line":195},[34,2307,2308],{"class":78},"\u002F\u002F ── 4. Use shallowRef for large data inside composables ──\n",[34,2310,2311],{"class":36,"line":200},[34,2312,2313],{"class":78},"\u002F\u002F Avoids deep reactivity proxy overhead for big arrays\u002Fobjects.\n",[34,2315,2316],{"class":36,"line":218},[34,2317,72],{"emptyLinePlaceholder":71},[34,2319,2320],{"class":36,"line":223},[34,2321,2322],{"class":78},"\u002F\u002F ── 5. Composables can use provide\u002Finject internally ──\n",[34,2324,2325],{"class":36,"line":235},[34,2326,2327],{"class":78},"\u002F\u002F Pattern: parent calls provideX(), descendants call useX() which injects.\n",[34,2329,2330],{"class":36,"line":252},[34,2331,2332],{"class":78},"\u002F\u002F Decouples creation from consumption — clean DI.\n",[15,2334,2336],{"id":2335},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,2338,2340],{"filename":2339,"language":581},"edge-cases.ts",[25,2341,2343],{"className":584,"code":2342,"language":581,"meta":29,"style":29},"\u002F\u002F ── 1. Module-scoped refs leak state on SSR ──\n\u002F\u002F Server: module state persists across requests → data leak.\n\u002F\u002F Fix: reset on server entry, or use Pinia (request-scoped).\n\n\u002F\u002F ── 2. onScopeDispose only works inside an active scope ──\n\u002F\u002F Calling onScopeDispose outside setup() or effectScope.run() throws.\n\u002F\u002F Don't call composables in setTimeout\u002Fcallbacks — call in setup().\n\n\u002F\u002F ── 3. \u003Cscript setup> is closed — no this, no $emit, no $parent ──\n\u002F\u002F Everything is top-level. Use defineExpose for parent access via refs.\n\u002F\u002F For programmatic navigation: import useRouter\u002FuseRoute, not this.$router.\n\n\u002F\u002F ── 4. setup() runs before created\u002Fdata — no access to Options API state ──\n\u002F\u002F Can't read this.$options, this.$data in setup(). Use Composition API only.\n\n\u002F\u002F ── 5. async \u003Cscript setup> requires \u003CSuspense> ──\n\u002F\u002F Without \u003CSuspense>, the component throws and the app crashes.\n\u002F\u002F Non-async alternatives: useFetch() in onMounted, watchEffect, etc.\n\n\u002F\u002F ── 6. Composable cleanup with onUnmounted vs onScopeDispose ──\n\u002F\u002F onScopeDispose: works in composables AND effectScope (more general).\n\u002F\u002F onUnmounted: only fires on component unmount (component-only).\n\u002F\u002F Prefer onScopeDispose in composable implementations.\n",[31,2344,2345,2350,2355,2360,2364,2369,2374,2379,2383,2388,2393,2398,2402,2407,2412,2416,2421,2426,2431,2435,2440,2445,2450],{"__ignoreMap":29},[34,2346,2347],{"class":36,"line":37},[34,2348,2349],{"class":78},"\u002F\u002F ── 1. Module-scoped refs leak state on SSR ──\n",[34,2351,2352],{"class":36,"line":51},[34,2353,2354],{"class":78},"\u002F\u002F Server: module state persists across requests → data leak.\n",[34,2356,2357],{"class":36,"line":68},[34,2358,2359],{"class":78},"\u002F\u002F Fix: reset on server entry, or use Pinia (request-scoped).\n",[34,2361,2362],{"class":36,"line":75},[34,2363,72],{"emptyLinePlaceholder":71},[34,2365,2366],{"class":36,"line":82},[34,2367,2368],{"class":78},"\u002F\u002F ── 2. onScopeDispose only works inside an active scope ──\n",[34,2370,2371],{"class":36,"line":88},[34,2372,2373],{"class":78},"\u002F\u002F Calling onScopeDispose outside setup() or effectScope.run() throws.\n",[34,2375,2376],{"class":36,"line":94},[34,2377,2378],{"class":78},"\u002F\u002F Don't call composables in setTimeout\u002Fcallbacks — call in setup().\n",[34,2380,2381],{"class":36,"line":100},[34,2382,72],{"emptyLinePlaceholder":71},[34,2384,2385],{"class":36,"line":112},[34,2386,2387],{"class":78},"\u002F\u002F ── 3. \u003Cscript setup> is closed — no this, no $emit, no $parent ──\n",[34,2389,2390],{"class":36,"line":118},[34,2391,2392],{"class":78},"\u002F\u002F Everything is top-level. Use defineExpose for parent access via refs.\n",[34,2394,2395],{"class":36,"line":135},[34,2396,2397],{"class":78},"\u002F\u002F For programmatic navigation: import useRouter\u002FuseRoute, not this.$router.\n",[34,2399,2400],{"class":36,"line":141},[34,2401,72],{"emptyLinePlaceholder":71},[34,2403,2404],{"class":36,"line":165},[34,2405,2406],{"class":78},"\u002F\u002F ── 4. setup() runs before created\u002Fdata — no access to Options API state ──\n",[34,2408,2409],{"class":36,"line":195},[34,2410,2411],{"class":78},"\u002F\u002F Can't read this.$options, this.$data in setup(). Use Composition API only.\n",[34,2413,2414],{"class":36,"line":200},[34,2415,72],{"emptyLinePlaceholder":71},[34,2417,2418],{"class":36,"line":218},[34,2419,2420],{"class":78},"\u002F\u002F ── 5. async \u003Cscript setup> requires \u003CSuspense> ──\n",[34,2422,2423],{"class":36,"line":223},[34,2424,2425],{"class":78},"\u002F\u002F Without \u003CSuspense>, the component throws and the app crashes.\n",[34,2427,2428],{"class":36,"line":235},[34,2429,2430],{"class":78},"\u002F\u002F Non-async alternatives: useFetch() in onMounted, watchEffect, etc.\n",[34,2432,2433],{"class":36,"line":252},[34,2434,72],{"emptyLinePlaceholder":71},[34,2436,2437],{"class":36,"line":258},[34,2438,2439],{"class":78},"\u002F\u002F ── 6. Composable cleanup with onUnmounted vs onScopeDispose ──\n",[34,2441,2442],{"class":36,"line":263},[34,2443,2444],{"class":78},"\u002F\u002F onScopeDispose: works in composables AND effectScope (more general).\n",[34,2446,2447],{"class":36,"line":269},[34,2448,2449],{"class":78},"\u002F\u002F onUnmounted: only fires on component unmount (component-only).\n",[34,2451,2452],{"class":36,"line":275},[34,2453,2454],{"class":78},"\u002F\u002F Prefer onScopeDispose in composable implementations.\n",[15,2456,2458],{"id":2457},"spot-the-bug","🧠 Spot the Bug",[2460,2461,2462],"p",{},"A composable's event listener is never removed, causing a memory leak.",[20,2464,2466],{"filename":2465,"language":581},"LeakBug.ts",[25,2467,2469],{"className":584,"code":2468,"language":581,"meta":29,"style":29},"import { ref, onMounted } from 'vue'\n\nexport function useMouse() {\n  const x = ref(0)\n  const y = ref(0)\n\n  onMounted(() => {\n    window.addEventListener('mousemove', (e) => {\n      x.value = e.clientX\n      y.value = e.clientY\n    })\n  })\n\n  return { x, y }\n}\n",[31,2470,2471,2482,2486,2496,2512,2528,2532,2543,2564,2573,2582,2586,2590,2594,2600],{"__ignoreMap":29},[34,2472,2473,2475,2478,2480],{"class":36,"line":37},[34,2474,55],{"class":54},[34,2476,2477],{"class":40}," { ref, onMounted } ",[34,2479,61],{"class":54},[34,2481,65],{"class":64},[34,2483,2484],{"class":36,"line":51},[34,2485,72],{"emptyLinePlaceholder":71},[34,2487,2488,2490,2492,2494],{"class":36,"line":68},[34,2489,103],{"class":54},[34,2491,711],{"class":54},[34,2493,1727],{"class":121},[34,2495,1308],{"class":40},[34,2497,2498,2500,2502,2504,2506,2508,2510],{"class":36,"line":75},[34,2499,770],{"class":54},[34,2501,1736],{"class":147},[34,2503,151],{"class":54},[34,2505,154],{"class":121},[34,2507,125],{"class":40},[34,2509,159],{"class":147},[34,2511,162],{"class":40},[34,2513,2514,2516,2518,2520,2522,2524,2526],{"class":36,"line":82},[34,2515,770],{"class":54},[34,2517,1753],{"class":147},[34,2519,151],{"class":54},[34,2521,154],{"class":121},[34,2523,125],{"class":40},[34,2525,159],{"class":147},[34,2527,162],{"class":40},[34,2529,2530],{"class":36,"line":88},[34,2531,72],{"emptyLinePlaceholder":71},[34,2533,2534,2537,2539,2541],{"class":36,"line":94},[34,2535,2536],{"class":121},"  onMounted",[34,2538,178],{"class":40},[34,2540,181],{"class":54},[34,2542,109],{"class":40},[34,2544,2545,2547,2549,2551,2553,2556,2558,2560,2562],{"class":36,"line":100},[34,2546,1852],{"class":40},[34,2548,1622],{"class":121},[34,2550,125],{"class":40},[34,2552,1825],{"class":64},[34,2554,2555],{"class":40},", (",[34,2557,1575],{"class":128},[34,2559,794],{"class":40},[34,2561,181],{"class":54},[34,2563,109],{"class":40},[34,2565,2566,2569,2571],{"class":36,"line":112},[34,2567,2568],{"class":40},"      x.value ",[34,2570,333],{"class":54},[34,2572,1795],{"class":40},[34,2574,2575,2578,2580],{"class":36,"line":118},[34,2576,2577],{"class":40},"      y.value ",[34,2579,333],{"class":54},[34,2581,1805],{"class":40},[34,2583,2584],{"class":36,"line":135},[34,2585,255],{"class":40},[34,2587,2588],{"class":36,"line":141},[34,2589,1180],{"class":40},[34,2591,2592],{"class":36,"line":165},[34,2593,72],{"emptyLinePlaceholder":71},[34,2595,2596,2598],{"class":36,"line":195},[34,2597,1191],{"class":54},[34,2599,1875],{"class":40},[34,2601,2602],{"class":36,"line":200},[34,2603,293],{"class":40},[2605,2606,2607,2611,2620,2630,2784],"details",{},[2608,2609,2610],"summary",{},"Answer",[2460,2612,2613,2614,2616,2617,2619],{},"The event listener is added but never removed. When the component unmounts, the listener persists, holding references to the component's ",[31,2615,1941],{}," and ",[31,2618,1947],{}," refs, preventing garbage collection.",[2460,2621,2622,2626,2627,422],{},[2623,2624,2625],"strong",{},"Fix"," — register cleanup via ",[31,2628,2629],{},"onScopeDispose",[20,2631,2633],{"filename":2632,"language":581},"LeakFixed.ts",[25,2634,2636],{"className":584,"code":2635,"language":581,"meta":29,"style":29},"import { ref, onScopeDispose } from 'vue'\n\nexport function useMouse() {\n  const x = ref(0)\n  const y = ref(0)\n\n  function handler(e: MouseEvent) {\n    x.value = e.clientX\n    y.value = e.clientY\n  }\n\n  window.addEventListener('mousemove', handler)\n  onScopeDispose(() => window.removeEventListener('mousemove', handler))\n\n  return { x, y }\n}\n",[31,2637,2638,2648,2652,2662,2678,2694,2698,2714,2722,2730,2734,2738,2750,2770,2774,2780],{"__ignoreMap":29},[34,2639,2640,2642,2644,2646],{"class":36,"line":37},[34,2641,55],{"class":54},[34,2643,1710],{"class":40},[34,2645,61],{"class":54},[34,2647,65],{"class":64},[34,2649,2650],{"class":36,"line":51},[34,2651,72],{"emptyLinePlaceholder":71},[34,2653,2654,2656,2658,2660],{"class":36,"line":68},[34,2655,103],{"class":54},[34,2657,711],{"class":54},[34,2659,1727],{"class":121},[34,2661,1308],{"class":40},[34,2663,2664,2666,2668,2670,2672,2674,2676],{"class":36,"line":75},[34,2665,770],{"class":54},[34,2667,1736],{"class":147},[34,2669,151],{"class":54},[34,2671,154],{"class":121},[34,2673,125],{"class":40},[34,2675,159],{"class":147},[34,2677,162],{"class":40},[34,2679,2680,2682,2684,2686,2688,2690,2692],{"class":36,"line":82},[34,2681,770],{"class":54},[34,2683,1753],{"class":147},[34,2685,151],{"class":54},[34,2687,154],{"class":121},[34,2689,125],{"class":40},[34,2691,159],{"class":147},[34,2693,162],{"class":40},[34,2695,2696],{"class":36,"line":88},[34,2697,72],{"emptyLinePlaceholder":71},[34,2699,2700,2702,2704,2706,2708,2710,2712],{"class":36,"line":94},[34,2701,1313],{"class":54},[34,2703,1774],{"class":121},[34,2705,125],{"class":40},[34,2707,1575],{"class":128},[34,2709,422],{"class":54},[34,2711,1783],{"class":121},[34,2713,132],{"class":40},[34,2715,2716,2718,2720],{"class":36,"line":100},[34,2717,1790],{"class":40},[34,2719,333],{"class":54},[34,2721,1795],{"class":40},[34,2723,2724,2726,2728],{"class":36,"line":112},[34,2725,1800],{"class":40},[34,2727,333],{"class":54},[34,2729,1805],{"class":40},[34,2731,2732],{"class":36,"line":118},[34,2733,287],{"class":40},[34,2735,2736],{"class":36,"line":135},[34,2737,72],{"emptyLinePlaceholder":71},[34,2739,2740,2742,2744,2746,2748],{"class":36,"line":141},[34,2741,1818],{"class":40},[34,2743,1622],{"class":121},[34,2745,125],{"class":40},[34,2747,1825],{"class":64},[34,2749,1828],{"class":40},[34,2751,2752,2754,2756,2758,2761,2763,2765,2767],{"class":36,"line":165},[34,2753,1644],{"class":121},[34,2755,178],{"class":40},[34,2757,181],{"class":54},[34,2759,2760],{"class":40}," window.",[34,2762,1658],{"class":121},[34,2764,125],{"class":40},[34,2766,1825],{"class":64},[34,2768,2769],{"class":40},", handler))\n",[34,2771,2772],{"class":36,"line":195},[34,2773,72],{"emptyLinePlaceholder":71},[34,2775,2776,2778],{"class":36,"line":200},[34,2777,1191],{"class":54},[34,2779,1875],{"class":40},[34,2781,2782],{"class":36,"line":218},[34,2783,293],{"class":40},[2460,2785,2786,2789,2790,2792],{},[2623,2787,2788],{},"The lesson",": every external resource (event listener, timer, WebSocket, subscription) registered in a composable must have a cleanup handler via ",[31,2791,2629],{},". Without it, the resource leaks past the component's lifetime.",[2794,2795,2796],"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 .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 .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}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);}",{"title":29,"searchDepth":51,"depth":51,"links":2798},[2799,2800,2801,2802,2803,2804,2805,2806],{"id":17,"depth":51,"text":18},{"id":576,"depth":51,"text":577},{"id":1202,"depth":51,"text":1203},{"id":1465,"depth":51,"text":1466},{"id":1982,"depth":51,"text":1983},{"id":2202,"depth":51,"text":2203},{"id":2335,"depth":51,"text":2336},{"id":2457,"depth":51,"text":2458},"setup() function form vs script setup, composable patterns with explicit return, lifecycle registration order, shared state via module-scoped refs, effect scope cleanup, and dependency injection in composables.","md",{},"\u002Fvue\u002F07-composition-api-in-depth",{"title":5,"description":2807},"vue\u002F07-composition-api-in-depth","J8s7JN7jEhtEAlixF2fs0eMyKQ95p87SzKb8bwJYYc8",1789924655494]