[{"data":1,"prerenderedAt":3271},["ShallowReactive",2],{"page-\u002Fvue\u002F03-reactivity-fundamentals":3},{"id":4,"title":5,"body":6,"description":3264,"extension":3265,"meta":3266,"navigation":58,"path":3267,"seo":3268,"stem":3269,"__hash__":3270},"content\u002Fvue\u002F03-reactivity-fundamentals.md","Vue 3 Engineering Reference — Reactivity Fundamentals",{"type":7,"value":8,"toc":3249},"minimark",[9,14,19,256,260,559,563,907,912,3245],[10,11,13],"h1",{"id":12},"_03-reactivity-fundamentals","03 — Reactivity Fundamentals",[15,16,18],"h2",{"id":17},"dependency-tracking-how-vue-knows-what-to-re-render","Dependency Tracking — How Vue Knows What to Re-render",[20,21,24],"code-wrapper",{"filename":22,"language":23},"reactivity-core.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 { reactive, effect, track, trigger, targetMap } from 'vue'\n\n\u002F\u002F ── Simplified Vue 3 reactivity core (the actual implementation) ──\n\u002F\u002F Internally Vue maintains a WeakMap\u003CtargetObject, Map\u003Ckey, Set\u003CEffect>>>\n\u002F\u002F called targetMap. This is the dependency graph.\n\n\u002F\u002F ── effect(fn): runs fn, registers fn as a dependency of every reactive\n\u002F\u002F    property it READS during this execution. Re-runs fn when any of those\n\u002F\u002F    properties are WRITTEN to. ──\n\nconst state = reactive({ count: 0 })\n\neffect(() => {\n  \u002F\u002F During this fn's execution, reading state.count calls track(state, 'count')\n  \u002F\u002F track() adds this effect to the Set at targetMap.get(state).get('count')\n  console.log(state.count)\n})\n\nstate.count = 1  \u002F\u002F trigger(state, 'count') → re-runs the effect above → logs 1\n\n\u002F\u002F ── The dependency graph after the effect runs: ──────────\n\u002F\u002F targetMap = WeakMap {\n\u002F\u002F   { count: 0 } => Map {\n\u002F\u002F     'count' => Set { [effectFn] }\n\u002F\u002F   }\n\u002F\u002F }\n\u002F\u002F Multiple effects reading the same property → all in the same Set.\n\u002F\u002F Writing triggers ALL effects in that Set, in creation order.\n","",[31,32,33,53,60,67,73,79,84,90,96,102,107,133,138,153,159,165,177,183,188,203,208,214,220,226,232,238,244,250],"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"," { reactive, effect, track, trigger, targetMap } ",[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 ── Simplified Vue 3 reactivity core (the actual implementation) ──\n",[34,68,70],{"class":36,"line":69},4,[34,71,72],{"class":65},"\u002F\u002F Internally Vue maintains a WeakMap\u003CtargetObject, Map\u003Ckey, Set\u003CEffect>>>\n",[34,74,76],{"class":36,"line":75},5,[34,77,78],{"class":65},"\u002F\u002F called targetMap. This is the dependency graph.\n",[34,80,82],{"class":36,"line":81},6,[34,83,59],{"emptyLinePlaceholder":58},[34,85,87],{"class":36,"line":86},7,[34,88,89],{"class":65},"\u002F\u002F ── effect(fn): runs fn, registers fn as a dependency of every reactive\n",[34,91,93],{"class":36,"line":92},8,[34,94,95],{"class":65},"\u002F\u002F    property it READS during this execution. Re-runs fn when any of those\n",[34,97,99],{"class":36,"line":98},9,[34,100,101],{"class":65},"\u002F\u002F    properties are WRITTEN to. ──\n",[34,103,105],{"class":36,"line":104},10,[34,106,59],{"emptyLinePlaceholder":58},[34,108,110,113,117,120,124,127,130],{"class":36,"line":109},11,[34,111,112],{"class":40},"const",[34,114,116],{"class":115},"snvgF"," state",[34,118,119],{"class":40}," =",[34,121,123],{"class":122},"sIsaT"," reactive",[34,125,126],{"class":44},"({ count: ",[34,128,129],{"class":115},"0",[34,131,132],{"class":44}," })\n",[34,134,136],{"class":36,"line":135},12,[34,137,59],{"emptyLinePlaceholder":58},[34,139,141,144,147,150],{"class":36,"line":140},13,[34,142,143],{"class":122},"effect",[34,145,146],{"class":44},"(() ",[34,148,149],{"class":40},"=>",[34,151,152],{"class":44}," {\n",[34,154,156],{"class":36,"line":155},14,[34,157,158],{"class":65},"  \u002F\u002F During this fn's execution, reading state.count calls track(state, 'count')\n",[34,160,162],{"class":36,"line":161},15,[34,163,164],{"class":65},"  \u002F\u002F track() adds this effect to the Set at targetMap.get(state).get('count')\n",[34,166,168,171,174],{"class":36,"line":167},16,[34,169,170],{"class":44},"  console.",[34,172,173],{"class":122},"log",[34,175,176],{"class":44},"(state.count)\n",[34,178,180],{"class":36,"line":179},17,[34,181,182],{"class":44},"})\n",[34,184,186],{"class":36,"line":185},18,[34,187,59],{"emptyLinePlaceholder":58},[34,189,191,194,197,200],{"class":36,"line":190},19,[34,192,193],{"class":44},"state.count ",[34,195,196],{"class":40},"=",[34,198,199],{"class":115}," 1",[34,201,202],{"class":65},"  \u002F\u002F trigger(state, 'count') → re-runs the effect above → logs 1\n",[34,204,206],{"class":36,"line":205},20,[34,207,59],{"emptyLinePlaceholder":58},[34,209,211],{"class":36,"line":210},21,[34,212,213],{"class":65},"\u002F\u002F ── The dependency graph after the effect runs: ──────────\n",[34,215,217],{"class":36,"line":216},22,[34,218,219],{"class":65},"\u002F\u002F targetMap = WeakMap {\n",[34,221,223],{"class":36,"line":222},23,[34,224,225],{"class":65},"\u002F\u002F   { count: 0 } => Map {\n",[34,227,229],{"class":36,"line":228},24,[34,230,231],{"class":65},"\u002F\u002F     'count' => Set { [effectFn] }\n",[34,233,235],{"class":36,"line":234},25,[34,236,237],{"class":65},"\u002F\u002F   }\n",[34,239,241],{"class":36,"line":240},26,[34,242,243],{"class":65},"\u002F\u002F }\n",[34,245,247],{"class":36,"line":246},27,[34,248,249],{"class":65},"\u002F\u002F Multiple effects reading the same property → all in the same Set.\n",[34,251,253],{"class":36,"line":252},28,[34,254,255],{"class":65},"\u002F\u002F Writing triggers ALL effects in that Set, in creation order.\n",[15,257,259],{"id":258},"ref-vs-reactive-when-to-use-which","ref vs reactive — When to Use Which",[20,261,263],{"filename":262,"language":23},"ref-vs-reactive.ts",[25,264,266],{"className":27,"code":265,"language":23,"meta":29,"style":29},"import { ref, reactive, computed, watch, type Ref } from 'vue'\n\n\u002F\u002F ── ref(): wraps a single value in { value: T } ──\n\u002F\u002F Required for primitives (string, number, boolean) — can't Proxy a primitive.\n\u002F\u002F Also works for objects, but adds an extra .value dereference.\nconst count = ref(0)          \u002F\u002F { value: 0 } with reactive get\u002Fset on .value\ncount.value++                 \u002F\u002F mutation via .value\nconst doubled = computed(() => count.value * 2)  \u002F\u002F read via .value\n\n\u002F\u002F ── reactive(): wraps an object in a Proxy ──\n\u002F\u002F No .value needed — property access is directly reactive.\n\u002F\u002F Best for grouped state (a form, a config object, a user record).\nconst form = reactive({\n  email: '',\n  password: '',\n  rememberMe: false,\n})\nform.email = 'ada@example.com'  \u002F\u002F direct mutation, no .value\n\n\u002F\u002F ── Rule of thumb: ─────────────────────────────────────\n\u002F\u002F Single primitive          → ref()\n\u002F\u002F Grouped object state      → reactive()\n\u002F\u002F Replacing the entire value → ref() (reactive() can't be reassigned)\n\n\u002F\u002F ── The reassignment trap with reactive(): ─────────────\nlet config = reactive({ theme: 'dark' })\n\u002F\u002F config = reactive({ theme: 'light' })  \u002F\u002F ❌ replaces the binding,\n\u002F\u002F   old Proxy is orphaned, components still bound to the OLD Proxy don't update.\n\u002F\u002F Use ref for anything that needs full replacement:\nconst configRef = ref({ theme: 'dark' })\nconfigRef.value = { theme: 'light' }      \u002F\u002F ✅ ref.value reassignment triggers\n",[31,267,268,285,289,294,299,304,327,338,369,373,378,383,388,402,413,422,432,436,449,453,458,463,468,473,477,482,502,510,515,521,539],{"__ignoreMap":29},[34,269,270,272,275,278,281,283],{"class":36,"line":37},[34,271,41],{"class":40},[34,273,274],{"class":44}," { ref, reactive, computed, watch, ",[34,276,277],{"class":40},"type",[34,279,280],{"class":44}," Ref } ",[34,282,48],{"class":40},[34,284,52],{"class":51},[34,286,287],{"class":36,"line":55},[34,288,59],{"emptyLinePlaceholder":58},[34,290,291],{"class":36,"line":62},[34,292,293],{"class":65},"\u002F\u002F ── ref(): wraps a single value in { value: T } ──\n",[34,295,296],{"class":36,"line":69},[34,297,298],{"class":65},"\u002F\u002F Required for primitives (string, number, boolean) — can't Proxy a primitive.\n",[34,300,301],{"class":36,"line":75},[34,302,303],{"class":65},"\u002F\u002F Also works for objects, but adds an extra .value dereference.\n",[34,305,306,308,311,313,316,319,321,324],{"class":36,"line":81},[34,307,112],{"class":40},[34,309,310],{"class":115}," count",[34,312,119],{"class":40},[34,314,315],{"class":122}," ref",[34,317,318],{"class":44},"(",[34,320,129],{"class":115},[34,322,323],{"class":44},")          ",[34,325,326],{"class":65},"\u002F\u002F { value: 0 } with reactive get\u002Fset on .value\n",[34,328,329,332,335],{"class":36,"line":86},[34,330,331],{"class":44},"count.value",[34,333,334],{"class":40},"++",[34,336,337],{"class":65},"                 \u002F\u002F mutation via .value\n",[34,339,340,342,345,347,350,352,354,357,360,363,366],{"class":36,"line":92},[34,341,112],{"class":40},[34,343,344],{"class":115}," doubled",[34,346,119],{"class":40},[34,348,349],{"class":122}," computed",[34,351,146],{"class":44},[34,353,149],{"class":40},[34,355,356],{"class":44}," count.value ",[34,358,359],{"class":40},"*",[34,361,362],{"class":115}," 2",[34,364,365],{"class":44},")  ",[34,367,368],{"class":65},"\u002F\u002F read via .value\n",[34,370,371],{"class":36,"line":98},[34,372,59],{"emptyLinePlaceholder":58},[34,374,375],{"class":36,"line":104},[34,376,377],{"class":65},"\u002F\u002F ── reactive(): wraps an object in a Proxy ──\n",[34,379,380],{"class":36,"line":109},[34,381,382],{"class":65},"\u002F\u002F No .value needed — property access is directly reactive.\n",[34,384,385],{"class":36,"line":135},[34,386,387],{"class":65},"\u002F\u002F Best for grouped state (a form, a config object, a user record).\n",[34,389,390,392,395,397,399],{"class":36,"line":140},[34,391,112],{"class":40},[34,393,394],{"class":115}," form",[34,396,119],{"class":40},[34,398,123],{"class":122},[34,400,401],{"class":44},"({\n",[34,403,404,407,410],{"class":36,"line":155},[34,405,406],{"class":44},"  email: ",[34,408,409],{"class":51},"''",[34,411,412],{"class":44},",\n",[34,414,415,418,420],{"class":36,"line":161},[34,416,417],{"class":44},"  password: ",[34,419,409],{"class":51},[34,421,412],{"class":44},[34,423,424,427,430],{"class":36,"line":167},[34,425,426],{"class":44},"  rememberMe: ",[34,428,429],{"class":115},"false",[34,431,412],{"class":44},[34,433,434],{"class":36,"line":179},[34,435,182],{"class":44},[34,437,438,441,443,446],{"class":36,"line":185},[34,439,440],{"class":44},"form.email ",[34,442,196],{"class":40},[34,444,445],{"class":51}," 'ada@example.com'",[34,447,448],{"class":65},"  \u002F\u002F direct mutation, no .value\n",[34,450,451],{"class":36,"line":190},[34,452,59],{"emptyLinePlaceholder":58},[34,454,455],{"class":36,"line":205},[34,456,457],{"class":65},"\u002F\u002F ── Rule of thumb: ─────────────────────────────────────\n",[34,459,460],{"class":36,"line":210},[34,461,462],{"class":65},"\u002F\u002F Single primitive          → ref()\n",[34,464,465],{"class":36,"line":216},[34,466,467],{"class":65},"\u002F\u002F Grouped object state      → reactive()\n",[34,469,470],{"class":36,"line":222},[34,471,472],{"class":65},"\u002F\u002F Replacing the entire value → ref() (reactive() can't be reassigned)\n",[34,474,475],{"class":36,"line":228},[34,476,59],{"emptyLinePlaceholder":58},[34,478,479],{"class":36,"line":234},[34,480,481],{"class":65},"\u002F\u002F ── The reassignment trap with reactive(): ─────────────\n",[34,483,484,487,490,492,494,497,500],{"class":36,"line":240},[34,485,486],{"class":40},"let",[34,488,489],{"class":44}," config ",[34,491,196],{"class":40},[34,493,123],{"class":122},[34,495,496],{"class":44},"({ theme: ",[34,498,499],{"class":51},"'dark'",[34,501,132],{"class":44},[34,503,504,507],{"class":36,"line":246},[34,505,506],{"class":65},"\u002F\u002F config = reactive({ theme: 'light' })",[34,508,509],{"class":65},"  \u002F\u002F ❌ replaces the binding,\n",[34,511,512],{"class":36,"line":252},[34,513,514],{"class":65},"\u002F\u002F   old Proxy is orphaned, components still bound to the OLD Proxy don't update.\n",[34,516,518],{"class":36,"line":517},29,[34,519,520],{"class":65},"\u002F\u002F Use ref for anything that needs full replacement:\n",[34,522,524,526,529,531,533,535,537],{"class":36,"line":523},30,[34,525,112],{"class":40},[34,527,528],{"class":115}," configRef",[34,530,119],{"class":40},[34,532,315],{"class":122},[34,534,496],{"class":44},[34,536,499],{"class":51},[34,538,132],{"class":44},[34,540,542,545,547,550,553,556],{"class":36,"line":541},31,[34,543,544],{"class":44},"configRef.value ",[34,546,196],{"class":40},[34,548,549],{"class":44}," { theme: ",[34,551,552],{"class":51},"'light'",[34,554,555],{"class":44}," }      ",[34,557,558],{"class":65},"\u002F\u002F ✅ ref.value reassignment triggers\n",[15,560,562],{"id":561},"template-unwrapping-where-value-disappears","Template Unwrapping — Where .value Disappears",[20,564,567],{"filename":565,"language":566},"Unwrapping.vue","vue",[25,568,571],{"className":569,"code":570,"language":566,"meta":29,"style":29},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup lang=\"ts\">\nimport { ref, reactive, computed, type Ref } from 'vue'\n\nconst count = ref(0)\nconst user = reactive({ name: 'Ada', roles: ['admin'] })\n\u002F\u002F ── Refs inside reactive objects are auto-unwrapped ──\n\u002F\u002F Accessing state.count gives 0, not the ref object.\nconst state = reactive({\n  count,           \u002F\u002F ref → unwrapped to .value automatically\n  label: 'Total',\n})\n\n\u002F\u002F ── But refs in arrays\u002FMaps are NOT unwrapped ──\nconst mixed = ref\u003CRef\u003Cnumber>[]>([ref(1), ref(2)])\n\u002F\u002F mixed.value[0] is a Ref\u003Cnumber>, not 1 — must use .value\n\n\u002F\u002F ── Computed returns a ref-like (readonly ref) ──\nconst upper = computed(() => user.name.toUpperCase())\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- Templates auto-unwrap top-level refs: count.value → count -->\n  \u003Cp>{{ count }}\u003C\u002Fp>           \u003C!-- 0, not [object Object] -->\n  \u003Cp>{{ upper }}\u003C\u002Fp>           \u003C!-- \"ADA\" -->\n\n  \u003C!-- Reactive object properties accessed directly: -->\n  \u003Cp>{{ user.name }}\u003C\u002Fp>      \u003C!-- \"Ada\" -->\n  \u003Cp>{{ state.count }}\u003C\u002Fp>     \u003C!-- 0 (unwrapped from the ref inside reactive) -->\n\u003C\u002Ftemplate>\n",[31,572,573,596,611,615,632,658,663,668,680,688,698,702,706,711,756,761,765,770,794,803,807,816,821,840,856,860,865,882,899],{"__ignoreMap":29},[34,574,575,578,582,585,588,590,593],{"class":36,"line":37},[34,576,577],{"class":44},"\u003C",[34,579,581],{"class":580},"sk71V","script",[34,583,584],{"class":122}," setup",[34,586,587],{"class":122}," lang",[34,589,196],{"class":44},[34,591,592],{"class":51},"\"ts\"",[34,594,595],{"class":44},">\n",[34,597,598,600,603,605,607,609],{"class":36,"line":55},[34,599,41],{"class":40},[34,601,602],{"class":44}," { ref, reactive, computed, ",[34,604,277],{"class":40},[34,606,280],{"class":44},[34,608,48],{"class":40},[34,610,52],{"class":51},[34,612,613],{"class":36,"line":62},[34,614,59],{"emptyLinePlaceholder":58},[34,616,617,619,621,623,625,627,629],{"class":36,"line":69},[34,618,112],{"class":40},[34,620,310],{"class":115},[34,622,119],{"class":40},[34,624,315],{"class":122},[34,626,318],{"class":44},[34,628,129],{"class":115},[34,630,631],{"class":44},")\n",[34,633,634,636,639,641,643,646,649,652,655],{"class":36,"line":75},[34,635,112],{"class":40},[34,637,638],{"class":115}," user",[34,640,119],{"class":40},[34,642,123],{"class":122},[34,644,645],{"class":44},"({ name: ",[34,647,648],{"class":51},"'Ada'",[34,650,651],{"class":44},", roles: [",[34,653,654],{"class":51},"'admin'",[34,656,657],{"class":44},"] })\n",[34,659,660],{"class":36,"line":81},[34,661,662],{"class":65},"\u002F\u002F ── Refs inside reactive objects are auto-unwrapped ──\n",[34,664,665],{"class":36,"line":86},[34,666,667],{"class":65},"\u002F\u002F Accessing state.count gives 0, not the ref object.\n",[34,669,670,672,674,676,678],{"class":36,"line":92},[34,671,112],{"class":40},[34,673,116],{"class":115},[34,675,119],{"class":40},[34,677,123],{"class":122},[34,679,401],{"class":44},[34,681,682,685],{"class":36,"line":98},[34,683,684],{"class":44},"  count,           ",[34,686,687],{"class":65},"\u002F\u002F ref → unwrapped to .value automatically\n",[34,689,690,693,696],{"class":36,"line":104},[34,691,692],{"class":44},"  label: ",[34,694,695],{"class":51},"'Total'",[34,697,412],{"class":44},[34,699,700],{"class":36,"line":109},[34,701,182],{"class":44},[34,703,704],{"class":36,"line":135},[34,705,59],{"emptyLinePlaceholder":58},[34,707,708],{"class":36,"line":140},[34,709,710],{"class":65},"\u002F\u002F ── But refs in arrays\u002FMaps are NOT unwrapped ──\n",[34,712,713,715,718,720,722,724,727,729,732,735,738,740,743,746,748,750,753],{"class":36,"line":155},[34,714,112],{"class":40},[34,716,717],{"class":115}," mixed",[34,719,119],{"class":40},[34,721,315],{"class":122},[34,723,577],{"class":44},[34,725,726],{"class":122},"Ref",[34,728,577],{"class":44},[34,730,731],{"class":115},"number",[34,733,734],{"class":44},">[]>([",[34,736,737],{"class":122},"ref",[34,739,318],{"class":44},[34,741,742],{"class":115},"1",[34,744,745],{"class":44},"), ",[34,747,737],{"class":122},[34,749,318],{"class":44},[34,751,752],{"class":115},"2",[34,754,755],{"class":44},")])\n",[34,757,758],{"class":36,"line":161},[34,759,760],{"class":65},"\u002F\u002F mixed.value[0] is a Ref\u003Cnumber>, not 1 — must use .value\n",[34,762,763],{"class":36,"line":167},[34,764,59],{"emptyLinePlaceholder":58},[34,766,767],{"class":36,"line":179},[34,768,769],{"class":65},"\u002F\u002F ── Computed returns a ref-like (readonly ref) ──\n",[34,771,772,774,777,779,781,783,785,788,791],{"class":36,"line":185},[34,773,112],{"class":40},[34,775,776],{"class":115}," upper",[34,778,119],{"class":40},[34,780,349],{"class":122},[34,782,146],{"class":44},[34,784,149],{"class":40},[34,786,787],{"class":44}," user.name.",[34,789,790],{"class":122},"toUpperCase",[34,792,793],{"class":44},"())\n",[34,795,796,799,801],{"class":36,"line":190},[34,797,798],{"class":44},"\u003C\u002F",[34,800,581],{"class":580},[34,802,595],{"class":44},[34,804,805],{"class":36,"line":205},[34,806,59],{"emptyLinePlaceholder":58},[34,808,809,811,814],{"class":36,"line":210},[34,810,577],{"class":44},[34,812,813],{"class":580},"template",[34,815,595],{"class":44},[34,817,818],{"class":36,"line":216},[34,819,820],{"class":65},"  \u003C!-- Templates auto-unwrap top-level refs: count.value → count -->\n",[34,822,823,826,829,832,834,837],{"class":36,"line":222},[34,824,825],{"class":44},"  \u003C",[34,827,828],{"class":580},"p",[34,830,831],{"class":44},">{{ count }}\u003C\u002F",[34,833,828],{"class":580},[34,835,836],{"class":44},">           ",[34,838,839],{"class":65},"\u003C!-- 0, not [object Object] -->\n",[34,841,842,844,846,849,851,853],{"class":36,"line":228},[34,843,825],{"class":44},[34,845,828],{"class":580},[34,847,848],{"class":44},">{{ upper }}\u003C\u002F",[34,850,828],{"class":580},[34,852,836],{"class":44},[34,854,855],{"class":65},"\u003C!-- \"ADA\" -->\n",[34,857,858],{"class":36,"line":234},[34,859,59],{"emptyLinePlaceholder":58},[34,861,862],{"class":36,"line":240},[34,863,864],{"class":65},"  \u003C!-- Reactive object properties accessed directly: -->\n",[34,866,867,869,871,874,876,879],{"class":36,"line":246},[34,868,825],{"class":44},[34,870,828],{"class":580},[34,872,873],{"class":44},">{{ user.name }}\u003C\u002F",[34,875,828],{"class":580},[34,877,878],{"class":44},">      ",[34,880,881],{"class":65},"\u003C!-- \"Ada\" -->\n",[34,883,884,886,888,891,893,896],{"class":36,"line":252},[34,885,825],{"class":44},[34,887,828],{"class":580},[34,889,890],{"class":44},">{{ state.count }}\u003C\u002F",[34,892,828],{"class":580},[34,894,895],{"class":44},">     ",[34,897,898],{"class":65},"\u003C!-- 0 (unwrapped from the ref inside reactive) -->\n",[34,900,901,903,905],{"class":36,"line":517},[34,902,798],{"class":44},[34,904,813],{"class":580},[34,906,595],{"class":44},[908,909,911],"h3",{"id":910},"anti-pattern-destructuring-reactive-objects","Anti-Pattern: Destructuring Reactive Objects",[20,913,915,1038,1197,1201,1522,1526,1706,1710,1976,1980,2121,2125,2423,2427,2606,2610,2954,2958,2961,3097],{"filename":914,"language":23},"destructuring-trap.ts",[25,916,918],{"className":27,"code":917,"language":23,"meta":29,"style":29},"import { reactive, watchEffect } from 'vue'\n\nconst state = reactive({ count: 0, label: 'Total' })\n\n\u002F\u002F ❌ WRONG: destructuring breaks reactivity\nconst { count, label } = state\n\u002F\u002F `count` is now a plain number (0), disconnected from the Proxy.\n\u002F\u002F Mutating state.count does NOT update the local `count` variable.\nwatchEffect(() => {\n  console.log(count)  \u002F\u002F logs 0 once, never again — count is not reactive\n})\nstate.count = 5  \u002F\u002F the effect does NOT re-run; local `count` is still 0\n",[31,919,920,931,935,956,960,965,989,994,999,1010,1022,1026],{"__ignoreMap":29},[34,921,922,924,927,929],{"class":36,"line":37},[34,923,41],{"class":40},[34,925,926],{"class":44}," { reactive, watchEffect } ",[34,928,48],{"class":40},[34,930,52],{"class":51},[34,932,933],{"class":36,"line":55},[34,934,59],{"emptyLinePlaceholder":58},[34,936,937,939,941,943,945,947,949,952,954],{"class":36,"line":62},[34,938,112],{"class":40},[34,940,116],{"class":115},[34,942,119],{"class":40},[34,944,123],{"class":122},[34,946,126],{"class":44},[34,948,129],{"class":115},[34,950,951],{"class":44},", label: ",[34,953,695],{"class":51},[34,955,132],{"class":44},[34,957,958],{"class":36,"line":69},[34,959,59],{"emptyLinePlaceholder":58},[34,961,962],{"class":36,"line":75},[34,963,964],{"class":65},"\u002F\u002F ❌ WRONG: destructuring breaks reactivity\n",[34,966,967,969,972,975,978,981,984,986],{"class":36,"line":81},[34,968,112],{"class":40},[34,970,971],{"class":44}," { ",[34,973,974],{"class":115},"count",[34,976,977],{"class":44},", ",[34,979,980],{"class":115},"label",[34,982,983],{"class":44}," } ",[34,985,196],{"class":40},[34,987,988],{"class":44}," state\n",[34,990,991],{"class":36,"line":86},[34,992,993],{"class":65},"\u002F\u002F `count` is now a plain number (0), disconnected from the Proxy.\n",[34,995,996],{"class":36,"line":92},[34,997,998],{"class":65},"\u002F\u002F Mutating state.count does NOT update the local `count` variable.\n",[34,1000,1001,1004,1006,1008],{"class":36,"line":98},[34,1002,1003],{"class":122},"watchEffect",[34,1005,146],{"class":44},[34,1007,149],{"class":40},[34,1009,152],{"class":44},[34,1011,1012,1014,1016,1019],{"class":36,"line":104},[34,1013,170],{"class":44},[34,1015,173],{"class":122},[34,1017,1018],{"class":44},"(count)  ",[34,1020,1021],{"class":65},"\u002F\u002F logs 0 once, never again — count is not reactive\n",[34,1023,1024],{"class":36,"line":109},[34,1025,182],{"class":44},[34,1027,1028,1030,1032,1035],{"class":36,"line":135},[34,1029,193],{"class":44},[34,1031,196],{"class":40},[34,1033,1034],{"class":115}," 5",[34,1036,1037],{"class":65},"  \u002F\u002F the effect does NOT re-run; local `count` is still 0\n",[20,1039,1041],{"filename":1040,"language":23},"destructuring-fix.ts",[25,1042,1044],{"className":27,"code":1043,"language":23,"meta":29,"style":29},"import { reactive, toRefs, toRef, watchEffect } from 'vue'\n\nconst state = reactive({ count: 0, label: 'Total' })\n\n\u002F\u002F ✅ FIX 1: toRefs() — converts each property to a ref linked to the source\n\u002F\u002F Each ref's .value reads\u002Fwrites through the original reactive object's Proxy.\nconst { count, label } = toRefs(state)\n\u002F\u002F count is Ref\u003Cnumber>; count.value === 0; state.count = 5 → count.value === 5\n\n\u002F\u002F ✅ FIX 2: toRef() — single property (lazy, doesn't evaluate until .value read)\nconst countRef = toRef(state, 'count')\n\u002F\u002F Useful when the property may not exist yet on the reactive object.\n\nwatchEffect(() => {\n  console.log(count.value)  \u002F\u002F re-runs when state.count changes\n})\nstate.count = 5  \u002F\u002F effect re-runs, logs 5\n",[31,1045,1046,1057,1061,1081,1085,1090,1095,1117,1122,1126,1131,1151,1156,1160,1170,1182,1186],{"__ignoreMap":29},[34,1047,1048,1050,1053,1055],{"class":36,"line":37},[34,1049,41],{"class":40},[34,1051,1052],{"class":44}," { reactive, toRefs, toRef, watchEffect } ",[34,1054,48],{"class":40},[34,1056,52],{"class":51},[34,1058,1059],{"class":36,"line":55},[34,1060,59],{"emptyLinePlaceholder":58},[34,1062,1063,1065,1067,1069,1071,1073,1075,1077,1079],{"class":36,"line":62},[34,1064,112],{"class":40},[34,1066,116],{"class":115},[34,1068,119],{"class":40},[34,1070,123],{"class":122},[34,1072,126],{"class":44},[34,1074,129],{"class":115},[34,1076,951],{"class":44},[34,1078,695],{"class":51},[34,1080,132],{"class":44},[34,1082,1083],{"class":36,"line":69},[34,1084,59],{"emptyLinePlaceholder":58},[34,1086,1087],{"class":36,"line":75},[34,1088,1089],{"class":65},"\u002F\u002F ✅ FIX 1: toRefs() — converts each property to a ref linked to the source\n",[34,1091,1092],{"class":36,"line":81},[34,1093,1094],{"class":65},"\u002F\u002F Each ref's .value reads\u002Fwrites through the original reactive object's Proxy.\n",[34,1096,1097,1099,1101,1103,1105,1107,1109,1111,1114],{"class":36,"line":86},[34,1098,112],{"class":40},[34,1100,971],{"class":44},[34,1102,974],{"class":115},[34,1104,977],{"class":44},[34,1106,980],{"class":115},[34,1108,983],{"class":44},[34,1110,196],{"class":40},[34,1112,1113],{"class":122}," toRefs",[34,1115,1116],{"class":44},"(state)\n",[34,1118,1119],{"class":36,"line":92},[34,1120,1121],{"class":65},"\u002F\u002F count is Ref\u003Cnumber>; count.value === 0; state.count = 5 → count.value === 5\n",[34,1123,1124],{"class":36,"line":98},[34,1125,59],{"emptyLinePlaceholder":58},[34,1127,1128],{"class":36,"line":104},[34,1129,1130],{"class":65},"\u002F\u002F ✅ FIX 2: toRef() — single property (lazy, doesn't evaluate until .value read)\n",[34,1132,1133,1135,1138,1140,1143,1146,1149],{"class":36,"line":109},[34,1134,112],{"class":40},[34,1136,1137],{"class":115}," countRef",[34,1139,119],{"class":40},[34,1141,1142],{"class":122}," toRef",[34,1144,1145],{"class":44},"(state, ",[34,1147,1148],{"class":51},"'count'",[34,1150,631],{"class":44},[34,1152,1153],{"class":36,"line":135},[34,1154,1155],{"class":65},"\u002F\u002F Useful when the property may not exist yet on the reactive object.\n",[34,1157,1158],{"class":36,"line":140},[34,1159,59],{"emptyLinePlaceholder":58},[34,1161,1162,1164,1166,1168],{"class":36,"line":155},[34,1163,1003],{"class":122},[34,1165,146],{"class":44},[34,1167,149],{"class":40},[34,1169,152],{"class":44},[34,1171,1172,1174,1176,1179],{"class":36,"line":161},[34,1173,170],{"class":44},[34,1175,173],{"class":122},[34,1177,1178],{"class":44},"(count.value)  ",[34,1180,1181],{"class":65},"\u002F\u002F re-runs when state.count changes\n",[34,1183,1184],{"class":36,"line":167},[34,1185,182],{"class":44},[34,1187,1188,1190,1192,1194],{"class":36,"line":179},[34,1189,193],{"class":44},[34,1191,196],{"class":40},[34,1193,1034],{"class":115},[34,1195,1196],{"class":65},"  \u002F\u002F effect re-runs, logs 5\n",[15,1198,1200],{"id":1199},"custom-ref-factory-pattern-for-debounced-search","Custom Ref — Factory Pattern for Debounced Search",[20,1202,1204],{"filename":1203,"language":23},"useDebouncedRef.ts",[25,1205,1207],{"className":27,"code":1206,"language":23,"meta":29,"style":29},"import { customRef, type Ref } from 'vue'\n\n\u002F\u002F ── customRef: gives manual control over track\u002Ftrigger ──\n\u002F\u002F Factory: lets you intercept reads and writes to a ref.\n\u002F\u002F Use cases: debounce, lazy evaluation, sync with external storage.\nexport function useDebouncedRef\u003CT>(initial: T, delay = 200): Ref\u003CT> {\n  let value = initial\n  let timer: ReturnType\u003Ctypeof setTimeout>\n\n  return customRef\u003CT>((track, trigger) => ({\n    get() {\n      \u002F\u002F track(): register the reading effect as a dependency\n      \u002F\u002F Without this, the effect won't re-run when the ref changes.\n      track()\n      return value\n    },\n    set(newValue: T) {\n      clearTimeout(timer)\n      timer = setTimeout(() => {\n        value = newValue\n        \u002F\u002F trigger(): notify all registered effects to re-run\n        \u002F\u002F Without this, the value changes but no one re-renders.\n        trigger()\n      }, delay)\n    },\n  }))\n}\n\n\u002F\u002F ── Usage in a component: ──────────────────────────────\n\u002F\u002F const searchQuery = useDebouncedRef('', 300)\n\u002F\u002F \u003Cinput v-model=\"searchQuery\" \u002F> — input updates immediately,\n\u002F\u002F but reactive consumers (computed, watch, template) only see\n\u002F\u002F changes 300ms after the last keystroke.\n",[31,1208,1209,1224,1228,1233,1238,1243,1297,1310,1330,1334,1365,1373,1378,1383,1391,1399,1404,1421,1429,1445,1455,1460,1465,1472,1477,1481,1486,1491,1495,1500,1505,1510,1516],{"__ignoreMap":29},[34,1210,1211,1213,1216,1218,1220,1222],{"class":36,"line":37},[34,1212,41],{"class":40},[34,1214,1215],{"class":44}," { customRef, ",[34,1217,277],{"class":40},[34,1219,280],{"class":44},[34,1221,48],{"class":40},[34,1223,52],{"class":51},[34,1225,1226],{"class":36,"line":55},[34,1227,59],{"emptyLinePlaceholder":58},[34,1229,1230],{"class":36,"line":62},[34,1231,1232],{"class":65},"\u002F\u002F ── customRef: gives manual control over track\u002Ftrigger ──\n",[34,1234,1235],{"class":36,"line":69},[34,1236,1237],{"class":65},"\u002F\u002F Factory: lets you intercept reads and writes to a ref.\n",[34,1239,1240],{"class":36,"line":75},[34,1241,1242],{"class":65},"\u002F\u002F Use cases: debounce, lazy evaluation, sync with external storage.\n",[34,1244,1245,1248,1251,1254,1256,1259,1262,1266,1269,1272,1274,1277,1279,1282,1285,1287,1290,1292,1294],{"class":36,"line":81},[34,1246,1247],{"class":40},"export",[34,1249,1250],{"class":40}," function",[34,1252,1253],{"class":122}," useDebouncedRef",[34,1255,577],{"class":44},[34,1257,1258],{"class":122},"T",[34,1260,1261],{"class":44},">(",[34,1263,1265],{"class":1264},"sCrzJ","initial",[34,1267,1268],{"class":40},":",[34,1270,1271],{"class":122}," T",[34,1273,977],{"class":44},[34,1275,1276],{"class":1264},"delay",[34,1278,119],{"class":40},[34,1280,1281],{"class":115}," 200",[34,1283,1284],{"class":44},")",[34,1286,1268],{"class":40},[34,1288,1289],{"class":122}," Ref",[34,1291,577],{"class":44},[34,1293,1258],{"class":122},[34,1295,1296],{"class":44},"> {\n",[34,1298,1299,1302,1305,1307],{"class":36,"line":86},[34,1300,1301],{"class":40},"  let",[34,1303,1304],{"class":44}," value ",[34,1306,196],{"class":40},[34,1308,1309],{"class":44}," initial\n",[34,1311,1312,1314,1317,1319,1322,1324,1327],{"class":36,"line":92},[34,1313,1301],{"class":40},[34,1315,1316],{"class":44}," timer",[34,1318,1268],{"class":40},[34,1320,1321],{"class":122}," ReturnType",[34,1323,577],{"class":44},[34,1325,1326],{"class":40},"typeof",[34,1328,1329],{"class":44}," setTimeout>\n",[34,1331,1332],{"class":36,"line":98},[34,1333,59],{"emptyLinePlaceholder":58},[34,1335,1336,1339,1342,1344,1346,1349,1352,1354,1357,1360,1362],{"class":36,"line":104},[34,1337,1338],{"class":40},"  return",[34,1340,1341],{"class":122}," customRef",[34,1343,577],{"class":44},[34,1345,1258],{"class":122},[34,1347,1348],{"class":44},">((",[34,1350,1351],{"class":1264},"track",[34,1353,977],{"class":44},[34,1355,1356],{"class":1264},"trigger",[34,1358,1359],{"class":44},") ",[34,1361,149],{"class":40},[34,1363,1364],{"class":44}," ({\n",[34,1366,1367,1370],{"class":36,"line":109},[34,1368,1369],{"class":122},"    get",[34,1371,1372],{"class":44},"() {\n",[34,1374,1375],{"class":36,"line":135},[34,1376,1377],{"class":65},"      \u002F\u002F track(): register the reading effect as a dependency\n",[34,1379,1380],{"class":36,"line":140},[34,1381,1382],{"class":65},"      \u002F\u002F Without this, the effect won't re-run when the ref changes.\n",[34,1384,1385,1388],{"class":36,"line":155},[34,1386,1387],{"class":122},"      track",[34,1389,1390],{"class":44},"()\n",[34,1392,1393,1396],{"class":36,"line":161},[34,1394,1395],{"class":40},"      return",[34,1397,1398],{"class":44}," value\n",[34,1400,1401],{"class":36,"line":167},[34,1402,1403],{"class":44},"    },\n",[34,1405,1406,1409,1411,1414,1416,1418],{"class":36,"line":179},[34,1407,1408],{"class":122},"    set",[34,1410,318],{"class":44},[34,1412,1413],{"class":1264},"newValue",[34,1415,1268],{"class":40},[34,1417,1271],{"class":122},[34,1419,1420],{"class":44},") {\n",[34,1422,1423,1426],{"class":36,"line":185},[34,1424,1425],{"class":122},"      clearTimeout",[34,1427,1428],{"class":44},"(timer)\n",[34,1430,1431,1434,1436,1439,1441,1443],{"class":36,"line":190},[34,1432,1433],{"class":44},"      timer ",[34,1435,196],{"class":40},[34,1437,1438],{"class":122}," setTimeout",[34,1440,146],{"class":44},[34,1442,149],{"class":40},[34,1444,152],{"class":44},[34,1446,1447,1450,1452],{"class":36,"line":205},[34,1448,1449],{"class":44},"        value ",[34,1451,196],{"class":40},[34,1453,1454],{"class":44}," newValue\n",[34,1456,1457],{"class":36,"line":210},[34,1458,1459],{"class":65},"        \u002F\u002F trigger(): notify all registered effects to re-run\n",[34,1461,1462],{"class":36,"line":216},[34,1463,1464],{"class":65},"        \u002F\u002F Without this, the value changes but no one re-renders.\n",[34,1466,1467,1470],{"class":36,"line":222},[34,1468,1469],{"class":122},"        trigger",[34,1471,1390],{"class":44},[34,1473,1474],{"class":36,"line":228},[34,1475,1476],{"class":44},"      }, delay)\n",[34,1478,1479],{"class":36,"line":234},[34,1480,1403],{"class":44},[34,1482,1483],{"class":36,"line":240},[34,1484,1485],{"class":44},"  }))\n",[34,1487,1488],{"class":36,"line":246},[34,1489,1490],{"class":44},"}\n",[34,1492,1493],{"class":36,"line":252},[34,1494,59],{"emptyLinePlaceholder":58},[34,1496,1497],{"class":36,"line":517},[34,1498,1499],{"class":65},"\u002F\u002F ── Usage in a component: ──────────────────────────────\n",[34,1501,1502],{"class":36,"line":523},[34,1503,1504],{"class":65},"\u002F\u002F const searchQuery = useDebouncedRef('', 300)\n",[34,1506,1507],{"class":36,"line":541},[34,1508,1509],{"class":65},"\u002F\u002F \u003Cinput v-model=\"searchQuery\" \u002F> — input updates immediately,\n",[34,1511,1513],{"class":36,"line":1512},32,[34,1514,1515],{"class":65},"\u002F\u002F but reactive consumers (computed, watch, template) only see\n",[34,1517,1519],{"class":36,"line":1518},33,[34,1520,1521],{"class":65},"\u002F\u002F changes 300ms after the last keystroke.\n",[15,1523,1525],{"id":1524},"shallowref-large-data-without-deep-tracking","shallowRef — Large Data Without Deep Tracking",[20,1527,1529],{"filename":1528,"language":23},"shallow-ref.ts",[25,1530,1532],{"className":27,"code":1531,"language":23,"meta":29,"style":29},"import { shallowRef, watch, triggerRef } from 'vue'\n\n\u002F\u002F ── shallowRef: only .value access is reactive ──\n\u002F\u002F The object inside is NOT made reactive — no deep Proxy.\n\u002F\u002F Mutating nested properties does NOT trigger effects.\n\u002F\u002F Only replacing .value (or calling triggerRef) triggers updates.\nconst hugeList = shallowRef\u003C{ id: number; data: ArrayBuffer }[]>([])\n\n\u002F\u002F ✅ Replacing .value triggers re-render:\nhugeList.value = newArray\n\n\u002F\u002F ❌ Mutating in place does NOT trigger:\nhugeList.value.push(newItem)  \u002F\u002F array changes, but Vue doesn't know\n\n\u002F\u002F ✅ Force-trigger after in-place mutation:\nhugeList.value.push(newItem)\ntriggerRef(hugeList)  \u002F\u002F manually notifies all dependents\n\n\u002F\u002F ── Why use shallowRef for large data: ──────────────────\n\u002F\u002F reactive() walks every nested property and installs Proxies recursively.\n\u002F\u002F For 10,000-item arrays of objects, that's 10,000+ Proxies — expensive init.\n\u002F\u002F shallowRef avoids this: one ref wrapper, zero nested Proxies.\n\u002F\u002F Use for: API response data, chart datasets, WebGL buffers, large table rows.\n",[31,1533,1534,1545,1549,1554,1559,1564,1569,1606,1610,1615,1625,1629,1634,1648,1652,1657,1666,1677,1681,1686,1691,1696,1701],{"__ignoreMap":29},[34,1535,1536,1538,1541,1543],{"class":36,"line":37},[34,1537,41],{"class":40},[34,1539,1540],{"class":44}," { shallowRef, watch, triggerRef } ",[34,1542,48],{"class":40},[34,1544,52],{"class":51},[34,1546,1547],{"class":36,"line":55},[34,1548,59],{"emptyLinePlaceholder":58},[34,1550,1551],{"class":36,"line":62},[34,1552,1553],{"class":65},"\u002F\u002F ── shallowRef: only .value access is reactive ──\n",[34,1555,1556],{"class":36,"line":69},[34,1557,1558],{"class":65},"\u002F\u002F The object inside is NOT made reactive — no deep Proxy.\n",[34,1560,1561],{"class":36,"line":75},[34,1562,1563],{"class":65},"\u002F\u002F Mutating nested properties does NOT trigger effects.\n",[34,1565,1566],{"class":36,"line":81},[34,1567,1568],{"class":65},"\u002F\u002F Only replacing .value (or calling triggerRef) triggers updates.\n",[34,1570,1571,1573,1576,1578,1581,1584,1587,1589,1592,1595,1598,1600,1603],{"class":36,"line":86},[34,1572,112],{"class":40},[34,1574,1575],{"class":115}," hugeList",[34,1577,119],{"class":40},[34,1579,1580],{"class":122}," shallowRef",[34,1582,1583],{"class":44},"\u003C{ ",[34,1585,1586],{"class":1264},"id",[34,1588,1268],{"class":40},[34,1590,1591],{"class":115}," number",[34,1593,1594],{"class":44},"; ",[34,1596,1597],{"class":1264},"data",[34,1599,1268],{"class":40},[34,1601,1602],{"class":122}," ArrayBuffer",[34,1604,1605],{"class":44}," }[]>([])\n",[34,1607,1608],{"class":36,"line":92},[34,1609,59],{"emptyLinePlaceholder":58},[34,1611,1612],{"class":36,"line":98},[34,1613,1614],{"class":65},"\u002F\u002F ✅ Replacing .value triggers re-render:\n",[34,1616,1617,1620,1622],{"class":36,"line":104},[34,1618,1619],{"class":44},"hugeList.value ",[34,1621,196],{"class":40},[34,1623,1624],{"class":44}," newArray\n",[34,1626,1627],{"class":36,"line":109},[34,1628,59],{"emptyLinePlaceholder":58},[34,1630,1631],{"class":36,"line":135},[34,1632,1633],{"class":65},"\u002F\u002F ❌ Mutating in place does NOT trigger:\n",[34,1635,1636,1639,1642,1645],{"class":36,"line":140},[34,1637,1638],{"class":44},"hugeList.value.",[34,1640,1641],{"class":122},"push",[34,1643,1644],{"class":44},"(newItem)  ",[34,1646,1647],{"class":65},"\u002F\u002F array changes, but Vue doesn't know\n",[34,1649,1650],{"class":36,"line":155},[34,1651,59],{"emptyLinePlaceholder":58},[34,1653,1654],{"class":36,"line":161},[34,1655,1656],{"class":65},"\u002F\u002F ✅ Force-trigger after in-place mutation:\n",[34,1658,1659,1661,1663],{"class":36,"line":167},[34,1660,1638],{"class":44},[34,1662,1641],{"class":122},[34,1664,1665],{"class":44},"(newItem)\n",[34,1667,1668,1671,1674],{"class":36,"line":179},[34,1669,1670],{"class":122},"triggerRef",[34,1672,1673],{"class":44},"(hugeList)  ",[34,1675,1676],{"class":65},"\u002F\u002F manually notifies all dependents\n",[34,1678,1679],{"class":36,"line":185},[34,1680,59],{"emptyLinePlaceholder":58},[34,1682,1683],{"class":36,"line":190},[34,1684,1685],{"class":65},"\u002F\u002F ── Why use shallowRef for large data: ──────────────────\n",[34,1687,1688],{"class":36,"line":205},[34,1689,1690],{"class":65},"\u002F\u002F reactive() walks every nested property and installs Proxies recursively.\n",[34,1692,1693],{"class":36,"line":210},[34,1694,1695],{"class":65},"\u002F\u002F For 10,000-item arrays of objects, that's 10,000+ Proxies — expensive init.\n",[34,1697,1698],{"class":36,"line":216},[34,1699,1700],{"class":65},"\u002F\u002F shallowRef avoids this: one ref wrapper, zero nested Proxies.\n",[34,1702,1703],{"class":36,"line":222},[34,1704,1705],{"class":65},"\u002F\u002F Use for: API response data, chart datasets, WebGL buffers, large table rows.\n",[15,1707,1709],{"id":1708},"readonly-and-shallowreadonly-immutable-views","readonly and shallowReadonly — Immutable Views",[20,1711,1713],{"filename":1712,"language":23},"readonly-state.ts",[25,1714,1716],{"className":27,"code":1715,"language":23,"meta":29,"style":29},"import { reactive, readonly, shallowReadonly, isReadonly } from 'vue'\n\nconst state = reactive({\n  user: { name: 'Ada', prefs: { theme: 'dark' } },\n  tokens: ['abc', 'def'],\n})\n\n\u002F\u002F ── readonly(): deep — every nested property becomes read-only ──\n\u002F\u002F Writing to any level throws in dev (silently fails in prod).\nconst protectedState = readonly(state)\n\u002F\u002F protectedState.user.name = 'X' → TypeError in dev\n\u002F\u002F protectedState.user is also readonly (deep recursion)\n\n\u002F\u002F ── shallowReadonly(): only top-level keys are locked ──\n\u002F\u002F Nested objects remain mutable — cheaper for large trees.\nconst shallowProtected = shallowReadonly(state)\n\u002F\u002F shallowProtected.user = {}          → TypeError (top-level)\n\u002F\u002F shallowProtected.user.name = 'X'      → ✅ allowed (nested not locked)\n\n\u002F\u002F ── Use case: provide readonly to components, keep mutable internally ──\n\u002F\u002F Parent owns the mutable reactive state; passes readonly() to children.\n\u002F\u002F Children can read but can't mutate — forces event-based updates.\nexport function useUserStore() {\n  const state = reactive({ user: null as User | null })\n  return {\n    state: readonly(state),    \u002F\u002F consumers get immutable view\n    setUser: (u: User) => { state.user = u },  \u002F\u002F mutations via explicit API\n  }\n}\n",[31,1717,1718,1729,1733,1745,1760,1776,1780,1784,1789,1794,1808,1813,1818,1822,1827,1832,1846,1851,1856,1860,1865,1870,1875,1886,1917,1923,1937,1967,1972],{"__ignoreMap":29},[34,1719,1720,1722,1725,1727],{"class":36,"line":37},[34,1721,41],{"class":40},[34,1723,1724],{"class":44}," { reactive, readonly, shallowReadonly, isReadonly } ",[34,1726,48],{"class":40},[34,1728,52],{"class":51},[34,1730,1731],{"class":36,"line":55},[34,1732,59],{"emptyLinePlaceholder":58},[34,1734,1735,1737,1739,1741,1743],{"class":36,"line":62},[34,1736,112],{"class":40},[34,1738,116],{"class":115},[34,1740,119],{"class":40},[34,1742,123],{"class":122},[34,1744,401],{"class":44},[34,1746,1747,1750,1752,1755,1757],{"class":36,"line":69},[34,1748,1749],{"class":44},"  user: { name: ",[34,1751,648],{"class":51},[34,1753,1754],{"class":44},", prefs: { theme: ",[34,1756,499],{"class":51},[34,1758,1759],{"class":44}," } },\n",[34,1761,1762,1765,1768,1770,1773],{"class":36,"line":75},[34,1763,1764],{"class":44},"  tokens: [",[34,1766,1767],{"class":51},"'abc'",[34,1769,977],{"class":44},[34,1771,1772],{"class":51},"'def'",[34,1774,1775],{"class":44},"],\n",[34,1777,1778],{"class":36,"line":81},[34,1779,182],{"class":44},[34,1781,1782],{"class":36,"line":86},[34,1783,59],{"emptyLinePlaceholder":58},[34,1785,1786],{"class":36,"line":92},[34,1787,1788],{"class":65},"\u002F\u002F ── readonly(): deep — every nested property becomes read-only ──\n",[34,1790,1791],{"class":36,"line":98},[34,1792,1793],{"class":65},"\u002F\u002F Writing to any level throws in dev (silently fails in prod).\n",[34,1795,1796,1798,1801,1803,1806],{"class":36,"line":104},[34,1797,112],{"class":40},[34,1799,1800],{"class":115}," protectedState",[34,1802,119],{"class":40},[34,1804,1805],{"class":122}," readonly",[34,1807,1116],{"class":44},[34,1809,1810],{"class":36,"line":109},[34,1811,1812],{"class":65},"\u002F\u002F protectedState.user.name = 'X' → TypeError in dev\n",[34,1814,1815],{"class":36,"line":135},[34,1816,1817],{"class":65},"\u002F\u002F protectedState.user is also readonly (deep recursion)\n",[34,1819,1820],{"class":36,"line":140},[34,1821,59],{"emptyLinePlaceholder":58},[34,1823,1824],{"class":36,"line":155},[34,1825,1826],{"class":65},"\u002F\u002F ── shallowReadonly(): only top-level keys are locked ──\n",[34,1828,1829],{"class":36,"line":161},[34,1830,1831],{"class":65},"\u002F\u002F Nested objects remain mutable — cheaper for large trees.\n",[34,1833,1834,1836,1839,1841,1844],{"class":36,"line":167},[34,1835,112],{"class":40},[34,1837,1838],{"class":115}," shallowProtected",[34,1840,119],{"class":40},[34,1842,1843],{"class":122}," shallowReadonly",[34,1845,1116],{"class":44},[34,1847,1848],{"class":36,"line":179},[34,1849,1850],{"class":65},"\u002F\u002F shallowProtected.user = {}          → TypeError (top-level)\n",[34,1852,1853],{"class":36,"line":185},[34,1854,1855],{"class":65},"\u002F\u002F shallowProtected.user.name = 'X'      → ✅ allowed (nested not locked)\n",[34,1857,1858],{"class":36,"line":190},[34,1859,59],{"emptyLinePlaceholder":58},[34,1861,1862],{"class":36,"line":205},[34,1863,1864],{"class":65},"\u002F\u002F ── Use case: provide readonly to components, keep mutable internally ──\n",[34,1866,1867],{"class":36,"line":210},[34,1868,1869],{"class":65},"\u002F\u002F Parent owns the mutable reactive state; passes readonly() to children.\n",[34,1871,1872],{"class":36,"line":216},[34,1873,1874],{"class":65},"\u002F\u002F Children can read but can't mutate — forces event-based updates.\n",[34,1876,1877,1879,1881,1884],{"class":36,"line":222},[34,1878,1247],{"class":40},[34,1880,1250],{"class":40},[34,1882,1883],{"class":122}," useUserStore",[34,1885,1372],{"class":44},[34,1887,1888,1891,1893,1895,1897,1900,1903,1906,1909,1912,1915],{"class":36,"line":228},[34,1889,1890],{"class":40},"  const",[34,1892,116],{"class":115},[34,1894,119],{"class":40},[34,1896,123],{"class":122},[34,1898,1899],{"class":44},"({ user: ",[34,1901,1902],{"class":115},"null",[34,1904,1905],{"class":40}," as",[34,1907,1908],{"class":122}," User",[34,1910,1911],{"class":40}," |",[34,1913,1914],{"class":115}," null",[34,1916,132],{"class":44},[34,1918,1919,1921],{"class":36,"line":234},[34,1920,1338],{"class":40},[34,1922,152],{"class":44},[34,1924,1925,1928,1931,1934],{"class":36,"line":240},[34,1926,1927],{"class":44},"    state: ",[34,1929,1930],{"class":122},"readonly",[34,1932,1933],{"class":44},"(state),    ",[34,1935,1936],{"class":65},"\u002F\u002F consumers get immutable view\n",[34,1938,1939,1942,1945,1948,1950,1952,1954,1956,1959,1961,1964],{"class":36,"line":246},[34,1940,1941],{"class":122},"    setUser",[34,1943,1944],{"class":44},": (",[34,1946,1947],{"class":1264},"u",[34,1949,1268],{"class":40},[34,1951,1908],{"class":122},[34,1953,1359],{"class":44},[34,1955,149],{"class":40},[34,1957,1958],{"class":44}," { state.user ",[34,1960,196],{"class":40},[34,1962,1963],{"class":44}," u },  ",[34,1965,1966],{"class":65},"\u002F\u002F mutations via explicit API\n",[34,1968,1969],{"class":36,"line":252},[34,1970,1971],{"class":44},"  }\n",[34,1973,1974],{"class":36,"line":517},[34,1975,1490],{"class":44},[15,1977,1979],{"id":1978},"markraw-permanently-exclude-from-reactivity","markRaw — Permanently Exclude from Reactivity",[20,1981,1983],{"filename":1982,"language":23},"mark-raw.ts",[25,1984,1986],{"className":27,"code":1985,"language":23,"meta":29,"style":29},"import { reactive, markRaw, isReactive } from 'vue'\n\n\u002F\u002F ── markRaw(): marks an object so reactive() will NEVER wrap it ──\n\u002F\u002F The object is stored as-is, no Proxy created, no tracking installed.\nconst mapInstance = markRaw(new Map([['key', 'value']]))\nconst state = reactive({ map: mapInstance })\nconsole.log(isReactive(state.map))  \u002F\u002F false — raw, not reactive\n\n\u002F\u002F ── When to use: ────────────────────────────────────────\n\u002F\u002F 1. Third-party class instances (Map, Set, Date, custom classes)\n\u002F\u002F    that have their own internal state — Proxy wrapping can break them.\n\u002F\u002F 2. Large binary data (ArrayBuffer, ImageData) — no point in tracking.\n\u002F\u002F 3. Renderer objects (WebGL contexts, chart instances).\n\n\u002F\u002F ⚠️ markRaw is PERMANENT — once marked, the object can never be made reactive.\n\u002F\u002F Use reactive() first if you need tracking, markRaw only for true escapes.\n",[31,1987,1988,1999,2003,2008,2013,2047,2060,2078,2082,2087,2092,2097,2102,2107,2111,2116],{"__ignoreMap":29},[34,1989,1990,1992,1995,1997],{"class":36,"line":37},[34,1991,41],{"class":40},[34,1993,1994],{"class":44}," { reactive, markRaw, isReactive } ",[34,1996,48],{"class":40},[34,1998,52],{"class":51},[34,2000,2001],{"class":36,"line":55},[34,2002,59],{"emptyLinePlaceholder":58},[34,2004,2005],{"class":36,"line":62},[34,2006,2007],{"class":65},"\u002F\u002F ── markRaw(): marks an object so reactive() will NEVER wrap it ──\n",[34,2009,2010],{"class":36,"line":69},[34,2011,2012],{"class":65},"\u002F\u002F The object is stored as-is, no Proxy created, no tracking installed.\n",[34,2014,2015,2017,2020,2022,2025,2027,2030,2033,2036,2039,2041,2044],{"class":36,"line":75},[34,2016,112],{"class":40},[34,2018,2019],{"class":115}," mapInstance",[34,2021,119],{"class":40},[34,2023,2024],{"class":122}," markRaw",[34,2026,318],{"class":44},[34,2028,2029],{"class":40},"new",[34,2031,2032],{"class":122}," Map",[34,2034,2035],{"class":44},"([[",[34,2037,2038],{"class":51},"'key'",[34,2040,977],{"class":44},[34,2042,2043],{"class":51},"'value'",[34,2045,2046],{"class":44},"]]))\n",[34,2048,2049,2051,2053,2055,2057],{"class":36,"line":81},[34,2050,112],{"class":40},[34,2052,116],{"class":115},[34,2054,119],{"class":40},[34,2056,123],{"class":122},[34,2058,2059],{"class":44},"({ map: mapInstance })\n",[34,2061,2062,2065,2067,2069,2072,2075],{"class":36,"line":86},[34,2063,2064],{"class":44},"console.",[34,2066,173],{"class":122},[34,2068,318],{"class":44},[34,2070,2071],{"class":122},"isReactive",[34,2073,2074],{"class":44},"(state.map))  ",[34,2076,2077],{"class":65},"\u002F\u002F false — raw, not reactive\n",[34,2079,2080],{"class":36,"line":92},[34,2081,59],{"emptyLinePlaceholder":58},[34,2083,2084],{"class":36,"line":98},[34,2085,2086],{"class":65},"\u002F\u002F ── When to use: ────────────────────────────────────────\n",[34,2088,2089],{"class":36,"line":104},[34,2090,2091],{"class":65},"\u002F\u002F 1. Third-party class instances (Map, Set, Date, custom classes)\n",[34,2093,2094],{"class":36,"line":109},[34,2095,2096],{"class":65},"\u002F\u002F    that have their own internal state — Proxy wrapping can break them.\n",[34,2098,2099],{"class":36,"line":135},[34,2100,2101],{"class":65},"\u002F\u002F 2. Large binary data (ArrayBuffer, ImageData) — no point in tracking.\n",[34,2103,2104],{"class":36,"line":140},[34,2105,2106],{"class":65},"\u002F\u002F 3. Renderer objects (WebGL contexts, chart instances).\n",[34,2108,2109],{"class":36,"line":155},[34,2110,59],{"emptyLinePlaceholder":58},[34,2112,2113],{"class":36,"line":161},[34,2114,2115],{"class":65},"\u002F\u002F ⚠️ markRaw is PERMANENT — once marked, the object can never be made reactive.\n",[34,2117,2118],{"class":36,"line":167},[34,2119,2120],{"class":65},"\u002F\u002F Use reactive() first if you need tracking, markRaw only for true escapes.\n",[15,2122,2124],{"id":2123},"effectscope-lifecycle-managed-effect-grouping","effectScope — Lifecycle-Managed Effect Grouping",[20,2126,2128],{"filename":2127,"language":23},"effect-scope.ts",[25,2129,2131],{"className":27,"code":2130,"language":23,"meta":29,"style":29},"import { effectScope, ref, watchEffect, onScopeDispose } from 'vue'\n\n\u002F\u002F ── effectScope: groups multiple effects for collective cleanup ──\n\u002F\u002F When the scope is stopped, ALL effects inside are auto-disposed.\n\u002F\u002F Critical for composables that create effects that must be cleaned up\n\u002F\u002F when the hosting component unmounts (or when the scope is manually stopped).\n\nfunction useMouseTracker() {\n  const x = ref(0)\n  const y = ref(0)\n\n  const scope = effectScope()\n\n  scope.run(() => {\n    \u002F\u002F All effects created inside run() are registered with this scope.\n    watchEffect(() => {\n      window.addEventListener('mousemove', (e) => {\n        x.value = e.clientX\n        y.value = e.clientY\n      })\n    })\n\n    \u002F\u002F onScopeDispose: runs when the parent scope stops — cleanup listeners.\n    onScopeDispose(() => {\n      window.removeEventListener('mousemove', handler)\n    })\n  })\n\n  return { x, y, stop: () => scope.stop() }\n  \u002F\u002F scope.stop() tears down ALL effects and runs all onScopeDispose callbacks.\n}\n\n\u002F\u002F ── In a component, \u003Cscript setup> creates an effectScope automatically ──\n\u002F\u002F All watch\u002FwatchEffect created in setup() are bound to the component's scope.\n\u002F\u002F Component unmount → scope.stop() → all effects disposed. No manual cleanup needed.\n",[31,2132,2133,2144,2148,2153,2158,2163,2168,2172,2182,2199,2216,2220,2234,2238,2252,2257,2268,2293,2303,2313,2318,2323,2327,2332,2343,2357,2361,2366,2370,2393,2398,2402,2406,2411,2417],{"__ignoreMap":29},[34,2134,2135,2137,2140,2142],{"class":36,"line":37},[34,2136,41],{"class":40},[34,2138,2139],{"class":44}," { effectScope, ref, watchEffect, onScopeDispose } ",[34,2141,48],{"class":40},[34,2143,52],{"class":51},[34,2145,2146],{"class":36,"line":55},[34,2147,59],{"emptyLinePlaceholder":58},[34,2149,2150],{"class":36,"line":62},[34,2151,2152],{"class":65},"\u002F\u002F ── effectScope: groups multiple effects for collective cleanup ──\n",[34,2154,2155],{"class":36,"line":69},[34,2156,2157],{"class":65},"\u002F\u002F When the scope is stopped, ALL effects inside are auto-disposed.\n",[34,2159,2160],{"class":36,"line":75},[34,2161,2162],{"class":65},"\u002F\u002F Critical for composables that create effects that must be cleaned up\n",[34,2164,2165],{"class":36,"line":81},[34,2166,2167],{"class":65},"\u002F\u002F when the hosting component unmounts (or when the scope is manually stopped).\n",[34,2169,2170],{"class":36,"line":86},[34,2171,59],{"emptyLinePlaceholder":58},[34,2173,2174,2177,2180],{"class":36,"line":92},[34,2175,2176],{"class":40},"function",[34,2178,2179],{"class":122}," useMouseTracker",[34,2181,1372],{"class":44},[34,2183,2184,2186,2189,2191,2193,2195,2197],{"class":36,"line":98},[34,2185,1890],{"class":40},[34,2187,2188],{"class":115}," x",[34,2190,119],{"class":40},[34,2192,315],{"class":122},[34,2194,318],{"class":44},[34,2196,129],{"class":115},[34,2198,631],{"class":44},[34,2200,2201,2203,2206,2208,2210,2212,2214],{"class":36,"line":104},[34,2202,1890],{"class":40},[34,2204,2205],{"class":115}," y",[34,2207,119],{"class":40},[34,2209,315],{"class":122},[34,2211,318],{"class":44},[34,2213,129],{"class":115},[34,2215,631],{"class":44},[34,2217,2218],{"class":36,"line":109},[34,2219,59],{"emptyLinePlaceholder":58},[34,2221,2222,2224,2227,2229,2232],{"class":36,"line":135},[34,2223,1890],{"class":40},[34,2225,2226],{"class":115}," scope",[34,2228,119],{"class":40},[34,2230,2231],{"class":122}," effectScope",[34,2233,1390],{"class":44},[34,2235,2236],{"class":36,"line":140},[34,2237,59],{"emptyLinePlaceholder":58},[34,2239,2240,2243,2246,2248,2250],{"class":36,"line":155},[34,2241,2242],{"class":44},"  scope.",[34,2244,2245],{"class":122},"run",[34,2247,146],{"class":44},[34,2249,149],{"class":40},[34,2251,152],{"class":44},[34,2253,2254],{"class":36,"line":161},[34,2255,2256],{"class":65},"    \u002F\u002F All effects created inside run() are registered with this scope.\n",[34,2258,2259,2262,2264,2266],{"class":36,"line":167},[34,2260,2261],{"class":122},"    watchEffect",[34,2263,146],{"class":44},[34,2265,149],{"class":40},[34,2267,152],{"class":44},[34,2269,2270,2273,2276,2278,2281,2284,2287,2289,2291],{"class":36,"line":179},[34,2271,2272],{"class":44},"      window.",[34,2274,2275],{"class":122},"addEventListener",[34,2277,318],{"class":44},[34,2279,2280],{"class":51},"'mousemove'",[34,2282,2283],{"class":44},", (",[34,2285,2286],{"class":1264},"e",[34,2288,1359],{"class":44},[34,2290,149],{"class":40},[34,2292,152],{"class":44},[34,2294,2295,2298,2300],{"class":36,"line":185},[34,2296,2297],{"class":44},"        x.value ",[34,2299,196],{"class":40},[34,2301,2302],{"class":44}," e.clientX\n",[34,2304,2305,2308,2310],{"class":36,"line":190},[34,2306,2307],{"class":44},"        y.value ",[34,2309,196],{"class":40},[34,2311,2312],{"class":44}," e.clientY\n",[34,2314,2315],{"class":36,"line":205},[34,2316,2317],{"class":44},"      })\n",[34,2319,2320],{"class":36,"line":210},[34,2321,2322],{"class":44},"    })\n",[34,2324,2325],{"class":36,"line":216},[34,2326,59],{"emptyLinePlaceholder":58},[34,2328,2329],{"class":36,"line":222},[34,2330,2331],{"class":65},"    \u002F\u002F onScopeDispose: runs when the parent scope stops — cleanup listeners.\n",[34,2333,2334,2337,2339,2341],{"class":36,"line":228},[34,2335,2336],{"class":122},"    onScopeDispose",[34,2338,146],{"class":44},[34,2340,149],{"class":40},[34,2342,152],{"class":44},[34,2344,2345,2347,2350,2352,2354],{"class":36,"line":234},[34,2346,2272],{"class":44},[34,2348,2349],{"class":122},"removeEventListener",[34,2351,318],{"class":44},[34,2353,2280],{"class":51},[34,2355,2356],{"class":44},", handler)\n",[34,2358,2359],{"class":36,"line":240},[34,2360,2322],{"class":44},[34,2362,2363],{"class":36,"line":246},[34,2364,2365],{"class":44},"  })\n",[34,2367,2368],{"class":36,"line":252},[34,2369,59],{"emptyLinePlaceholder":58},[34,2371,2372,2374,2377,2380,2383,2385,2388,2390],{"class":36,"line":517},[34,2373,1338],{"class":40},[34,2375,2376],{"class":44}," { x, y, ",[34,2378,2379],{"class":122},"stop",[34,2381,2382],{"class":44},": () ",[34,2384,149],{"class":40},[34,2386,2387],{"class":44}," scope.",[34,2389,2379],{"class":122},[34,2391,2392],{"class":44},"() }\n",[34,2394,2395],{"class":36,"line":523},[34,2396,2397],{"class":65},"  \u002F\u002F scope.stop() tears down ALL effects and runs all onScopeDispose callbacks.\n",[34,2399,2400],{"class":36,"line":541},[34,2401,1490],{"class":44},[34,2403,2404],{"class":36,"line":1512},[34,2405,59],{"emptyLinePlaceholder":58},[34,2407,2408],{"class":36,"line":1518},[34,2409,2410],{"class":65},"\u002F\u002F ── In a component, \u003Cscript setup> creates an effectScope automatically ──\n",[34,2412,2414],{"class":36,"line":2413},34,[34,2415,2416],{"class":65},"\u002F\u002F All watch\u002FwatchEffect created in setup() are bound to the component's scope.\n",[34,2418,2420],{"class":36,"line":2419},35,[34,2421,2422],{"class":65},"\u002F\u002F Component unmount → scope.stop() → all effects disposed. No manual cleanup needed.\n",[15,2424,2426],{"id":2425},"tips-tricks","💡 Tips & Tricks",[20,2428,2430],{"filename":2429,"language":23},"tips.ts",[25,2431,2433],{"className":27,"code":2432,"language":23,"meta":29,"style":29},"import { ref, unref, toValue, isRef, isReactive, isProxy, toRaw } from 'vue'\n\n\u002F\u002F ── 1. unref() vs toValue() — normalize refs to values ──\nconst r = ref(42)\nunref(r)           \u002F\u002F 42 — unwraps if ref, returns value as-is if not\ntoValue(r)         \u002F\u002F 42 — also unwraps refs AND getters (computed-like)\n\u002F\u002F toValue is newer (3.3+), handles: ref, computed, getter function, plain value.\n\n\u002F\u002F ── 2. toRaw() — get the underlying object behind a reactive Proxy ──\nconst state = reactive({ a: 1 })\ntoRaw(state)  \u002F\u002F { a: 1 } — the original object, no Proxy wrapper\n\u002F\u002F Useful for passing reactive state to libraries that don't expect Proxies.\n\n\u002F\u002F ── 3. isProxy() \u002F isReactive() \u002F isReadonly() — type guards ──\nisProxy(state)      \u002F\u002F true — it's a reactive or readonly proxy\nisReactive(state)   \u002F\u002F true — it's a reactive proxy\nisReadonly(state)   \u002F\u002F false\n\n\u002F\u002F ── 4. ref unwrapping in reactive() is shallow at one level ──\n\u002F\u002F reactive({ count: ref(0) }).count → 0 (unwrapped)\n\u002F\u002F But reactive({ nested: { count: ref(0) } }).nested.count → ref(0) (NOT unwrapped)\n\u002F\u002F Unwrapping only happens at the direct property level, not nested.\n",[31,2434,2435,2446,2450,2455,2473,2484,2495,2500,2504,2509,2526,2537,2542,2546,2551,2562,2572,2582,2586,2591,2596,2601],{"__ignoreMap":29},[34,2436,2437,2439,2442,2444],{"class":36,"line":37},[34,2438,41],{"class":40},[34,2440,2441],{"class":44}," { ref, unref, toValue, isRef, isReactive, isProxy, toRaw } ",[34,2443,48],{"class":40},[34,2445,52],{"class":51},[34,2447,2448],{"class":36,"line":55},[34,2449,59],{"emptyLinePlaceholder":58},[34,2451,2452],{"class":36,"line":62},[34,2453,2454],{"class":65},"\u002F\u002F ── 1. unref() vs toValue() — normalize refs to values ──\n",[34,2456,2457,2459,2462,2464,2466,2468,2471],{"class":36,"line":69},[34,2458,112],{"class":40},[34,2460,2461],{"class":115}," r",[34,2463,119],{"class":40},[34,2465,315],{"class":122},[34,2467,318],{"class":44},[34,2469,2470],{"class":115},"42",[34,2472,631],{"class":44},[34,2474,2475,2478,2481],{"class":36,"line":75},[34,2476,2477],{"class":122},"unref",[34,2479,2480],{"class":44},"(r)           ",[34,2482,2483],{"class":65},"\u002F\u002F 42 — unwraps if ref, returns value as-is if not\n",[34,2485,2486,2489,2492],{"class":36,"line":81},[34,2487,2488],{"class":122},"toValue",[34,2490,2491],{"class":44},"(r)         ",[34,2493,2494],{"class":65},"\u002F\u002F 42 — also unwraps refs AND getters (computed-like)\n",[34,2496,2497],{"class":36,"line":86},[34,2498,2499],{"class":65},"\u002F\u002F toValue is newer (3.3+), handles: ref, computed, getter function, plain value.\n",[34,2501,2502],{"class":36,"line":92},[34,2503,59],{"emptyLinePlaceholder":58},[34,2505,2506],{"class":36,"line":98},[34,2507,2508],{"class":65},"\u002F\u002F ── 2. toRaw() — get the underlying object behind a reactive Proxy ──\n",[34,2510,2511,2513,2515,2517,2519,2522,2524],{"class":36,"line":104},[34,2512,112],{"class":40},[34,2514,116],{"class":115},[34,2516,119],{"class":40},[34,2518,123],{"class":122},[34,2520,2521],{"class":44},"({ a: ",[34,2523,742],{"class":115},[34,2525,132],{"class":44},[34,2527,2528,2531,2534],{"class":36,"line":109},[34,2529,2530],{"class":122},"toRaw",[34,2532,2533],{"class":44},"(state)  ",[34,2535,2536],{"class":65},"\u002F\u002F { a: 1 } — the original object, no Proxy wrapper\n",[34,2538,2539],{"class":36,"line":135},[34,2540,2541],{"class":65},"\u002F\u002F Useful for passing reactive state to libraries that don't expect Proxies.\n",[34,2543,2544],{"class":36,"line":140},[34,2545,59],{"emptyLinePlaceholder":58},[34,2547,2548],{"class":36,"line":155},[34,2549,2550],{"class":65},"\u002F\u002F ── 3. isProxy() \u002F isReactive() \u002F isReadonly() — type guards ──\n",[34,2552,2553,2556,2559],{"class":36,"line":161},[34,2554,2555],{"class":122},"isProxy",[34,2557,2558],{"class":44},"(state)      ",[34,2560,2561],{"class":65},"\u002F\u002F true — it's a reactive or readonly proxy\n",[34,2563,2564,2566,2569],{"class":36,"line":167},[34,2565,2071],{"class":122},[34,2567,2568],{"class":44},"(state)   ",[34,2570,2571],{"class":65},"\u002F\u002F true — it's a reactive proxy\n",[34,2573,2574,2577,2579],{"class":36,"line":179},[34,2575,2576],{"class":122},"isReadonly",[34,2578,2568],{"class":44},[34,2580,2581],{"class":65},"\u002F\u002F false\n",[34,2583,2584],{"class":36,"line":185},[34,2585,59],{"emptyLinePlaceholder":58},[34,2587,2588],{"class":36,"line":190},[34,2589,2590],{"class":65},"\u002F\u002F ── 4. ref unwrapping in reactive() is shallow at one level ──\n",[34,2592,2593],{"class":36,"line":205},[34,2594,2595],{"class":65},"\u002F\u002F reactive({ count: ref(0) }).count → 0 (unwrapped)\n",[34,2597,2598],{"class":36,"line":210},[34,2599,2600],{"class":65},"\u002F\u002F But reactive({ nested: { count: ref(0) } }).nested.count → ref(0) (NOT unwrapped)\n",[34,2602,2603],{"class":36,"line":216},[34,2604,2605],{"class":65},"\u002F\u002F Unwrapping only happens at the direct property level, not nested.\n",[15,2607,2609],{"id":2608},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,2611,2613],{"filename":2612,"language":23},"edge-cases.ts",[25,2614,2616],{"className":27,"code":2615,"language":23,"meta":29,"style":29},"import { ref, reactive, computed, watch } from 'vue'\n\n\u002F\u002F ── 1. reactive() + destructuring = lost reactivity ──\nconst s = reactive({ a: 1 })\nconst { a } = s  \u002F\u002F a is now 1 (plain number), NOT reactive\ns.a = 2          \u002F\u002F the local `a` is still 1 — no connection\n\u002F\u002F Fix: use toRefs() before destructuring.\n\n\u002F\u002F ── 2. ref() in reactive arrays is NOT unwrapped ──\nconst arr = reactive([ref(1), ref(2)])\narr[0]  \u002F\u002F ref(1), NOT 1 — arrays don't unwrap nested refs\n\u002F\u002F Only direct properties of reactive objects unwrap. Arrays, Maps don't.\n\n\u002F\u002F ── 3. Reassigning a reactive() variable breaks the Proxy ──\nlet state = reactive({ count: 0 })\nstate = reactive({ count: 1 })  \u002F\u002F old Proxy orphaned, bindings lost\n\u002F\u002F Fix: use ref() if you need to replace the whole value.\n\n\u002F\u002F ── 4. computed() is lazy — won't run until read ──\nconst expensive = computed(() => heavyCalc(state.count))\n\u002F\u002F If nothing reads expensive.value, heavyCalc never runs.\n\u002F\u002F To force eager evaluation, use watchEffect (immediate, not lazy).\n\n\u002F\u002F ── 5. Circular references in reactive objects ──\nconst a = reactive({})\nconst b = reactive({ parent: a })\na.child = b  \u002F\u002F circular ref — Vue handles this fine (Proxy doesn't recurse on creation)\n\u002F\u002F But watch with deep:true on a circular structure can cause infinite loops.\n\n\u002F\u002F ── 6. Map\u002FSet inside reactive() — methods return reactive versions ──\nconst map = reactive(new Map([['k', { v: 1 }]]))\nmap.get('k')  \u002F\u002F returns a REACTIVE proxy of the stored object\n\u002F\u002F This is why Map\u002FSet work with reactivity in Vue 3 — Vue wraps values on get.\n",[31,2617,2618,2629,2633,2638,2655,2674,2686,2691,2695,2700,2730,2743,2748,2752,2757,2774,2793,2798,2802,2807,2828,2833,2838,2842,2847,2861,2875,2888,2893,2897,2902,2932,2949],{"__ignoreMap":29},[34,2619,2620,2622,2625,2627],{"class":36,"line":37},[34,2621,41],{"class":40},[34,2623,2624],{"class":44}," { ref, reactive, computed, watch } ",[34,2626,48],{"class":40},[34,2628,52],{"class":51},[34,2630,2631],{"class":36,"line":55},[34,2632,59],{"emptyLinePlaceholder":58},[34,2634,2635],{"class":36,"line":62},[34,2636,2637],{"class":65},"\u002F\u002F ── 1. reactive() + destructuring = lost reactivity ──\n",[34,2639,2640,2642,2645,2647,2649,2651,2653],{"class":36,"line":69},[34,2641,112],{"class":40},[34,2643,2644],{"class":115}," s",[34,2646,119],{"class":40},[34,2648,123],{"class":122},[34,2650,2521],{"class":44},[34,2652,742],{"class":115},[34,2654,132],{"class":44},[34,2656,2657,2659,2661,2664,2666,2668,2671],{"class":36,"line":75},[34,2658,112],{"class":40},[34,2660,971],{"class":44},[34,2662,2663],{"class":115},"a",[34,2665,983],{"class":44},[34,2667,196],{"class":40},[34,2669,2670],{"class":44}," s  ",[34,2672,2673],{"class":65},"\u002F\u002F a is now 1 (plain number), NOT reactive\n",[34,2675,2676,2679,2681,2683],{"class":36,"line":81},[34,2677,2678],{"class":44},"s.a ",[34,2680,196],{"class":40},[34,2682,362],{"class":115},[34,2684,2685],{"class":65},"          \u002F\u002F the local `a` is still 1 — no connection\n",[34,2687,2688],{"class":36,"line":86},[34,2689,2690],{"class":65},"\u002F\u002F Fix: use toRefs() before destructuring.\n",[34,2692,2693],{"class":36,"line":92},[34,2694,59],{"emptyLinePlaceholder":58},[34,2696,2697],{"class":36,"line":98},[34,2698,2699],{"class":65},"\u002F\u002F ── 2. ref() in reactive arrays is NOT unwrapped ──\n",[34,2701,2702,2704,2707,2709,2711,2714,2716,2718,2720,2722,2724,2726,2728],{"class":36,"line":104},[34,2703,112],{"class":40},[34,2705,2706],{"class":115}," arr",[34,2708,119],{"class":40},[34,2710,123],{"class":122},[34,2712,2713],{"class":44},"([",[34,2715,737],{"class":122},[34,2717,318],{"class":44},[34,2719,742],{"class":115},[34,2721,745],{"class":44},[34,2723,737],{"class":122},[34,2725,318],{"class":44},[34,2727,752],{"class":115},[34,2729,755],{"class":44},[34,2731,2732,2735,2737,2740],{"class":36,"line":109},[34,2733,2734],{"class":44},"arr[",[34,2736,129],{"class":115},[34,2738,2739],{"class":44},"]  ",[34,2741,2742],{"class":65},"\u002F\u002F ref(1), NOT 1 — arrays don't unwrap nested refs\n",[34,2744,2745],{"class":36,"line":135},[34,2746,2747],{"class":65},"\u002F\u002F Only direct properties of reactive objects unwrap. Arrays, Maps don't.\n",[34,2749,2750],{"class":36,"line":140},[34,2751,59],{"emptyLinePlaceholder":58},[34,2753,2754],{"class":36,"line":155},[34,2755,2756],{"class":65},"\u002F\u002F ── 3. Reassigning a reactive() variable breaks the Proxy ──\n",[34,2758,2759,2761,2764,2766,2768,2770,2772],{"class":36,"line":161},[34,2760,486],{"class":40},[34,2762,2763],{"class":44}," state ",[34,2765,196],{"class":40},[34,2767,123],{"class":122},[34,2769,126],{"class":44},[34,2771,129],{"class":115},[34,2773,132],{"class":44},[34,2775,2776,2779,2781,2783,2785,2787,2790],{"class":36,"line":167},[34,2777,2778],{"class":44},"state ",[34,2780,196],{"class":40},[34,2782,123],{"class":122},[34,2784,126],{"class":44},[34,2786,742],{"class":115},[34,2788,2789],{"class":44}," })  ",[34,2791,2792],{"class":65},"\u002F\u002F old Proxy orphaned, bindings lost\n",[34,2794,2795],{"class":36,"line":179},[34,2796,2797],{"class":65},"\u002F\u002F Fix: use ref() if you need to replace the whole value.\n",[34,2799,2800],{"class":36,"line":185},[34,2801,59],{"emptyLinePlaceholder":58},[34,2803,2804],{"class":36,"line":190},[34,2805,2806],{"class":65},"\u002F\u002F ── 4. computed() is lazy — won't run until read ──\n",[34,2808,2809,2811,2814,2816,2818,2820,2822,2825],{"class":36,"line":205},[34,2810,112],{"class":40},[34,2812,2813],{"class":115}," expensive",[34,2815,119],{"class":40},[34,2817,349],{"class":122},[34,2819,146],{"class":44},[34,2821,149],{"class":40},[34,2823,2824],{"class":122}," heavyCalc",[34,2826,2827],{"class":44},"(state.count))\n",[34,2829,2830],{"class":36,"line":210},[34,2831,2832],{"class":65},"\u002F\u002F If nothing reads expensive.value, heavyCalc never runs.\n",[34,2834,2835],{"class":36,"line":216},[34,2836,2837],{"class":65},"\u002F\u002F To force eager evaluation, use watchEffect (immediate, not lazy).\n",[34,2839,2840],{"class":36,"line":222},[34,2841,59],{"emptyLinePlaceholder":58},[34,2843,2844],{"class":36,"line":228},[34,2845,2846],{"class":65},"\u002F\u002F ── 5. Circular references in reactive objects ──\n",[34,2848,2849,2851,2854,2856,2858],{"class":36,"line":234},[34,2850,112],{"class":40},[34,2852,2853],{"class":115}," a",[34,2855,119],{"class":40},[34,2857,123],{"class":122},[34,2859,2860],{"class":44},"({})\n",[34,2862,2863,2865,2868,2870,2872],{"class":36,"line":240},[34,2864,112],{"class":40},[34,2866,2867],{"class":115}," b",[34,2869,119],{"class":40},[34,2871,123],{"class":122},[34,2873,2874],{"class":44},"({ parent: a })\n",[34,2876,2877,2880,2882,2885],{"class":36,"line":246},[34,2878,2879],{"class":44},"a.child ",[34,2881,196],{"class":40},[34,2883,2884],{"class":44}," b  ",[34,2886,2887],{"class":65},"\u002F\u002F circular ref — Vue handles this fine (Proxy doesn't recurse on creation)\n",[34,2889,2890],{"class":36,"line":252},[34,2891,2892],{"class":65},"\u002F\u002F But watch with deep:true on a circular structure can cause infinite loops.\n",[34,2894,2895],{"class":36,"line":517},[34,2896,59],{"emptyLinePlaceholder":58},[34,2898,2899],{"class":36,"line":523},[34,2900,2901],{"class":65},"\u002F\u002F ── 6. Map\u002FSet inside reactive() — methods return reactive versions ──\n",[34,2903,2904,2906,2909,2911,2913,2915,2917,2919,2921,2924,2927,2929],{"class":36,"line":541},[34,2905,112],{"class":40},[34,2907,2908],{"class":115}," map",[34,2910,119],{"class":40},[34,2912,123],{"class":122},[34,2914,318],{"class":44},[34,2916,2029],{"class":40},[34,2918,2032],{"class":122},[34,2920,2035],{"class":44},[34,2922,2923],{"class":51},"'k'",[34,2925,2926],{"class":44},", { v: ",[34,2928,742],{"class":115},[34,2930,2931],{"class":44}," }]]))\n",[34,2933,2934,2937,2940,2942,2944,2946],{"class":36,"line":1512},[34,2935,2936],{"class":44},"map.",[34,2938,2939],{"class":122},"get",[34,2941,318],{"class":44},[34,2943,2923],{"class":51},[34,2945,365],{"class":44},[34,2947,2948],{"class":65},"\u002F\u002F returns a REACTIVE proxy of the stored object\n",[34,2950,2951],{"class":36,"line":1518},[34,2952,2953],{"class":65},"\u002F\u002F This is why Map\u002FSet work with reactivity in Vue 3 — Vue wraps values on get.\n",[15,2955,2957],{"id":2956},"spot-the-bug","🧠 Spot the Bug",[828,2959,2960],{},"A developer destructures a reactive form object and loses reactivity in the extracted fields.",[20,2962,2964],{"filename":2963,"language":23},"DestructuringBug.ts",[25,2965,2967],{"className":27,"code":2966,"language":23,"meta":29,"style":29},"import { reactive, watchEffect } from 'vue'\n\nconst form = reactive({ email: '', password: '' })\n\nfunction handleSubmit() {\n  const { email, password } = form  \u002F\u002F plain strings — disconnected from Proxy\n  api.login(email, password)        \u002F\u002F always sees initial '' values\n}\n\nwatchEffect(() => {\n  \u002F\u002F This effect reads `form.email` directly — still reactive\n  console.log('email changed:', form.email)\n})\n",[31,2968,2969,2979,2983,3005,3009,3018,3042,3056,3060,3064,3074,3079,3093],{"__ignoreMap":29},[34,2970,2971,2973,2975,2977],{"class":36,"line":37},[34,2972,41],{"class":40},[34,2974,926],{"class":44},[34,2976,48],{"class":40},[34,2978,52],{"class":51},[34,2980,2981],{"class":36,"line":55},[34,2982,59],{"emptyLinePlaceholder":58},[34,2984,2985,2987,2989,2991,2993,2996,2998,3001,3003],{"class":36,"line":62},[34,2986,112],{"class":40},[34,2988,394],{"class":115},[34,2990,119],{"class":40},[34,2992,123],{"class":122},[34,2994,2995],{"class":44},"({ email: ",[34,2997,409],{"class":51},[34,2999,3000],{"class":44},", password: ",[34,3002,409],{"class":51},[34,3004,132],{"class":44},[34,3006,3007],{"class":36,"line":69},[34,3008,59],{"emptyLinePlaceholder":58},[34,3010,3011,3013,3016],{"class":36,"line":75},[34,3012,2176],{"class":40},[34,3014,3015],{"class":122}," handleSubmit",[34,3017,1372],{"class":44},[34,3019,3020,3022,3024,3027,3029,3032,3034,3036,3039],{"class":36,"line":81},[34,3021,1890],{"class":40},[34,3023,971],{"class":44},[34,3025,3026],{"class":115},"email",[34,3028,977],{"class":44},[34,3030,3031],{"class":115},"password",[34,3033,983],{"class":44},[34,3035,196],{"class":40},[34,3037,3038],{"class":44}," form  ",[34,3040,3041],{"class":65},"\u002F\u002F plain strings — disconnected from Proxy\n",[34,3043,3044,3047,3050,3053],{"class":36,"line":86},[34,3045,3046],{"class":44},"  api.",[34,3048,3049],{"class":122},"login",[34,3051,3052],{"class":44},"(email, password)        ",[34,3054,3055],{"class":65},"\u002F\u002F always sees initial '' values\n",[34,3057,3058],{"class":36,"line":92},[34,3059,1490],{"class":44},[34,3061,3062],{"class":36,"line":98},[34,3063,59],{"emptyLinePlaceholder":58},[34,3065,3066,3068,3070,3072],{"class":36,"line":104},[34,3067,1003],{"class":122},[34,3069,146],{"class":44},[34,3071,149],{"class":40},[34,3073,152],{"class":44},[34,3075,3076],{"class":36,"line":109},[34,3077,3078],{"class":65},"  \u002F\u002F This effect reads `form.email` directly — still reactive\n",[34,3080,3081,3083,3085,3087,3090],{"class":36,"line":135},[34,3082,170],{"class":44},[34,3084,173],{"class":122},[34,3086,318],{"class":44},[34,3088,3089],{"class":51},"'email changed:'",[34,3091,3092],{"class":44},", form.email)\n",[34,3094,3095],{"class":36,"line":140},[34,3096,182],{"class":44},[3098,3099,3100,3104,3118,3129,3232],"details",{},[3101,3102,3103],"summary",{},"Answer",[828,3105,3106,3107,3111,3112,3114,3115,3117],{},"Destructuring a reactive object copies the ",[3108,3109,3110],"em",{},"current values"," out of the Proxy. The local ",[31,3113,3026],{}," and ",[31,3116,3031],{}," are plain strings with no connection to the reactive system — they're snapshots, not live references.",[828,3119,3120,3124,3125,3128],{},[3121,3122,3123],"strong",{},"Fix"," — use ",[31,3126,3127],{},"toRefs()"," to create refs that read\u002Fwrite through the original Proxy:",[20,3130,3132],{"filename":3131,"language":23},"DestructuringFixed.ts",[25,3133,3135],{"className":27,"code":3134,"language":23,"meta":29,"style":29},"import { reactive, toRefs } from 'vue'\n\nconst form = reactive({ email: '', password: '' })\n\n\u002F\u002F toRefs creates ref wrappers that delegate to the original reactive object\nconst { email, password } = toRefs(form)\n\nfunction handleSubmit() {\n  \u002F\u002F email.value and password.value read through the Proxy — always current\n  api.login(email.value, password.value)\n}\n",[31,3136,3137,3148,3152,3172,3176,3181,3202,3206,3214,3219,3228],{"__ignoreMap":29},[34,3138,3139,3141,3144,3146],{"class":36,"line":37},[34,3140,41],{"class":40},[34,3142,3143],{"class":44}," { reactive, toRefs } ",[34,3145,48],{"class":40},[34,3147,52],{"class":51},[34,3149,3150],{"class":36,"line":55},[34,3151,59],{"emptyLinePlaceholder":58},[34,3153,3154,3156,3158,3160,3162,3164,3166,3168,3170],{"class":36,"line":62},[34,3155,112],{"class":40},[34,3157,394],{"class":115},[34,3159,119],{"class":40},[34,3161,123],{"class":122},[34,3163,2995],{"class":44},[34,3165,409],{"class":51},[34,3167,3000],{"class":44},[34,3169,409],{"class":51},[34,3171,132],{"class":44},[34,3173,3174],{"class":36,"line":69},[34,3175,59],{"emptyLinePlaceholder":58},[34,3177,3178],{"class":36,"line":75},[34,3179,3180],{"class":65},"\u002F\u002F toRefs creates ref wrappers that delegate to the original reactive object\n",[34,3182,3183,3185,3187,3189,3191,3193,3195,3197,3199],{"class":36,"line":81},[34,3184,112],{"class":40},[34,3186,971],{"class":44},[34,3188,3026],{"class":115},[34,3190,977],{"class":44},[34,3192,3031],{"class":115},[34,3194,983],{"class":44},[34,3196,196],{"class":40},[34,3198,1113],{"class":122},[34,3200,3201],{"class":44},"(form)\n",[34,3203,3204],{"class":36,"line":86},[34,3205,59],{"emptyLinePlaceholder":58},[34,3207,3208,3210,3212],{"class":36,"line":92},[34,3209,2176],{"class":40},[34,3211,3015],{"class":122},[34,3213,1372],{"class":44},[34,3215,3216],{"class":36,"line":98},[34,3217,3218],{"class":65},"  \u002F\u002F email.value and password.value read through the Proxy — always current\n",[34,3220,3221,3223,3225],{"class":36,"line":104},[34,3222,3046],{"class":44},[34,3224,3049],{"class":122},[34,3226,3227],{"class":44},"(email.value, password.value)\n",[34,3229,3230],{"class":36,"line":109},[34,3231,1490],{"class":44},[828,3233,3234,3237,3238,3241,3242,3244],{},[3121,3235,3236],{},"The lesson",": destructuring a ",[31,3239,3240],{},"reactive()"," object severs the reactive link. ",[31,3243,3127],{}," repairs it by creating per-property refs that proxy through to the original object's getters\u002Fsetters.",[3246,3247,3248],"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 .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}",{"title":29,"searchDepth":55,"depth":55,"links":3250},[3251,3252,3253,3256,3257,3258,3259,3260,3261,3262,3263],{"id":17,"depth":55,"text":18},{"id":258,"depth":55,"text":259},{"id":561,"depth":55,"text":562,"children":3254},[3255],{"id":910,"depth":62,"text":911},{"id":1199,"depth":55,"text":1200},{"id":1524,"depth":55,"text":1525},{"id":1708,"depth":55,"text":1709},{"id":1978,"depth":55,"text":1979},{"id":2123,"depth":55,"text":2124},{"id":2425,"depth":55,"text":2426},{"id":2608,"depth":55,"text":2609},{"id":2956,"depth":55,"text":2957},"Proxy-based reactivity internals — track\u002Ftrigger dependency graph, ref vs reactive unwrapping, destructuring traps, toRef\u002FtoRefs ref creation, shallowRef for large data, custom ref factories.","md",{},"\u002Fvue\u002F03-reactivity-fundamentals",{"title":5,"description":3264},"vue\u002F03-reactivity-fundamentals","z2hmE50FrCcuIDB_O_JsnbZrE057T9GY6ojJJeF8yrA",1789924655370]