[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fruntime":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fruntime":1760,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fruntime":1784},{"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":1756,"stem":1757,"id":1758,"extension":1759},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fruntime","运行时优化","减少不必要的渲染与响应式开销。构建侧见 build。","技能小册",[12,13,14],"vue","performance","runtime","2021-06-10",false,{"type":18,"value":19,"toc":1748},"minimark",[20,23,33,36,41,55,78,84,162,167,353,356,371,379,391,416,482,491,495,528,651,661,665,675,680,703,708,727,732,876,879,883,890,901,924,1029,1035,1045,1052,1067,1155,1160,1171,1177,1181,1232,1240,1249,1330,1335,1363,1367,1374,1538,1548,1552,1589,1592,1671,1674,1678,1681,1744],[21,22,8],"h1",{"id":8},[24,25,26,27,32],"p",{},"减少不必要的渲染与响应式开销。构建侧见 ",[28,29,31],"a",{"href":30},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fbuild","build","。",[24,34,35],{},"运行时优化旨在减少不必要的 DOM 操作、响应式开销和计算成本，从而提升应用的交互流畅度和响应速度。以下是 Vue 3 中关键的运行时优化策略。",[37,38,40],"h3",{"id":39},"一列表渲染优化","一、列表渲染优化",[42,43,45,46,50,51,54],"h4",{"id":44},"_1-v-for-与-v-if-共用的冲突与解决方案","1. ",[47,48,49],"code",{},"v-for"," 与 ",[47,52,53],{},"v-if"," 共用的冲突与解决方案",[24,56,57,58,60,61,63,64,66,67,69,70,72,73,77],{},"在同一个元素上同时使用 ",[47,59,49],{}," 和 ",[47,62,53],{}," 时，Vue 会先执行 ",[47,65,49],{}," 再执行 ",[47,68,53],{},"（因为 ",[47,71,49],{}," 优先级更高），这意味着即使只有少数项需要渲染，也会遍历整个列表，造成性能浪费。Vue 官方",[74,75,76],"strong",{},"不推荐","这种写法，并会在开发模式下给出警告。",[24,79,80,83],{},[74,81,82],{},"❌ 错误示例","：",[85,86,91],"pre",{"className":87,"code":88,"language":89,"meta":90,"style":90},"language-html shiki shiki-themes material-theme-lighter github-light github-dark","\u003Cli v-for=\"user in users\" v-if=\"user.isActive\" :key=\"user.id\">{{ user.name }}\u003C\u002Fli>\n","html","",[47,92,93],{"__ignoreMap":90},[94,95,98,102,106,110,113,117,121,123,126,128,130,133,135,138,140,142,145,147,150,154,157,159],"span",{"class":96,"line":97},"line",1,[94,99,101],{"class":100},"sP7_E","\u003C",[94,103,105],{"class":104},"sQzsp","li",[94,107,109],{"class":108},"s9AJx"," v-for",[94,111,112],{"class":100},"=",[94,114,116],{"class":115},"sjJ54","\"",[94,118,120],{"class":119},"s_sjI","user in users",[94,122,116],{"class":115},[94,124,125],{"class":108}," v-if",[94,127,112],{"class":100},[94,129,116],{"class":115},[94,131,132],{"class":119},"user.isActive",[94,134,116],{"class":115},[94,136,137],{"class":108}," :key",[94,139,112],{"class":100},[94,141,116],{"class":115},[94,143,144],{"class":119},"user.id",[94,146,116],{"class":115},[94,148,149],{"class":100},">",[94,151,153],{"class":152},"su5hD","{{ user.name }}",[94,155,156],{"class":100},"\u003C\u002F",[94,158,105],{"class":104},[94,160,161],{"class":100},">\n",[24,163,164],{},[74,165,166],{},"✅ 解决方案：使用计算属性过滤",[85,168,171],{"className":169,"code":170,"language":12,"meta":90,"style":90},"language-vue shiki shiki-themes material-theme-lighter github-light github-dark","\u003Cscript setup>\nimport { computed } from \"vue\";\nconst activeUsers = computed(() => users.value.filter((u) => u.isActive));\n\u003C\u002Fscript>\n\u003Ctemplate>\n  \u003Cli v-for=\"user in activeUsers\" :key=\"user.id\">\n    {{ user.name }}\n  \u003C\u002Fli>\n\u003C\u002Ftemplate>\n",[47,172,173,185,214,278,287,297,328,334,344],{"__ignoreMap":90},[94,174,175,177,180,183],{"class":96,"line":97},[94,176,101],{"class":100},[94,178,179],{"class":104},"script",[94,181,182],{"class":108}," setup",[94,184,161],{"class":100},[94,186,188,192,195,198,201,204,207,209,211],{"class":96,"line":187},2,[94,189,191],{"class":190},"sVHd0","import",[94,193,194],{"class":100}," {",[94,196,197],{"class":152}," computed",[94,199,200],{"class":100}," }",[94,202,203],{"class":190}," from",[94,205,206],{"class":115}," \"",[94,208,12],{"class":119},[94,210,116],{"class":115},[94,212,213],{"class":100},";\n",[94,215,217,221,225,229,232,235,238,241,244,247,250,252,255,257,259,263,266,268,271,273,276],{"class":96,"line":216},3,[94,218,220],{"class":219},"sbsja","const",[94,222,224],{"class":223},"s_hVV"," activeUsers",[94,226,228],{"class":227},"smGrS"," =",[94,230,197],{"class":231},"sGLFI",[94,233,234],{"class":152},"(",[94,236,237],{"class":100},"()",[94,239,240],{"class":219}," =>",[94,242,243],{"class":152}," users",[94,245,246],{"class":100},".",[94,248,249],{"class":152},"value",[94,251,246],{"class":100},[94,253,254],{"class":231},"filter",[94,256,234],{"class":152},[94,258,234],{"class":100},[94,260,262],{"class":261},"s99_P","u",[94,264,265],{"class":100},")",[94,267,240],{"class":219},[94,269,270],{"class":152}," u",[94,272,246],{"class":100},[94,274,275],{"class":152},"isActive))",[94,277,213],{"class":100},[94,279,281,283,285],{"class":96,"line":280},4,[94,282,156],{"class":100},[94,284,179],{"class":104},[94,286,161],{"class":100},[94,288,290,292,295],{"class":96,"line":289},5,[94,291,101],{"class":100},[94,293,294],{"class":104},"template",[94,296,161],{"class":100},[94,298,300,303,305,307,309,311,314,316,318,320,322,324,326],{"class":96,"line":299},6,[94,301,302],{"class":100},"  \u003C",[94,304,105],{"class":104},[94,306,109],{"class":108},[94,308,112],{"class":100},[94,310,116],{"class":115},[94,312,313],{"class":119},"user in activeUsers",[94,315,116],{"class":115},[94,317,137],{"class":108},[94,319,112],{"class":100},[94,321,116],{"class":115},[94,323,144],{"class":119},[94,325,116],{"class":115},[94,327,161],{"class":100},[94,329,331],{"class":96,"line":330},7,[94,332,333],{"class":152},"    {{ user.name }}\n",[94,335,337,340,342],{"class":96,"line":336},8,[94,338,339],{"class":100},"  \u003C\u002F",[94,341,105],{"class":104},[94,343,161],{"class":100},[94,345,347,349,351],{"class":96,"line":346},9,[94,348,156],{"class":100},[94,350,294],{"class":104},[94,352,161],{"class":100},[24,354,355],{},"这样只遍历需要渲染的数据，且计算属性会缓存结果，只在依赖变化时重新计算。",[24,357,358,361,362,365,366,60,368,370],{},[74,359,360],{},"替代方案","：在外层包裹 ",[47,363,364],{},"\u003Ctemplate>"," 分别使用 ",[47,367,53],{},[47,369,49],{},"，但不推荐。",[42,372,374,375,378],{"id":373},"_2-key-的作用机制与使用原则","2. ",[47,376,377],{},"key"," 的作用机制与使用原则",[24,380,381,383,384,386,387,390],{},[47,382,377],{}," 是 Vue 虚拟 DOM 算法中用于标识节点复用的重要属性。当列表重新排序、插入或删除时，正确的 ",[47,385,377],{}," 可以",[74,388,389],{},"最大化复用","已有 DOM 元素，减少不必要的 DOM 操作。",[392,393,394,407],"ul",{},[105,395,396,399,400,403,404],{},[74,397,398],{},"使用唯一 ID","（如数据库主键、",[47,401,402],{},"uuid","）：",[47,405,406],{},"\u003Cli v-for=\"item in list\" :key=\"item.id\">",[105,408,409,412,413,415],{},[74,410,411],{},"避免使用数组索引","作为 ",[47,414,377],{},"：当列表顺序变化或中间插入\u002F删除元素时，索引会导致错误的节点复用，引发状态错乱和性能下降。",[85,417,419],{"className":169,"code":418,"language":12,"meta":90,"style":90},"\u003C!-- ❌ 坏：用索引作为 key -->\n\u003Cli v-for=\"(item, index) in list\" :key=\"index\">\n\u003C!-- ✅ 好：用唯一标识 -->\n\u003Cli v-for=\"item in list\" :key=\"item.id\">\n\n",[47,420,421,427,472,477],{"__ignoreMap":90},[94,422,423],{"class":96,"line":97},[94,424,426],{"class":425},"sutJx","\u003C!-- ❌ 坏：用索引作为 key -->\n",[94,428,429,431,433,435,437,439,442,445,448,451,454,456,459,461,463,465,468,470],{"class":96,"line":187},[94,430,101],{"class":100},[94,432,105],{"class":104},[94,434,109],{"class":190},[94,436,112],{"class":100},[94,438,116],{"class":115},[94,440,441],{"class":152},"(item",[94,443,444],{"class":100},",",[94,446,447],{"class":152}," index) ",[94,449,450],{"class":227},"in",[94,452,453],{"class":152}," list",[94,455,116],{"class":115},[94,457,458],{"class":100}," :",[94,460,377],{"class":108},[94,462,112],{"class":100},[94,464,116],{"class":115},[94,466,467],{"class":152},"index",[94,469,116],{"class":115},[94,471,161],{"class":100},[94,473,474],{"class":96,"line":216},[94,475,476],{"class":152},"\u003C!-- ✅ 好：用唯一标识 -->\n",[94,478,479],{"class":96,"line":280},[94,480,481],{"class":152},"\u003Cli v-for=\"item in list\" :key=\"item.id\">\n",[24,483,484,485,487,488,490],{},"若列表静态且不会变化，也可以不用 ",[47,486,377],{},"（Vue 会用就地复用策略），但在大多数动态场景下，提供稳定的 ",[47,489,377],{}," 是优化和防止 bug 的必要手段。",[42,492,494],{"id":493},"_3-计算属性-vs-方法的选择","3. 计算属性 vs 方法的选择",[392,496,497,511],{},[105,498,499,506,507,510],{},[74,500,501,502,505],{},"计算属性（",[47,503,504],{},"computed","）","：基于响应式依赖缓存，只有当依赖的数据发生变化时才重新求值。适合",[74,508,509],{},"派生状态、数据转换、过滤列表","等场景。",[105,512,513,519,520,523,524,527],{},[74,514,515,516,505],{},"方法（",[47,517,518],{},"methods","：每次重新渲染都会执行，没有缓存。适合",[74,521,522],{},"需要副作用","或",[74,525,526],{},"每次调用都需要最新值的操作","（如时间格式化、随机数）。",[85,529,533],{"className":530,"code":531,"language":532,"meta":90,"style":90},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F 计算属性：只会在 users 变化时重新计算\nconst activeUsers = computed(() => users.value.filter((u) => u.active));\n\n\u002F\u002F 方法：每次渲染都会执行\nfunction getActiveUsers() {\n  return users.value.filter((u) => u.active);\n}\n","js",[47,534,535,540,585,591,596,609,646],{"__ignoreMap":90},[94,536,537],{"class":96,"line":97},[94,538,539],{"class":425},"\u002F\u002F 计算属性：只会在 users 变化时重新计算\n",[94,541,542,544,546,548,550,552,554,556,558,560,562,564,566,568,570,572,574,576,578,580,583],{"class":96,"line":187},[94,543,220],{"class":219},[94,545,224],{"class":223},[94,547,228],{"class":227},[94,549,197],{"class":231},[94,551,234],{"class":152},[94,553,237],{"class":100},[94,555,240],{"class":219},[94,557,243],{"class":152},[94,559,246],{"class":100},[94,561,249],{"class":152},[94,563,246],{"class":100},[94,565,254],{"class":231},[94,567,234],{"class":152},[94,569,234],{"class":100},[94,571,262],{"class":261},[94,573,265],{"class":100},[94,575,240],{"class":219},[94,577,270],{"class":152},[94,579,246],{"class":100},[94,581,582],{"class":152},"active))",[94,584,213],{"class":100},[94,586,587],{"class":96,"line":216},[94,588,590],{"emptyLinePlaceholder":589},true,"\n",[94,592,593],{"class":96,"line":280},[94,594,595],{"class":425},"\u002F\u002F 方法：每次渲染都会执行\n",[94,597,598,601,604,606],{"class":96,"line":289},[94,599,600],{"class":219},"function",[94,602,603],{"class":231}," getActiveUsers",[94,605,237],{"class":100},[94,607,608],{"class":100}," {\n",[94,610,611,614,616,618,620,622,624,627,629,631,633,635,637,639,642,644],{"class":96,"line":299},[94,612,613],{"class":190},"  return",[94,615,243],{"class":152},[94,617,246],{"class":100},[94,619,249],{"class":152},[94,621,246],{"class":100},[94,623,254],{"class":231},[94,625,234],{"class":626},"skxfh",[94,628,234],{"class":100},[94,630,262],{"class":261},[94,632,265],{"class":100},[94,634,240],{"class":219},[94,636,270],{"class":152},[94,638,246],{"class":100},[94,640,641],{"class":152},"active",[94,643,265],{"class":626},[94,645,213],{"class":100},[94,647,648],{"class":96,"line":330},[94,649,650],{"class":100},"}\n",[24,652,653,654,657,658,32],{},"在模板中多次引用 ",[47,655,656],{},"activeUsers","，计算属性只会执行一次过滤；而方法调用几次就执行几次。",[74,659,660],{},"优先使用计算属性",[42,662,664],{"id":663},"_4-虚拟滚动万级列表渲染优化","4. 虚拟滚动（万级列表渲染优化）",[24,666,667,668,670,671,674],{},"当需要渲染大量列表项（成千上万）时，直接使用 ",[47,669,49],{}," 会导致 DOM 节点过多、内存占用高、滚动卡顿。",[74,672,673],{},"虚拟滚动","（Virtual Scrolling）的核心思想是：只渲染可视区域内的元素，以及上下少量的缓冲项，随着滚动动态更新渲染内容。",[24,676,677,83],{},[74,678,679],{},"实现原理",[392,681,682,689,692],{},[105,683,684,685,688],{},"监听容器的 ",[47,686,687],{},"scroll"," 事件，计算当前滚动位置。",[105,690,691],{},"根据每个项的高度（固定或预估）确定可视区起始索引和结束索引。",[105,693,694,695,698,699,702],{},"仅渲染该区间的 DOM，并通过 ",[47,696,697],{},"transform: translateY"," 或 ",[47,700,701],{},"padding"," 模拟完整列表的滚动高度。",[24,704,705,83],{},[74,706,707],{},"Vue 生态常用库",[392,709,710,716,721],{},[105,711,712,715],{},[47,713,714],{},"vue-virtual-scroller","（官方推荐）",[105,717,718],{},[47,719,720],{},"vue3-virtual-scroll-list",[105,722,723,726],{},[47,724,725],{},"tanstack-virtual","（框架无关）",[24,728,729,730,403],{},"使用示例（",[47,731,714],{},[85,733,735],{"className":169,"code":734,"language":12,"meta":90,"style":90},"\u003Ctemplate>\n  \u003CRecycleScroller :items=\"list\" :item-size=\"50\" key-field=\"id\">\n    \u003Ctemplate #default=\"{ item }\">\n      \u003Cdiv class=\"item\">{{ item.name }}\u003C\u002Fdiv>\n    \u003C\u002Ftemplate>\n  \u003C\u002FRecycleScroller>\n\u003C\u002Ftemplate>\n",[47,736,737,745,790,820,851,860,868],{"__ignoreMap":90},[94,738,739,741,743],{"class":96,"line":97},[94,740,101],{"class":100},[94,742,294],{"class":104},[94,744,161],{"class":100},[94,746,747,749,752,755,757,759,762,764,767,769,771,774,776,779,781,783,786,788],{"class":96,"line":187},[94,748,302],{"class":100},[94,750,751],{"class":104},"RecycleScroller",[94,753,754],{"class":108}," :items",[94,756,112],{"class":100},[94,758,116],{"class":115},[94,760,761],{"class":119},"list",[94,763,116],{"class":115},[94,765,766],{"class":108}," :item-size",[94,768,112],{"class":100},[94,770,116],{"class":115},[94,772,773],{"class":119},"50",[94,775,116],{"class":115},[94,777,778],{"class":108}," key-field",[94,780,112],{"class":100},[94,782,116],{"class":115},[94,784,785],{"class":119},"id",[94,787,116],{"class":115},[94,789,161],{"class":100},[94,791,792,795,797,800,803,805,807,810,813,816,818],{"class":96,"line":216},[94,793,794],{"class":100},"    \u003C",[94,796,294],{"class":104},[94,798,799],{"class":100}," #",[94,801,802],{"class":108},"default",[94,804,112],{"class":100},[94,806,116],{"class":115},[94,808,809],{"class":100},"{",[94,811,812],{"class":152}," item ",[94,814,815],{"class":100},"}",[94,817,116],{"class":115},[94,819,161],{"class":100},[94,821,822,825,828,831,833,835,838,840,842,845,847,849],{"class":96,"line":280},[94,823,824],{"class":100},"      \u003C",[94,826,827],{"class":104},"div",[94,829,830],{"class":108}," class",[94,832,112],{"class":100},[94,834,116],{"class":115},[94,836,837],{"class":119},"item",[94,839,116],{"class":115},[94,841,149],{"class":100},[94,843,844],{"class":152},"{{ item.name }}",[94,846,156],{"class":100},[94,848,827],{"class":104},[94,850,161],{"class":100},[94,852,853,856,858],{"class":96,"line":289},[94,854,855],{"class":100},"    \u003C\u002F",[94,857,294],{"class":104},[94,859,161],{"class":100},[94,861,862,864,866],{"class":96,"line":299},[94,863,339],{"class":100},[94,865,751],{"class":104},[94,867,161],{"class":100},[94,869,870,872,874],{"class":96,"line":330},[94,871,156],{"class":100},[94,873,294],{"class":104},[94,875,161],{"class":100},[24,877,878],{},"对于表格、瀑布流等复杂场景，虚拟滚动能极大提升渲染性能。",[37,880,882],{"id":881},"二复杂对象性能处理","二、复杂对象性能处理",[24,884,885,886,889],{},"当处理大型、深度嵌套的对象时，全量响应式代理（",[47,887,888],{},"reactive","）会带来性能开销：初始化时递归遍历所有属性，每个属性都成为响应式数据。可通过以下 API 降低开销。",[42,891,45,893,896,897,900],{"id":892},"_1-shallowref-shallowreactive-用于大对象",[47,894,895],{},"shallowRef"," \u002F ",[47,898,899],{},"shallowReactive"," 用于大对象",[392,902,903,910],{},[105,904,905,909],{},[74,906,907],{},[47,908,899],{},"：只代理对象的第一层属性，深层属性保持原始（非响应式）。",[105,911,912,916,917,920,921,923],{},[74,913,914],{},[47,915,895],{},"：只追踪 ",[47,918,919],{},".value"," 的引用变化，如果 ",[47,922,919],{}," 是对象，对象内部变化不会触发更新。",[85,925,927],{"className":530,"code":926,"language":532,"meta":90,"style":90},"import { shallowReactive } from \"vue\";\nconst bigData = shallowReactive({\n  meta: { version: 1 }, \u002F\u002F meta 内部变化不会触发更新\n  items: [\n    \u002F* 大量数据 *\u002F\n  ], \u002F\u002F items 内部变化不会触发更新\n});\n\u002F\u002F 只有替换整个 bigData.meta 或 bigData.items 才会触发更新\n",[47,928,929,950,966,991,1001,1006,1016,1024],{"__ignoreMap":90},[94,930,931,933,935,938,940,942,944,946,948],{"class":96,"line":97},[94,932,191],{"class":190},[94,934,194],{"class":100},[94,936,937],{"class":152}," shallowReactive",[94,939,200],{"class":100},[94,941,203],{"class":190},[94,943,206],{"class":115},[94,945,12],{"class":119},[94,947,116],{"class":115},[94,949,213],{"class":100},[94,951,952,954,957,959,961,963],{"class":96,"line":187},[94,953,220],{"class":219},[94,955,956],{"class":223}," bigData",[94,958,228],{"class":227},[94,960,937],{"class":231},[94,962,234],{"class":152},[94,964,965],{"class":100},"{\n",[94,967,968,971,974,976,979,981,985,988],{"class":96,"line":216},[94,969,970],{"class":626},"  meta",[94,972,973],{"class":100},":",[94,975,194],{"class":100},[94,977,978],{"class":626}," version",[94,980,973],{"class":100},[94,982,984],{"class":983},"srdBf"," 1",[94,986,987],{"class":100}," },",[94,989,990],{"class":425}," \u002F\u002F meta 内部变化不会触发更新\n",[94,992,993,996,998],{"class":96,"line":280},[94,994,995],{"class":626},"  items",[94,997,973],{"class":100},[94,999,1000],{"class":152}," [\n",[94,1002,1003],{"class":96,"line":289},[94,1004,1005],{"class":425},"    \u002F* 大量数据 *\u002F\n",[94,1007,1008,1011,1013],{"class":96,"line":299},[94,1009,1010],{"class":152},"  ]",[94,1012,444],{"class":100},[94,1014,1015],{"class":425}," \u002F\u002F items 内部变化不会触发更新\n",[94,1017,1018,1020,1022],{"class":96,"line":330},[94,1019,815],{"class":100},[94,1021,265],{"class":152},[94,1023,213],{"class":100},[94,1025,1026],{"class":96,"line":336},[94,1027,1028],{"class":425},"\u002F\u002F 只有替换整个 bigData.meta 或 bigData.items 才会触发更新\n",[24,1030,1031,1034],{},[74,1032,1033],{},"适用场景","：展示静态配置、不需要响应式深层的第三方实例、可整体替换的大数据集。",[24,1036,1037,1040,1041,1044],{},[74,1038,1039],{},"注意","：一旦使用浅响应式，修改深层属性不会导致视图更新，需谨慎或配合 ",[47,1042,1043],{},"triggerRef"," 强制更新。",[42,1046,374,1048,1051],{"id":1047},"_2-markraw-标记永久不可响应化的数据",[47,1049,1050],{},"markRaw"," 标记永久不可响应化的数据",[24,1053,1054,1056,1057,1060,1061,698,1063,1066],{},[47,1055,1050],{}," 标记的对象或值将",[74,1058,1059],{},"永远不会被代理","，即使传入 ",[47,1062,888],{},[47,1064,1065],{},"ref"," 也会保持原始状态。",[85,1068,1070],{"className":530,"code":1069,"language":532,"meta":90,"style":90},"import { markRaw, reactive } from \"vue\";\nconst externalLib = markRaw(new SomeLibrary());\nconst state = reactive({ lib: externalLib }); \u002F\u002F lib 不会被代理\n",[47,1071,1072,1098,1122],{"__ignoreMap":90},[94,1073,1074,1076,1078,1081,1083,1086,1088,1090,1092,1094,1096],{"class":96,"line":97},[94,1075,191],{"class":190},[94,1077,194],{"class":100},[94,1079,1080],{"class":152}," markRaw",[94,1082,444],{"class":100},[94,1084,1085],{"class":152}," reactive",[94,1087,200],{"class":100},[94,1089,203],{"class":190},[94,1091,206],{"class":115},[94,1093,12],{"class":119},[94,1095,116],{"class":115},[94,1097,213],{"class":100},[94,1099,1100,1102,1105,1107,1109,1111,1114,1117,1120],{"class":96,"line":187},[94,1101,220],{"class":219},[94,1103,1104],{"class":223}," externalLib",[94,1106,228],{"class":227},[94,1108,1080],{"class":231},[94,1110,234],{"class":152},[94,1112,1113],{"class":227},"new",[94,1115,1116],{"class":231}," SomeLibrary",[94,1118,1119],{"class":152},"())",[94,1121,213],{"class":100},[94,1123,1124,1126,1129,1131,1133,1135,1137,1140,1142,1145,1147,1149,1152],{"class":96,"line":216},[94,1125,220],{"class":219},[94,1127,1128],{"class":223}," state",[94,1130,228],{"class":227},[94,1132,1085],{"class":231},[94,1134,234],{"class":152},[94,1136,809],{"class":100},[94,1138,1139],{"class":626}," lib",[94,1141,973],{"class":100},[94,1143,1144],{"class":152}," externalLib ",[94,1146,815],{"class":100},[94,1148,265],{"class":152},[94,1150,1151],{"class":100},";",[94,1153,1154],{"class":425}," \u002F\u002F lib 不会被代理\n",[24,1156,1157,83],{},[74,1158,1159],{},"典型场景",[392,1161,1162,1165,1168],{},[105,1163,1164],{},"第三方类实例（如 ECharts、Map 实例）。",[105,1166,1167],{},"永远不需要响应式的巨大数据。",[105,1169,1170],{},"不可变的外部数据源。",[24,1172,1173,1176],{},[74,1174,1175],{},"收益","：避免不必要的性能开销，同时防止意外触发响应式更新。",[37,1178,1180],{"id":1179},"三watch-vs-watcheffect","三、watch vs watchEffect",[1182,1183,1184,1199],"table",{},[1185,1186,1187],"thead",{},[1188,1189,1190,1194,1197],"tr",{},[1191,1192,1193],"th",{},"API",[1191,1195,1196],{},"特点",[1191,1198,1033],{},[1200,1201,1202,1219],"tbody",{},[1188,1203,1204,1210,1216],{},[1205,1206,1207],"td",{},[47,1208,1209],{},"watch",[1205,1211,1212,1213],{},"懒执行，指定数据源，可访问旧值，可配置 ",[47,1214,1215],{},"{ deep, immediate }",[1205,1217,1218],{},"需要监听特定数据、需要旧值、需要控制深度",[1188,1220,1221,1226,1229],{},[1205,1222,1223],{},[47,1224,1225],{},"watchEffect",[1205,1227,1228],{},"立即执行，自动收集依赖，无法获取旧值",[1205,1230,1231],{},"副作用逻辑简单，依赖较多时自动追踪",[42,1233,1235,1236,1239],{"id":1234},"_1-深度监听deep-true的性能代价","1. 深度监听（",[47,1237,1238],{},"deep: true","）的性能代价",[24,1241,1242,1243,1245,1246,1248],{},"当对一个大型深层对象使用 ",[47,1244,1209],{}," 的 ",[47,1247,1238],{}," 时，Vue 需要递归遍历对象的所有属性以检测变化。如果对象层级深、体积大，这会造成显著的 CPU 开销。",[85,1250,1252],{"className":530,"code":1251,"language":532,"meta":90,"style":90},"\u002F\u002F ❌ 避免在大型对象上使用 deep\nwatch(\n  () => state.bigObject,\n  (newVal) => {\n    \u002F\u002F 任何深层属性变化都会触发\n  },\n  { deep: true },\n);\n",[47,1253,1254,1259,1266,1283,1297,1302,1307,1324],{"__ignoreMap":90},[94,1255,1256],{"class":96,"line":97},[94,1257,1258],{"class":425},"\u002F\u002F ❌ 避免在大型对象上使用 deep\n",[94,1260,1261,1263],{"class":96,"line":187},[94,1262,1209],{"class":231},[94,1264,1265],{"class":152},"(\n",[94,1267,1268,1271,1273,1275,1277,1280],{"class":96,"line":216},[94,1269,1270],{"class":100},"  ()",[94,1272,240],{"class":219},[94,1274,1128],{"class":152},[94,1276,246],{"class":100},[94,1278,1279],{"class":152},"bigObject",[94,1281,1282],{"class":100},",\n",[94,1284,1285,1288,1291,1293,1295],{"class":96,"line":280},[94,1286,1287],{"class":100},"  (",[94,1289,1290],{"class":261},"newVal",[94,1292,265],{"class":100},[94,1294,240],{"class":219},[94,1296,608],{"class":100},[94,1298,1299],{"class":96,"line":289},[94,1300,1301],{"class":425},"    \u002F\u002F 任何深层属性变化都会触发\n",[94,1303,1304],{"class":96,"line":299},[94,1305,1306],{"class":100},"  },\n",[94,1308,1309,1312,1315,1317,1321],{"class":96,"line":330},[94,1310,1311],{"class":100},"  {",[94,1313,1314],{"class":626}," deep",[94,1316,973],{"class":100},[94,1318,1320],{"class":1319},"syTEX"," true",[94,1322,1323],{"class":100}," },\n",[94,1325,1326,1328],{"class":96,"line":336},[94,1327,265],{"class":152},[94,1329,213],{"class":100},[24,1331,1332,83],{},[74,1333,1334],{},"更好的做法",[392,1336,1337,1346,1355],{},[105,1338,1339,1342,1343],{},[74,1340,1341],{},"监听特定路径","：使用 ",[47,1344,1345],{},"watch(() => state.bigObject.someField, ...)",[105,1347,1348,1351,1352,1354],{},[74,1349,1350],{},"使用计算属性派生需要的值","：如果只需要某个计算结果，使用 ",[47,1353,504],{}," 可自动缓存。",[105,1356,1357,1360,1361,32],{},[74,1358,1359],{},"手动浅比较","：对于嵌套对象，可以在需要的地方手动 ",[47,1362,1043],{},[42,1364,1366],{"id":1365},"_2-优先使用计算属性替代深度监听","2. 优先使用计算属性替代深度监听",[24,1368,1369,1370,1373],{},"许多场景下，我们其实并不需要监听整个对象的变化，而是需要基于对象派生某个状态。",[74,1371,1372],{},"计算属性","天然具备缓存和依赖追踪，比深度监听更高效。",[85,1375,1377],{"className":530,"code":1376,"language":532,"meta":90,"style":90},"\u002F\u002F 坏：深度监听\nwatch(\n  () => state.user,\n  (user) => {\n    fullName.value = `${user.firstName} ${user.lastName}`;\n  },\n  { deep: true },\n);\n\n\u002F\u002F 好：计算属性自动追踪具体依赖\nconst fullName = computed(() => `${state.user.firstName} ${state.user.lastName}`);\n",[47,1378,1379,1384,1390,1405,1417,1455,1459,1471,1477,1481,1487],{"__ignoreMap":90},[94,1380,1381],{"class":96,"line":97},[94,1382,1383],{"class":425},"\u002F\u002F 坏：深度监听\n",[94,1385,1386,1388],{"class":96,"line":187},[94,1387,1209],{"class":231},[94,1389,1265],{"class":152},[94,1391,1392,1394,1396,1398,1400,1403],{"class":96,"line":216},[94,1393,1270],{"class":100},[94,1395,240],{"class":219},[94,1397,1128],{"class":152},[94,1399,246],{"class":100},[94,1401,1402],{"class":152},"user",[94,1404,1282],{"class":100},[94,1406,1407,1409,1411,1413,1415],{"class":96,"line":280},[94,1408,1287],{"class":100},[94,1410,1402],{"class":261},[94,1412,265],{"class":100},[94,1414,240],{"class":219},[94,1416,608],{"class":100},[94,1418,1419,1422,1424,1426,1428,1431,1433,1435,1438,1440,1443,1445,1447,1450,1453],{"class":96,"line":289},[94,1420,1421],{"class":152},"    fullName",[94,1423,246],{"class":100},[94,1425,249],{"class":152},[94,1427,228],{"class":227},[94,1429,1430],{"class":115}," `${",[94,1432,1402],{"class":152},[94,1434,246],{"class":115},[94,1436,1437],{"class":152},"firstName",[94,1439,815],{"class":115},[94,1441,1442],{"class":115}," ${",[94,1444,1402],{"class":152},[94,1446,246],{"class":115},[94,1448,1449],{"class":152},"lastName",[94,1451,1452],{"class":115},"}`",[94,1454,213],{"class":100},[94,1456,1457],{"class":96,"line":299},[94,1458,1306],{"class":100},[94,1460,1461,1463,1465,1467,1469],{"class":96,"line":330},[94,1462,1311],{"class":100},[94,1464,1314],{"class":626},[94,1466,973],{"class":100},[94,1468,1320],{"class":1319},[94,1470,1323],{"class":100},[94,1472,1473,1475],{"class":96,"line":336},[94,1474,265],{"class":152},[94,1476,213],{"class":100},[94,1478,1479],{"class":96,"line":346},[94,1480,590],{"emptyLinePlaceholder":589},[94,1482,1484],{"class":96,"line":1483},10,[94,1485,1486],{"class":425},"\u002F\u002F 好：计算属性自动追踪具体依赖\n",[94,1488,1490,1492,1495,1497,1499,1501,1503,1505,1507,1510,1512,1514,1516,1518,1520,1522,1524,1526,1528,1530,1532,1534,1536],{"class":96,"line":1489},11,[94,1491,220],{"class":219},[94,1493,1494],{"class":223}," fullName",[94,1496,228],{"class":227},[94,1498,197],{"class":231},[94,1500,234],{"class":152},[94,1502,237],{"class":100},[94,1504,240],{"class":219},[94,1506,1430],{"class":115},[94,1508,1509],{"class":152},"state",[94,1511,246],{"class":115},[94,1513,1402],{"class":152},[94,1515,246],{"class":115},[94,1517,1437],{"class":152},[94,1519,815],{"class":115},[94,1521,1442],{"class":115},[94,1523,1509],{"class":152},[94,1525,246],{"class":115},[94,1527,1402],{"class":152},[94,1529,246],{"class":115},[94,1531,1449],{"class":152},[94,1533,1452],{"class":115},[94,1535,265],{"class":152},[94,1537,213],{"class":100},[24,1539,1540,1541,523,1544,1547],{},"如果必须监听深度改变（例如需要保存整个表单到服务器），可以考虑",[74,1542,1543],{},"防抖",[74,1545,1546],{},"仅在用户提交时序列化","，而非每次变化都深度遍历。",[37,1549,1551],{"id":1550},"四其他运行时优化提示","四、其他运行时优化提示",[392,1553,1554,1562,1570,1583],{},[105,1555,1556,1561],{},[74,1557,1558],{},[47,1559,1560],{},"v-once","：标记静态内容，只渲染一次，后续不再更新。适用于纯静态的文本或元素。",[105,1563,1564,1569],{},[74,1565,1566],{},[47,1567,1568],{},"v-memo","（Vue 3.2+）：记忆化子树，仅在依赖数组变化时才重新渲染。适合子组件列表。",[105,1571,1572,1578,1579,1582],{},[74,1573,1574,1575,1577],{},"使用 ",[47,1576,895],{}," 处理不需要响应式的组件实例","：例如 ",[47,1580,1581],{},"const map = shallowRef(null)"," 存储地图实例。",[105,1584,1585,1588],{},[74,1586,1587],{},"异步组件 + Suspense","：将非关键组件异步加载，减少初始渲染压力。",[37,1590,1591],{"id":1591},"总结",[1182,1593,1594,1604],{},[1185,1595,1596],{},[1188,1597,1598,1601],{},[1191,1599,1600],{},"优化主题",[1191,1602,1603],{},"关键手段",[1200,1605,1606,1624,1641,1654],{},[1188,1607,1608,1613],{},[1205,1609,1610],{},[74,1611,1612],{},"列表渲染",[1205,1614,1615,1616,50,1618,1620,1621,1623],{},"分离 ",[47,1617,49],{},[47,1619,53],{},"（用计算属性）；使用唯一 ",[47,1622,377],{},"；优先计算属性而非方法；万级列表使用虚拟滚动",[1188,1625,1626,1631],{},[1205,1627,1628],{},[74,1629,1630],{},"大对象处理",[1205,1632,1633,1635,1636,1635,1638,1640],{},[47,1634,895],{},"、",[47,1637,899],{},[47,1639,1050],{}," 避免深度响应式开销",[1188,1642,1643,1648],{},[1205,1644,1645],{},[74,1646,1647],{},"侦听器优化",[1205,1649,1650,1651,1653],{},"避免大对象深度监听；用计算属性取代深度 ",[47,1652,1209],{},"；明确监听目标路径",[1188,1655,1656,1661],{},[1205,1657,1658],{},[74,1659,1660],{},"其他",[1205,1662,1663,1635,1665,1667,1668,1670],{},[47,1664,1560],{},[47,1666,1568],{},"、异步组件、",[47,1669,895],{}," 外部实例",[24,1672,1673],{},"运行时优化往往是细节的堆叠。可结合 Lighthouse 与 Chrome Performance 面板定位瓶颈。",[1675,1676,1677],"h2",{"id":1677},"参考文献",[24,1679,1680],{},"以下链接在编写时均可正常访问：",[1182,1682,1683,1693],{},[1185,1684,1685],{},[1188,1686,1687,1690],{},[1191,1688,1689],{},"资料",[1191,1691,1692],{},"说明",[1200,1694,1695,1708,1720,1732],{},[1188,1696,1697,1705],{},[1205,1698,1699],{},[28,1700,1704],{"href":1701,"rel":1702},"https:\u002F\u002Fcn.vuejs.org\u002Fguide\u002Fessentials\u002Flist.html",[1703],"nofollow","Vue：列表渲染",[1205,1706,1707],{},"v-for 与 key",[1188,1709,1710,1717],{},[1205,1711,1712],{},[28,1713,1716],{"href":1714,"rel":1715},"https:\u002F\u002Fcn.vuejs.org\u002Fguide\u002Fbest-practices\u002Fperformance.html",[1703],"Vue：性能",[1205,1718,1719],{},"官方建议",[1188,1721,1722,1729],{},[1205,1723,1724],{},[28,1725,1728],{"href":1726,"rel":1727},"https:\u002F\u002Fcn.vuejs.org\u002Fapi\u002Fbuilt-in-directives.html#v-memo",[1703],"Vue：v-memo",[1205,1730,1731],{},"记忆化",[1188,1733,1734,1741],{},[1205,1735,1736],{},[28,1737,1740],{"href":1738,"rel":1739},"https:\u002F\u002Fweb.dev\u002Farticles\u002Fvitals",[1703],"web.dev：Web Vitals",[1205,1742,1743],{},"核心指标（英文）",[1745,1746,1747],"style",{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}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 .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--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 .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 .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 .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 .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 .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 .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":90,"searchDepth":187,"depth":187,"links":1749},[1750,1751,1752,1753,1754,1755],{"id":39,"depth":216,"text":40},{"id":881,"depth":216,"text":882},{"id":1179,"depth":216,"text":1180},{"id":1550,"depth":216,"text":1551},{"id":1591,"depth":216,"text":1591},{"id":1677,"depth":187,"text":1677},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FVue\u002Fperformance\u002Fruntime","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FVue\u002Fperformance\u002Fruntime.md","md",{"items":1761},[1762,1766,1769,1772,1775,1780],{"path":1763,"title":1764,"description":1765,"category":10,"score":346},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets","资源优化","图片懒加载、按需引入与 Web Vitals 监控。见 HTML 图片、CSS 字体优化。",{"path":30,"title":1767,"description":1768,"category":10,"score":346},"编译与打包优化","构建阶段的代码分割、Tree Shaking 与 SFC 编译优化。运行时见 runtime。",{"path":1770,"title":8,"description":1771,"category":10,"score":330},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fperformance\u002Fruntime","在 变更检测 策略正确的前提下，减少模板计算与 DOM 规模。涵盖 OnPush、@for track、纯管道、虚拟滚动、懒加载、构建体积分析与常见性能陷阱。",{"path":1773,"title":8,"description":1774,"category":10,"score":330},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fruntime","减少不必要的组件重渲染与昂贵计算。涵盖重渲染成因、memo\u002FuseMemo\u002FuseCallback、虚拟滚动、Web Worker、Profiler 分析、最佳实践与常见坑。构建侧见 build。",{"path":1776,"title":1777,"description":1778,"category":1779,"score":299},"\u002Fknowledge\u002Fteam-documents\u002F19","GridView 宫格加载渲染优化","系统首页 GridView 宫格模块接口耗时不高，但首次进入总耗时接近 22s。复盘耗时分层定位过程、代码层面的瓶颈（约 2000 行、100 个 tab 重复节点）、优化手段与最终指标对比。","团队文档",{"path":1781,"title":1782,"description":1783,"category":1779,"score":299},"\u002Fknowledge\u002Fteam-documents\u002F13","新商家系统性能优化实践","商家系统新版本上线后，团队持续针对构建效率与用户体验做了一轮系统性优化。本文记录核心方案与落地结果，供后续版本复用。",{"series":1785,"title":13,"items":1786,"index":187,"prev":1792,"next":1793},"booklet\u002Fframe\u002Fvue\u002Fperformance",[1787,1789,1791],{"path":1763,"title":1764,"date":1788,"pinned":16},"2025-11-06",{"path":30,"title":1767,"date":1790,"pinned":16},"2021-08-02",{"path":7,"title":8,"date":15,"pinned":16},{"path":30,"title":1767,"date":1790,"pinned":16},null]