[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Ftrack":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Ftrack":1668,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Ftrack":1693},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":15,"pinned":16,"draft":16,"body":17,"seo":1664,"stem":1665,"id":1666,"extension":1667},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Ftrack","依赖收集与派发更新机制","说明 Vue 3 中 track \u002F trigger 与 targetMap 的工作方式。前置：v2-v3 差异。","技能小册",[12,13,14],"vue","reactivity","track","2025-01-13",false,{"type":18,"value":19,"toc":1637},"minimark",[20,23,45,50,62,67,91,290,296,306,340,348,355,370,535,544,548,555,559,733,740,757,779,941,946,953,977,988,994,1035,1041,1093,1101,1341,1345,1458,1462,1526,1530,1573,1576,1579,1582,1633],[21,22,8],"h1",{"id":8},[24,25,26,27,30,31,34,35,38,39,44],"p",{},"说明 Vue 3 中 ",[28,29,14],"code",{}," \u002F ",[28,32,33],{},"trigger"," 与 ",[28,36,37],{},"targetMap"," 的工作方式。前置：",[40,41,43],"a",{"href":42},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fv2-v3","v2-v3 差异","。",[46,47,49],"h2",{"id":48},"一依赖收集track","一、依赖收集（Track）",[24,51,52,53,57,58,61],{},"依赖收集的目的是建立 ",[54,55,56],"strong",{},"数据 → 副作用函数（effect）"," 的映射关系，使得当数据变化时，能够自动执行相关的副作用（如组件渲染、计算属性、",[28,59,60],{},"watch","）。",[63,64,66],"h3",{"id":65},"_1-触发时机在-getter-拦截中","1. 触发时机：在 getter 拦截中",[24,68,69,70,73,74,77,78,81,82,84,85,87,88,90],{},"Vue 3 使用 ",[28,71,72],{},"Proxy"," 拦截对象属性的读取操作（",[28,75,76],{},"get"," 陷阱）。当组件渲染、",[28,79,80],{},"computed"," 或 ",[28,83,60],{}," 运行时，会读取响应式数据，从而触发 ",[28,86,76],{}," 拦截器，",[28,89,14],{}," 函数被调用。",[92,93,98],"pre",{"className":94,"code":95,"language":96,"meta":97,"style":97},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F 简化逻辑\nfunction createGetter() {\n  return function get(target, key, receiver) {\n    const res = Reflect.get(target, key, receiver);\n    \u002F\u002F 依赖收集\n    track(target, key);\n    \u002F\u002F 如果是对象，递归代理（懒代理）\n    if (isObject(res)) return reactive(res);\n    return res;\n  };\n}\n","js","",[28,99,100,109,127,163,204,210,228,234,268,278,284],{"__ignoreMap":97},[101,102,105],"span",{"class":103,"line":104},"line",1,[101,106,108],{"class":107},"sutJx","\u002F\u002F 简化逻辑\n",[101,110,112,116,120,124],{"class":103,"line":111},2,[101,113,115],{"class":114},"sbsja","function",[101,117,119],{"class":118},"sGLFI"," createGetter",[101,121,123],{"class":122},"sP7_E","()",[101,125,126],{"class":122}," {\n",[101,128,130,134,137,140,143,147,150,153,155,158,161],{"class":103,"line":129},3,[101,131,133],{"class":132},"sVHd0","  return",[101,135,136],{"class":114}," function",[101,138,139],{"class":118}," get",[101,141,142],{"class":122},"(",[101,144,146],{"class":145},"s99_P","target",[101,148,149],{"class":122},",",[101,151,152],{"class":145}," key",[101,154,149],{"class":122},[101,156,157],{"class":145}," receiver",[101,159,160],{"class":122},")",[101,162,126],{"class":122},[101,164,166,169,173,177,181,184,186,189,191,193,195,197,199,201],{"class":103,"line":165},4,[101,167,168],{"class":114},"    const",[101,170,172],{"class":171},"s_hVV"," res",[101,174,176],{"class":175},"smGrS"," =",[101,178,180],{"class":179},"su5hD"," Reflect",[101,182,183],{"class":122},".",[101,185,76],{"class":118},[101,187,142],{"class":188},"skxfh",[101,190,146],{"class":179},[101,192,149],{"class":122},[101,194,152],{"class":179},[101,196,149],{"class":122},[101,198,157],{"class":179},[101,200,160],{"class":188},[101,202,203],{"class":122},";\n",[101,205,207],{"class":103,"line":206},5,[101,208,209],{"class":107},"    \u002F\u002F 依赖收集\n",[101,211,213,216,218,220,222,224,226],{"class":103,"line":212},6,[101,214,215],{"class":118},"    track",[101,217,142],{"class":188},[101,219,146],{"class":179},[101,221,149],{"class":122},[101,223,152],{"class":179},[101,225,160],{"class":188},[101,227,203],{"class":122},[101,229,231],{"class":103,"line":230},7,[101,232,233],{"class":107},"    \u002F\u002F 如果是对象，递归代理（懒代理）\n",[101,235,237,240,243,246,248,251,254,257,260,262,264,266],{"class":103,"line":236},8,[101,238,239],{"class":132},"    if",[101,241,242],{"class":188}," (",[101,244,245],{"class":118},"isObject",[101,247,142],{"class":188},[101,249,250],{"class":179},"res",[101,252,253],{"class":188},")) ",[101,255,256],{"class":132},"return",[101,258,259],{"class":118}," reactive",[101,261,142],{"class":188},[101,263,250],{"class":179},[101,265,160],{"class":188},[101,267,203],{"class":122},[101,269,271,274,276],{"class":103,"line":270},9,[101,272,273],{"class":132},"    return",[101,275,172],{"class":179},[101,277,203],{"class":122},[101,279,281],{"class":103,"line":280},10,[101,282,283],{"class":122},"  };\n",[101,285,287],{"class":103,"line":286},11,[101,288,289],{"class":122},"}\n",[63,291,293,294],{"id":292},"_2-映射关系的存储结构targetmap","2. 映射关系的存储结构：",[28,295,37],{},[24,297,298,299,302,303,305],{},"Vue 3 维护了一个全局的 ",[28,300,301],{},"WeakMap","，名为 ",[28,304,37],{},"。其结构为：",[307,308,309,319],"ul",{},[310,311,312,315,316,318],"li",{},[54,313,314],{},"键","：原始对象（",[28,317,146],{},"）",[310,320,321,324,325,328,329,332,333,336,337,61],{},[54,322,323],{},"值","：一个 ",[28,326,327],{},"Map","，键为属性名（",[28,330,331],{},"key","），值为一个 ",[28,334,335],{},"Set"," 集合，存放依赖该属性的所有副作用函数（",[28,338,339],{},"effect",[92,341,346],{"className":342,"code":344,"language":345},[343],"language-text","targetMap (WeakMap)\n  ├─ target1 (obj) → Map\n  │   ├─ key1 → Set[effect1, effect2, ...]\n  │   └─ key2 → Set[effect3, ...]\n  └─ target2 → Map...\n","text",[28,347,344],{"__ignoreMap":97},[63,349,351,352],{"id":350},"_3-当前激活的副作用activeeffect","3. 当前激活的副作用：",[28,353,354],{},"activeEffect",[24,356,357,358,360,361,363,364,366,367,369],{},"为了知道当前正在执行的副作用是哪一个，Vue 3 使用了一个全局变量 ",[28,359,354],{},"。当执行 ",[28,362,339],{}," 函数时，会先将自身赋值给 ",[28,365,354],{},"，然后执行回调（回调中读取数据，触发 ",[28,368,14],{},"），最后重置。",[92,371,373],{"className":94,"code":372,"language":96,"meta":97,"style":97},"function effect(fn) {\n  const _effect = new ReactiveEffect(fn);\n  _effect.run(); \u002F\u002F 首次执行，触发依赖收集\n}\n\nclass ReactiveEffect {\n  constructor(fn) {\n    this.fn = fn;\n  }\n  run() {\n    activeEffect = this;\n    return this.fn();\n  }\n}\n",[28,374,375,391,415,433,437,443,453,466,483,488,498,510,525,530],{"__ignoreMap":97},[101,376,377,379,382,384,387,389],{"class":103,"line":104},[101,378,115],{"class":114},[101,380,381],{"class":118}," effect",[101,383,142],{"class":122},[101,385,386],{"class":145},"fn",[101,388,160],{"class":122},[101,390,126],{"class":122},[101,392,393,396,399,401,404,407,409,411,413],{"class":103,"line":111},[101,394,395],{"class":114},"  const",[101,397,398],{"class":171}," _effect",[101,400,176],{"class":175},[101,402,403],{"class":175}," new",[101,405,406],{"class":118}," ReactiveEffect",[101,408,142],{"class":188},[101,410,386],{"class":179},[101,412,160],{"class":188},[101,414,203],{"class":122},[101,416,417,420,422,425,427,430],{"class":103,"line":129},[101,418,419],{"class":179},"  _effect",[101,421,183],{"class":122},[101,423,424],{"class":118},"run",[101,426,123],{"class":188},[101,428,429],{"class":122},";",[101,431,432],{"class":107}," \u002F\u002F 首次执行，触发依赖收集\n",[101,434,435],{"class":103,"line":165},[101,436,289],{"class":122},[101,438,439],{"class":103,"line":206},[101,440,442],{"emptyLinePlaceholder":441},true,"\n",[101,444,445,448,451],{"class":103,"line":212},[101,446,447],{"class":114},"class",[101,449,406],{"class":450},"sbgvK",[101,452,126],{"class":122},[101,454,455,458,460,462,464],{"class":103,"line":230},[101,456,457],{"class":114},"  constructor",[101,459,142],{"class":122},[101,461,386],{"class":145},[101,463,160],{"class":122},[101,465,126],{"class":122},[101,467,468,472,474,476,478,481],{"class":103,"line":236},[101,469,471],{"class":470},"s39Yj","    this",[101,473,183],{"class":122},[101,475,386],{"class":179},[101,477,176],{"class":175},[101,479,480],{"class":179}," fn",[101,482,203],{"class":122},[101,484,485],{"class":103,"line":270},[101,486,487],{"class":122},"  }\n",[101,489,490,494,496],{"class":103,"line":280},[101,491,493],{"class":492},"sVXei","  run",[101,495,123],{"class":122},[101,497,126],{"class":122},[101,499,500,503,505,508],{"class":103,"line":286},[101,501,502],{"class":179},"    activeEffect",[101,504,176],{"class":175},[101,506,507],{"class":470}," this",[101,509,203],{"class":122},[101,511,513,515,517,519,521,523],{"class":103,"line":512},12,[101,514,273],{"class":132},[101,516,507],{"class":470},[101,518,183],{"class":122},[101,520,386],{"class":118},[101,522,123],{"class":188},[101,524,203],{"class":122},[101,526,528],{"class":103,"line":527},13,[101,529,487],{"class":122},[101,531,533],{"class":103,"line":532},14,[101,534,289],{"class":122},[24,536,537,538,540,541,543],{},"在 ",[28,539,14],{}," 中，如果存在 ",[28,542,354],{},"，就将它添加到对应属性的依赖集合中。",[46,545,547],{"id":546},"二派发更新trigger","二、派发更新（Trigger）",[24,549,550,551,554],{},"当响应式数据被修改时（触发 ",[28,552,553],{},"set"," 拦截器），Vue 3 会找出依赖该数据的所有副作用函数，并重新执行它们。",[63,556,558],{"id":557},"_1-触发时机在-setter-拦截中","1. 触发时机：在 setter 拦截中",[92,560,562],{"className":94,"code":561,"language":96,"meta":97,"style":97},"function createSetter() {\n  return function set(target, key, value, receiver) {\n    const oldValue = target[key];\n    const result = Reflect.set(target, key, value, receiver);\n    \u002F\u002F 如果值真的变化了，触发更新\n    if (oldValue !== value) {\n      trigger(target, key, value, oldValue);\n    }\n    return result;\n  };\n}\n",[28,563,564,575,605,627,662,667,687,712,717,725,729],{"__ignoreMap":97},[101,565,566,568,571,573],{"class":103,"line":104},[101,567,115],{"class":114},[101,569,570],{"class":118}," createSetter",[101,572,123],{"class":122},[101,574,126],{"class":122},[101,576,577,579,581,584,586,588,590,592,594,597,599,601,603],{"class":103,"line":111},[101,578,133],{"class":132},[101,580,136],{"class":114},[101,582,583],{"class":118}," set",[101,585,142],{"class":122},[101,587,146],{"class":145},[101,589,149],{"class":122},[101,591,152],{"class":145},[101,593,149],{"class":122},[101,595,596],{"class":145}," value",[101,598,149],{"class":122},[101,600,157],{"class":145},[101,602,160],{"class":122},[101,604,126],{"class":122},[101,606,607,609,612,614,617,620,622,625],{"class":103,"line":129},[101,608,168],{"class":114},[101,610,611],{"class":171}," oldValue",[101,613,176],{"class":175},[101,615,616],{"class":179}," target",[101,618,619],{"class":188},"[",[101,621,331],{"class":179},[101,623,624],{"class":188},"]",[101,626,203],{"class":122},[101,628,629,631,634,636,638,640,642,644,646,648,650,652,654,656,658,660],{"class":103,"line":165},[101,630,168],{"class":114},[101,632,633],{"class":171}," result",[101,635,176],{"class":175},[101,637,180],{"class":179},[101,639,183],{"class":122},[101,641,553],{"class":118},[101,643,142],{"class":188},[101,645,146],{"class":179},[101,647,149],{"class":122},[101,649,152],{"class":179},[101,651,149],{"class":122},[101,653,596],{"class":179},[101,655,149],{"class":122},[101,657,157],{"class":179},[101,659,160],{"class":188},[101,661,203],{"class":122},[101,663,664],{"class":103,"line":206},[101,665,666],{"class":107},"    \u002F\u002F 如果值真的变化了，触发更新\n",[101,668,669,671,673,676,679,681,684],{"class":103,"line":212},[101,670,239],{"class":132},[101,672,242],{"class":188},[101,674,675],{"class":179},"oldValue",[101,677,678],{"class":175}," !==",[101,680,596],{"class":179},[101,682,683],{"class":188},") ",[101,685,686],{"class":122},"{\n",[101,688,689,692,694,696,698,700,702,704,706,708,710],{"class":103,"line":230},[101,690,691],{"class":118},"      trigger",[101,693,142],{"class":188},[101,695,146],{"class":179},[101,697,149],{"class":122},[101,699,152],{"class":179},[101,701,149],{"class":122},[101,703,596],{"class":179},[101,705,149],{"class":122},[101,707,611],{"class":179},[101,709,160],{"class":188},[101,711,203],{"class":122},[101,713,714],{"class":103,"line":236},[101,715,716],{"class":122},"    }\n",[101,718,719,721,723],{"class":103,"line":270},[101,720,273],{"class":132},[101,722,633],{"class":179},[101,724,203],{"class":122},[101,726,727],{"class":103,"line":280},[101,728,283],{"class":122},[101,730,731],{"class":103,"line":286},[101,732,289],{"class":122},[63,734,736,737],{"id":735},"_2-异步更新队列与-nexttick","2. 异步更新队列与 ",[28,738,739],{},"nextTick",[24,741,742,743,746,747,81,750,753,754,44],{},"Vue 3 并不会同步立即执行每个副作用，而是将需要执行的副作用",[54,744,745],{},"缓冲到一个队列","中，然后通过微任务（",[28,748,749],{},"Promise.then",[28,751,752],{},"queueMicrotask","）在下一轮事件循环统一执行。这称为 ",[54,755,756],{},"批量异步更新",[307,758,759,762,768],{},[310,760,761],{},"相同的副作用只会被加入队列一次（去重）。",[310,763,764,765,767],{},"通过 ",[28,766,739],{}," 可以在 DOM 更新后执行回调。",[310,769,770,771,774,775,778],{},"底层实现：",[28,772,773],{},"queueFlush"," 函数将 ",[28,776,777],{},"flushJobs"," 放入微任务队列。",[92,780,782],{"className":94,"code":781,"language":96,"meta":97,"style":97},"let isFlushPending = false;\nconst pendingPreFlushCbs = [];\nfunction queueJob(job) {\n  if (!pendingPreFlushCbs.includes(job)) {\n    pendingPreFlushCbs.push(job);\n    if (!isFlushPending) {\n      isFlushPending = true;\n      Promise.resolve().then(flushJobs);\n    }\n  }\n}\n",[28,783,784,801,816,832,858,876,891,903,929,933,937],{"__ignoreMap":97},[101,785,786,789,792,795,799],{"class":103,"line":104},[101,787,788],{"class":114},"let",[101,790,791],{"class":179}," isFlushPending ",[101,793,794],{"class":175},"=",[101,796,798],{"class":797},"syTEX"," false",[101,800,203],{"class":122},[101,802,803,806,809,811,814],{"class":103,"line":111},[101,804,805],{"class":114},"const",[101,807,808],{"class":171}," pendingPreFlushCbs",[101,810,176],{"class":175},[101,812,813],{"class":179}," []",[101,815,203],{"class":122},[101,817,818,820,823,825,828,830],{"class":103,"line":129},[101,819,115],{"class":114},[101,821,822],{"class":118}," queueJob",[101,824,142],{"class":122},[101,826,827],{"class":145},"job",[101,829,160],{"class":122},[101,831,126],{"class":122},[101,833,834,837,839,842,845,847,850,852,854,856],{"class":103,"line":165},[101,835,836],{"class":132},"  if",[101,838,242],{"class":188},[101,840,841],{"class":175},"!",[101,843,844],{"class":179},"pendingPreFlushCbs",[101,846,183],{"class":122},[101,848,849],{"class":118},"includes",[101,851,142],{"class":188},[101,853,827],{"class":179},[101,855,253],{"class":188},[101,857,686],{"class":122},[101,859,860,863,865,868,870,872,874],{"class":103,"line":206},[101,861,862],{"class":179},"    pendingPreFlushCbs",[101,864,183],{"class":122},[101,866,867],{"class":118},"push",[101,869,142],{"class":188},[101,871,827],{"class":179},[101,873,160],{"class":188},[101,875,203],{"class":122},[101,877,878,880,882,884,887,889],{"class":103,"line":212},[101,879,239],{"class":132},[101,881,242],{"class":188},[101,883,841],{"class":175},[101,885,886],{"class":179},"isFlushPending",[101,888,683],{"class":188},[101,890,686],{"class":122},[101,892,893,896,898,901],{"class":103,"line":230},[101,894,895],{"class":179},"      isFlushPending",[101,897,176],{"class":175},[101,899,900],{"class":797}," true",[101,902,203],{"class":122},[101,904,905,909,911,914,916,918,921,923,925,927],{"class":103,"line":236},[101,906,908],{"class":907},"sZMiF","      Promise",[101,910,183],{"class":122},[101,912,913],{"class":118},"resolve",[101,915,123],{"class":188},[101,917,183],{"class":122},[101,919,920],{"class":118},"then",[101,922,142],{"class":188},[101,924,777],{"class":179},[101,926,160],{"class":188},[101,928,203],{"class":122},[101,930,931],{"class":103,"line":270},[101,932,716],{"class":122},[101,934,935],{"class":103,"line":280},[101,936,487],{"class":122},[101,938,939],{"class":103,"line":286},[101,940,289],{"class":122},[24,942,943,945],{},[28,944,777],{}," 会将队列中的所有副作用按顺序执行，完成之后再清空。",[63,947,949,950,952],{"id":948},"_3-trigger-的查找过程","3. ",[28,951,33],{}," 的查找过程",[24,954,955,957,958,960,961,963,964,966,967,969,970,972,973,976],{},[28,956,33],{}," 根据被修改的 ",[28,959,146],{}," 和 ",[28,962,331],{},"，从 ",[28,965,37],{}," 中取出对应的依赖集合，遍历执行每个 ",[28,968,339],{},"。如果 ",[28,971,339],{}," 配置了 ",[28,974,975],{},"scheduler","（调度器），则会调用调度器（通常是放入队列）。",[46,978,980,981,34,984,987],{"id":979},"三ref-与-reactive-的底层实现差异","三、",[28,982,983],{},"ref",[28,985,986],{},"reactive"," 的底层实现差异",[63,989,991,992],{"id":990},"_1-reactive","1. ",[28,993,986],{},[307,995,996,1006,1015],{},[310,997,998,1001,1002,1005],{},[54,999,1000],{},"适用对象","：仅能代理",[54,1003,1004],{},"对象类型","（Object、Array、Map、Set 等）。",[310,1007,1008,1011,1012,1014],{},[54,1009,1010],{},"实现","：直接基于 ",[28,1013,72],{}," 包装原对象，返回一个代理对象。代理对象和原对象保持引用关系，所有属性访问都被拦截。",[310,1016,1017,1020,1021,1024,1025,1024,1028,1031,1032,1034],{},[54,1018,1019],{},"缺陷","：不能直接处理原始值（",[28,1022,1023],{},"number","、",[28,1026,1027],{},"string",[28,1029,1030],{},"boolean"," 等），因为 ",[28,1033,72],{}," 无法代理原始值。",[63,1036,1038,1039],{"id":1037},"_2-ref","2. ",[28,1040,983],{},[307,1042,1043,1051,1082],{},[310,1044,1045,1047,1048,44],{},[54,1046,1000],{},"：可以包装任何类型，尤其是",[54,1049,1050],{},"原始值",[310,1052,1053,1055,1056,1059,1060],{},[54,1054,1010],{},"：创建一个包含 ",[28,1057,1058],{},"value"," 属性的普通对象，并为该属性添加响应式逻辑。\n",[307,1061,1062,1068],{},[310,1063,1064,1065,1067],{},"如果传入的是对象，内部会调用 ",[28,1066,986],{}," 将该对象转换为响应式代理。",[310,1069,1070,1071,1074,1075,30,1078,1081],{},"如果传入的是原始值，则通过包装对象 ",[28,1072,1073],{},".value"," 的 getter\u002Fsetter 实现响应式（内部仍使用类似 ",[28,1076,1077],{},"trackRefValue",[28,1079,1080],{},"triggerRefValue"," 的独立依赖管理）。",[310,1083,1084,1087,1088,1090,1091,61],{},[54,1085,1086],{},"访问方式","：必须通过 ",[28,1089,1073],{}," 读取或修改（模板中自动解包，但脚本中需要 ",[28,1092,1073],{},[1094,1095,1097,1098,1100],"h4",{"id":1096},"简化版-ref-实现","简化版 ",[28,1099,983],{}," 实现",[92,1102,1104],{"className":94,"code":1103,"language":96,"meta":97,"style":97},"function ref(value) {\n  const r = {\n    _value: value,\n    get value() {\n      track(r, \"value\"); \u002F\u002F 依赖收集\n      return this._value;\n    },\n    set value(newVal) {\n      if (newVal !== this._value) {\n        this._value = newVal;\n        trigger(r, \"value\"); \u002F\u002F 派发更新\n      }\n    },\n  };\n  if (isObject(value)) r._value = reactive(value);\n  return r;\n}\n",[28,1105,1106,1121,1132,1145,1156,1185,1199,1204,1220,1241,1257,1281,1286,1290,1294,1327,1336],{"__ignoreMap":97},[101,1107,1108,1110,1113,1115,1117,1119],{"class":103,"line":104},[101,1109,115],{"class":114},[101,1111,1112],{"class":118}," ref",[101,1114,142],{"class":122},[101,1116,1058],{"class":145},[101,1118,160],{"class":122},[101,1120,126],{"class":122},[101,1122,1123,1125,1128,1130],{"class":103,"line":111},[101,1124,395],{"class":114},[101,1126,1127],{"class":171}," r",[101,1129,176],{"class":175},[101,1131,126],{"class":122},[101,1133,1134,1137,1140,1142],{"class":103,"line":129},[101,1135,1136],{"class":188},"    _value",[101,1138,1139],{"class":122},":",[101,1141,596],{"class":179},[101,1143,1144],{"class":122},",\n",[101,1146,1147,1150,1152,1154],{"class":103,"line":165},[101,1148,1149],{"class":114},"    get",[101,1151,596],{"class":492},[101,1153,123],{"class":122},[101,1155,126],{"class":122},[101,1157,1158,1161,1163,1166,1168,1172,1175,1178,1180,1182],{"class":103,"line":206},[101,1159,1160],{"class":118},"      track",[101,1162,142],{"class":188},[101,1164,1165],{"class":179},"r",[101,1167,149],{"class":122},[101,1169,1171],{"class":1170},"sjJ54"," \"",[101,1173,1058],{"class":1174},"s_sjI",[101,1176,1177],{"class":1170},"\"",[101,1179,160],{"class":188},[101,1181,429],{"class":122},[101,1183,1184],{"class":107}," \u002F\u002F 依赖收集\n",[101,1186,1187,1190,1192,1194,1197],{"class":103,"line":212},[101,1188,1189],{"class":132},"      return",[101,1191,507],{"class":470},[101,1193,183],{"class":122},[101,1195,1196],{"class":179},"_value",[101,1198,203],{"class":122},[101,1200,1201],{"class":103,"line":230},[101,1202,1203],{"class":122},"    },\n",[101,1205,1206,1209,1211,1213,1216,1218],{"class":103,"line":236},[101,1207,1208],{"class":114},"    set",[101,1210,596],{"class":492},[101,1212,142],{"class":122},[101,1214,1215],{"class":145},"newVal",[101,1217,160],{"class":122},[101,1219,126],{"class":122},[101,1221,1222,1225,1227,1229,1231,1233,1235,1237,1239],{"class":103,"line":270},[101,1223,1224],{"class":132},"      if",[101,1226,242],{"class":188},[101,1228,1215],{"class":179},[101,1230,678],{"class":175},[101,1232,507],{"class":470},[101,1234,183],{"class":122},[101,1236,1196],{"class":179},[101,1238,683],{"class":188},[101,1240,686],{"class":122},[101,1242,1243,1246,1248,1250,1252,1255],{"class":103,"line":280},[101,1244,1245],{"class":470},"        this",[101,1247,183],{"class":122},[101,1249,1196],{"class":179},[101,1251,176],{"class":175},[101,1253,1254],{"class":179}," newVal",[101,1256,203],{"class":122},[101,1258,1259,1262,1264,1266,1268,1270,1272,1274,1276,1278],{"class":103,"line":286},[101,1260,1261],{"class":118},"        trigger",[101,1263,142],{"class":188},[101,1265,1165],{"class":179},[101,1267,149],{"class":122},[101,1269,1171],{"class":1170},[101,1271,1058],{"class":1174},[101,1273,1177],{"class":1170},[101,1275,160],{"class":188},[101,1277,429],{"class":122},[101,1279,1280],{"class":107}," \u002F\u002F 派发更新\n",[101,1282,1283],{"class":103,"line":512},[101,1284,1285],{"class":122},"      }\n",[101,1287,1288],{"class":103,"line":527},[101,1289,1203],{"class":122},[101,1291,1292],{"class":103,"line":532},[101,1293,283],{"class":122},[101,1295,1297,1299,1301,1303,1305,1307,1309,1311,1313,1315,1317,1319,1321,1323,1325],{"class":103,"line":1296},15,[101,1298,836],{"class":132},[101,1300,242],{"class":188},[101,1302,245],{"class":118},[101,1304,142],{"class":188},[101,1306,1058],{"class":179},[101,1308,253],{"class":188},[101,1310,1165],{"class":179},[101,1312,183],{"class":122},[101,1314,1196],{"class":179},[101,1316,176],{"class":175},[101,1318,259],{"class":118},[101,1320,142],{"class":188},[101,1322,1058],{"class":179},[101,1324,160],{"class":188},[101,1326,203],{"class":122},[101,1328,1330,1332,1334],{"class":103,"line":1329},16,[101,1331,133],{"class":132},[101,1333,1127],{"class":179},[101,1335,203],{"class":122},[101,1337,1339],{"class":103,"line":1338},17,[101,1340,289],{"class":122},[63,1342,1344],{"id":1343},"_3-核心差异总结","3. 核心差异总结",[1346,1347,1348,1366],"table",{},[1349,1350,1351],"thead",{},[1352,1353,1354,1358,1362],"tr",{},[1355,1356,1357],"th",{},"特性",[1355,1359,1360],{},[28,1361,986],{},[1355,1363,1364],{},[28,1365,983],{},[1367,1368,1369,1383,1397,1411,1430,1445],"tbody",{},[1352,1370,1371,1377,1380],{},[1372,1373,1374],"td",{},[54,1375,1376],{},"输入类型",[1372,1378,1379],{},"仅对象（包括数组、集合）",[1372,1381,1382],{},"任意类型（原始值 + 对象）",[1352,1384,1385,1390,1394],{},[1372,1386,1387],{},[54,1388,1389],{},"底层代理",[1372,1391,1392],{},[28,1393,72],{},[1372,1395,1396],{},"普通对象 + getter\u002Fsetter（对象时内嵌 reactive）",[1352,1398,1399,1403,1406],{},[1372,1400,1401],{},[54,1402,1086],{},[1372,1404,1405],{},"直接访问属性",[1372,1407,764,1408,1410],{},[28,1409,1073],{}," 访问",[1352,1412,1413,1418,1424],{},[1372,1414,1415],{},[54,1416,1417],{},"解构响应性",[1372,1419,1420,1421],{},"会丢失，需 ",[28,1422,1423],{},"toRefs",[1372,1425,1426,1427,1429],{},"直接解构 ",[28,1428,1073],{}," 也会丢失，一般不解构",[1352,1431,1432,1437,1440],{},[1372,1433,1434],{},[54,1435,1436],{},"模板中的行为",[1372,1438,1439],{},"直接使用",[1372,1441,1442,1443,318],{},"自动解包（直接写变量名，无需 ",[28,1444,1073],{},[1352,1446,1447,1452,1455],{},[1372,1448,1449],{},[54,1450,1451],{},"典型使用场景",[1372,1453,1454],{},"复杂状态对象、表单数据",[1372,1456,1457],{},"单个原始值、需要重新赋值的场景",[46,1459,1461],{"id":1460},"四工作流示例组件渲染","四、工作流示例：组件渲染",[1463,1464,1465,1480,1497,1509,1521],"ol",{},[310,1466,1467,1470,1471,1474,1475,1477,1478,61],{},[54,1468,1469],{},"组件初始化","：创建 ",[28,1472,1473],{},"setup()"," 中定义的响应式数据（",[28,1476,983],{},"\u002F",[28,1479,986],{},[310,1481,1482,1485,1486,1489,1490,1493,1494,1496],{},[54,1483,1484],{},"首次渲染","：执行 ",[28,1487,1488],{},"render"," 函数（副作用），读取响应式数据 → 触发 ",[28,1491,1492],{},"getter"," → ",[28,1495,14],{}," 收集当前渲染副作用作为依赖。",[310,1498,1499,1502,1503,1493,1506,1508],{},[54,1500,1501],{},"数据变更","：用户交互或异步操作修改数据 → 触发 ",[28,1504,1505],{},"setter",[28,1507,33],{}," 将渲染副作用放入异步队列。",[310,1510,1511,1514,1515,1517,1518,1520],{},[54,1512,1513],{},"微任务执行","：",[28,1516,777],{}," 执行队列，重新调用 ",[28,1519,1488],{}," 函数，更新虚拟 DOM 并 patch 到真实 DOM。",[310,1522,1523,1525],{},[28,1524,739],{}," 回调在 DOM 更新后执行。",[46,1527,1529],{"id":1528},"五性能相关的设计考量","五、性能相关的设计考量",[307,1531,1532,1540,1553,1567],{},[310,1533,1534,1514,1537,1539],{},[54,1535,1536],{},"懒递归",[28,1538,986],{}," 只在读取嵌套对象时才进行深层代理，避免初始化开销。",[310,1541,1542,1545,1546,1548,1549,1552],{},[54,1543,1544],{},"依赖清理","：每次重新执行副作用前，会清除旧依赖，避免无效依赖积累（通过 ",[28,1547,339],{}," 的 ",[28,1550,1551],{},"deps"," 数组）。",[310,1554,1555,1558,1559,1548,1561,81,1564,61],{},[54,1556,1557],{},"调度器","：允许自定义更新时机（如 ",[28,1560,60],{},[28,1562,1563],{},"flush: 'sync'",[28,1565,1566],{},"'post'",[310,1568,1569,1572],{},[54,1570,1571],{},"异步队列去重","：同一个副作用多次触发只会执行一次。",[24,1574,1575],{},"理解这些机制，有助于写出更高性能的 Vue 应用，也能在遇到响应式相关 BUG（如数据改变但视图未更新）时快速定位问题。",[46,1577,1578],{"id":1578},"参考文献",[24,1580,1581],{},"以下链接在编写时均可正常访问：",[1346,1583,1584,1594],{},[1349,1585,1586],{},[1352,1587,1588,1591],{},[1355,1589,1590],{},"资料",[1355,1592,1593],{},"说明",[1367,1595,1596,1609,1621],{},[1352,1597,1598,1606],{},[1372,1599,1600],{},[40,1601,1605],{"href":1602,"rel":1603},"https:\u002F\u002Fcn.vuejs.org\u002Fguide\u002Fextras\u002Freactivity-in-depth.html",[1604],"nofollow","Vue：深入响应式",[1372,1607,1608],{},"官方说明",[1352,1610,1611,1618],{},[1372,1612,1613],{},[40,1614,1617],{"href":1615,"rel":1616},"https:\u002F\u002Fcn.vuejs.org\u002Fapi\u002Freactivity-core.html#ref",[1604],"Vue：ref",[1372,1619,1620],{},"ref API",[1352,1622,1623,1630],{},[1372,1624,1625],{},[40,1626,1629],{"href":1627,"rel":1628},"https:\u002F\u002Fcn.vuejs.org\u002Fapi\u002Freactivity-core.html#reactive",[1604],"Vue：reactive",[1372,1631,1632],{},"reactive API",[1634,1635,1636],"style",{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":97,"searchDepth":111,"depth":111,"links":1638},[1639,1646,1653,1661,1662,1663],{"id":48,"depth":111,"text":49,"children":1640},[1641,1642,1644],{"id":65,"depth":129,"text":66},{"id":292,"depth":129,"text":1643},"2. 映射关系的存储结构：targetMap",{"id":350,"depth":129,"text":1645},"3. 当前激活的副作用：activeEffect",{"id":546,"depth":111,"text":547,"children":1647},[1648,1649,1651],{"id":557,"depth":129,"text":558},{"id":735,"depth":129,"text":1650},"2. 异步更新队列与 nextTick",{"id":948,"depth":129,"text":1652},"3. trigger 的查找过程",{"id":979,"depth":111,"text":1654,"children":1655},"三、ref 与 reactive 的底层实现差异",[1656,1658,1660],{"id":990,"depth":129,"text":1657},"1. reactive",{"id":1037,"depth":129,"text":1659},"2. ref",{"id":1343,"depth":129,"text":1344},{"id":1460,"depth":111,"text":1461},{"id":1528,"depth":111,"text":1529},{"id":1578,"depth":111,"text":1578},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FVue\u002Freactivity\u002Ftrack","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FVue\u002Freactivity\u002Ftrack.md","md",{"items":1669},[1670,1674,1677,1681,1685,1689],{"path":1671,"title":1672,"description":1673,"category":10,"score":270},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fproxy","响应式进阶","toRefs、shallowRef、readonly、customRef 等 API 的适用场景与注意点。前置：依赖收集。",{"path":42,"title":1675,"description":1676,"category":10,"score":270},"Vue 2 与 Vue 3 响应式差异","Vue 2 与 Vue 3 响应式实现的核心差异如下。基础概念见 Vue 基础。",{"path":1678,"title":1679,"description":1680,"category":10,"score":165},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets","资源优化","图片懒加载、按需引入与 Web Vitals 监控。见 HTML 图片、CSS 字体优化。",{"path":1682,"title":1683,"description":1684,"category":10,"score":165},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fstatus","状态管理：Pinia 与 Vuex","Pinia 是 Vue 官方推荐的状态管理库（Vue 3 新项目默认选型）。Vuex 4 仍可与 Vue 3 配合，用于维护旧项目。见 Vue 基础。",{"path":1686,"title":1687,"description":1688,"category":10,"score":165},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fecology","Vue 生态关键库深度集成","VueUse、Vite 与 Nuxt 是 Vue 3 生态中常用的效率与工程化方案。概览见 Vue 基础。",{"path":1690,"title":1691,"description":1692,"category":10,"score":165},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Ftest","测试策略","Vue 应用常用 Vitest + @vue\u002Ftest-utils 做单元\u002F组件测试，Playwright 或 Cypress 做 E2E。见 Vue 基础。",{"series":1694,"title":13,"items":1695,"index":104,"prev":1701,"next":1702},"booklet\u002Fframe\u002Fvue\u002Freactivity",[1696,1698,1699],{"path":1671,"title":1672,"date":1697,"pinned":16},"2023-03-21",{"path":7,"title":8,"date":15,"pinned":16},{"path":42,"title":1675,"date":1700,"pinned":16},"2022-07-06",{"path":1671,"title":1672,"date":1697,"pinned":16},{"path":42,"title":1675,"date":1700,"pinned":16}]