[{"data":1,"prerenderedAt":4228},["ShallowReactive",2],{"page-\u002Fvue\u002F04-computed-and-watchers":3},{"id":4,"title":5,"body":6,"description":4221,"extension":4222,"meta":4223,"navigation":58,"path":4224,"seo":4225,"stem":4226,"__hash__":4227},"content\u002Fvue\u002F04-computed-and-watchers.md","Vue 3 Engineering Reference — Computed & Watchers",{"type":7,"value":8,"toc":4206},"minimark",[9,14,19,366,370,636,640,925,929,1373,1377,1590,1594,1903,1907,2246,2250,2593,2597,2884,2888,3150,3154,3489,3493,3851,3855,3859,3976,4202],[10,11,13],"h1",{"id":12},"_04-computed-watchers","04 — Computed & Watchers",[15,16,18],"h2",{"id":17},"computed-caching-and-the-dirty-flag","Computed — Caching and the Dirty Flag",[20,21,24],"code-wrapper",{"filename":22,"language":23},"computed-internals.ts","typescript",[25,26,30],"pre",{"className":27,"code":28,"language":23,"meta":29,"style":29},"language-typescript shiki shiki-themes github-light github-dark","import { ref, computed, effect, dirty } from 'vue'\n\n\u002F\u002F ── Computed internals (simplified from @vue\u002Freactivity) ──\n\u002F\u002F A computed has three states:\n\u002F\u002F   1. clean\u002Fdirty: whether the cached value is valid\n\u002F\u002F   2. value: the cached result\n\u002F\u002F   3. dep: set of effects that depend on THIS computed\n\nconst count = ref(0)\nconst doubled = computed(() => count.value * 2)\n\n\u002F\u002F ── How caching actually works: ──────────────────────────\n\u002F\u002F 1. First read: runs the getter, caches result, marks as \"clean\"\n\u002F\u002F 2. Subsequent reads: returns cached value WITHOUT running getter\n\u002F\u002F 3. count.value changes → marks doubled as \"dirty\" (not clean)\n\u002F\u002F 4. Next read of doubled.value → getter re-runs, new value cached\n\n\u002F\u002F ── The dirty flag chain: ────────────────────────────────\n\u002F\u002F count (ref) changes → trigger → marks doubled (computed) as dirty\n\u002F\u002F doubled is dirty → trigger → marks any effect that read doubled as dirty\n\u002F\u002F The cascade continues until an effect actually re-runs and re-reads.\n\n\u002F\u002F ── Key: computed is LAZY — getter never runs if nobody reads it ──\nconst expensive = computed(() => {\n  console.log('running heavy calc')  \u002F\u002F only runs when .value is read\n  return hugeArray.value.map(x => x * 2)\n})\ncount.value++  \u002F\u002F marks expensive as dirty, but getter does NOT run yet\n\u002F\u002F Only when something reads expensive.value does the getter execute.\n\n\u002F\u002F ── Eager evaluation: use watchEffect if you need immediate execution ──\nwatchEffect(() => {\n  console.log('count is', count.value)  \u002F\u002F runs immediately + on every change\n})\n","",[31,32,33,53,60,67,73,79,85,91,96,122,152,157,163,169,175,181,187,192,198,204,210,216,221,227,246,266,296,302,314,320,325,331,343,361],"code",{"__ignoreMap":29},[34,35,38,42,46,49],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"svdQ7","import",[34,43,45],{"class":44},"ssxIu"," { ref, computed, effect, dirty } ",[34,47,48],{"class":40},"from",[34,50,52],{"class":51},"sJ6F3"," 'vue'\n",[34,54,56],{"class":36,"line":55},2,[34,57,59],{"emptyLinePlaceholder":58},true,"\n",[34,61,63],{"class":36,"line":62},3,[34,64,66],{"class":65},"sdCPZ","\u002F\u002F ── Computed internals (simplified from @vue\u002Freactivity) ──\n",[34,68,70],{"class":36,"line":69},4,[34,71,72],{"class":65},"\u002F\u002F A computed has three states:\n",[34,74,76],{"class":36,"line":75},5,[34,77,78],{"class":65},"\u002F\u002F   1. clean\u002Fdirty: whether the cached value is valid\n",[34,80,82],{"class":36,"line":81},6,[34,83,84],{"class":65},"\u002F\u002F   2. value: the cached result\n",[34,86,88],{"class":36,"line":87},7,[34,89,90],{"class":65},"\u002F\u002F   3. dep: set of effects that depend on THIS computed\n",[34,92,94],{"class":36,"line":93},8,[34,95,59],{"emptyLinePlaceholder":58},[34,97,99,102,106,109,113,116,119],{"class":36,"line":98},9,[34,100,101],{"class":40},"const",[34,103,105],{"class":104},"snvgF"," count",[34,107,108],{"class":40}," =",[34,110,112],{"class":111},"sIsaT"," ref",[34,114,115],{"class":44},"(",[34,117,118],{"class":104},"0",[34,120,121],{"class":44},")\n",[34,123,125,127,130,132,135,138,141,144,147,150],{"class":36,"line":124},10,[34,126,101],{"class":40},[34,128,129],{"class":104}," doubled",[34,131,108],{"class":40},[34,133,134],{"class":111}," computed",[34,136,137],{"class":44},"(() ",[34,139,140],{"class":40},"=>",[34,142,143],{"class":44}," count.value ",[34,145,146],{"class":40},"*",[34,148,149],{"class":104}," 2",[34,151,121],{"class":44},[34,153,155],{"class":36,"line":154},11,[34,156,59],{"emptyLinePlaceholder":58},[34,158,160],{"class":36,"line":159},12,[34,161,162],{"class":65},"\u002F\u002F ── How caching actually works: ──────────────────────────\n",[34,164,166],{"class":36,"line":165},13,[34,167,168],{"class":65},"\u002F\u002F 1. First read: runs the getter, caches result, marks as \"clean\"\n",[34,170,172],{"class":36,"line":171},14,[34,173,174],{"class":65},"\u002F\u002F 2. Subsequent reads: returns cached value WITHOUT running getter\n",[34,176,178],{"class":36,"line":177},15,[34,179,180],{"class":65},"\u002F\u002F 3. count.value changes → marks doubled as \"dirty\" (not clean)\n",[34,182,184],{"class":36,"line":183},16,[34,185,186],{"class":65},"\u002F\u002F 4. Next read of doubled.value → getter re-runs, new value cached\n",[34,188,190],{"class":36,"line":189},17,[34,191,59],{"emptyLinePlaceholder":58},[34,193,195],{"class":36,"line":194},18,[34,196,197],{"class":65},"\u002F\u002F ── The dirty flag chain: ────────────────────────────────\n",[34,199,201],{"class":36,"line":200},19,[34,202,203],{"class":65},"\u002F\u002F count (ref) changes → trigger → marks doubled (computed) as dirty\n",[34,205,207],{"class":36,"line":206},20,[34,208,209],{"class":65},"\u002F\u002F doubled is dirty → trigger → marks any effect that read doubled as dirty\n",[34,211,213],{"class":36,"line":212},21,[34,214,215],{"class":65},"\u002F\u002F The cascade continues until an effect actually re-runs and re-reads.\n",[34,217,219],{"class":36,"line":218},22,[34,220,59],{"emptyLinePlaceholder":58},[34,222,224],{"class":36,"line":223},23,[34,225,226],{"class":65},"\u002F\u002F ── Key: computed is LAZY — getter never runs if nobody reads it ──\n",[34,228,230,232,235,237,239,241,243],{"class":36,"line":229},24,[34,231,101],{"class":40},[34,233,234],{"class":104}," expensive",[34,236,108],{"class":40},[34,238,134],{"class":111},[34,240,137],{"class":44},[34,242,140],{"class":40},[34,244,245],{"class":44}," {\n",[34,247,249,252,255,257,260,263],{"class":36,"line":248},25,[34,250,251],{"class":44},"  console.",[34,253,254],{"class":111},"log",[34,256,115],{"class":44},[34,258,259],{"class":51},"'running heavy calc'",[34,261,262],{"class":44},")  ",[34,264,265],{"class":65},"\u002F\u002F only runs when .value is read\n",[34,267,269,272,275,278,280,284,287,290,292,294],{"class":36,"line":268},26,[34,270,271],{"class":40},"  return",[34,273,274],{"class":44}," hugeArray.value.",[34,276,277],{"class":111},"map",[34,279,115],{"class":44},[34,281,283],{"class":282},"sCrzJ","x",[34,285,286],{"class":40}," =>",[34,288,289],{"class":44}," x ",[34,291,146],{"class":40},[34,293,149],{"class":104},[34,295,121],{"class":44},[34,297,299],{"class":36,"line":298},27,[34,300,301],{"class":44},"})\n",[34,303,305,308,311],{"class":36,"line":304},28,[34,306,307],{"class":44},"count.value",[34,309,310],{"class":40},"++",[34,312,313],{"class":65},"  \u002F\u002F marks expensive as dirty, but getter does NOT run yet\n",[34,315,317],{"class":36,"line":316},29,[34,318,319],{"class":65},"\u002F\u002F Only when something reads expensive.value does the getter execute.\n",[34,321,323],{"class":36,"line":322},30,[34,324,59],{"emptyLinePlaceholder":58},[34,326,328],{"class":36,"line":327},31,[34,329,330],{"class":65},"\u002F\u002F ── Eager evaluation: use watchEffect if you need immediate execution ──\n",[34,332,334,337,339,341],{"class":36,"line":333},32,[34,335,336],{"class":111},"watchEffect",[34,338,137],{"class":44},[34,340,140],{"class":40},[34,342,245],{"class":44},[34,344,346,348,350,352,355,358],{"class":36,"line":345},33,[34,347,251],{"class":44},[34,349,254],{"class":111},[34,351,115],{"class":44},[34,353,354],{"class":51},"'count is'",[34,356,357],{"class":44},", count.value)  ",[34,359,360],{"class":65},"\u002F\u002F runs immediately + on every change\n",[34,362,364],{"class":36,"line":363},34,[34,365,301],{"class":44},[15,367,369],{"id":368},"production-pattern-computed-with-getters-vue-33","Production Pattern — Computed with Getters (Vue 3.3+)",[20,371,373],{"filename":372,"language":23},"toValue-pattern.ts",[25,374,376],{"className":27,"code":375,"language":23,"meta":29,"style":29},"import { computed, toValue, type MaybeRefOrGetter } from 'vue'\n\n\u002F\u002F ── toValue: normalize ref | getter | plain value → value ──\n\u002F\u002F Enables composables that accept ANY reactive source as input.\n\n\u002F\u002F Computed that accepts either a ref, a getter, or a plain value:\nfunction useNormalized\u003CT>(source: MaybeRefOrGetter\u003CT>, fn: (v: T) => T) {\n  return computed(() => fn(toValue(source)))\n  \u002F\u002F toValue(source):\n  \u002F\u002F   ref → source.value\n  \u002F\u002F   getter function → source()\n  \u002F\u002F   plain value → source\n}\n\n\u002F\u002F ── Usage — caller picks the reactive style they prefer: ──\nconst items = ref([1, 2, 3])\nconst normalized = useNormalized(items, arr => arr.map(x => x * 2))\n\u002F\u002F OR: useNormalized(() => store.items, fn)  ← getter\n\u002F\u002F OR: useNormalized(staticArray, fn)        ← plain value\n\n\u002F\u002F ── Why this matters: ────────────────────────────────────\n\u002F\u002F Pre-3.3, composables had to use unref(source) which only handled refs.\n\u002F\u002F Getters (common in Pinia stores and component props) required manual wrapping.\n\u002F\u002F toValue unifies all three — write composable once, accept any source.\n",[31,377,378,395,399,404,409,413,418,477,498,503,508,513,518,523,527,532,563,602,607,612,616,621,626,631],{"__ignoreMap":29},[34,379,380,382,385,388,391,393],{"class":36,"line":37},[34,381,41],{"class":40},[34,383,384],{"class":44}," { computed, toValue, ",[34,386,387],{"class":40},"type",[34,389,390],{"class":44}," MaybeRefOrGetter } ",[34,392,48],{"class":40},[34,394,52],{"class":51},[34,396,397],{"class":36,"line":55},[34,398,59],{"emptyLinePlaceholder":58},[34,400,401],{"class":36,"line":62},[34,402,403],{"class":65},"\u002F\u002F ── toValue: normalize ref | getter | plain value → value ──\n",[34,405,406],{"class":36,"line":69},[34,407,408],{"class":65},"\u002F\u002F Enables composables that accept ANY reactive source as input.\n",[34,410,411],{"class":36,"line":75},[34,412,59],{"emptyLinePlaceholder":58},[34,414,415],{"class":36,"line":81},[34,416,417],{"class":65},"\u002F\u002F Computed that accepts either a ref, a getter, or a plain value:\n",[34,419,420,423,426,429,432,435,438,441,444,446,448,451,454,456,459,462,464,467,470,472,474],{"class":36,"line":87},[34,421,422],{"class":40},"function",[34,424,425],{"class":111}," useNormalized",[34,427,428],{"class":44},"\u003C",[34,430,431],{"class":111},"T",[34,433,434],{"class":44},">(",[34,436,437],{"class":282},"source",[34,439,440],{"class":40},":",[34,442,443],{"class":111}," MaybeRefOrGetter",[34,445,428],{"class":44},[34,447,431],{"class":111},[34,449,450],{"class":44},">, ",[34,452,453],{"class":111},"fn",[34,455,440],{"class":40},[34,457,458],{"class":44}," (",[34,460,461],{"class":282},"v",[34,463,440],{"class":40},[34,465,466],{"class":111}," T",[34,468,469],{"class":44},") ",[34,471,140],{"class":40},[34,473,466],{"class":111},[34,475,476],{"class":44},") {\n",[34,478,479,481,483,485,487,490,492,495],{"class":36,"line":93},[34,480,271],{"class":40},[34,482,134],{"class":111},[34,484,137],{"class":44},[34,486,140],{"class":40},[34,488,489],{"class":111}," fn",[34,491,115],{"class":44},[34,493,494],{"class":111},"toValue",[34,496,497],{"class":44},"(source)))\n",[34,499,500],{"class":36,"line":98},[34,501,502],{"class":65},"  \u002F\u002F toValue(source):\n",[34,504,505],{"class":36,"line":124},[34,506,507],{"class":65},"  \u002F\u002F   ref → source.value\n",[34,509,510],{"class":36,"line":154},[34,511,512],{"class":65},"  \u002F\u002F   getter function → source()\n",[34,514,515],{"class":36,"line":159},[34,516,517],{"class":65},"  \u002F\u002F   plain value → source\n",[34,519,520],{"class":36,"line":165},[34,521,522],{"class":44},"}\n",[34,524,525],{"class":36,"line":171},[34,526,59],{"emptyLinePlaceholder":58},[34,528,529],{"class":36,"line":177},[34,530,531],{"class":65},"\u002F\u002F ── Usage — caller picks the reactive style they prefer: ──\n",[34,533,534,536,539,541,543,546,549,552,555,557,560],{"class":36,"line":183},[34,535,101],{"class":40},[34,537,538],{"class":104}," items",[34,540,108],{"class":40},[34,542,112],{"class":111},[34,544,545],{"class":44},"([",[34,547,548],{"class":104},"1",[34,550,551],{"class":44},", ",[34,553,554],{"class":104},"2",[34,556,551],{"class":44},[34,558,559],{"class":104},"3",[34,561,562],{"class":44},"])\n",[34,564,565,567,570,572,574,577,580,582,585,587,589,591,593,595,597,599],{"class":36,"line":189},[34,566,101],{"class":40},[34,568,569],{"class":104}," normalized",[34,571,108],{"class":40},[34,573,425],{"class":111},[34,575,576],{"class":44},"(items, ",[34,578,579],{"class":282},"arr",[34,581,286],{"class":40},[34,583,584],{"class":44}," arr.",[34,586,277],{"class":111},[34,588,115],{"class":44},[34,590,283],{"class":282},[34,592,286],{"class":40},[34,594,289],{"class":44},[34,596,146],{"class":40},[34,598,149],{"class":104},[34,600,601],{"class":44},"))\n",[34,603,604],{"class":36,"line":194},[34,605,606],{"class":65},"\u002F\u002F OR: useNormalized(() => store.items, fn)  ← getter\n",[34,608,609],{"class":36,"line":200},[34,610,611],{"class":65},"\u002F\u002F OR: useNormalized(staticArray, fn)        ← plain value\n",[34,613,614],{"class":36,"line":206},[34,615,59],{"emptyLinePlaceholder":58},[34,617,618],{"class":36,"line":212},[34,619,620],{"class":65},"\u002F\u002F ── Why this matters: ────────────────────────────────────\n",[34,622,623],{"class":36,"line":218},[34,624,625],{"class":65},"\u002F\u002F Pre-3.3, composables had to use unref(source) which only handled refs.\n",[34,627,628],{"class":36,"line":223},[34,629,630],{"class":65},"\u002F\u002F Getters (common in Pinia stores and component props) required manual wrapping.\n",[34,632,633],{"class":36,"line":229},[34,634,635],{"class":65},"\u002F\u002F toValue unifies all three — write composable once, accept any source.\n",[15,637,639],{"id":638},"writable-computed-gettersetter-for-form-state","Writable Computed — Getter\u002FSetter for Form State",[20,641,643],{"filename":642,"language":23},"writable-computed.ts",[25,644,646],{"className":27,"code":645,"language":23,"meta":29,"style":29},"import { ref, computed } from 'vue'\n\nconst firstName = ref('Ada')\nconst lastName = ref('Lovelace')\n\n\u002F\u002F ── Writable computed: get derives from deps, set distributes to deps ──\nconst fullName = computed({\n  get() {\n    return `${firstName.value} ${lastName.value}`\n  },\n  set(newValue: string) {\n    \u002F\u002F Split the input and write back to the source refs\n    const [first, ...rest] = newValue.split(' ')\n    firstName.value = first\n    lastName.value = rest.join(' ')\n  },\n})\n\nfullName.value = 'Grace Hopper'\nconsole.log(firstName.value)  \u002F\u002F 'Grace'\nconsole.log(lastName.value)   \u002F\u002F 'Hopper'\n\n\u002F\u002F ── Anti-pattern: set without writing to source deps ──\n\u002F\u002F If the setter doesn't write to the reactive deps that the getter reads,\n\u002F\u002F the next get() returns the derived value (ignoring what was set),\n\u002F\u002F making the set appear to \"not stick.\"\n",[31,647,648,659,663,681,699,703,708,722,730,760,765,782,787,825,835,854,858,862,866,876,889,901,905,910,915,920],{"__ignoreMap":29},[34,649,650,652,655,657],{"class":36,"line":37},[34,651,41],{"class":40},[34,653,654],{"class":44}," { ref, computed } ",[34,656,48],{"class":40},[34,658,52],{"class":51},[34,660,661],{"class":36,"line":55},[34,662,59],{"emptyLinePlaceholder":58},[34,664,665,667,670,672,674,676,679],{"class":36,"line":62},[34,666,101],{"class":40},[34,668,669],{"class":104}," firstName",[34,671,108],{"class":40},[34,673,112],{"class":111},[34,675,115],{"class":44},[34,677,678],{"class":51},"'Ada'",[34,680,121],{"class":44},[34,682,683,685,688,690,692,694,697],{"class":36,"line":69},[34,684,101],{"class":40},[34,686,687],{"class":104}," lastName",[34,689,108],{"class":40},[34,691,112],{"class":111},[34,693,115],{"class":44},[34,695,696],{"class":51},"'Lovelace'",[34,698,121],{"class":44},[34,700,701],{"class":36,"line":75},[34,702,59],{"emptyLinePlaceholder":58},[34,704,705],{"class":36,"line":81},[34,706,707],{"class":65},"\u002F\u002F ── Writable computed: get derives from deps, set distributes to deps ──\n",[34,709,710,712,715,717,719],{"class":36,"line":87},[34,711,101],{"class":40},[34,713,714],{"class":104}," fullName",[34,716,108],{"class":40},[34,718,134],{"class":111},[34,720,721],{"class":44},"({\n",[34,723,724,727],{"class":36,"line":93},[34,725,726],{"class":111},"  get",[34,728,729],{"class":44},"() {\n",[34,731,732,735,738,741,744,747,750,753,755,757],{"class":36,"line":98},[34,733,734],{"class":40},"    return",[34,736,737],{"class":51}," `${",[34,739,740],{"class":44},"firstName",[34,742,743],{"class":51},".",[34,745,746],{"class":44},"value",[34,748,749],{"class":51},"} ${",[34,751,752],{"class":44},"lastName",[34,754,743],{"class":51},[34,756,746],{"class":44},[34,758,759],{"class":51},"}`\n",[34,761,762],{"class":36,"line":124},[34,763,764],{"class":44},"  },\n",[34,766,767,770,772,775,777,780],{"class":36,"line":154},[34,768,769],{"class":111},"  set",[34,771,115],{"class":44},[34,773,774],{"class":282},"newValue",[34,776,440],{"class":40},[34,778,779],{"class":104}," string",[34,781,476],{"class":44},[34,783,784],{"class":36,"line":159},[34,785,786],{"class":65},"    \u002F\u002F Split the input and write back to the source refs\n",[34,788,789,792,795,798,800,803,806,809,812,815,818,820,823],{"class":36,"line":165},[34,790,791],{"class":40},"    const",[34,793,794],{"class":44}," [",[34,796,797],{"class":104},"first",[34,799,551],{"class":44},[34,801,802],{"class":40},"...",[34,804,805],{"class":104},"rest",[34,807,808],{"class":44},"] ",[34,810,811],{"class":40},"=",[34,813,814],{"class":44}," newValue.",[34,816,817],{"class":111},"split",[34,819,115],{"class":44},[34,821,822],{"class":51},"' '",[34,824,121],{"class":44},[34,826,827,830,832],{"class":36,"line":171},[34,828,829],{"class":44},"    firstName.value ",[34,831,811],{"class":40},[34,833,834],{"class":44}," first\n",[34,836,837,840,842,845,848,850,852],{"class":36,"line":177},[34,838,839],{"class":44},"    lastName.value ",[34,841,811],{"class":40},[34,843,844],{"class":44}," rest.",[34,846,847],{"class":111},"join",[34,849,115],{"class":44},[34,851,822],{"class":51},[34,853,121],{"class":44},[34,855,856],{"class":36,"line":183},[34,857,764],{"class":44},[34,859,860],{"class":36,"line":189},[34,861,301],{"class":44},[34,863,864],{"class":36,"line":194},[34,865,59],{"emptyLinePlaceholder":58},[34,867,868,871,873],{"class":36,"line":200},[34,869,870],{"class":44},"fullName.value ",[34,872,811],{"class":40},[34,874,875],{"class":51}," 'Grace Hopper'\n",[34,877,878,881,883,886],{"class":36,"line":206},[34,879,880],{"class":44},"console.",[34,882,254],{"class":111},[34,884,885],{"class":44},"(firstName.value)  ",[34,887,888],{"class":65},"\u002F\u002F 'Grace'\n",[34,890,891,893,895,898],{"class":36,"line":212},[34,892,880],{"class":44},[34,894,254],{"class":111},[34,896,897],{"class":44},"(lastName.value)   ",[34,899,900],{"class":65},"\u002F\u002F 'Hopper'\n",[34,902,903],{"class":36,"line":218},[34,904,59],{"emptyLinePlaceholder":58},[34,906,907],{"class":36,"line":223},[34,908,909],{"class":65},"\u002F\u002F ── Anti-pattern: set without writing to source deps ──\n",[34,911,912],{"class":36,"line":229},[34,913,914],{"class":65},"\u002F\u002F If the setter doesn't write to the reactive deps that the getter reads,\n",[34,916,917],{"class":36,"line":248},[34,918,919],{"class":65},"\u002F\u002F the next get() returns the derived value (ignoring what was set),\n",[34,921,922],{"class":36,"line":268},[34,923,924],{"class":65},"\u002F\u002F making the set appear to \"not stick.\"\n",[15,926,928],{"id":927},"watch-source-types-and-callback-signature","watch — Source Types and Callback Signature",[20,930,932],{"filename":931,"language":23},"watch-sources.ts",[25,933,935],{"className":27,"code":934,"language":23,"meta":29,"style":29},"import { ref, reactive, watch, toRefs, type WatchCallback, type WatchSource } from 'vue'\n\n\u002F\u002F ── 1. Watch a ref ──\nconst count = ref(0)\nwatch(count, (newVal, oldVal) => {\n  console.log(`${oldVal} → ${newVal}`)\n})\n\n\u002F\u002F ── 2. Watch a getter (computed-like) ──\nconst user = reactive({ name: 'Ada', age: 36 })\nwatch(\n  () => user.age,                    \u002F\u002F getter: must return the value to track\n  (newAge, oldAge) => {\n    console.log(`age: ${oldAge} → ${newAge}`)\n  }\n)\n\n\u002F\u002F ── 3. Watch multiple sources — new\u002Fold are arrays ──\nconst a = ref(1)\nconst b = ref(2)\nwatch([a, b], ([newA, newB], [oldA, oldB]) => {\n  console.log(`a: ${oldA}→${newA}, b: ${oldB}→${newB}`)\n})\n\n\u002F\u002F ── 4. Watch a reactive object — deep by default ──\nwatch(user, (newUser, oldUser) => {\n  \u002F\u002F ⚠️ newUser and oldUser are the SAME object reference (mutated in place)\n  \u002F\u002F For a snapshot, use a getter: () => ({ ...user })\n  console.log('user changed (deep)')\n})\n\n\u002F\u002F ── 5. Watch getter returning array — deep NOT automatic ──\nwatch(\n  () => user.roles,  \u002F\u002F returns the array reference (same unless replaced)\n  (roles) => { \u002F* fires only when .roles is reassigned, not on push\u002Fsplice *\u002F },\n  { deep: true }  \u002F\u002F opt-in to deep watching of array contents\n)\n",[31,936,937,958,962,967,983,1005,1028,1032,1036,1041,1067,1074,1087,1106,1128,1133,1137,1141,1146,1163,1180,1213,1244,1248,1252,1257,1278,1283,1288,1301,1305,1309,1314,1320,1332,1353,1368],{"__ignoreMap":29},[34,938,939,941,944,946,949,951,954,956],{"class":36,"line":37},[34,940,41],{"class":40},[34,942,943],{"class":44}," { ref, reactive, watch, toRefs, ",[34,945,387],{"class":40},[34,947,948],{"class":44}," WatchCallback, ",[34,950,387],{"class":40},[34,952,953],{"class":44}," WatchSource } ",[34,955,48],{"class":40},[34,957,52],{"class":51},[34,959,960],{"class":36,"line":55},[34,961,59],{"emptyLinePlaceholder":58},[34,963,964],{"class":36,"line":62},[34,965,966],{"class":65},"\u002F\u002F ── 1. Watch a ref ──\n",[34,968,969,971,973,975,977,979,981],{"class":36,"line":69},[34,970,101],{"class":40},[34,972,105],{"class":104},[34,974,108],{"class":40},[34,976,112],{"class":111},[34,978,115],{"class":44},[34,980,118],{"class":104},[34,982,121],{"class":44},[34,984,985,988,991,994,996,999,1001,1003],{"class":36,"line":75},[34,986,987],{"class":111},"watch",[34,989,990],{"class":44},"(count, (",[34,992,993],{"class":282},"newVal",[34,995,551],{"class":44},[34,997,998],{"class":282},"oldVal",[34,1000,469],{"class":44},[34,1002,140],{"class":40},[34,1004,245],{"class":44},[34,1006,1007,1009,1011,1013,1016,1018,1021,1023,1026],{"class":36,"line":81},[34,1008,251],{"class":44},[34,1010,254],{"class":111},[34,1012,115],{"class":44},[34,1014,1015],{"class":51},"`${",[34,1017,998],{"class":44},[34,1019,1020],{"class":51},"} → ${",[34,1022,993],{"class":44},[34,1024,1025],{"class":51},"}`",[34,1027,121],{"class":44},[34,1029,1030],{"class":36,"line":87},[34,1031,301],{"class":44},[34,1033,1034],{"class":36,"line":93},[34,1035,59],{"emptyLinePlaceholder":58},[34,1037,1038],{"class":36,"line":98},[34,1039,1040],{"class":65},"\u002F\u002F ── 2. Watch a getter (computed-like) ──\n",[34,1042,1043,1045,1048,1050,1053,1056,1058,1061,1064],{"class":36,"line":124},[34,1044,101],{"class":40},[34,1046,1047],{"class":104}," user",[34,1049,108],{"class":40},[34,1051,1052],{"class":111}," reactive",[34,1054,1055],{"class":44},"({ name: ",[34,1057,678],{"class":51},[34,1059,1060],{"class":44},", age: ",[34,1062,1063],{"class":104},"36",[34,1065,1066],{"class":44}," })\n",[34,1068,1069,1071],{"class":36,"line":154},[34,1070,987],{"class":111},[34,1072,1073],{"class":44},"(\n",[34,1075,1076,1079,1081,1084],{"class":36,"line":159},[34,1077,1078],{"class":44},"  () ",[34,1080,140],{"class":40},[34,1082,1083],{"class":44}," user.age,                    ",[34,1085,1086],{"class":65},"\u002F\u002F getter: must return the value to track\n",[34,1088,1089,1092,1095,1097,1100,1102,1104],{"class":36,"line":165},[34,1090,1091],{"class":44},"  (",[34,1093,1094],{"class":282},"newAge",[34,1096,551],{"class":44},[34,1098,1099],{"class":282},"oldAge",[34,1101,469],{"class":44},[34,1103,140],{"class":40},[34,1105,245],{"class":44},[34,1107,1108,1111,1113,1115,1118,1120,1122,1124,1126],{"class":36,"line":171},[34,1109,1110],{"class":44},"    console.",[34,1112,254],{"class":111},[34,1114,115],{"class":44},[34,1116,1117],{"class":51},"`age: ${",[34,1119,1099],{"class":44},[34,1121,1020],{"class":51},[34,1123,1094],{"class":44},[34,1125,1025],{"class":51},[34,1127,121],{"class":44},[34,1129,1130],{"class":36,"line":177},[34,1131,1132],{"class":44},"  }\n",[34,1134,1135],{"class":36,"line":183},[34,1136,121],{"class":44},[34,1138,1139],{"class":36,"line":189},[34,1140,59],{"emptyLinePlaceholder":58},[34,1142,1143],{"class":36,"line":194},[34,1144,1145],{"class":65},"\u002F\u002F ── 3. Watch multiple sources — new\u002Fold are arrays ──\n",[34,1147,1148,1150,1153,1155,1157,1159,1161],{"class":36,"line":200},[34,1149,101],{"class":40},[34,1151,1152],{"class":104}," a",[34,1154,108],{"class":40},[34,1156,112],{"class":111},[34,1158,115],{"class":44},[34,1160,548],{"class":104},[34,1162,121],{"class":44},[34,1164,1165,1167,1170,1172,1174,1176,1178],{"class":36,"line":206},[34,1166,101],{"class":40},[34,1168,1169],{"class":104}," b",[34,1171,108],{"class":40},[34,1173,112],{"class":111},[34,1175,115],{"class":44},[34,1177,554],{"class":104},[34,1179,121],{"class":44},[34,1181,1182,1184,1187,1190,1192,1195,1198,1201,1203,1206,1209,1211],{"class":36,"line":212},[34,1183,987],{"class":111},[34,1185,1186],{"class":44},"([a, b], ([",[34,1188,1189],{"class":282},"newA",[34,1191,551],{"class":44},[34,1193,1194],{"class":282},"newB",[34,1196,1197],{"class":44},"], [",[34,1199,1200],{"class":282},"oldA",[34,1202,551],{"class":44},[34,1204,1205],{"class":282},"oldB",[34,1207,1208],{"class":44},"]) ",[34,1210,140],{"class":40},[34,1212,245],{"class":44},[34,1214,1215,1217,1219,1221,1224,1226,1229,1231,1234,1236,1238,1240,1242],{"class":36,"line":218},[34,1216,251],{"class":44},[34,1218,254],{"class":111},[34,1220,115],{"class":44},[34,1222,1223],{"class":51},"`a: ${",[34,1225,1200],{"class":44},[34,1227,1228],{"class":51},"}→${",[34,1230,1189],{"class":44},[34,1232,1233],{"class":51},"}, b: ${",[34,1235,1205],{"class":44},[34,1237,1228],{"class":51},[34,1239,1194],{"class":44},[34,1241,1025],{"class":51},[34,1243,121],{"class":44},[34,1245,1246],{"class":36,"line":223},[34,1247,301],{"class":44},[34,1249,1250],{"class":36,"line":229},[34,1251,59],{"emptyLinePlaceholder":58},[34,1253,1254],{"class":36,"line":248},[34,1255,1256],{"class":65},"\u002F\u002F ── 4. Watch a reactive object — deep by default ──\n",[34,1258,1259,1261,1264,1267,1269,1272,1274,1276],{"class":36,"line":268},[34,1260,987],{"class":111},[34,1262,1263],{"class":44},"(user, (",[34,1265,1266],{"class":282},"newUser",[34,1268,551],{"class":44},[34,1270,1271],{"class":282},"oldUser",[34,1273,469],{"class":44},[34,1275,140],{"class":40},[34,1277,245],{"class":44},[34,1279,1280],{"class":36,"line":298},[34,1281,1282],{"class":65},"  \u002F\u002F ⚠️ newUser and oldUser are the SAME object reference (mutated in place)\n",[34,1284,1285],{"class":36,"line":304},[34,1286,1287],{"class":65},"  \u002F\u002F For a snapshot, use a getter: () => ({ ...user })\n",[34,1289,1290,1292,1294,1296,1299],{"class":36,"line":316},[34,1291,251],{"class":44},[34,1293,254],{"class":111},[34,1295,115],{"class":44},[34,1297,1298],{"class":51},"'user changed (deep)'",[34,1300,121],{"class":44},[34,1302,1303],{"class":36,"line":322},[34,1304,301],{"class":44},[34,1306,1307],{"class":36,"line":327},[34,1308,59],{"emptyLinePlaceholder":58},[34,1310,1311],{"class":36,"line":333},[34,1312,1313],{"class":65},"\u002F\u002F ── 5. Watch getter returning array — deep NOT automatic ──\n",[34,1315,1316,1318],{"class":36,"line":345},[34,1317,987],{"class":111},[34,1319,1073],{"class":44},[34,1321,1322,1324,1326,1329],{"class":36,"line":363},[34,1323,1078],{"class":44},[34,1325,140],{"class":40},[34,1327,1328],{"class":44}," user.roles,  ",[34,1330,1331],{"class":65},"\u002F\u002F returns the array reference (same unless replaced)\n",[34,1333,1335,1337,1340,1342,1344,1347,1350],{"class":36,"line":1334},35,[34,1336,1091],{"class":44},[34,1338,1339],{"class":282},"roles",[34,1341,469],{"class":44},[34,1343,140],{"class":40},[34,1345,1346],{"class":44}," { ",[34,1348,1349],{"class":65},"\u002F* fires only when .roles is reassigned, not on push\u002Fsplice *\u002F",[34,1351,1352],{"class":44}," },\n",[34,1354,1356,1359,1362,1365],{"class":36,"line":1355},36,[34,1357,1358],{"class":44},"  { deep: ",[34,1360,1361],{"class":104},"true",[34,1363,1364],{"class":44}," }  ",[34,1366,1367],{"class":65},"\u002F\u002F opt-in to deep watching of array contents\n",[34,1369,1371],{"class":36,"line":1370},37,[34,1372,121],{"class":44},[15,1374,1376],{"id":1375},"watch-vs-watcheffect-when-to-use-which","watch vs watchEffect — When to Use Which",[20,1378,1380],{"filename":1379,"language":23},"watch-vs-effect.ts",[25,1381,1383],{"className":27,"code":1382,"language":23,"meta":29,"style":29},"import { ref, watch, watchEffect } from 'vue'\n\nconst query = ref('')\n\n\u002F\u002F ── watchEffect: auto-tracks, runs immediately, no old value ──\nwatchEffect(() => {\n  \u002F\u002F Automatically tracks every reactive read inside this function.\n  \u002F\u002F Runs IMMEDIATELY (sync, before mount if in setup).\n  \u002F\u002F Re-runs whenever ANY tracked ref changes.\n  fetch(`\u002Fapi\u002Fsearch?q=${query.value}`)  \u002F\u002F auto-tracks query\n})\n\u002F\u002F Pros: no explicit source list, immediate execution, simple\n\u002F\u002F Cons: no access to old value, fires on initial run (may be unwanted)\n\n\u002F\u002F ── watch: explicit source, lazy, gets old + new value ──\nwatch(query, (newVal, oldVal) => {\n  \u002F\u002F Only runs AFTER query changes (not on initial setup).\n  \u002F\u002F Has access to both old and new values.\n  fetch(`\u002Fapi\u002Fsearch?q=${newVal}`)\n}, { immediate: false })  \u002F\u002F default — set true to fire on init\n\n\u002F\u002F ── Decision matrix: ────────────────────────────────────\n\u002F\u002F Need old value?              → watch\n\u002F\u002F Need to skip initial run?    → watch\n\u002F\u002F Side effect tracks many deps → watchEffect (auto-tracking, less code)\n\u002F\u002F Need to debounce\u002Fthrottle?   → watch (control flush timing)\n\u002F\u002F Async setup with cleanup     → watch (onCleanup param)\n",[31,1384,1385,1396,1400,1418,1422,1427,1437,1442,1447,1452,1476,1480,1485,1490,1494,1499,1518,1523,1528,1542,1556,1560,1565,1570,1575,1580,1585],{"__ignoreMap":29},[34,1386,1387,1389,1392,1394],{"class":36,"line":37},[34,1388,41],{"class":40},[34,1390,1391],{"class":44}," { ref, watch, watchEffect } ",[34,1393,48],{"class":40},[34,1395,52],{"class":51},[34,1397,1398],{"class":36,"line":55},[34,1399,59],{"emptyLinePlaceholder":58},[34,1401,1402,1404,1407,1409,1411,1413,1416],{"class":36,"line":62},[34,1403,101],{"class":40},[34,1405,1406],{"class":104}," query",[34,1408,108],{"class":40},[34,1410,112],{"class":111},[34,1412,115],{"class":44},[34,1414,1415],{"class":51},"''",[34,1417,121],{"class":44},[34,1419,1420],{"class":36,"line":69},[34,1421,59],{"emptyLinePlaceholder":58},[34,1423,1424],{"class":36,"line":75},[34,1425,1426],{"class":65},"\u002F\u002F ── watchEffect: auto-tracks, runs immediately, no old value ──\n",[34,1428,1429,1431,1433,1435],{"class":36,"line":81},[34,1430,336],{"class":111},[34,1432,137],{"class":44},[34,1434,140],{"class":40},[34,1436,245],{"class":44},[34,1438,1439],{"class":36,"line":87},[34,1440,1441],{"class":65},"  \u002F\u002F Automatically tracks every reactive read inside this function.\n",[34,1443,1444],{"class":36,"line":93},[34,1445,1446],{"class":65},"  \u002F\u002F Runs IMMEDIATELY (sync, before mount if in setup).\n",[34,1448,1449],{"class":36,"line":98},[34,1450,1451],{"class":65},"  \u002F\u002F Re-runs whenever ANY tracked ref changes.\n",[34,1453,1454,1457,1459,1462,1465,1467,1469,1471,1473],{"class":36,"line":124},[34,1455,1456],{"class":111},"  fetch",[34,1458,115],{"class":44},[34,1460,1461],{"class":51},"`\u002Fapi\u002Fsearch?q=${",[34,1463,1464],{"class":44},"query",[34,1466,743],{"class":51},[34,1468,746],{"class":44},[34,1470,1025],{"class":51},[34,1472,262],{"class":44},[34,1474,1475],{"class":65},"\u002F\u002F auto-tracks query\n",[34,1477,1478],{"class":36,"line":154},[34,1479,301],{"class":44},[34,1481,1482],{"class":36,"line":159},[34,1483,1484],{"class":65},"\u002F\u002F Pros: no explicit source list, immediate execution, simple\n",[34,1486,1487],{"class":36,"line":165},[34,1488,1489],{"class":65},"\u002F\u002F Cons: no access to old value, fires on initial run (may be unwanted)\n",[34,1491,1492],{"class":36,"line":171},[34,1493,59],{"emptyLinePlaceholder":58},[34,1495,1496],{"class":36,"line":177},[34,1497,1498],{"class":65},"\u002F\u002F ── watch: explicit source, lazy, gets old + new value ──\n",[34,1500,1501,1503,1506,1508,1510,1512,1514,1516],{"class":36,"line":183},[34,1502,987],{"class":111},[34,1504,1505],{"class":44},"(query, (",[34,1507,993],{"class":282},[34,1509,551],{"class":44},[34,1511,998],{"class":282},[34,1513,469],{"class":44},[34,1515,140],{"class":40},[34,1517,245],{"class":44},[34,1519,1520],{"class":36,"line":189},[34,1521,1522],{"class":65},"  \u002F\u002F Only runs AFTER query changes (not on initial setup).\n",[34,1524,1525],{"class":36,"line":194},[34,1526,1527],{"class":65},"  \u002F\u002F Has access to both old and new values.\n",[34,1529,1530,1532,1534,1536,1538,1540],{"class":36,"line":200},[34,1531,1456],{"class":111},[34,1533,115],{"class":44},[34,1535,1461],{"class":51},[34,1537,993],{"class":44},[34,1539,1025],{"class":51},[34,1541,121],{"class":44},[34,1543,1544,1547,1550,1553],{"class":36,"line":206},[34,1545,1546],{"class":44},"}, { immediate: ",[34,1548,1549],{"class":104},"false",[34,1551,1552],{"class":44}," })  ",[34,1554,1555],{"class":65},"\u002F\u002F default — set true to fire on init\n",[34,1557,1558],{"class":36,"line":212},[34,1559,59],{"emptyLinePlaceholder":58},[34,1561,1562],{"class":36,"line":218},[34,1563,1564],{"class":65},"\u002F\u002F ── Decision matrix: ────────────────────────────────────\n",[34,1566,1567],{"class":36,"line":223},[34,1568,1569],{"class":65},"\u002F\u002F Need old value?              → watch\n",[34,1571,1572],{"class":36,"line":229},[34,1573,1574],{"class":65},"\u002F\u002F Need to skip initial run?    → watch\n",[34,1576,1577],{"class":36,"line":248},[34,1578,1579],{"class":65},"\u002F\u002F Side effect tracks many deps → watchEffect (auto-tracking, less code)\n",[34,1581,1582],{"class":36,"line":268},[34,1583,1584],{"class":65},"\u002F\u002F Need to debounce\u002Fthrottle?   → watch (control flush timing)\n",[34,1586,1587],{"class":36,"line":298},[34,1588,1589],{"class":65},"\u002F\u002F Async setup with cleanup     → watch (onCleanup param)\n",[15,1591,1593],{"id":1592},"flush-timing-pre-post-sync","Flush Timing — pre, post, sync",[20,1595,1597],{"filename":1596,"language":23},"flush-timing.ts",[25,1598,1600],{"className":27,"code":1599,"language":23,"meta":29,"style":29},"import { ref, watch, nextTick } from 'vue'\n\nconst count = ref(0)\n\n\u002F\u002F ── flush: 'pre' (default) — runs BEFORE component re-render ──\n\u002F\u002F The watcher callback fires before the DOM updates.\n\u002F\u002F Useful: when the callback modifies state that the template reads,\n\u002F\u002F so the render uses the updated value (avoids a double render).\nwatch(count, (n) => {\n  console.log('pre-flush: count is', n, 'DOM not yet updated')\n}, { flush: 'pre' })\n\n\u002F\u002F ── flush: 'post' — runs AFTER component re-render ──\n\u002F\u002F The watcher fires after Vue has patched the DOM.\n\u002F\u002F Useful: when you need to read updated DOM measurements.\nwatch(count, (n) => {\n  \u002F\u002F Safe to measure the DOM here — it reflects the new count\n  const el = document.querySelector('#counter')\n  console.log('post-flush: DOM shows', el?.textContent)\n}, { flush: 'post' })\n\n\u002F\u002F ── flush: 'sync' — runs synchronously on mutation ──\n\u002F\u002F Bypasses the queue — fires immediately when the source changes.\n\u002F\u002F ⚠️ Can cause infinite loops if the callback mutates the source.\n\u002F\u002F Use for: debug logging, or when order-of-execution matters precisely.\nwatch(count, (n) => {\n  console.log('sync: immediate', n)\n}, { flush: 'sync' })\n\n\u002F\u002F ── nextTick: wait for the current flush queue to drain ──\ncount.value = 5\n\u002F\u002F DOM not yet updated here\nnextTick(() => {\n  \u002F\u002F DOM IS updated — all pre and post watchers have fired\n  console.log('after nextTick, DOM is current')\n})\n",[31,1601,1602,1613,1617,1633,1637,1642,1647,1652,1657,1672,1691,1701,1705,1710,1715,1720,1734,1739,1762,1776,1785,1789,1794,1799,1804,1809,1823,1837,1846,1850,1855,1865,1870,1881,1886,1899],{"__ignoreMap":29},[34,1603,1604,1606,1609,1611],{"class":36,"line":37},[34,1605,41],{"class":40},[34,1607,1608],{"class":44}," { ref, watch, nextTick } ",[34,1610,48],{"class":40},[34,1612,52],{"class":51},[34,1614,1615],{"class":36,"line":55},[34,1616,59],{"emptyLinePlaceholder":58},[34,1618,1619,1621,1623,1625,1627,1629,1631],{"class":36,"line":62},[34,1620,101],{"class":40},[34,1622,105],{"class":104},[34,1624,108],{"class":40},[34,1626,112],{"class":111},[34,1628,115],{"class":44},[34,1630,118],{"class":104},[34,1632,121],{"class":44},[34,1634,1635],{"class":36,"line":69},[34,1636,59],{"emptyLinePlaceholder":58},[34,1638,1639],{"class":36,"line":75},[34,1640,1641],{"class":65},"\u002F\u002F ── flush: 'pre' (default) — runs BEFORE component re-render ──\n",[34,1643,1644],{"class":36,"line":81},[34,1645,1646],{"class":65},"\u002F\u002F The watcher callback fires before the DOM updates.\n",[34,1648,1649],{"class":36,"line":87},[34,1650,1651],{"class":65},"\u002F\u002F Useful: when the callback modifies state that the template reads,\n",[34,1653,1654],{"class":36,"line":93},[34,1655,1656],{"class":65},"\u002F\u002F so the render uses the updated value (avoids a double render).\n",[34,1658,1659,1661,1663,1666,1668,1670],{"class":36,"line":98},[34,1660,987],{"class":111},[34,1662,990],{"class":44},[34,1664,1665],{"class":282},"n",[34,1667,469],{"class":44},[34,1669,140],{"class":40},[34,1671,245],{"class":44},[34,1673,1674,1676,1678,1680,1683,1686,1689],{"class":36,"line":124},[34,1675,251],{"class":44},[34,1677,254],{"class":111},[34,1679,115],{"class":44},[34,1681,1682],{"class":51},"'pre-flush: count is'",[34,1684,1685],{"class":44},", n, ",[34,1687,1688],{"class":51},"'DOM not yet updated'",[34,1690,121],{"class":44},[34,1692,1693,1696,1699],{"class":36,"line":154},[34,1694,1695],{"class":44},"}, { flush: ",[34,1697,1698],{"class":51},"'pre'",[34,1700,1066],{"class":44},[34,1702,1703],{"class":36,"line":159},[34,1704,59],{"emptyLinePlaceholder":58},[34,1706,1707],{"class":36,"line":165},[34,1708,1709],{"class":65},"\u002F\u002F ── flush: 'post' — runs AFTER component re-render ──\n",[34,1711,1712],{"class":36,"line":171},[34,1713,1714],{"class":65},"\u002F\u002F The watcher fires after Vue has patched the DOM.\n",[34,1716,1717],{"class":36,"line":177},[34,1718,1719],{"class":65},"\u002F\u002F Useful: when you need to read updated DOM measurements.\n",[34,1721,1722,1724,1726,1728,1730,1732],{"class":36,"line":183},[34,1723,987],{"class":111},[34,1725,990],{"class":44},[34,1727,1665],{"class":282},[34,1729,469],{"class":44},[34,1731,140],{"class":40},[34,1733,245],{"class":44},[34,1735,1736],{"class":36,"line":189},[34,1737,1738],{"class":65},"  \u002F\u002F Safe to measure the DOM here — it reflects the new count\n",[34,1740,1741,1744,1747,1749,1752,1755,1757,1760],{"class":36,"line":194},[34,1742,1743],{"class":40},"  const",[34,1745,1746],{"class":104}," el",[34,1748,108],{"class":40},[34,1750,1751],{"class":44}," document.",[34,1753,1754],{"class":111},"querySelector",[34,1756,115],{"class":44},[34,1758,1759],{"class":51},"'#counter'",[34,1761,121],{"class":44},[34,1763,1764,1766,1768,1770,1773],{"class":36,"line":200},[34,1765,251],{"class":44},[34,1767,254],{"class":111},[34,1769,115],{"class":44},[34,1771,1772],{"class":51},"'post-flush: DOM shows'",[34,1774,1775],{"class":44},", el?.textContent)\n",[34,1777,1778,1780,1783],{"class":36,"line":206},[34,1779,1695],{"class":44},[34,1781,1782],{"class":51},"'post'",[34,1784,1066],{"class":44},[34,1786,1787],{"class":36,"line":212},[34,1788,59],{"emptyLinePlaceholder":58},[34,1790,1791],{"class":36,"line":218},[34,1792,1793],{"class":65},"\u002F\u002F ── flush: 'sync' — runs synchronously on mutation ──\n",[34,1795,1796],{"class":36,"line":223},[34,1797,1798],{"class":65},"\u002F\u002F Bypasses the queue — fires immediately when the source changes.\n",[34,1800,1801],{"class":36,"line":229},[34,1802,1803],{"class":65},"\u002F\u002F ⚠️ Can cause infinite loops if the callback mutates the source.\n",[34,1805,1806],{"class":36,"line":248},[34,1807,1808],{"class":65},"\u002F\u002F Use for: debug logging, or when order-of-execution matters precisely.\n",[34,1810,1811,1813,1815,1817,1819,1821],{"class":36,"line":268},[34,1812,987],{"class":111},[34,1814,990],{"class":44},[34,1816,1665],{"class":282},[34,1818,469],{"class":44},[34,1820,140],{"class":40},[34,1822,245],{"class":44},[34,1824,1825,1827,1829,1831,1834],{"class":36,"line":298},[34,1826,251],{"class":44},[34,1828,254],{"class":111},[34,1830,115],{"class":44},[34,1832,1833],{"class":51},"'sync: immediate'",[34,1835,1836],{"class":44},", n)\n",[34,1838,1839,1841,1844],{"class":36,"line":304},[34,1840,1695],{"class":44},[34,1842,1843],{"class":51},"'sync'",[34,1845,1066],{"class":44},[34,1847,1848],{"class":36,"line":316},[34,1849,59],{"emptyLinePlaceholder":58},[34,1851,1852],{"class":36,"line":322},[34,1853,1854],{"class":65},"\u002F\u002F ── nextTick: wait for the current flush queue to drain ──\n",[34,1856,1857,1860,1862],{"class":36,"line":327},[34,1858,1859],{"class":44},"count.value ",[34,1861,811],{"class":40},[34,1863,1864],{"class":104}," 5\n",[34,1866,1867],{"class":36,"line":333},[34,1868,1869],{"class":65},"\u002F\u002F DOM not yet updated here\n",[34,1871,1872,1875,1877,1879],{"class":36,"line":345},[34,1873,1874],{"class":111},"nextTick",[34,1876,137],{"class":44},[34,1878,140],{"class":40},[34,1880,245],{"class":44},[34,1882,1883],{"class":36,"line":363},[34,1884,1885],{"class":65},"  \u002F\u002F DOM IS updated — all pre and post watchers have fired\n",[34,1887,1888,1890,1892,1894,1897],{"class":36,"line":1334},[34,1889,251],{"class":44},[34,1891,254],{"class":111},[34,1893,115],{"class":44},[34,1895,1896],{"class":51},"'after nextTick, DOM is current'",[34,1898,121],{"class":44},[34,1900,1901],{"class":36,"line":1355},[34,1902,301],{"class":44},[15,1904,1906],{"id":1905},"deep-watch-cost-and-when-to-avoid-it","Deep Watch — Cost and When to Avoid It",[20,1908,1910],{"filename":1909,"language":23},"deep-watch.ts",[25,1911,1913],{"className":27,"code":1912,"language":23,"meta":29,"style":29},"import { reactive, watch } from 'vue'\n\nconst tree = reactive({\n  nodes: Array.from({ length: 1000 }, (_, i) => ({\n    id: i,\n    children: Array.from({ length: 100 }, (_, j) => ({ id: `${i}-${j}` }))\n  }))\n})\n\n\u002F\u002F ❌ WRONG: deep:true on a large reactive object\nwatch(tree, (newTree) => {\n  \u002F\u002F deep:true walks EVERY property of EVERY nested object to track them.\n  \u002F\u002F For 1000×100 = 100,000 nested objects, this installs 100,000+ track calls.\n  \u002F\u002F Every nested mutation triggers this callback — even irrelevant ones.\n  saveToServer(newTree)\n}, { deep: true })\n\n\u002F\u002F ✅ CORRECT: watch a specific path, not the whole tree\nwatch(\n  () => tree.nodes.length,  \u002F\u002F only track the array length\n  (len) => {\n    console.log('node count changed:', len)\n  }\n)\n\n\u002F\u002F ✅ CORRECT: watch a getter that returns a shallow snapshot\nwatch(\n  () => tree.nodes.map(n => n.id),  \u002F\u002F new array on each change\n  (ids) => {\n    console.log('node ids changed:', ids)\n  }\n)\n\n\u002F\u002F ── Rule: deep:true is O(n) in object depth × breadth ──\n\u002F\u002F For forms (flat object, ~20 fields): fine, use deep.\n\u002F\u002F For trees\u002Ftables (1000+ nodes): never deep — watch specific paths instead.\n",[31,1914,1915,1926,1930,1943,1974,1979,2021,2026,2030,2034,2039,2055,2060,2065,2070,2078,2087,2091,2096,2102,2120,2133,2147,2151,2155,2159,2164,2170,2192,2205,2219,2223,2227,2231,2236,2241],{"__ignoreMap":29},[34,1916,1917,1919,1922,1924],{"class":36,"line":37},[34,1918,41],{"class":40},[34,1920,1921],{"class":44}," { reactive, watch } ",[34,1923,48],{"class":40},[34,1925,52],{"class":51},[34,1927,1928],{"class":36,"line":55},[34,1929,59],{"emptyLinePlaceholder":58},[34,1931,1932,1934,1937,1939,1941],{"class":36,"line":62},[34,1933,101],{"class":40},[34,1935,1936],{"class":104}," tree",[34,1938,108],{"class":40},[34,1940,1052],{"class":111},[34,1942,721],{"class":44},[34,1944,1945,1948,1950,1953,1956,1959,1962,1964,1967,1969,1971],{"class":36,"line":69},[34,1946,1947],{"class":44},"  nodes: Array.",[34,1949,48],{"class":111},[34,1951,1952],{"class":44},"({ length: ",[34,1954,1955],{"class":104},"1000",[34,1957,1958],{"class":44}," }, (",[34,1960,1961],{"class":282},"_",[34,1963,551],{"class":44},[34,1965,1966],{"class":282},"i",[34,1968,469],{"class":44},[34,1970,140],{"class":40},[34,1972,1973],{"class":44}," ({\n",[34,1975,1976],{"class":36,"line":75},[34,1977,1978],{"class":44},"    id: i,\n",[34,1980,1981,1984,1986,1988,1991,1993,1995,1997,2000,2002,2004,2007,2009,2011,2014,2016,2018],{"class":36,"line":81},[34,1982,1983],{"class":44},"    children: Array.",[34,1985,48],{"class":111},[34,1987,1952],{"class":44},[34,1989,1990],{"class":104},"100",[34,1992,1958],{"class":44},[34,1994,1961],{"class":282},[34,1996,551],{"class":44},[34,1998,1999],{"class":282},"j",[34,2001,469],{"class":44},[34,2003,140],{"class":40},[34,2005,2006],{"class":44}," ({ id: ",[34,2008,1015],{"class":51},[34,2010,1966],{"class":44},[34,2012,2013],{"class":51},"}-${",[34,2015,1999],{"class":44},[34,2017,1025],{"class":51},[34,2019,2020],{"class":44}," }))\n",[34,2022,2023],{"class":36,"line":87},[34,2024,2025],{"class":44},"  }))\n",[34,2027,2028],{"class":36,"line":93},[34,2029,301],{"class":44},[34,2031,2032],{"class":36,"line":98},[34,2033,59],{"emptyLinePlaceholder":58},[34,2035,2036],{"class":36,"line":124},[34,2037,2038],{"class":65},"\u002F\u002F ❌ WRONG: deep:true on a large reactive object\n",[34,2040,2041,2043,2046,2049,2051,2053],{"class":36,"line":154},[34,2042,987],{"class":111},[34,2044,2045],{"class":44},"(tree, (",[34,2047,2048],{"class":282},"newTree",[34,2050,469],{"class":44},[34,2052,140],{"class":40},[34,2054,245],{"class":44},[34,2056,2057],{"class":36,"line":159},[34,2058,2059],{"class":65},"  \u002F\u002F deep:true walks EVERY property of EVERY nested object to track them.\n",[34,2061,2062],{"class":36,"line":165},[34,2063,2064],{"class":65},"  \u002F\u002F For 1000×100 = 100,000 nested objects, this installs 100,000+ track calls.\n",[34,2066,2067],{"class":36,"line":171},[34,2068,2069],{"class":65},"  \u002F\u002F Every nested mutation triggers this callback — even irrelevant ones.\n",[34,2071,2072,2075],{"class":36,"line":177},[34,2073,2074],{"class":111},"  saveToServer",[34,2076,2077],{"class":44},"(newTree)\n",[34,2079,2080,2083,2085],{"class":36,"line":183},[34,2081,2082],{"class":44},"}, { deep: ",[34,2084,1361],{"class":104},[34,2086,1066],{"class":44},[34,2088,2089],{"class":36,"line":189},[34,2090,59],{"emptyLinePlaceholder":58},[34,2092,2093],{"class":36,"line":194},[34,2094,2095],{"class":65},"\u002F\u002F ✅ CORRECT: watch a specific path, not the whole tree\n",[34,2097,2098,2100],{"class":36,"line":200},[34,2099,987],{"class":111},[34,2101,1073],{"class":44},[34,2103,2104,2106,2108,2111,2114,2117],{"class":36,"line":206},[34,2105,1078],{"class":44},[34,2107,140],{"class":40},[34,2109,2110],{"class":44}," tree.nodes.",[34,2112,2113],{"class":104},"length",[34,2115,2116],{"class":44},",  ",[34,2118,2119],{"class":65},"\u002F\u002F only track the array length\n",[34,2121,2122,2124,2127,2129,2131],{"class":36,"line":212},[34,2123,1091],{"class":44},[34,2125,2126],{"class":282},"len",[34,2128,469],{"class":44},[34,2130,140],{"class":40},[34,2132,245],{"class":44},[34,2134,2135,2137,2139,2141,2144],{"class":36,"line":218},[34,2136,1110],{"class":44},[34,2138,254],{"class":111},[34,2140,115],{"class":44},[34,2142,2143],{"class":51},"'node count changed:'",[34,2145,2146],{"class":44},", len)\n",[34,2148,2149],{"class":36,"line":223},[34,2150,1132],{"class":44},[34,2152,2153],{"class":36,"line":229},[34,2154,121],{"class":44},[34,2156,2157],{"class":36,"line":248},[34,2158,59],{"emptyLinePlaceholder":58},[34,2160,2161],{"class":36,"line":268},[34,2162,2163],{"class":65},"\u002F\u002F ✅ CORRECT: watch a getter that returns a shallow snapshot\n",[34,2165,2166,2168],{"class":36,"line":298},[34,2167,987],{"class":111},[34,2169,1073],{"class":44},[34,2171,2172,2174,2176,2178,2180,2182,2184,2186,2189],{"class":36,"line":304},[34,2173,1078],{"class":44},[34,2175,140],{"class":40},[34,2177,2110],{"class":44},[34,2179,277],{"class":111},[34,2181,115],{"class":44},[34,2183,1665],{"class":282},[34,2185,286],{"class":40},[34,2187,2188],{"class":44}," n.id),  ",[34,2190,2191],{"class":65},"\u002F\u002F new array on each change\n",[34,2193,2194,2196,2199,2201,2203],{"class":36,"line":316},[34,2195,1091],{"class":44},[34,2197,2198],{"class":282},"ids",[34,2200,469],{"class":44},[34,2202,140],{"class":40},[34,2204,245],{"class":44},[34,2206,2207,2209,2211,2213,2216],{"class":36,"line":322},[34,2208,1110],{"class":44},[34,2210,254],{"class":111},[34,2212,115],{"class":44},[34,2214,2215],{"class":51},"'node ids changed:'",[34,2217,2218],{"class":44},", ids)\n",[34,2220,2221],{"class":36,"line":327},[34,2222,1132],{"class":44},[34,2224,2225],{"class":36,"line":333},[34,2226,121],{"class":44},[34,2228,2229],{"class":36,"line":345},[34,2230,59],{"emptyLinePlaceholder":58},[34,2232,2233],{"class":36,"line":363},[34,2234,2235],{"class":65},"\u002F\u002F ── Rule: deep:true is O(n) in object depth × breadth ──\n",[34,2237,2238],{"class":36,"line":1334},[34,2239,2240],{"class":65},"\u002F\u002F For forms (flat object, ~20 fields): fine, use deep.\n",[34,2242,2243],{"class":36,"line":1355},[34,2244,2245],{"class":65},"\u002F\u002F For trees\u002Ftables (1000+ nodes): never deep — watch specific paths instead.\n",[15,2247,2249],{"id":2248},"cleanup-race-condition-prevention-in-async-watchers","Cleanup — Race Condition Prevention in Async Watchers",[20,2251,2253],{"filename":2252,"language":23},"watch-cleanup.ts",[25,2254,2256],{"className":27,"code":2255,"language":23,"meta":29,"style":29},"import { ref, watch, type WatchCallback } from 'vue'\n\nconst searchQuery = ref('')\n\n\u002F\u002F ── onCleanup: cancel stale async work when source changes again ──\n\u002F\u002F The 3rd arg of the watch callback is an onCleanup function.\n\u002F\u002F Register a cleanup function that runs BEFORE the next invocation.\n\u002F\u002F Critical for preventing race conditions in debounced search.\nwatch(searchQuery, async (newQuery, oldQuery, onCleanup) => {\n  const controller = new AbortController()\n  const signal = controller.signal\n\n  \u002F\u002F Register cleanup — aborts the previous fetch if query changes again\n  onCleanup(() => controller.abort())\n\n  try {\n    const res = await fetch(`\u002Fapi\u002Fsearch?q=${newQuery}`, { signal })\n    const data = await res.json()\n    \u002F\u002F If controller was aborted (user typed again), this line is skipped\n    \u002F\u002F because the fetch threw an AbortError, caught below.\n    results.value = data\n  } catch (e) {\n    if (e instanceof DOMException && e.name === 'AbortError') {\n      return  \u002F\u002F expected — stale request was canceled\n    }\n    throw e    \u002F\u002F re-throw unexpected errors\n  }\n})\n\n\u002F\u002F ── Without cleanup: ─────────────────────────────────────\n\u002F\u002F If user types \"a\" then \"ab\" quickly:\n\u002F\u002F   1. fetch(\"a\") starts\n\u002F\u002F   2. fetch(\"ab\") starts\n\u002F\u002F   3. fetch(\"ab\") resolves first → results = [ab data]\n\u002F\u002F   4. fetch(\"a\") resolves last → results = [a data] ← WRONG, stale!\n\u002F\u002F Cleanup aborts the \"a\" request when \"ab\" starts → no stale overwrite.\n",[31,2257,2258,2274,2278,2295,2299,2304,2309,2314,2319,2350,2368,2380,2384,2389,2407,2411,2418,2444,2463,2468,2473,2483,2494,2522,2530,2535,2546,2550,2554,2558,2563,2568,2573,2578,2583,2588],{"__ignoreMap":29},[34,2259,2260,2262,2265,2267,2270,2272],{"class":36,"line":37},[34,2261,41],{"class":40},[34,2263,2264],{"class":44}," { ref, watch, ",[34,2266,387],{"class":40},[34,2268,2269],{"class":44}," WatchCallback } ",[34,2271,48],{"class":40},[34,2273,52],{"class":51},[34,2275,2276],{"class":36,"line":55},[34,2277,59],{"emptyLinePlaceholder":58},[34,2279,2280,2282,2285,2287,2289,2291,2293],{"class":36,"line":62},[34,2281,101],{"class":40},[34,2283,2284],{"class":104}," searchQuery",[34,2286,108],{"class":40},[34,2288,112],{"class":111},[34,2290,115],{"class":44},[34,2292,1415],{"class":51},[34,2294,121],{"class":44},[34,2296,2297],{"class":36,"line":69},[34,2298,59],{"emptyLinePlaceholder":58},[34,2300,2301],{"class":36,"line":75},[34,2302,2303],{"class":65},"\u002F\u002F ── onCleanup: cancel stale async work when source changes again ──\n",[34,2305,2306],{"class":36,"line":81},[34,2307,2308],{"class":65},"\u002F\u002F The 3rd arg of the watch callback is an onCleanup function.\n",[34,2310,2311],{"class":36,"line":87},[34,2312,2313],{"class":65},"\u002F\u002F Register a cleanup function that runs BEFORE the next invocation.\n",[34,2315,2316],{"class":36,"line":93},[34,2317,2318],{"class":65},"\u002F\u002F Critical for preventing race conditions in debounced search.\n",[34,2320,2321,2323,2326,2329,2331,2334,2336,2339,2341,2344,2346,2348],{"class":36,"line":98},[34,2322,987],{"class":111},[34,2324,2325],{"class":44},"(searchQuery, ",[34,2327,2328],{"class":40},"async",[34,2330,458],{"class":44},[34,2332,2333],{"class":282},"newQuery",[34,2335,551],{"class":44},[34,2337,2338],{"class":282},"oldQuery",[34,2340,551],{"class":44},[34,2342,2343],{"class":282},"onCleanup",[34,2345,469],{"class":44},[34,2347,140],{"class":40},[34,2349,245],{"class":44},[34,2351,2352,2354,2357,2359,2362,2365],{"class":36,"line":124},[34,2353,1743],{"class":40},[34,2355,2356],{"class":104}," controller",[34,2358,108],{"class":40},[34,2360,2361],{"class":40}," new",[34,2363,2364],{"class":111}," AbortController",[34,2366,2367],{"class":44},"()\n",[34,2369,2370,2372,2375,2377],{"class":36,"line":154},[34,2371,1743],{"class":40},[34,2373,2374],{"class":104}," signal",[34,2376,108],{"class":40},[34,2378,2379],{"class":44}," controller.signal\n",[34,2381,2382],{"class":36,"line":159},[34,2383,59],{"emptyLinePlaceholder":58},[34,2385,2386],{"class":36,"line":165},[34,2387,2388],{"class":65},"  \u002F\u002F Register cleanup — aborts the previous fetch if query changes again\n",[34,2390,2391,2394,2396,2398,2401,2404],{"class":36,"line":171},[34,2392,2393],{"class":111},"  onCleanup",[34,2395,137],{"class":44},[34,2397,140],{"class":40},[34,2399,2400],{"class":44}," controller.",[34,2402,2403],{"class":111},"abort",[34,2405,2406],{"class":44},"())\n",[34,2408,2409],{"class":36,"line":177},[34,2410,59],{"emptyLinePlaceholder":58},[34,2412,2413,2416],{"class":36,"line":183},[34,2414,2415],{"class":40},"  try",[34,2417,245],{"class":44},[34,2419,2420,2422,2425,2427,2430,2433,2435,2437,2439,2441],{"class":36,"line":189},[34,2421,791],{"class":40},[34,2423,2424],{"class":104}," res",[34,2426,108],{"class":40},[34,2428,2429],{"class":40}," await",[34,2431,2432],{"class":111}," fetch",[34,2434,115],{"class":44},[34,2436,1461],{"class":51},[34,2438,2333],{"class":44},[34,2440,1025],{"class":51},[34,2442,2443],{"class":44},", { signal })\n",[34,2445,2446,2448,2451,2453,2455,2458,2461],{"class":36,"line":194},[34,2447,791],{"class":40},[34,2449,2450],{"class":104}," data",[34,2452,108],{"class":40},[34,2454,2429],{"class":40},[34,2456,2457],{"class":44}," res.",[34,2459,2460],{"class":111},"json",[34,2462,2367],{"class":44},[34,2464,2465],{"class":36,"line":200},[34,2466,2467],{"class":65},"    \u002F\u002F If controller was aborted (user typed again), this line is skipped\n",[34,2469,2470],{"class":36,"line":206},[34,2471,2472],{"class":65},"    \u002F\u002F because the fetch threw an AbortError, caught below.\n",[34,2474,2475,2478,2480],{"class":36,"line":212},[34,2476,2477],{"class":44},"    results.value ",[34,2479,811],{"class":40},[34,2481,2482],{"class":44}," data\n",[34,2484,2485,2488,2491],{"class":36,"line":218},[34,2486,2487],{"class":44},"  } ",[34,2489,2490],{"class":40},"catch",[34,2492,2493],{"class":44}," (e) {\n",[34,2495,2496,2499,2502,2505,2508,2511,2514,2517,2520],{"class":36,"line":223},[34,2497,2498],{"class":40},"    if",[34,2500,2501],{"class":44}," (e ",[34,2503,2504],{"class":40},"instanceof",[34,2506,2507],{"class":111}," DOMException",[34,2509,2510],{"class":40}," &&",[34,2512,2513],{"class":44}," e.name ",[34,2515,2516],{"class":40},"===",[34,2518,2519],{"class":51}," 'AbortError'",[34,2521,476],{"class":44},[34,2523,2524,2527],{"class":36,"line":229},[34,2525,2526],{"class":40},"      return",[34,2528,2529],{"class":65},"  \u002F\u002F expected — stale request was canceled\n",[34,2531,2532],{"class":36,"line":248},[34,2533,2534],{"class":44},"    }\n",[34,2536,2537,2540,2543],{"class":36,"line":268},[34,2538,2539],{"class":40},"    throw",[34,2541,2542],{"class":44}," e    ",[34,2544,2545],{"class":65},"\u002F\u002F re-throw unexpected errors\n",[34,2547,2548],{"class":36,"line":298},[34,2549,1132],{"class":44},[34,2551,2552],{"class":36,"line":304},[34,2553,301],{"class":44},[34,2555,2556],{"class":36,"line":316},[34,2557,59],{"emptyLinePlaceholder":58},[34,2559,2560],{"class":36,"line":322},[34,2561,2562],{"class":65},"\u002F\u002F ── Without cleanup: ─────────────────────────────────────\n",[34,2564,2565],{"class":36,"line":327},[34,2566,2567],{"class":65},"\u002F\u002F If user types \"a\" then \"ab\" quickly:\n",[34,2569,2570],{"class":36,"line":333},[34,2571,2572],{"class":65},"\u002F\u002F   1. fetch(\"a\") starts\n",[34,2574,2575],{"class":36,"line":345},[34,2576,2577],{"class":65},"\u002F\u002F   2. fetch(\"ab\") starts\n",[34,2579,2580],{"class":36,"line":363},[34,2581,2582],{"class":65},"\u002F\u002F   3. fetch(\"ab\") resolves first → results = [ab data]\n",[34,2584,2585],{"class":36,"line":1334},[34,2586,2587],{"class":65},"\u002F\u002F   4. fetch(\"a\") resolves last → results = [a data] ← WRONG, stale!\n",[34,2589,2590],{"class":36,"line":1355},[34,2591,2592],{"class":65},"\u002F\u002F Cleanup aborts the \"a\" request when \"ab\" starts → no stale overwrite.\n",[15,2594,2596],{"id":2595},"immediate-watch-with-async-initialization","Immediate Watch with Async Initialization",[20,2598,2600],{"filename":2599,"language":23},"immediate-watch.ts",[25,2601,2603],{"className":27,"code":2602,"language":23,"meta":29,"style":29},"import { ref, watch, onMounted } from 'vue'\n\nconst userId = ref\u003Cnumber | null>(null)\n\n\u002F\u002F ── immediate: true — fires on setup AND on every change ──\n\u002F\u002F Useful for \"load data whenever the id changes, including initial load.\"\nwatch(\n  () => userId.value,\n  async (id, _oldId, onCleanup) => {\n    if (id === null) return  \u002F\u002F guard: don't fetch before id is set\n\n    onCleanup(() => controller?.abort())\n\n    const controller = new AbortController()\n    const res = await fetch(`\u002Fapi\u002Fusers\u002F${id}`, { signal: controller.signal })\n    userData.value = await res.json()\n  },\n  { immediate: true }\n)\n\n\u002F\u002F ── onMounted fires AFTER setup, but immediate watch fires DURING setup ──\n\u002F\u002F If the fetch needs DOM or component refs, use immediate:false + onMounted:\nonMounted(async () => {\n  \u002F\u002F DOM is ready, component is mounted — safe to measure, query elements\n  const rect = containerRef.value?.getBoundingClientRect()\n  await loadData(userId.value)\n})\n",[31,2604,2605,2616,2620,2649,2653,2658,2663,2669,2678,2703,2722,2726,2742,2746,2760,2784,2799,2803,2813,2817,2821,2826,2831,2847,2852,2869,2880],{"__ignoreMap":29},[34,2606,2607,2609,2612,2614],{"class":36,"line":37},[34,2608,41],{"class":40},[34,2610,2611],{"class":44}," { ref, watch, onMounted } ",[34,2613,48],{"class":40},[34,2615,52],{"class":51},[34,2617,2618],{"class":36,"line":55},[34,2619,59],{"emptyLinePlaceholder":58},[34,2621,2622,2624,2627,2629,2631,2633,2636,2639,2642,2644,2647],{"class":36,"line":62},[34,2623,101],{"class":40},[34,2625,2626],{"class":104}," userId",[34,2628,108],{"class":40},[34,2630,112],{"class":111},[34,2632,428],{"class":44},[34,2634,2635],{"class":104},"number",[34,2637,2638],{"class":40}," |",[34,2640,2641],{"class":104}," null",[34,2643,434],{"class":44},[34,2645,2646],{"class":104},"null",[34,2648,121],{"class":44},[34,2650,2651],{"class":36,"line":69},[34,2652,59],{"emptyLinePlaceholder":58},[34,2654,2655],{"class":36,"line":75},[34,2656,2657],{"class":65},"\u002F\u002F ── immediate: true — fires on setup AND on every change ──\n",[34,2659,2660],{"class":36,"line":81},[34,2661,2662],{"class":65},"\u002F\u002F Useful for \"load data whenever the id changes, including initial load.\"\n",[34,2664,2665,2667],{"class":36,"line":87},[34,2666,987],{"class":111},[34,2668,1073],{"class":44},[34,2670,2671,2673,2675],{"class":36,"line":93},[34,2672,1078],{"class":44},[34,2674,140],{"class":40},[34,2676,2677],{"class":44}," userId.value,\n",[34,2679,2680,2683,2685,2688,2690,2693,2695,2697,2699,2701],{"class":36,"line":98},[34,2681,2682],{"class":40},"  async",[34,2684,458],{"class":44},[34,2686,2687],{"class":282},"id",[34,2689,551],{"class":44},[34,2691,2692],{"class":282},"_oldId",[34,2694,551],{"class":44},[34,2696,2343],{"class":282},[34,2698,469],{"class":44},[34,2700,140],{"class":40},[34,2702,245],{"class":44},[34,2704,2705,2707,2710,2712,2714,2716,2719],{"class":36,"line":124},[34,2706,2498],{"class":40},[34,2708,2709],{"class":44}," (id ",[34,2711,2516],{"class":40},[34,2713,2641],{"class":104},[34,2715,469],{"class":44},[34,2717,2718],{"class":40},"return",[34,2720,2721],{"class":65},"  \u002F\u002F guard: don't fetch before id is set\n",[34,2723,2724],{"class":36,"line":154},[34,2725,59],{"emptyLinePlaceholder":58},[34,2727,2728,2731,2733,2735,2738,2740],{"class":36,"line":159},[34,2729,2730],{"class":111},"    onCleanup",[34,2732,137],{"class":44},[34,2734,140],{"class":40},[34,2736,2737],{"class":44}," controller?.",[34,2739,2403],{"class":111},[34,2741,2406],{"class":44},[34,2743,2744],{"class":36,"line":165},[34,2745,59],{"emptyLinePlaceholder":58},[34,2747,2748,2750,2752,2754,2756,2758],{"class":36,"line":171},[34,2749,791],{"class":40},[34,2751,2356],{"class":104},[34,2753,108],{"class":40},[34,2755,2361],{"class":40},[34,2757,2364],{"class":111},[34,2759,2367],{"class":44},[34,2761,2762,2764,2766,2768,2770,2772,2774,2777,2779,2781],{"class":36,"line":177},[34,2763,791],{"class":40},[34,2765,2424],{"class":104},[34,2767,108],{"class":40},[34,2769,2429],{"class":40},[34,2771,2432],{"class":111},[34,2773,115],{"class":44},[34,2775,2776],{"class":51},"`\u002Fapi\u002Fusers\u002F${",[34,2778,2687],{"class":44},[34,2780,1025],{"class":51},[34,2782,2783],{"class":44},", { signal: controller.signal })\n",[34,2785,2786,2789,2791,2793,2795,2797],{"class":36,"line":183},[34,2787,2788],{"class":44},"    userData.value ",[34,2790,811],{"class":40},[34,2792,2429],{"class":40},[34,2794,2457],{"class":44},[34,2796,2460],{"class":111},[34,2798,2367],{"class":44},[34,2800,2801],{"class":36,"line":189},[34,2802,764],{"class":44},[34,2804,2805,2808,2810],{"class":36,"line":194},[34,2806,2807],{"class":44},"  { immediate: ",[34,2809,1361],{"class":104},[34,2811,2812],{"class":44}," }\n",[34,2814,2815],{"class":36,"line":200},[34,2816,121],{"class":44},[34,2818,2819],{"class":36,"line":206},[34,2820,59],{"emptyLinePlaceholder":58},[34,2822,2823],{"class":36,"line":212},[34,2824,2825],{"class":65},"\u002F\u002F ── onMounted fires AFTER setup, but immediate watch fires DURING setup ──\n",[34,2827,2828],{"class":36,"line":218},[34,2829,2830],{"class":65},"\u002F\u002F If the fetch needs DOM or component refs, use immediate:false + onMounted:\n",[34,2832,2833,2836,2838,2840,2843,2845],{"class":36,"line":223},[34,2834,2835],{"class":111},"onMounted",[34,2837,115],{"class":44},[34,2839,2328],{"class":40},[34,2841,2842],{"class":44}," () ",[34,2844,140],{"class":40},[34,2846,245],{"class":44},[34,2848,2849],{"class":36,"line":229},[34,2850,2851],{"class":65},"  \u002F\u002F DOM is ready, component is mounted — safe to measure, query elements\n",[34,2853,2854,2856,2859,2861,2864,2867],{"class":36,"line":248},[34,2855,1743],{"class":40},[34,2857,2858],{"class":104}," rect",[34,2860,108],{"class":40},[34,2862,2863],{"class":44}," containerRef.value?.",[34,2865,2866],{"class":111},"getBoundingClientRect",[34,2868,2367],{"class":44},[34,2870,2871,2874,2877],{"class":36,"line":268},[34,2872,2873],{"class":40},"  await",[34,2875,2876],{"class":111}," loadData",[34,2878,2879],{"class":44},"(userId.value)\n",[34,2881,2882],{"class":36,"line":298},[34,2883,301],{"class":44},[15,2885,2887],{"id":2886},"computed-cache-invalidation-stale-closure-trap","Computed Cache Invalidation — Stale Closure Trap",[20,2889,2891],{"filename":2890,"language":23},"stale-closure.ts",[25,2892,2894],{"className":27,"code":2893,"language":23,"meta":29,"style":29},"import { ref, computed, watch } from 'vue'\n\nconst multiplier = ref(2)\nconst items = ref([1, 2, 3])\n\n\u002F\u002F ❌ WRONG: computed with external mutable dependency\nconst bad = computed(() => {\n  \u002F\u002F multiplier.value is tracked, but externalVar is NOT — changes to it\n  \u002F\u002F won't re-run this getter, producing stale results.\n  const externalVar = someExternalStore.getState()  \u002F\u002F not reactive\n  return items.value.map(x => x * multiplier.value * externalVar)\n})\n\n\u002F\u002F ✅ CORRECT: all dependencies must be reactive\nconst good = computed(() => {\n  \u002F\u002F everything read here is reactive — getter re-runs when any changes\n  return items.value.map(x => x * multiplier.value)\n})\n\n\u002F\u002F ── Watchers don't have this problem — they're explicit about sources ──\nwatch([items, multiplier], () => {\n  \u002F\u002F both items and multiplier are tracked as sources\n  const result = items.value.map(x => x * multiplier.value)\n})\n",[31,2895,2896,2907,2911,2928,2952,2956,2961,2978,2983,2988,3009,3036,3040,3044,3049,3066,3071,3092,3096,3100,3105,3116,3121,3146],{"__ignoreMap":29},[34,2897,2898,2900,2903,2905],{"class":36,"line":37},[34,2899,41],{"class":40},[34,2901,2902],{"class":44}," { ref, computed, watch } ",[34,2904,48],{"class":40},[34,2906,52],{"class":51},[34,2908,2909],{"class":36,"line":55},[34,2910,59],{"emptyLinePlaceholder":58},[34,2912,2913,2915,2918,2920,2922,2924,2926],{"class":36,"line":62},[34,2914,101],{"class":40},[34,2916,2917],{"class":104}," multiplier",[34,2919,108],{"class":40},[34,2921,112],{"class":111},[34,2923,115],{"class":44},[34,2925,554],{"class":104},[34,2927,121],{"class":44},[34,2929,2930,2932,2934,2936,2938,2940,2942,2944,2946,2948,2950],{"class":36,"line":69},[34,2931,101],{"class":40},[34,2933,538],{"class":104},[34,2935,108],{"class":40},[34,2937,112],{"class":111},[34,2939,545],{"class":44},[34,2941,548],{"class":104},[34,2943,551],{"class":44},[34,2945,554],{"class":104},[34,2947,551],{"class":44},[34,2949,559],{"class":104},[34,2951,562],{"class":44},[34,2953,2954],{"class":36,"line":75},[34,2955,59],{"emptyLinePlaceholder":58},[34,2957,2958],{"class":36,"line":81},[34,2959,2960],{"class":65},"\u002F\u002F ❌ WRONG: computed with external mutable dependency\n",[34,2962,2963,2965,2968,2970,2972,2974,2976],{"class":36,"line":87},[34,2964,101],{"class":40},[34,2966,2967],{"class":104}," bad",[34,2969,108],{"class":40},[34,2971,134],{"class":111},[34,2973,137],{"class":44},[34,2975,140],{"class":40},[34,2977,245],{"class":44},[34,2979,2980],{"class":36,"line":93},[34,2981,2982],{"class":65},"  \u002F\u002F multiplier.value is tracked, but externalVar is NOT — changes to it\n",[34,2984,2985],{"class":36,"line":98},[34,2986,2987],{"class":65},"  \u002F\u002F won't re-run this getter, producing stale results.\n",[34,2989,2990,2992,2995,2997,3000,3003,3006],{"class":36,"line":124},[34,2991,1743],{"class":40},[34,2993,2994],{"class":104}," externalVar",[34,2996,108],{"class":40},[34,2998,2999],{"class":44}," someExternalStore.",[34,3001,3002],{"class":111},"getState",[34,3004,3005],{"class":44},"()  ",[34,3007,3008],{"class":65},"\u002F\u002F not reactive\n",[34,3010,3011,3013,3016,3018,3020,3022,3024,3026,3028,3031,3033],{"class":36,"line":154},[34,3012,271],{"class":40},[34,3014,3015],{"class":44}," items.value.",[34,3017,277],{"class":111},[34,3019,115],{"class":44},[34,3021,283],{"class":282},[34,3023,286],{"class":40},[34,3025,289],{"class":44},[34,3027,146],{"class":40},[34,3029,3030],{"class":44}," multiplier.value ",[34,3032,146],{"class":40},[34,3034,3035],{"class":44}," externalVar)\n",[34,3037,3038],{"class":36,"line":159},[34,3039,301],{"class":44},[34,3041,3042],{"class":36,"line":165},[34,3043,59],{"emptyLinePlaceholder":58},[34,3045,3046],{"class":36,"line":171},[34,3047,3048],{"class":65},"\u002F\u002F ✅ CORRECT: all dependencies must be reactive\n",[34,3050,3051,3053,3056,3058,3060,3062,3064],{"class":36,"line":177},[34,3052,101],{"class":40},[34,3054,3055],{"class":104}," good",[34,3057,108],{"class":40},[34,3059,134],{"class":111},[34,3061,137],{"class":44},[34,3063,140],{"class":40},[34,3065,245],{"class":44},[34,3067,3068],{"class":36,"line":183},[34,3069,3070],{"class":65},"  \u002F\u002F everything read here is reactive — getter re-runs when any changes\n",[34,3072,3073,3075,3077,3079,3081,3083,3085,3087,3089],{"class":36,"line":189},[34,3074,271],{"class":40},[34,3076,3015],{"class":44},[34,3078,277],{"class":111},[34,3080,115],{"class":44},[34,3082,283],{"class":282},[34,3084,286],{"class":40},[34,3086,289],{"class":44},[34,3088,146],{"class":40},[34,3090,3091],{"class":44}," multiplier.value)\n",[34,3093,3094],{"class":36,"line":194},[34,3095,301],{"class":44},[34,3097,3098],{"class":36,"line":200},[34,3099,59],{"emptyLinePlaceholder":58},[34,3101,3102],{"class":36,"line":206},[34,3103,3104],{"class":65},"\u002F\u002F ── Watchers don't have this problem — they're explicit about sources ──\n",[34,3106,3107,3109,3112,3114],{"class":36,"line":212},[34,3108,987],{"class":111},[34,3110,3111],{"class":44},"([items, multiplier], () ",[34,3113,140],{"class":40},[34,3115,245],{"class":44},[34,3117,3118],{"class":36,"line":218},[34,3119,3120],{"class":65},"  \u002F\u002F both items and multiplier are tracked as sources\n",[34,3122,3123,3125,3128,3130,3132,3134,3136,3138,3140,3142,3144],{"class":36,"line":223},[34,3124,1743],{"class":40},[34,3126,3127],{"class":104}," result",[34,3129,108],{"class":40},[34,3131,3015],{"class":44},[34,3133,277],{"class":111},[34,3135,115],{"class":44},[34,3137,283],{"class":282},[34,3139,286],{"class":40},[34,3141,289],{"class":44},[34,3143,146],{"class":40},[34,3145,3091],{"class":44},[34,3147,3148],{"class":36,"line":229},[34,3149,301],{"class":44},[15,3151,3153],{"id":3152},"tips-tricks","💡 Tips & Tricks",[20,3155,3157],{"filename":3156,"language":23},"tips.ts",[25,3158,3160],{"className":27,"code":3159,"language":23,"meta":29,"style":29},"import { ref, computed, watch, watchEffect, onWatcherCleanup } from 'vue'\n\n\u002F\u002F ── 1. onWatcherCleanup (3.5+) — cleanup without the 3rd callback param ──\nwatch(searchQuery, async (q) => {\n  const controller = new AbortController()\n  onWatcherCleanup(() => controller.abort())  \u002F\u002F same as onCleanup, cleaner\n  await fetch(`\u002Fapi?q=${q}`, { signal: controller.signal })\n})\n\n\u002F\u002F ── 2. Debounced watcher — combine watch + setTimeout ──\nfunction debounceWatch(source, cb, delay = 300) {\n  let timer: ReturnType\u003Ctypeof setTimeout>\n  watch(source, (val) => {\n    clearTimeout(timer)\n    timer = setTimeout(() => cb(val), delay)\n  })\n}\n\n\u002F\u002F ── 3. Computed chaining — one computed can depend on another ──\nconst base = computed(() => items.value.length)\nconst derived = computed(() => base.value * 2)  \u002F\u002F re-runs when base is dirty\n\n\u002F\u002F ── 4. watchEffect returns a stop function — manual teardown ──\nconst stop = watchEffect(() => { \u002F* ... *\u002F })\nstop()  \u002F\u002F stops the effect, runs any registered cleanup\n\n\u002F\u002F ── 5. once: true (3.4+) — stop after first invocation ──\nwatch(eventSource, handler, { once: true })  \u002F\u002F fires once, auto-stops\n",[31,3161,3162,3173,3177,3182,3201,3215,3234,3251,3255,3259,3264,3292,3313,3330,3338,3358,3363,3367,3371,3376,3397,3424,3428,3433,3456,3466,3470,3475],{"__ignoreMap":29},[34,3163,3164,3166,3169,3171],{"class":36,"line":37},[34,3165,41],{"class":40},[34,3167,3168],{"class":44}," { ref, computed, watch, watchEffect, onWatcherCleanup } ",[34,3170,48],{"class":40},[34,3172,52],{"class":51},[34,3174,3175],{"class":36,"line":55},[34,3176,59],{"emptyLinePlaceholder":58},[34,3178,3179],{"class":36,"line":62},[34,3180,3181],{"class":65},"\u002F\u002F ── 1. onWatcherCleanup (3.5+) — cleanup without the 3rd callback param ──\n",[34,3183,3184,3186,3188,3190,3192,3195,3197,3199],{"class":36,"line":69},[34,3185,987],{"class":111},[34,3187,2325],{"class":44},[34,3189,2328],{"class":40},[34,3191,458],{"class":44},[34,3193,3194],{"class":282},"q",[34,3196,469],{"class":44},[34,3198,140],{"class":40},[34,3200,245],{"class":44},[34,3202,3203,3205,3207,3209,3211,3213],{"class":36,"line":75},[34,3204,1743],{"class":40},[34,3206,2356],{"class":104},[34,3208,108],{"class":40},[34,3210,2361],{"class":40},[34,3212,2364],{"class":111},[34,3214,2367],{"class":44},[34,3216,3217,3220,3222,3224,3226,3228,3231],{"class":36,"line":81},[34,3218,3219],{"class":111},"  onWatcherCleanup",[34,3221,137],{"class":44},[34,3223,140],{"class":40},[34,3225,2400],{"class":44},[34,3227,2403],{"class":111},[34,3229,3230],{"class":44},"())  ",[34,3232,3233],{"class":65},"\u002F\u002F same as onCleanup, cleaner\n",[34,3235,3236,3238,3240,3242,3245,3247,3249],{"class":36,"line":87},[34,3237,2873],{"class":40},[34,3239,2432],{"class":111},[34,3241,115],{"class":44},[34,3243,3244],{"class":51},"`\u002Fapi?q=${",[34,3246,3194],{"class":44},[34,3248,1025],{"class":51},[34,3250,2783],{"class":44},[34,3252,3253],{"class":36,"line":93},[34,3254,301],{"class":44},[34,3256,3257],{"class":36,"line":98},[34,3258,59],{"emptyLinePlaceholder":58},[34,3260,3261],{"class":36,"line":124},[34,3262,3263],{"class":65},"\u002F\u002F ── 2. Debounced watcher — combine watch + setTimeout ──\n",[34,3265,3266,3268,3271,3273,3275,3277,3280,3282,3285,3287,3290],{"class":36,"line":154},[34,3267,422],{"class":40},[34,3269,3270],{"class":111}," debounceWatch",[34,3272,115],{"class":44},[34,3274,437],{"class":282},[34,3276,551],{"class":44},[34,3278,3279],{"class":282},"cb",[34,3281,551],{"class":44},[34,3283,3284],{"class":282},"delay",[34,3286,108],{"class":40},[34,3288,3289],{"class":104}," 300",[34,3291,476],{"class":44},[34,3293,3294,3297,3300,3302,3305,3307,3310],{"class":36,"line":159},[34,3295,3296],{"class":40},"  let",[34,3298,3299],{"class":44}," timer",[34,3301,440],{"class":40},[34,3303,3304],{"class":111}," ReturnType",[34,3306,428],{"class":44},[34,3308,3309],{"class":40},"typeof",[34,3311,3312],{"class":44}," setTimeout>\n",[34,3314,3315,3318,3321,3324,3326,3328],{"class":36,"line":165},[34,3316,3317],{"class":111},"  watch",[34,3319,3320],{"class":44},"(source, (",[34,3322,3323],{"class":282},"val",[34,3325,469],{"class":44},[34,3327,140],{"class":40},[34,3329,245],{"class":44},[34,3331,3332,3335],{"class":36,"line":171},[34,3333,3334],{"class":111},"    clearTimeout",[34,3336,3337],{"class":44},"(timer)\n",[34,3339,3340,3343,3345,3348,3350,3352,3355],{"class":36,"line":177},[34,3341,3342],{"class":44},"    timer ",[34,3344,811],{"class":40},[34,3346,3347],{"class":111}," setTimeout",[34,3349,137],{"class":44},[34,3351,140],{"class":40},[34,3353,3354],{"class":111}," cb",[34,3356,3357],{"class":44},"(val), delay)\n",[34,3359,3360],{"class":36,"line":183},[34,3361,3362],{"class":44},"  })\n",[34,3364,3365],{"class":36,"line":189},[34,3366,522],{"class":44},[34,3368,3369],{"class":36,"line":194},[34,3370,59],{"emptyLinePlaceholder":58},[34,3372,3373],{"class":36,"line":200},[34,3374,3375],{"class":65},"\u002F\u002F ── 3. Computed chaining — one computed can depend on another ──\n",[34,3377,3378,3380,3383,3385,3387,3389,3391,3393,3395],{"class":36,"line":206},[34,3379,101],{"class":40},[34,3381,3382],{"class":104}," base",[34,3384,108],{"class":40},[34,3386,134],{"class":111},[34,3388,137],{"class":44},[34,3390,140],{"class":40},[34,3392,3015],{"class":44},[34,3394,2113],{"class":104},[34,3396,121],{"class":44},[34,3398,3399,3401,3404,3406,3408,3410,3412,3415,3417,3419,3421],{"class":36,"line":212},[34,3400,101],{"class":40},[34,3402,3403],{"class":104}," derived",[34,3405,108],{"class":40},[34,3407,134],{"class":111},[34,3409,137],{"class":44},[34,3411,140],{"class":40},[34,3413,3414],{"class":44}," base.value ",[34,3416,146],{"class":40},[34,3418,149],{"class":104},[34,3420,262],{"class":44},[34,3422,3423],{"class":65},"\u002F\u002F re-runs when base is dirty\n",[34,3425,3426],{"class":36,"line":218},[34,3427,59],{"emptyLinePlaceholder":58},[34,3429,3430],{"class":36,"line":223},[34,3431,3432],{"class":65},"\u002F\u002F ── 4. watchEffect returns a stop function — manual teardown ──\n",[34,3434,3435,3437,3440,3442,3445,3447,3449,3451,3454],{"class":36,"line":229},[34,3436,101],{"class":40},[34,3438,3439],{"class":104}," stop",[34,3441,108],{"class":40},[34,3443,3444],{"class":111}," watchEffect",[34,3446,137],{"class":44},[34,3448,140],{"class":40},[34,3450,1346],{"class":44},[34,3452,3453],{"class":65},"\u002F* ... *\u002F",[34,3455,1066],{"class":44},[34,3457,3458,3461,3463],{"class":36,"line":248},[34,3459,3460],{"class":111},"stop",[34,3462,3005],{"class":44},[34,3464,3465],{"class":65},"\u002F\u002F stops the effect, runs any registered cleanup\n",[34,3467,3468],{"class":36,"line":268},[34,3469,59],{"emptyLinePlaceholder":58},[34,3471,3472],{"class":36,"line":298},[34,3473,3474],{"class":65},"\u002F\u002F ── 5. once: true (3.4+) — stop after first invocation ──\n",[34,3476,3477,3479,3482,3484,3486],{"class":36,"line":304},[34,3478,987],{"class":111},[34,3480,3481],{"class":44},"(eventSource, handler, { once: ",[34,3483,1361],{"class":104},[34,3485,1552],{"class":44},[34,3487,3488],{"class":65},"\u002F\u002F fires once, auto-stops\n",[15,3490,3492],{"id":3491},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,3494,3496],{"filename":3495,"language":23},"edge-cases.ts",[25,3497,3499],{"className":27,"code":3498,"language":23,"meta":29,"style":29},"import { ref, reactive, computed, watch } from 'vue'\n\n\u002F\u002F ── 1. Computed returning objects — same reference → watcher doesn't fire ──\nconst state = reactive({ list: [1, 2, 3] })\nconst snapshot = computed(() => state.list)\nwatch(snapshot, (n, o) => { \u002F* fires? *\u002F })\nstate.list.push(4)\n\u002F\u002F If computed returns the same array reference (push mutates in place),\n\u002F\u002F the watcher sees the same reference and does NOT fire.\n\u002F\u002F Fix: return a NEW array: computed(() => [...state.list])\n\n\u002F\u002F ── 2. watch on reactive object — old === new (same reference) ──\nconst obj = reactive({ a: 1 })\nwatch(obj, (newVal, oldVal) => {\n  console.log(newVal === oldVal)  \u002F\u002F true — reactive objects are mutated in place\n  \u002F\u002F To get a true snapshot, use a getter: () => ({ ...obj })\n\n\u002F\u002F ── 3. Sync flush + mutation = infinite loop ──\nwatch(count, (n) => {\n  count.value = n + 1  \u002F\u002F sync flush: immediately triggers watch again → stack overflow\n}, { flush: 'sync' })\n\u002F\u002F Fix: never mutate the source inside a sync watcher.\n\n\u002F\u002F ── 4. Computed getter with side effects ──\nconst bad = computed(() => {\n  api.log('computed read')  \u002F\u002F side effect in getter — anti-pattern\n  return state.value * 2\n})\n\u002F\u002F Getters should be pure. Side effects belong in watch\u002FwatchEffect.\n\n\u002F\u002F ── 5. watch immediate + async — first run fires before onMounted ──\n\u002F\u002F If the callback touches template refs, they're null on the first (immediate) run.\n\u002F\u002F Fix: guard with if (!ref.value) return, or use onMounted instead of immediate.\n\n\u002F\u002F ── 6. Multiple watchers fire in creation order, not dependency order ──\n\u002F\u002F Watch A depends on X, Watch B depends on A. When X changes:\n\u002F\u002F   A fires first, then B fires (A's update propagated to B). ← correct\n\u002F\u002F But if A and B both depend on X (no chain), order is creation order.\n",[31,3500,3501,3512,3516,3521,3548,3566,3591,3606,3611,3616,3621,3625,3630,3648,3667,3684,3689,3693,3698,3712,3731,3739,3744,3748,3753,3769,3786,3798,3802,3807,3811,3816,3821,3826,3830,3835,3840,3845],{"__ignoreMap":29},[34,3502,3503,3505,3508,3510],{"class":36,"line":37},[34,3504,41],{"class":40},[34,3506,3507],{"class":44}," { ref, reactive, computed, watch } ",[34,3509,48],{"class":40},[34,3511,52],{"class":51},[34,3513,3514],{"class":36,"line":55},[34,3515,59],{"emptyLinePlaceholder":58},[34,3517,3518],{"class":36,"line":62},[34,3519,3520],{"class":65},"\u002F\u002F ── 1. Computed returning objects — same reference → watcher doesn't fire ──\n",[34,3522,3523,3525,3528,3530,3532,3535,3537,3539,3541,3543,3545],{"class":36,"line":69},[34,3524,101],{"class":40},[34,3526,3527],{"class":104}," state",[34,3529,108],{"class":40},[34,3531,1052],{"class":111},[34,3533,3534],{"class":44},"({ list: [",[34,3536,548],{"class":104},[34,3538,551],{"class":44},[34,3540,554],{"class":104},[34,3542,551],{"class":44},[34,3544,559],{"class":104},[34,3546,3547],{"class":44},"] })\n",[34,3549,3550,3552,3555,3557,3559,3561,3563],{"class":36,"line":75},[34,3551,101],{"class":40},[34,3553,3554],{"class":104}," snapshot",[34,3556,108],{"class":40},[34,3558,134],{"class":111},[34,3560,137],{"class":44},[34,3562,140],{"class":40},[34,3564,3565],{"class":44}," state.list)\n",[34,3567,3568,3570,3573,3575,3577,3580,3582,3584,3586,3589],{"class":36,"line":81},[34,3569,987],{"class":111},[34,3571,3572],{"class":44},"(snapshot, (",[34,3574,1665],{"class":282},[34,3576,551],{"class":44},[34,3578,3579],{"class":282},"o",[34,3581,469],{"class":44},[34,3583,140],{"class":40},[34,3585,1346],{"class":44},[34,3587,3588],{"class":65},"\u002F* fires? *\u002F",[34,3590,1066],{"class":44},[34,3592,3593,3596,3599,3601,3604],{"class":36,"line":87},[34,3594,3595],{"class":44},"state.list.",[34,3597,3598],{"class":111},"push",[34,3600,115],{"class":44},[34,3602,3603],{"class":104},"4",[34,3605,121],{"class":44},[34,3607,3608],{"class":36,"line":93},[34,3609,3610],{"class":65},"\u002F\u002F If computed returns the same array reference (push mutates in place),\n",[34,3612,3613],{"class":36,"line":98},[34,3614,3615],{"class":65},"\u002F\u002F the watcher sees the same reference and does NOT fire.\n",[34,3617,3618],{"class":36,"line":124},[34,3619,3620],{"class":65},"\u002F\u002F Fix: return a NEW array: computed(() => [...state.list])\n",[34,3622,3623],{"class":36,"line":154},[34,3624,59],{"emptyLinePlaceholder":58},[34,3626,3627],{"class":36,"line":159},[34,3628,3629],{"class":65},"\u002F\u002F ── 2. watch on reactive object — old === new (same reference) ──\n",[34,3631,3632,3634,3637,3639,3641,3644,3646],{"class":36,"line":165},[34,3633,101],{"class":40},[34,3635,3636],{"class":104}," obj",[34,3638,108],{"class":40},[34,3640,1052],{"class":111},[34,3642,3643],{"class":44},"({ a: ",[34,3645,548],{"class":104},[34,3647,1066],{"class":44},[34,3649,3650,3652,3655,3657,3659,3661,3663,3665],{"class":36,"line":171},[34,3651,987],{"class":111},[34,3653,3654],{"class":44},"(obj, (",[34,3656,993],{"class":282},[34,3658,551],{"class":44},[34,3660,998],{"class":282},[34,3662,469],{"class":44},[34,3664,140],{"class":40},[34,3666,245],{"class":44},[34,3668,3669,3671,3673,3676,3678,3681],{"class":36,"line":177},[34,3670,251],{"class":44},[34,3672,254],{"class":111},[34,3674,3675],{"class":44},"(newVal ",[34,3677,2516],{"class":40},[34,3679,3680],{"class":44}," oldVal)  ",[34,3682,3683],{"class":65},"\u002F\u002F true — reactive objects are mutated in place\n",[34,3685,3686],{"class":36,"line":183},[34,3687,3688],{"class":65},"  \u002F\u002F To get a true snapshot, use a getter: () => ({ ...obj })\n",[34,3690,3691],{"class":36,"line":189},[34,3692,59],{"emptyLinePlaceholder":58},[34,3694,3695],{"class":36,"line":194},[34,3696,3697],{"class":65},"\u002F\u002F ── 3. Sync flush + mutation = infinite loop ──\n",[34,3699,3700,3702,3704,3706,3708,3710],{"class":36,"line":200},[34,3701,987],{"class":111},[34,3703,990],{"class":44},[34,3705,1665],{"class":282},[34,3707,469],{"class":44},[34,3709,140],{"class":40},[34,3711,245],{"class":44},[34,3713,3714,3717,3719,3722,3725,3728],{"class":36,"line":206},[34,3715,3716],{"class":44},"  count.value ",[34,3718,811],{"class":40},[34,3720,3721],{"class":44}," n ",[34,3723,3724],{"class":40},"+",[34,3726,3727],{"class":104}," 1",[34,3729,3730],{"class":65},"  \u002F\u002F sync flush: immediately triggers watch again → stack overflow\n",[34,3732,3733,3735,3737],{"class":36,"line":212},[34,3734,1695],{"class":44},[34,3736,1843],{"class":51},[34,3738,1066],{"class":44},[34,3740,3741],{"class":36,"line":218},[34,3742,3743],{"class":65},"\u002F\u002F Fix: never mutate the source inside a sync watcher.\n",[34,3745,3746],{"class":36,"line":223},[34,3747,59],{"emptyLinePlaceholder":58},[34,3749,3750],{"class":36,"line":229},[34,3751,3752],{"class":65},"\u002F\u002F ── 4. Computed getter with side effects ──\n",[34,3754,3755,3757,3759,3761,3763,3765,3767],{"class":36,"line":248},[34,3756,101],{"class":40},[34,3758,2967],{"class":104},[34,3760,108],{"class":40},[34,3762,134],{"class":111},[34,3764,137],{"class":44},[34,3766,140],{"class":40},[34,3768,245],{"class":44},[34,3770,3771,3774,3776,3778,3781,3783],{"class":36,"line":268},[34,3772,3773],{"class":44},"  api.",[34,3775,254],{"class":111},[34,3777,115],{"class":44},[34,3779,3780],{"class":51},"'computed read'",[34,3782,262],{"class":44},[34,3784,3785],{"class":65},"\u002F\u002F side effect in getter — anti-pattern\n",[34,3787,3788,3790,3793,3795],{"class":36,"line":298},[34,3789,271],{"class":40},[34,3791,3792],{"class":44}," state.value ",[34,3794,146],{"class":40},[34,3796,3797],{"class":104}," 2\n",[34,3799,3800],{"class":36,"line":304},[34,3801,301],{"class":44},[34,3803,3804],{"class":36,"line":316},[34,3805,3806],{"class":65},"\u002F\u002F Getters should be pure. Side effects belong in watch\u002FwatchEffect.\n",[34,3808,3809],{"class":36,"line":322},[34,3810,59],{"emptyLinePlaceholder":58},[34,3812,3813],{"class":36,"line":327},[34,3814,3815],{"class":65},"\u002F\u002F ── 5. watch immediate + async — first run fires before onMounted ──\n",[34,3817,3818],{"class":36,"line":333},[34,3819,3820],{"class":65},"\u002F\u002F If the callback touches template refs, they're null on the first (immediate) run.\n",[34,3822,3823],{"class":36,"line":345},[34,3824,3825],{"class":65},"\u002F\u002F Fix: guard with if (!ref.value) return, or use onMounted instead of immediate.\n",[34,3827,3828],{"class":36,"line":363},[34,3829,59],{"emptyLinePlaceholder":58},[34,3831,3832],{"class":36,"line":1334},[34,3833,3834],{"class":65},"\u002F\u002F ── 6. Multiple watchers fire in creation order, not dependency order ──\n",[34,3836,3837],{"class":36,"line":1355},[34,3838,3839],{"class":65},"\u002F\u002F Watch A depends on X, Watch B depends on A. When X changes:\n",[34,3841,3842],{"class":36,"line":1370},[34,3843,3844],{"class":65},"\u002F\u002F   A fires first, then B fires (A's update propagated to B). ← correct\n",[34,3846,3848],{"class":36,"line":3847},38,[34,3849,3850],{"class":65},"\u002F\u002F But if A and B both depend on X (no chain), order is creation order.\n",[15,3852,3854],{"id":3853},"spot-the-bug","🧠 Spot the Bug",[3856,3857,3858],"p",{},"A search feature shows stale results when the user types fast.",[20,3860,3862],{"filename":3861,"language":23},"RaceBug.ts",[25,3863,3865],{"className":27,"code":3864,"language":23,"meta":29,"style":29},"import { ref, watch } from 'vue'\n\nconst query = ref('')\nconst results = ref([])\n\nwatch(query, async (q) => {\n  const res = await fetch(`\u002Fapi\u002Fsearch?q=${q}`)\n  results.value = await res.json()\n})\n",[31,3866,3867,3878,3882,3898,3912,3916,3935,3957,3972],{"__ignoreMap":29},[34,3868,3869,3871,3874,3876],{"class":36,"line":37},[34,3870,41],{"class":40},[34,3872,3873],{"class":44}," { ref, watch } ",[34,3875,48],{"class":40},[34,3877,52],{"class":51},[34,3879,3880],{"class":36,"line":55},[34,3881,59],{"emptyLinePlaceholder":58},[34,3883,3884,3886,3888,3890,3892,3894,3896],{"class":36,"line":62},[34,3885,101],{"class":40},[34,3887,1406],{"class":104},[34,3889,108],{"class":40},[34,3891,112],{"class":111},[34,3893,115],{"class":44},[34,3895,1415],{"class":51},[34,3897,121],{"class":44},[34,3899,3900,3902,3905,3907,3909],{"class":36,"line":69},[34,3901,101],{"class":40},[34,3903,3904],{"class":104}," results",[34,3906,108],{"class":40},[34,3908,112],{"class":111},[34,3910,3911],{"class":44},"([])\n",[34,3913,3914],{"class":36,"line":75},[34,3915,59],{"emptyLinePlaceholder":58},[34,3917,3918,3920,3923,3925,3927,3929,3931,3933],{"class":36,"line":81},[34,3919,987],{"class":111},[34,3921,3922],{"class":44},"(query, ",[34,3924,2328],{"class":40},[34,3926,458],{"class":44},[34,3928,3194],{"class":282},[34,3930,469],{"class":44},[34,3932,140],{"class":40},[34,3934,245],{"class":44},[34,3936,3937,3939,3941,3943,3945,3947,3949,3951,3953,3955],{"class":36,"line":87},[34,3938,1743],{"class":40},[34,3940,2424],{"class":104},[34,3942,108],{"class":40},[34,3944,2429],{"class":40},[34,3946,2432],{"class":111},[34,3948,115],{"class":44},[34,3950,1461],{"class":51},[34,3952,3194],{"class":44},[34,3954,1025],{"class":51},[34,3956,121],{"class":44},[34,3958,3959,3962,3964,3966,3968,3970],{"class":36,"line":93},[34,3960,3961],{"class":44},"  results.value ",[34,3963,811],{"class":40},[34,3965,2429],{"class":40},[34,3967,2457],{"class":44},[34,3969,2460],{"class":111},[34,3971,2367],{"class":44},[34,3973,3974],{"class":36,"line":98},[34,3975,301],{"class":44},[3977,3978,3979,3983,3991,3998,4193],"details",{},[3980,3981,3982],"summary",{},"Answer",[3856,3984,3985,3986,3990],{},"No cleanup — older fetches can resolve ",[3987,3988,3989],"em",{},"after"," newer ones, overwriting correct results with stale data.",[3856,3992,3993,3997],{},[3994,3995,3996],"strong",{},"Fix"," — abort the previous request when a new one starts:",[20,3999,4001],{"filename":4000,"language":23},"RaceFixed.ts",[25,4002,4004],{"className":27,"code":4003,"language":23,"meta":29,"style":29},"import { ref, watch } from 'vue'\n\nconst query = ref('')\nconst results = ref([])\n\nwatch(query, async (q, _old, onCleanup) => {\n  const controller = new AbortController()\n  onCleanup(() => controller.abort())  \u002F\u002F cancel previous fetch on new change\n  try {\n    const res = await fetch(`\u002Fapi\u002Fsearch?q=${q}`, { signal: controller.signal })\n    results.value = await res.json()\n  } catch (e) {\n    if (e.name === 'AbortError') return  \u002F\u002F expected — stale request canceled\n    throw e\n  }\n})\n",[31,4005,4006,4016,4020,4036,4048,4052,4079,4093,4110,4116,4138,4152,4160,4178,4185,4189],{"__ignoreMap":29},[34,4007,4008,4010,4012,4014],{"class":36,"line":37},[34,4009,41],{"class":40},[34,4011,3873],{"class":44},[34,4013,48],{"class":40},[34,4015,52],{"class":51},[34,4017,4018],{"class":36,"line":55},[34,4019,59],{"emptyLinePlaceholder":58},[34,4021,4022,4024,4026,4028,4030,4032,4034],{"class":36,"line":62},[34,4023,101],{"class":40},[34,4025,1406],{"class":104},[34,4027,108],{"class":40},[34,4029,112],{"class":111},[34,4031,115],{"class":44},[34,4033,1415],{"class":51},[34,4035,121],{"class":44},[34,4037,4038,4040,4042,4044,4046],{"class":36,"line":69},[34,4039,101],{"class":40},[34,4041,3904],{"class":104},[34,4043,108],{"class":40},[34,4045,112],{"class":111},[34,4047,3911],{"class":44},[34,4049,4050],{"class":36,"line":75},[34,4051,59],{"emptyLinePlaceholder":58},[34,4053,4054,4056,4058,4060,4062,4064,4066,4069,4071,4073,4075,4077],{"class":36,"line":81},[34,4055,987],{"class":111},[34,4057,3922],{"class":44},[34,4059,2328],{"class":40},[34,4061,458],{"class":44},[34,4063,3194],{"class":282},[34,4065,551],{"class":44},[34,4067,4068],{"class":282},"_old",[34,4070,551],{"class":44},[34,4072,2343],{"class":282},[34,4074,469],{"class":44},[34,4076,140],{"class":40},[34,4078,245],{"class":44},[34,4080,4081,4083,4085,4087,4089,4091],{"class":36,"line":87},[34,4082,1743],{"class":40},[34,4084,2356],{"class":104},[34,4086,108],{"class":40},[34,4088,2361],{"class":40},[34,4090,2364],{"class":111},[34,4092,2367],{"class":44},[34,4094,4095,4097,4099,4101,4103,4105,4107],{"class":36,"line":93},[34,4096,2393],{"class":111},[34,4098,137],{"class":44},[34,4100,140],{"class":40},[34,4102,2400],{"class":44},[34,4104,2403],{"class":111},[34,4106,3230],{"class":44},[34,4108,4109],{"class":65},"\u002F\u002F cancel previous fetch on new change\n",[34,4111,4112,4114],{"class":36,"line":98},[34,4113,2415],{"class":40},[34,4115,245],{"class":44},[34,4117,4118,4120,4122,4124,4126,4128,4130,4132,4134,4136],{"class":36,"line":124},[34,4119,791],{"class":40},[34,4121,2424],{"class":104},[34,4123,108],{"class":40},[34,4125,2429],{"class":40},[34,4127,2432],{"class":111},[34,4129,115],{"class":44},[34,4131,1461],{"class":51},[34,4133,3194],{"class":44},[34,4135,1025],{"class":51},[34,4137,2783],{"class":44},[34,4139,4140,4142,4144,4146,4148,4150],{"class":36,"line":154},[34,4141,2477],{"class":44},[34,4143,811],{"class":40},[34,4145,2429],{"class":40},[34,4147,2457],{"class":44},[34,4149,2460],{"class":111},[34,4151,2367],{"class":44},[34,4153,4154,4156,4158],{"class":36,"line":159},[34,4155,2487],{"class":44},[34,4157,2490],{"class":40},[34,4159,2493],{"class":44},[34,4161,4162,4164,4167,4169,4171,4173,4175],{"class":36,"line":165},[34,4163,2498],{"class":40},[34,4165,4166],{"class":44}," (e.name ",[34,4168,2516],{"class":40},[34,4170,2519],{"class":51},[34,4172,469],{"class":44},[34,4174,2718],{"class":40},[34,4176,4177],{"class":65},"  \u002F\u002F expected — stale request canceled\n",[34,4179,4180,4182],{"class":36,"line":171},[34,4181,2539],{"class":40},[34,4183,4184],{"class":44}," e\n",[34,4186,4187],{"class":36,"line":177},[34,4188,1132],{"class":44},[34,4190,4191],{"class":36,"line":183},[34,4192,301],{"class":44},[3856,4194,4195,4198,4199,4201],{},[3994,4196,4197],{},"The lesson",": async watchers must register cleanup (via ",[31,4200,2343],{},") to cancel stale work. Without it, out-of-order resolution causes data races.",[4203,4204,4205],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}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 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 .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":55,"depth":55,"links":4207},[4208,4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,4220],{"id":17,"depth":55,"text":18},{"id":368,"depth":55,"text":369},{"id":638,"depth":55,"text":639},{"id":927,"depth":55,"text":928},{"id":1375,"depth":55,"text":1376},{"id":1592,"depth":55,"text":1593},{"id":1905,"depth":55,"text":1906},{"id":2248,"depth":55,"text":2249},{"id":2595,"depth":55,"text":2596},{"id":2886,"depth":55,"text":2887},{"id":3152,"depth":55,"text":3153},{"id":3491,"depth":55,"text":3492},{"id":3853,"depth":55,"text":3854},"Computed caching internals, dirty flag mechanism, watch vs watchEffect flush timing, deep watcher cost analysis, scheduler control, and multi-source watcher patterns for production apps.","md",{},"\u002Fvue\u002F04-computed-and-watchers",{"title":5,"description":4221},"vue\u002F04-computed-and-watchers","CMoTkuLSi6948gZ5mdbJ2T_IAaWsJl38eZrNdgNM-YY",1789924655428]