[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fproxy":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fproxy":1916,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fproxy":1941},{"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":1912,"stem":1913,"id":1914,"extension":1915},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fproxy","响应式进阶","toRefs、shallowRef、readonly、customRef 等 API 的适用场景与注意点。前置：依赖收集。","技能小册",[12,13,14],"vue","reactivity","proxy","2023-03-21",false,{"type":18,"value":19,"toc":1876},"minimark",[20,23,47,50,55,64,78,265,271,281,287,300,438,447,452,468,570,581,585,601,725,731,741,747,750,889,903,909,921,1027,1037,1041,1062,1071,1076,1079,1188,1194,1205,1210,1213,1309,1314,1318,1326,1332,1341,1347,1578,1582,1621,1625,1658,1665,1693,1697,1800,1803,1806,1809,1872],[21,22,8],"h1",{"id":8},[24,25,26,30,31,30,34,30,37,40,41,46],"p",{},[27,28,29],"code",{},"toRefs","、",[27,32,33],{},"shallowRef",[27,35,36],{},"readonly",[27,38,39],{},"customRef"," 等 API 的适用场景与注意点。前置：",[42,43,45],"a",{"href":44},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Ftrack","依赖收集","。",[24,48,49],{},"以下是 Vue 3 响应式系统的高级主题详解，涵盖响应式丢失的成因与补救、浅响应式、只读响应式及自定义响应式。",[51,52,54],"h2",{"id":53},"一响应式丢失问题","一、响应式丢失问题",[56,57,59,60,63],"h3",{"id":58},"_1-成因解构-reactive-对象","1. 成因：解构 ",[27,61,62],{},"reactive"," 对象",[24,65,66,68,69,72,73,77],{},[27,67,62],{}," 返回的是一个 ",[27,70,71],{},"Proxy"," 对象，其属性访问会被拦截。但若将该对象的属性解构到普通变量，这些变量就只是",[74,75,76],"strong",{},"初始值的快照","，不再具有响应性。",[79,80,85],"pre",{"className":81,"code":82,"language":83,"meta":84,"style":84},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","import { reactive } from \"vue\";\n\nconst state = reactive({ count: 0, name: \"Alice\" });\nlet { count, name } = state; \u002F\u002F count 和 name 此时是普通数字\u002F字符串\n\n\u002F\u002F 修改 state.count 会触发视图更新\nstate.count++; \u002F\u002F ✅ 响应式\n\u002F\u002F 但直接修改解构后的 count 不会影响原状态，也不会触发更新\ncount++; \u002F\u002F ❌ 失去响应式\n","js","",[27,86,87,123,130,187,216,221,227,247,253],{"__ignoreMap":84},[88,89,92,96,100,104,107,110,114,117,120],"span",{"class":90,"line":91},"line",1,[88,93,95],{"class":94},"sVHd0","import",[88,97,99],{"class":98},"sP7_E"," {",[88,101,103],{"class":102},"su5hD"," reactive",[88,105,106],{"class":98}," }",[88,108,109],{"class":94}," from",[88,111,113],{"class":112},"sjJ54"," \"",[88,115,12],{"class":116},"s_sjI",[88,118,119],{"class":112},"\"",[88,121,122],{"class":98},";\n",[88,124,126],{"class":90,"line":125},2,[88,127,129],{"emptyLinePlaceholder":128},true,"\n",[88,131,133,137,141,145,148,151,154,158,161,165,168,171,173,175,178,180,182,185],{"class":90,"line":132},3,[88,134,136],{"class":135},"sbsja","const",[88,138,140],{"class":139},"s_hVV"," state",[88,142,144],{"class":143},"smGrS"," =",[88,146,103],{"class":147},"sGLFI",[88,149,150],{"class":102},"(",[88,152,153],{"class":98},"{",[88,155,157],{"class":156},"skxfh"," count",[88,159,160],{"class":98},":",[88,162,164],{"class":163},"srdBf"," 0",[88,166,167],{"class":98},",",[88,169,170],{"class":156}," name",[88,172,160],{"class":98},[88,174,113],{"class":112},[88,176,177],{"class":116},"Alice",[88,179,119],{"class":112},[88,181,106],{"class":98},[88,183,184],{"class":102},")",[88,186,122],{"class":98},[88,188,190,193,195,197,199,202,205,207,209,212],{"class":90,"line":189},4,[88,191,192],{"class":135},"let",[88,194,99],{"class":98},[88,196,157],{"class":102},[88,198,167],{"class":98},[88,200,201],{"class":102}," name ",[88,203,204],{"class":98},"}",[88,206,144],{"class":143},[88,208,140],{"class":102},[88,210,211],{"class":98},";",[88,213,215],{"class":214},"sutJx"," \u002F\u002F count 和 name 此时是普通数字\u002F字符串\n",[88,217,219],{"class":90,"line":218},5,[88,220,129],{"emptyLinePlaceholder":128},[88,222,224],{"class":90,"line":223},6,[88,225,226],{"class":214},"\u002F\u002F 修改 state.count 会触发视图更新\n",[88,228,230,233,236,239,242,244],{"class":90,"line":229},7,[88,231,232],{"class":102},"state",[88,234,235],{"class":98},".",[88,237,238],{"class":102},"count",[88,240,241],{"class":143},"++",[88,243,211],{"class":98},[88,245,246],{"class":214}," \u002F\u002F ✅ 响应式\n",[88,248,250],{"class":90,"line":249},8,[88,251,252],{"class":214},"\u002F\u002F 但直接修改解构后的 count 不会影响原状态，也不会触发更新\n",[88,254,256,258,260,262],{"class":90,"line":255},9,[88,257,238],{"class":102},[88,259,241],{"class":143},[88,261,211],{"class":98},[88,263,264],{"class":214}," \u002F\u002F ❌ 失去响应式\n",[24,266,267,268,270],{},"同样的问题也发生在将 ",[27,269,62],{}," 对象的属性作为参数传递给函数、或从组合式函数中返回解构对象时。",[56,272,274,275,277,278],{"id":273},"_2-解决方案torefs-与-toref","2. 解决方案：",[27,276,29],{}," 与 ",[27,279,280],{},"toRef",[282,283,285],"h4",{"id":284},"torefs",[27,286,29],{},[24,288,289,290,292,293,296,297,299],{},"将 ",[27,291,62],{}," 对象转换为一个普通对象，该对象的每个属性都是指向原对象对应属性的 ",[27,294,295],{},"ref","。这样解构后得到的每个属性都是 ",[27,298,295],{}," 对象，保持响应连接。",[79,301,303],{"className":81,"code":302,"language":83,"meta":84,"style":84},"import { reactive, toRefs } from \"vue\";\n\nconst state = reactive({ count: 0, name: \"Alice\" });\nconst stateAsRefs = toRefs(state);\n\u002F\u002F stateAsRefs 结构: { count: Ref(0), name: Ref('Alice') }\n\n\u002F\u002F 解构后仍然是 ref\nconst { count, name } = stateAsRefs;\ncount.value++; \u002F\u002F ✅ 等价于 state.count++，触发更新\n",[27,304,305,330,334,372,388,393,397,402,422],{"__ignoreMap":84},[88,306,307,309,311,313,315,318,320,322,324,326,328],{"class":90,"line":91},[88,308,95],{"class":94},[88,310,99],{"class":98},[88,312,103],{"class":102},[88,314,167],{"class":98},[88,316,317],{"class":102}," toRefs",[88,319,106],{"class":98},[88,321,109],{"class":94},[88,323,113],{"class":112},[88,325,12],{"class":116},[88,327,119],{"class":112},[88,329,122],{"class":98},[88,331,332],{"class":90,"line":125},[88,333,129],{"emptyLinePlaceholder":128},[88,335,336,338,340,342,344,346,348,350,352,354,356,358,360,362,364,366,368,370],{"class":90,"line":132},[88,337,136],{"class":135},[88,339,140],{"class":139},[88,341,144],{"class":143},[88,343,103],{"class":147},[88,345,150],{"class":102},[88,347,153],{"class":98},[88,349,157],{"class":156},[88,351,160],{"class":98},[88,353,164],{"class":163},[88,355,167],{"class":98},[88,357,170],{"class":156},[88,359,160],{"class":98},[88,361,113],{"class":112},[88,363,177],{"class":116},[88,365,119],{"class":112},[88,367,106],{"class":98},[88,369,184],{"class":102},[88,371,122],{"class":98},[88,373,374,376,379,381,383,386],{"class":90,"line":189},[88,375,136],{"class":135},[88,377,378],{"class":139}," stateAsRefs",[88,380,144],{"class":143},[88,382,317],{"class":147},[88,384,385],{"class":102},"(state)",[88,387,122],{"class":98},[88,389,390],{"class":90,"line":218},[88,391,392],{"class":214},"\u002F\u002F stateAsRefs 结构: { count: Ref(0), name: Ref('Alice') }\n",[88,394,395],{"class":90,"line":223},[88,396,129],{"emptyLinePlaceholder":128},[88,398,399],{"class":90,"line":229},[88,400,401],{"class":214},"\u002F\u002F 解构后仍然是 ref\n",[88,403,404,406,408,410,412,414,416,418,420],{"class":90,"line":249},[88,405,136],{"class":135},[88,407,99],{"class":98},[88,409,157],{"class":139},[88,411,167],{"class":98},[88,413,170],{"class":139},[88,415,106],{"class":98},[88,417,144],{"class":143},[88,419,378],{"class":102},[88,421,122],{"class":98},[88,423,424,426,428,431,433,435],{"class":90,"line":255},[88,425,238],{"class":102},[88,427,235],{"class":98},[88,429,430],{"class":102},"value",[88,432,241],{"class":143},[88,434,211],{"class":98},[88,436,437],{"class":214}," \u002F\u002F ✅ 等价于 state.count++，触发更新\n",[24,439,440,443,444,446],{},[74,441,442],{},"使用场景","：将整个 ",[27,445,62],{}," 对象的属性解构到多个变量，或从组合式函数返回响应式状态时。",[282,448,450],{"id":449},"toref",[27,451,280],{},[24,453,454,455,457,458,461,462,464,465,467],{},"为 ",[27,456,62],{}," 对象的",[74,459,460],{},"单个属性","创建一个 ",[27,463,295],{},"，保持响应连接。与 ",[27,466,29],{}," 的区别是只处理一个属性。",[79,469,471],{"className":81,"code":470,"language":83,"meta":84,"style":84},"import { reactive, toRef } from \"vue\";\n\nconst state = reactive({ count: 0 });\nconst countRef = toRef(state, \"count\");\ncountRef.value++; \u002F\u002F ✅ 等价于 state.count++\n",[27,472,473,498,502,528,554],{"__ignoreMap":84},[88,474,475,477,479,481,483,486,488,490,492,494,496],{"class":90,"line":91},[88,476,95],{"class":94},[88,478,99],{"class":98},[88,480,103],{"class":102},[88,482,167],{"class":98},[88,484,485],{"class":102}," toRef",[88,487,106],{"class":98},[88,489,109],{"class":94},[88,491,113],{"class":112},[88,493,12],{"class":116},[88,495,119],{"class":112},[88,497,122],{"class":98},[88,499,500],{"class":90,"line":125},[88,501,129],{"emptyLinePlaceholder":128},[88,503,504,506,508,510,512,514,516,518,520,522,524,526],{"class":90,"line":132},[88,505,136],{"class":135},[88,507,140],{"class":139},[88,509,144],{"class":143},[88,511,103],{"class":147},[88,513,150],{"class":102},[88,515,153],{"class":98},[88,517,157],{"class":156},[88,519,160],{"class":98},[88,521,164],{"class":163},[88,523,106],{"class":98},[88,525,184],{"class":102},[88,527,122],{"class":98},[88,529,530,532,535,537,539,542,544,546,548,550,552],{"class":90,"line":189},[88,531,136],{"class":135},[88,533,534],{"class":139}," countRef",[88,536,144],{"class":143},[88,538,485],{"class":147},[88,540,541],{"class":102},"(state",[88,543,167],{"class":98},[88,545,113],{"class":112},[88,547,238],{"class":116},[88,549,119],{"class":112},[88,551,184],{"class":102},[88,553,122],{"class":98},[88,555,556,559,561,563,565,567],{"class":90,"line":218},[88,557,558],{"class":102},"countRef",[88,560,235],{"class":98},[88,562,430],{"class":102},[88,564,241],{"class":143},[88,566,211],{"class":98},[88,568,569],{"class":214}," \u002F\u002F ✅ 等价于 state.count++\n",[24,571,572,574,575,577,578,580],{},[74,573,442],{},"：仅需要将某一个属性变为 ",[27,576,295],{}," 并保持响应性，例如传递给可接收 ",[27,579,295],{}," 的组合式函数。",[56,582,584],{"id":583},"_3-原理简述","3. 原理简述",[24,586,587,589,590,592,593,596,597,600],{},[27,588,280],{}," \u002F ",[27,591,29],{}," 内部创建了一个",[74,594,595],{},"指向源对象属性的访问器","，其 ",[27,598,599],{},".value"," 的 getter\u002Fsetter 直接操作源对象的对应属性，并在读写时触发依赖收集与更新。",[79,602,604],{"className":81,"code":603,"language":83,"meta":84,"style":84},"function toRef(target, key) {\n  return {\n    get value() {\n      return target[key];\n    },\n    set value(newVal) {\n      target[key] = newVal;\n    },\n  };\n}\n",[27,605,606,629,636,650,669,674,690,710,714,719],{"__ignoreMap":84},[88,607,608,611,613,615,619,621,624,626],{"class":90,"line":91},[88,609,610],{"class":135},"function",[88,612,485],{"class":147},[88,614,150],{"class":98},[88,616,618],{"class":617},"s99_P","target",[88,620,167],{"class":98},[88,622,623],{"class":617}," key",[88,625,184],{"class":98},[88,627,628],{"class":98}," {\n",[88,630,631,634],{"class":90,"line":125},[88,632,633],{"class":94},"  return",[88,635,628],{"class":98},[88,637,638,641,645,648],{"class":90,"line":132},[88,639,640],{"class":135},"    get",[88,642,644],{"class":643},"sVXei"," value",[88,646,647],{"class":98},"()",[88,649,628],{"class":98},[88,651,652,655,658,661,664,667],{"class":90,"line":189},[88,653,654],{"class":94},"      return",[88,656,657],{"class":102}," target",[88,659,660],{"class":156},"[",[88,662,663],{"class":102},"key",[88,665,666],{"class":156},"]",[88,668,122],{"class":98},[88,670,671],{"class":90,"line":218},[88,672,673],{"class":98},"    },\n",[88,675,676,679,681,683,686,688],{"class":90,"line":223},[88,677,678],{"class":135},"    set",[88,680,644],{"class":643},[88,682,150],{"class":98},[88,684,685],{"class":617},"newVal",[88,687,184],{"class":98},[88,689,628],{"class":98},[88,691,692,695,697,699,702,705,708],{"class":90,"line":229},[88,693,694],{"class":102},"      target",[88,696,660],{"class":156},[88,698,663],{"class":102},[88,700,701],{"class":156},"] ",[88,703,704],{"class":143},"=",[88,706,707],{"class":102}," newVal",[88,709,122],{"class":98},[88,711,712],{"class":90,"line":249},[88,713,673],{"class":98},[88,715,716],{"class":90,"line":255},[88,717,718],{"class":98},"  };\n",[88,720,722],{"class":90,"line":721},10,[88,723,724],{"class":98},"}\n",[24,726,727,728,730],{},"注意：",[27,729,280],{}," 即使源属性当前不存在，也会创建一个可写的 ref，当设置值时会在源对象上新增属性（响应式）。",[51,732,734,735,589,737,740],{"id":733},"二浅响应式shallowref-shallowreactive","二、浅响应式（",[27,736,33],{},[27,738,739],{},"shallowReactive","）",[56,742,744,745],{"id":743},"_1-shallowreactive","1. ",[27,746,739],{},[24,748,749],{},"只拦截对象的第一层属性，深层属性不会被转换为响应式。适用于大型数据结构，但只需监听顶层变化。",[79,751,753],{"className":81,"code":752,"language":83,"meta":84,"style":84},"import { shallowReactive } from \"vue\";\n\nconst state = shallowReactive({\n  user: { name: \"Alice\" }, \u002F\u002F 深层对象，不是响应式的\n  count: 0, \u002F\u002F 第一层，响应式\n});\n\nstate.count++; \u002F\u002F ✅ 触发更新\nstate.user.name = \"Bob\"; \u002F\u002F ❌ 不会触发更新，因为 user 不是响应式\n",[27,754,755,776,780,795,820,834,842,846,861],{"__ignoreMap":84},[88,756,757,759,761,764,766,768,770,772,774],{"class":90,"line":91},[88,758,95],{"class":94},[88,760,99],{"class":98},[88,762,763],{"class":102}," shallowReactive",[88,765,106],{"class":98},[88,767,109],{"class":94},[88,769,113],{"class":112},[88,771,12],{"class":116},[88,773,119],{"class":112},[88,775,122],{"class":98},[88,777,778],{"class":90,"line":125},[88,779,129],{"emptyLinePlaceholder":128},[88,781,782,784,786,788,790,792],{"class":90,"line":132},[88,783,136],{"class":135},[88,785,140],{"class":139},[88,787,144],{"class":143},[88,789,763],{"class":147},[88,791,150],{"class":102},[88,793,794],{"class":98},"{\n",[88,796,797,800,802,804,806,808,810,812,814,817],{"class":90,"line":189},[88,798,799],{"class":156},"  user",[88,801,160],{"class":98},[88,803,99],{"class":98},[88,805,170],{"class":156},[88,807,160],{"class":98},[88,809,113],{"class":112},[88,811,177],{"class":116},[88,813,119],{"class":112},[88,815,816],{"class":98}," },",[88,818,819],{"class":214}," \u002F\u002F 深层对象，不是响应式的\n",[88,821,822,825,827,829,831],{"class":90,"line":218},[88,823,824],{"class":156},"  count",[88,826,160],{"class":98},[88,828,164],{"class":163},[88,830,167],{"class":98},[88,832,833],{"class":214}," \u002F\u002F 第一层，响应式\n",[88,835,836,838,840],{"class":90,"line":223},[88,837,204],{"class":98},[88,839,184],{"class":102},[88,841,122],{"class":98},[88,843,844],{"class":90,"line":229},[88,845,129],{"emptyLinePlaceholder":128},[88,847,848,850,852,854,856,858],{"class":90,"line":249},[88,849,232],{"class":102},[88,851,235],{"class":98},[88,853,238],{"class":102},[88,855,241],{"class":143},[88,857,211],{"class":98},[88,859,860],{"class":214}," \u002F\u002F ✅ 触发更新\n",[88,862,863,865,867,870,872,875,877,879,882,884,886],{"class":90,"line":255},[88,864,232],{"class":102},[88,866,235],{"class":98},[88,868,869],{"class":102},"user",[88,871,235],{"class":98},[88,873,874],{"class":102},"name ",[88,876,704],{"class":143},[88,878,113],{"class":112},[88,880,881],{"class":116},"Bob",[88,883,119],{"class":112},[88,885,211],{"class":98},[88,887,888],{"class":214}," \u002F\u002F ❌ 不会触发更新，因为 user 不是响应式\n",[24,890,891,894,895,898,899,902],{},[74,892,893],{},"性能收益","：避免深度递归代理，提升初始化和访问速度，节省内存。\n",[74,896,897],{},"风险","：修改深层对象时不会触发视图更新，容易造成 bug。因此仅适用于",[74,900,901],{},"结构扁平、深层属性不会变更","的场景（如静态配置）。",[56,904,906,907],{"id":905},"_2-shallowref","2. ",[27,908,33],{},[24,910,911,912,914,915,917,918,46],{},"只对 ",[27,913,599],{}," 的访问进行响应式追踪，如果 ",[27,916,599],{}," 是一个对象，",[74,919,920],{},"不会递归将其内部属性转为响应式",[79,922,924],{"className":81,"code":923,"language":83,"meta":84,"style":84},"import { shallowRef } from \"vue\";\n\nconst data = shallowRef({ count: 0 });\ndata.value.count = 1; \u002F\u002F ❌ 不会触发更新\ndata.value = { count: 1 }; \u002F\u002F ✅ 替换整个 value 会触发更新\n",[27,925,926,947,951,978,1002],{"__ignoreMap":84},[88,927,928,930,932,935,937,939,941,943,945],{"class":90,"line":91},[88,929,95],{"class":94},[88,931,99],{"class":98},[88,933,934],{"class":102}," shallowRef",[88,936,106],{"class":98},[88,938,109],{"class":94},[88,940,113],{"class":112},[88,942,12],{"class":116},[88,944,119],{"class":112},[88,946,122],{"class":98},[88,948,949],{"class":90,"line":125},[88,950,129],{"emptyLinePlaceholder":128},[88,952,953,955,958,960,962,964,966,968,970,972,974,976],{"class":90,"line":132},[88,954,136],{"class":135},[88,956,957],{"class":139}," data",[88,959,144],{"class":143},[88,961,934],{"class":147},[88,963,150],{"class":102},[88,965,153],{"class":98},[88,967,157],{"class":156},[88,969,160],{"class":98},[88,971,164],{"class":163},[88,973,106],{"class":98},[88,975,184],{"class":102},[88,977,122],{"class":98},[88,979,980,983,985,987,989,992,994,997,999],{"class":90,"line":189},[88,981,982],{"class":102},"data",[88,984,235],{"class":98},[88,986,430],{"class":102},[88,988,235],{"class":98},[88,990,991],{"class":102},"count ",[88,993,704],{"class":143},[88,995,996],{"class":163}," 1",[88,998,211],{"class":98},[88,1000,1001],{"class":214}," \u002F\u002F ❌ 不会触发更新\n",[88,1003,1004,1006,1008,1011,1013,1015,1017,1019,1021,1024],{"class":90,"line":218},[88,1005,982],{"class":102},[88,1007,235],{"class":98},[88,1009,1010],{"class":102},"value ",[88,1012,704],{"class":143},[88,1014,99],{"class":98},[88,1016,157],{"class":156},[88,1018,160],{"class":98},[88,1020,996],{"class":163},[88,1022,1023],{"class":98}," };",[88,1025,1026],{"class":214}," \u002F\u002F ✅ 替换整个 value 会触发更新\n",[24,1028,1029,1032,1033,1036],{},[74,1030,1031],{},"典型应用","：存储大对象但不需要其内部属性的响应式（如图表配置、第三方实例）。配合 ",[27,1034,1035],{},"triggerRef"," 可手动强制更新。",[56,1038,1040],{"id":1039},"_3-何时使用浅响应式","3. 何时使用浅响应式？",[1042,1043,1044,1048,1055],"ul",{},[1045,1046,1047],"li",{},"性能敏感的大型列表\u002F树，仅需监听引用变化。",[1045,1049,1050,1051,1054],{},"第三方库对象（如 ",[27,1052,1053],{},"ECharts"," 实例）不希望被代理。",[1045,1056,1057,1058,1061],{},"与 ",[27,1059,1060],{},"markRaw"," 标记永久不可代理的对象配合。",[51,1063,1065,1066,30,1068,740],{"id":1064},"三只读响应式readonlyshallowreadonly","三、只读响应式（",[27,1067,36],{},[27,1069,1070],{},"shallowReadonly",[56,1072,744,1074],{"id":1073},"_1-readonly",[27,1075,36],{},[24,1077,1078],{},"接受一个响应式对象（或普通对象），返回一个只读代理。任何修改操作（属性赋值、删除）都会在开发模式下抛出警告。",[79,1080,1082],{"className":81,"code":1081,"language":83,"meta":84,"style":84},"import { reactive, readonly } from \"vue\";\n\nconst original = reactive({ count: 0 });\nconst copy = readonly(original);\ncopy.count++; \u002F\u002F ⚠️ 警告: Set operation on key \"count\" failed: target is readonly.\noriginal.count++; \u002F\u002F ✅ 可以修改原对象，copy 也会相应变化（因为是同一个底层对象）\n",[27,1083,1084,1109,1113,1140,1156,1172],{"__ignoreMap":84},[88,1085,1086,1088,1090,1092,1094,1097,1099,1101,1103,1105,1107],{"class":90,"line":91},[88,1087,95],{"class":94},[88,1089,99],{"class":98},[88,1091,103],{"class":102},[88,1093,167],{"class":98},[88,1095,1096],{"class":102}," readonly",[88,1098,106],{"class":98},[88,1100,109],{"class":94},[88,1102,113],{"class":112},[88,1104,12],{"class":116},[88,1106,119],{"class":112},[88,1108,122],{"class":98},[88,1110,1111],{"class":90,"line":125},[88,1112,129],{"emptyLinePlaceholder":128},[88,1114,1115,1117,1120,1122,1124,1126,1128,1130,1132,1134,1136,1138],{"class":90,"line":132},[88,1116,136],{"class":135},[88,1118,1119],{"class":139}," original",[88,1121,144],{"class":143},[88,1123,103],{"class":147},[88,1125,150],{"class":102},[88,1127,153],{"class":98},[88,1129,157],{"class":156},[88,1131,160],{"class":98},[88,1133,164],{"class":163},[88,1135,106],{"class":98},[88,1137,184],{"class":102},[88,1139,122],{"class":98},[88,1141,1142,1144,1147,1149,1151,1154],{"class":90,"line":189},[88,1143,136],{"class":135},[88,1145,1146],{"class":139}," copy",[88,1148,144],{"class":143},[88,1150,1096],{"class":147},[88,1152,1153],{"class":102},"(original)",[88,1155,122],{"class":98},[88,1157,1158,1161,1163,1165,1167,1169],{"class":90,"line":218},[88,1159,1160],{"class":102},"copy",[88,1162,235],{"class":98},[88,1164,238],{"class":102},[88,1166,241],{"class":143},[88,1168,211],{"class":98},[88,1170,1171],{"class":214}," \u002F\u002F ⚠️ 警告: Set operation on key \"count\" failed: target is readonly.\n",[88,1173,1174,1177,1179,1181,1183,1185],{"class":90,"line":223},[88,1175,1176],{"class":102},"original",[88,1178,235],{"class":98},[88,1180,238],{"class":102},[88,1182,241],{"class":143},[88,1184,211],{"class":98},[88,1186,1187],{"class":214}," \u002F\u002F ✅ 可以修改原对象，copy 也会相应变化（因为是同一个底层对象）\n",[24,1189,1190,1193],{},[74,1191,1192],{},"应用场景","：",[1042,1195,1196,1199,1202],{},[1045,1197,1198],{},"保护组件的 props（内部不应修改）。",[1045,1200,1201],{},"向外部提供不可变的状态（如 Pinia store 的 getter 返回只读状态）。",[1045,1203,1204],{},"防止意外修改共享状态。",[56,1206,906,1208],{"id":1207},"_2-shallowreadonly",[27,1209,1070],{},[24,1211,1212],{},"只确保对象的第一层属性只读，深层属性仍可修改。",[79,1214,1216],{"className":81,"code":1215,"language":83,"meta":84,"style":84},"const state = shallowReadonly({ user: { name: \"Alice\" }, version: 1 });\nstate.version = 2; \u002F\u002F 警告\nstate.user.name = \"Bob\"; \u002F\u002F ✅ 允许（深层不是只读）\n",[27,1217,1218,1265,1284],{"__ignoreMap":84},[88,1219,1220,1222,1224,1226,1229,1231,1233,1236,1238,1240,1242,1244,1246,1248,1250,1252,1255,1257,1259,1261,1263],{"class":90,"line":91},[88,1221,136],{"class":135},[88,1223,140],{"class":139},[88,1225,144],{"class":143},[88,1227,1228],{"class":147}," shallowReadonly",[88,1230,150],{"class":102},[88,1232,153],{"class":98},[88,1234,1235],{"class":156}," user",[88,1237,160],{"class":98},[88,1239,99],{"class":98},[88,1241,170],{"class":156},[88,1243,160],{"class":98},[88,1245,113],{"class":112},[88,1247,177],{"class":116},[88,1249,119],{"class":112},[88,1251,816],{"class":98},[88,1253,1254],{"class":156}," version",[88,1256,160],{"class":98},[88,1258,996],{"class":163},[88,1260,106],{"class":98},[88,1262,184],{"class":102},[88,1264,122],{"class":98},[88,1266,1267,1269,1271,1274,1276,1279,1281],{"class":90,"line":125},[88,1268,232],{"class":102},[88,1270,235],{"class":98},[88,1272,1273],{"class":102},"version ",[88,1275,704],{"class":143},[88,1277,1278],{"class":163}," 2",[88,1280,211],{"class":98},[88,1282,1283],{"class":214}," \u002F\u002F 警告\n",[88,1285,1286,1288,1290,1292,1294,1296,1298,1300,1302,1304,1306],{"class":90,"line":132},[88,1287,232],{"class":102},[88,1289,235],{"class":98},[88,1291,869],{"class":102},[88,1293,235],{"class":98},[88,1295,874],{"class":102},[88,1297,704],{"class":143},[88,1299,113],{"class":112},[88,1301,881],{"class":116},[88,1303,119],{"class":112},[88,1305,211],{"class":98},[88,1307,1308],{"class":214}," \u002F\u002F ✅ 允许（深层不是只读）\n",[24,1310,1311,1313],{},[74,1312,1192],{},"：深层结构巨大但只需保护顶层引用的场合。",[56,1315,1317],{"id":1316},"_3-嵌套只读","3. 嵌套只读",[24,1319,1320,1322,1323,1325],{},[27,1321,36],{}," 会递归地将所有嵌套对象也变为只读，而 ",[27,1324,1070],{}," 只影响第一层。",[51,1327,1329,1330,740],{"id":1328},"四自定义响应式定制-ref","四、自定义响应式（定制 ",[27,1331,295],{},[24,1333,1334,1335,1337,1338,1340],{},"通过 ",[27,1336,39],{}," 可以创建带有自定义逻辑的 ",[27,1339,295],{},"，例如实现防抖、节流、本地存储同步等。",[56,1342,744,1344,1346],{"id":1343},"_1-customref-基本用法",[27,1345,39],{}," 基本用法",[79,1348,1350],{"className":81,"code":1349,"language":83,"meta":84,"style":84},"import { customRef } from \"vue\";\n\nfunction useDebouncedRef(value, delay = 200) {\n  let timeout;\n  return customRef((track, trigger) => ({\n    get() {\n      track(); \u002F\u002F 依赖收集\n      return value;\n    },\n    set(newValue) {\n      clearTimeout(timeout);\n      timeout = setTimeout(() => {\n        value = newValue;\n        trigger(); \u002F\u002F 触发更新\n      }, delay);\n    },\n  }));\n}\n",[27,1351,1352,1373,1377,1402,1412,1440,1448,1460,1468,1472,1485,1500,1519,1532,1545,1557,1562,1573],{"__ignoreMap":84},[88,1353,1354,1356,1358,1361,1363,1365,1367,1369,1371],{"class":90,"line":91},[88,1355,95],{"class":94},[88,1357,99],{"class":98},[88,1359,1360],{"class":102}," customRef",[88,1362,106],{"class":98},[88,1364,109],{"class":94},[88,1366,113],{"class":112},[88,1368,12],{"class":116},[88,1370,119],{"class":112},[88,1372,122],{"class":98},[88,1374,1375],{"class":90,"line":125},[88,1376,129],{"emptyLinePlaceholder":128},[88,1378,1379,1381,1384,1386,1388,1390,1393,1395,1398,1400],{"class":90,"line":132},[88,1380,610],{"class":135},[88,1382,1383],{"class":147}," useDebouncedRef",[88,1385,150],{"class":98},[88,1387,430],{"class":617},[88,1389,167],{"class":98},[88,1391,1392],{"class":617}," delay",[88,1394,144],{"class":143},[88,1396,1397],{"class":163}," 200",[88,1399,184],{"class":98},[88,1401,628],{"class":98},[88,1403,1404,1407,1410],{"class":90,"line":189},[88,1405,1406],{"class":135},"  let",[88,1408,1409],{"class":102}," timeout",[88,1411,122],{"class":98},[88,1413,1414,1416,1418,1420,1422,1425,1427,1430,1432,1435,1438],{"class":90,"line":218},[88,1415,633],{"class":94},[88,1417,1360],{"class":147},[88,1419,150],{"class":156},[88,1421,150],{"class":98},[88,1423,1424],{"class":617},"track",[88,1426,167],{"class":98},[88,1428,1429],{"class":617}," trigger",[88,1431,184],{"class":98},[88,1433,1434],{"class":135}," =>",[88,1436,1437],{"class":156}," (",[88,1439,794],{"class":98},[88,1441,1442,1444,1446],{"class":90,"line":223},[88,1443,640],{"class":643},[88,1445,647],{"class":98},[88,1447,628],{"class":98},[88,1449,1450,1453,1455,1457],{"class":90,"line":229},[88,1451,1452],{"class":147},"      track",[88,1454,647],{"class":156},[88,1456,211],{"class":98},[88,1458,1459],{"class":214}," \u002F\u002F 依赖收集\n",[88,1461,1462,1464,1466],{"class":90,"line":249},[88,1463,654],{"class":94},[88,1465,644],{"class":102},[88,1467,122],{"class":98},[88,1469,1470],{"class":90,"line":255},[88,1471,673],{"class":98},[88,1473,1474,1476,1478,1481,1483],{"class":90,"line":721},[88,1475,678],{"class":643},[88,1477,150],{"class":98},[88,1479,1480],{"class":617},"newValue",[88,1482,184],{"class":98},[88,1484,628],{"class":98},[88,1486,1488,1491,1493,1496,1498],{"class":90,"line":1487},11,[88,1489,1490],{"class":147},"      clearTimeout",[88,1492,150],{"class":156},[88,1494,1495],{"class":102},"timeout",[88,1497,184],{"class":156},[88,1499,122],{"class":98},[88,1501,1503,1506,1508,1511,1513,1515,1517],{"class":90,"line":1502},12,[88,1504,1505],{"class":102},"      timeout",[88,1507,144],{"class":143},[88,1509,1510],{"class":147}," setTimeout",[88,1512,150],{"class":156},[88,1514,647],{"class":98},[88,1516,1434],{"class":135},[88,1518,628],{"class":98},[88,1520,1522,1525,1527,1530],{"class":90,"line":1521},13,[88,1523,1524],{"class":102},"        value",[88,1526,144],{"class":143},[88,1528,1529],{"class":102}," newValue",[88,1531,122],{"class":98},[88,1533,1535,1538,1540,1542],{"class":90,"line":1534},14,[88,1536,1537],{"class":147},"        trigger",[88,1539,647],{"class":156},[88,1541,211],{"class":98},[88,1543,1544],{"class":214}," \u002F\u002F 触发更新\n",[88,1546,1548,1551,1553,1555],{"class":90,"line":1547},15,[88,1549,1550],{"class":98},"      },",[88,1552,1392],{"class":102},[88,1554,184],{"class":156},[88,1556,122],{"class":98},[88,1558,1560],{"class":90,"line":1559},16,[88,1561,673],{"class":98},[88,1563,1565,1568,1571],{"class":90,"line":1564},17,[88,1566,1567],{"class":98},"  }",[88,1569,1570],{"class":156},"))",[88,1572,122],{"class":98},[88,1574,1576],{"class":90,"line":1575},18,[88,1577,724],{"class":98},[56,1579,1581],{"id":1580},"_2-使用示例","2. 使用示例",[79,1583,1585],{"className":81,"code":1584,"language":83,"meta":84,"style":84},"const text = useDebouncedRef(\"hello\", 500);\n\u002F\u002F 在模板中绑定 input，输入停止 500ms 后才会更新 text\n",[27,1586,1587,1616],{"__ignoreMap":84},[88,1588,1589,1591,1594,1596,1598,1600,1602,1605,1607,1609,1612,1614],{"class":90,"line":91},[88,1590,136],{"class":135},[88,1592,1593],{"class":139}," text",[88,1595,144],{"class":143},[88,1597,1383],{"class":147},[88,1599,150],{"class":102},[88,1601,119],{"class":112},[88,1603,1604],{"class":116},"hello",[88,1606,119],{"class":112},[88,1608,167],{"class":98},[88,1610,1611],{"class":163}," 500",[88,1613,184],{"class":102},[88,1615,122],{"class":98},[88,1617,1618],{"class":90,"line":125},[88,1619,1620],{"class":214},"\u002F\u002F 在模板中绑定 input，输入停止 500ms 后才会更新 text\n",[56,1622,1624],{"id":1623},"_3-更多应用场景","3. 更多应用场景",[1042,1626,1627,1640,1646,1652],{},[1045,1628,1629,1193,1632,1635,1636,1639],{},[74,1630,1631],{},"本地存储同步 ref",[27,1633,1634],{},"get"," 时从 localStorage 读取，",[27,1637,1638],{},"set"," 时写入。",[1045,1641,1642,1645],{},[74,1643,1644],{},"节流 ref","：限制高频赋值频率。",[1045,1647,1648,1651],{},[74,1649,1650],{},"验证 ref","：在 setter 中进行数据校验，无效值不更新并报错。",[1045,1653,1654,1657],{},[74,1655,1656],{},"联动 ref","：一个 ref 的值变化会同步修改另一个数据。",[56,1659,1661,1662,1664],{"id":1660},"_4-与普通-ref-的区别","4. 与普通 ",[27,1663,295],{}," 的区别",[1042,1666,1667,1680],{},[1045,1668,1669,1670,1672,1673,589,1676,1679],{},"普通 ",[27,1671,295],{}," 基于 ",[27,1674,1675],{},"trackRefValue",[27,1677,1678],{},"triggerRefValue"," 内置逻辑。",[1045,1681,1682,1684,1685,1688,1689,1692],{},[27,1683,39],{}," 完全让开发者控制何时 ",[27,1686,1687],{},"track()"," 收集依赖，何时 ",[27,1690,1691],{},"trigger()"," 派发更新，适合需要劫持读写流程的复杂需求。",[51,1694,1696],{"id":1695},"五总结","五、总结",[1698,1699,1700,1716],"table",{},[1701,1702,1703],"thead",{},[1704,1705,1706,1710,1713],"tr",{},[1707,1708,1709],"th",{},"概念",[1707,1711,1712],{},"作用",[1707,1714,1715],{},"典型场景",[1717,1718,1719,1737,1749,1764,1776,1788],"tbody",{},[1704,1720,1721,1728,1734],{},[1722,1723,1724,589,1726],"td",{},[27,1725,29],{},[27,1727,280],{},[1722,1729,1730,1731,1733],{},"解决解构丢失响应，保持 ",[27,1732,62],{}," 属性连接",[1722,1735,1736],{},"组合式函数返回、解构赋值",[1704,1738,1739,1743,1746],{},[1722,1740,1741],{},[27,1742,739],{},[1722,1744,1745],{},"只代理第一层，深层不响应",[1722,1747,1748],{},"性能优化，大对象且无需深层响应",[1704,1750,1751,1755,1761],{},[1722,1752,1753],{},[27,1754,33],{},[1722,1756,1757,1758,1760],{},"只追踪 ",[27,1759,599],{}," 变化，内部对象无响应",[1722,1762,1763],{},"存储第三方实例、大对象",[1704,1765,1766,1770,1773],{},[1722,1767,1768],{},[27,1769,36],{},[1722,1771,1772],{},"递归只读代理，防止修改",[1722,1774,1775],{},"保护 props、对外暴露不可变数据",[1704,1777,1778,1782,1785],{},[1722,1779,1780],{},[27,1781,1070],{},[1722,1783,1784],{},"第一层只读，深层可改",[1722,1786,1787],{},"结构很深但只需保护顶层",[1704,1789,1790,1794,1797],{},[1722,1791,1792],{},[27,1793,39],{},[1722,1795,1796],{},"完全自定义 ref 的 get\u002Fset 行为",[1722,1798,1799],{},"防抖、节流、本地存储同步、联动、校验",[24,1801,1802],{},"理解这些进阶 API，能让你在复杂场景下既保证响应式的正确性，又能灵活优化性能，构建更健壮的 Vue 应用。",[51,1804,1805],{"id":1805},"参考文献",[24,1807,1808],{},"以下链接在编写时均可正常访问：",[1698,1810,1811,1821],{},[1701,1812,1813],{},[1704,1814,1815,1818],{},[1707,1816,1817],{},"资料",[1707,1819,1820],{},"说明",[1717,1822,1823,1836,1848,1860],{},[1704,1824,1825,1833],{},[1722,1826,1827],{},[42,1828,1832],{"href":1829,"rel":1830},"https:\u002F\u002Fcn.vuejs.org\u002Fapi\u002Freactivity-utilities.html#toref",[1831],"nofollow","Vue：toRef \u002F toRefs",[1722,1834,1835],{},"保持解构响应性",[1704,1837,1838,1845],{},[1722,1839,1840],{},[42,1841,1844],{"href":1842,"rel":1843},"https:\u002F\u002Fcn.vuejs.org\u002Fapi\u002Freactivity-advanced.html#shallowref",[1831],"Vue：shallowRef",[1722,1846,1847],{},"浅响应",[1704,1849,1850,1857],{},[1722,1851,1852],{},[42,1853,1856],{"href":1854,"rel":1855},"https:\u002F\u002Fcn.vuejs.org\u002Fapi\u002Freactivity-utilities.html#readonly",[1831],"Vue：readonly",[1722,1858,1859],{},"只读代理",[1704,1861,1862,1869],{},[1722,1863,1864],{},[42,1865,1868],{"href":1866,"rel":1867},"https:\u002F\u002Fcn.vuejs.org\u002Fapi\u002Freactivity-advanced.html#customref",[1831],"Vue：customRef",[1722,1870,1871],{},"自定义 ref",[1873,1874,1875],"style",{},"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 .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}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 .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .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 .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 .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":84,"searchDepth":125,"depth":125,"links":1877},[1878,1885,1893,1901,1910,1911],{"id":53,"depth":125,"text":54,"children":1879},[1880,1882,1884],{"id":58,"depth":132,"text":1881},"1. 成因：解构 reactive 对象",{"id":273,"depth":132,"text":1883},"2. 解决方案：toRefs 与 toRef",{"id":583,"depth":132,"text":584},{"id":733,"depth":125,"text":1886,"children":1887},"二、浅响应式（shallowRef \u002F shallowReactive）",[1888,1890,1892],{"id":743,"depth":132,"text":1889},"1. shallowReactive",{"id":905,"depth":132,"text":1891},"2. shallowRef",{"id":1039,"depth":132,"text":1040},{"id":1064,"depth":125,"text":1894,"children":1895},"三、只读响应式（readonly、shallowReadonly）",[1896,1898,1900],{"id":1073,"depth":132,"text":1897},"1. readonly",{"id":1207,"depth":132,"text":1899},"2. shallowReadonly",{"id":1316,"depth":132,"text":1317},{"id":1328,"depth":125,"text":1902,"children":1903},"四、自定义响应式（定制 ref）",[1904,1906,1907,1908],{"id":1343,"depth":132,"text":1905},"1. customRef 基本用法",{"id":1580,"depth":132,"text":1581},{"id":1623,"depth":132,"text":1624},{"id":1660,"depth":132,"text":1909},"4. 与普通 ref 的区别",{"id":1695,"depth":125,"text":1696},{"id":1805,"depth":125,"text":1805},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FVue\u002Freactivity\u002Fproxy","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FVue\u002Freactivity\u002Fproxy.md","md",{"items":1917},[1918,1921,1925,1929,1933,1937],{"path":44,"title":1919,"description":1920,"category":10,"score":255},"依赖收集与派发更新机制","说明 Vue 3 中 track \u002F trigger 与 targetMap 的工作方式。前置：v2-v3 差异。",{"path":1922,"title":1923,"description":1924,"category":10,"score":255},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fv2-v3","Vue 2 与 Vue 3 响应式差异","Vue 2 与 Vue 3 响应式实现的核心差异如下。基础概念见 Vue 基础。",{"path":1926,"title":1927,"description":1928,"category":10,"score":189},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets","资源优化","图片懒加载、按需引入与 Web Vitals 监控。见 HTML 图片、CSS 字体优化。",{"path":1930,"title":1931,"description":1932,"category":10,"score":189},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fstatus","状态管理：Pinia 与 Vuex","Pinia 是 Vue 官方推荐的状态管理库（Vue 3 新项目默认选型）。Vuex 4 仍可与 Vue 3 配合，用于维护旧项目。见 Vue 基础。",{"path":1934,"title":1935,"description":1936,"category":10,"score":189},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fecology","Vue 生态关键库深度集成","VueUse、Vite 与 Nuxt 是 Vue 3 生态中常用的效率与工程化方案。概览见 Vue 基础。",{"path":1938,"title":1939,"description":1940,"category":10,"score":189},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Ftest","测试策略","Vue 应用常用 Vitest + @vue\u002Ftest-utils 做单元\u002F组件测试，Playwright 或 Cypress 做 E2E。见 Vue 基础。",{"series":1942,"title":13,"items":1943,"index":1949,"prev":1950,"next":1951},"booklet\u002Fframe\u002Fvue\u002Freactivity",[1944,1945,1947],{"path":7,"title":8,"date":15,"pinned":16},{"path":44,"title":1919,"date":1946,"pinned":16},"2025-01-13",{"path":1922,"title":1923,"date":1948,"pinned":16},"2022-07-06",0,null,{"path":44,"title":1919,"date":1946,"pinned":16}]