[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets":1806,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets":1833},{"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":1802,"stem":1803,"id":1804,"extension":1805},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets","资源优化","图片懒加载、按需引入与 Web Vitals 监控。见 HTML 图片、CSS 字体优化。","技能小册",[12,13,14],"vue","performance","assets","2025-11-06",false,{"type":18,"value":19,"toc":1795},"minimark",[20,23,38,41,46,49,54,69,76,416,424,427,768,771,834,838,857,867,871,874,878,896,1004,1014,1018,1033,1038,1087,1092,1250,1257,1261,1288,1292,1306,1310,1313,1317,1320,1326,1358,1363,1375,1379,1390,1451,1456,1474,1551,1555,1648,1652,1659,1662,1718,1721,1725,1728,1791],[21,22,8],"h1",{"id":8},[24,25,26,27,32,33,37],"p",{},"图片懒加载、按需引入与 Web Vitals 监控。见 ",[28,29,31],"a",{"href":30},"\u002Fknowledge\u002Fbooklet\u002Fweb\u002Fhtml\u002Ftag","HTML 图片","、",[28,34,36],{"href":35},"\u002Fknowledge\u002Fbooklet\u002Fweb\u002Fcss\u002Ffont-optimization","CSS 字体优化","。",[24,39,40],{},"资源优化聚焦于减少网络传输体积、延迟非关键资源加载以及监控真实性能，从而提升页面加载速度和用户体验。",[42,43,45],"h3",{"id":44},"一图片懒加载配合-intersectionobserver-的-vue-指令","一、图片懒加载：配合 IntersectionObserver 的 Vue 指令",[24,47,48],{},"图片懒加载是一种优化首屏加载的常用手段：只有当图片进入视口（或视口附近）时才加载真实图片，避免一次性加载大量不可见图。",[50,51,53],"h4",{"id":52},"_1-原生实现intersectionobserver","1. 原生实现：IntersectionObserver",[24,55,56,60,61,64,65,68],{},[57,58,59],"code",{},"IntersectionObserver"," 可以高效监听元素与视口的交叉状态，相比 ",[57,62,63],{},"scroll"," 事件 + ",[57,66,67],{},"getBoundingClientRect","，它由浏览器底层异步处理，性能更优。",[24,70,71,75],{},[72,73,74],"strong",{},"基础用法","：",[77,78,83],"pre",{"className":79,"code":80,"language":81,"meta":82,"style":82},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","const observer = new IntersectionObserver(\n  (entries) => {\n    entries.forEach((entry) => {\n      if (entry.isIntersecting) {\n        const img = entry.target;\n        const src = img.dataset.src;\n        if (src) {\n          img.src = src;\n          img.removeAttribute(\"data-src\");\n        }\n        observer.unobserve(img);\n      }\n    });\n  },\n  { rootMargin: \"0px 0px 100px 0px\" },\n); \u002F\u002F 提前 100px 开始加载\ndocument.querySelectorAll(\"img[data-src]\").forEach((img) => observer.observe(img));\n","js","",[57,84,85,113,133,160,183,205,229,243,259,285,291,311,317,327,333,356,368],{"__ignoreMap":82},[86,87,90,94,98,102,105,109],"span",{"class":88,"line":89},"line",1,[86,91,93],{"class":92},"sbsja","const",[86,95,97],{"class":96},"s_hVV"," observer",[86,99,101],{"class":100},"smGrS"," =",[86,103,104],{"class":100}," new",[86,106,108],{"class":107},"sGLFI"," IntersectionObserver",[86,110,112],{"class":111},"su5hD","(\n",[86,114,116,120,124,127,130],{"class":88,"line":115},2,[86,117,119],{"class":118},"sP7_E","  (",[86,121,123],{"class":122},"s99_P","entries",[86,125,126],{"class":118},")",[86,128,129],{"class":92}," =>",[86,131,132],{"class":118}," {\n",[86,134,136,139,142,145,149,151,154,156,158],{"class":88,"line":135},3,[86,137,138],{"class":111},"    entries",[86,140,141],{"class":118},".",[86,143,144],{"class":107},"forEach",[86,146,148],{"class":147},"skxfh","(",[86,150,148],{"class":118},[86,152,153],{"class":122},"entry",[86,155,126],{"class":118},[86,157,129],{"class":92},[86,159,132],{"class":118},[86,161,163,167,170,172,174,177,180],{"class":88,"line":162},4,[86,164,166],{"class":165},"sVHd0","      if",[86,168,169],{"class":147}," (",[86,171,153],{"class":111},[86,173,141],{"class":118},[86,175,176],{"class":111},"isIntersecting",[86,178,179],{"class":147},") ",[86,181,182],{"class":118},"{\n",[86,184,186,189,192,194,197,199,202],{"class":88,"line":185},5,[86,187,188],{"class":92},"        const",[86,190,191],{"class":96}," img",[86,193,101],{"class":100},[86,195,196],{"class":111}," entry",[86,198,141],{"class":118},[86,200,201],{"class":111},"target",[86,203,204],{"class":118},";\n",[86,206,208,210,213,215,217,219,222,224,227],{"class":88,"line":207},6,[86,209,188],{"class":92},[86,211,212],{"class":96}," src",[86,214,101],{"class":100},[86,216,191],{"class":111},[86,218,141],{"class":118},[86,220,221],{"class":111},"dataset",[86,223,141],{"class":118},[86,225,226],{"class":111},"src",[86,228,204],{"class":118},[86,230,232,235,237,239,241],{"class":88,"line":231},7,[86,233,234],{"class":165},"        if",[86,236,169],{"class":147},[86,238,226],{"class":111},[86,240,179],{"class":147},[86,242,182],{"class":118},[86,244,246,249,251,253,255,257],{"class":88,"line":245},8,[86,247,248],{"class":111},"          img",[86,250,141],{"class":118},[86,252,226],{"class":111},[86,254,101],{"class":100},[86,256,212],{"class":111},[86,258,204],{"class":118},[86,260,262,264,266,269,271,275,279,281,283],{"class":88,"line":261},9,[86,263,248],{"class":111},[86,265,141],{"class":118},[86,267,268],{"class":107},"removeAttribute",[86,270,148],{"class":147},[86,272,274],{"class":273},"sjJ54","\"",[86,276,278],{"class":277},"s_sjI","data-src",[86,280,274],{"class":273},[86,282,126],{"class":147},[86,284,204],{"class":118},[86,286,288],{"class":88,"line":287},10,[86,289,290],{"class":118},"        }\n",[86,292,294,297,299,302,304,307,309],{"class":88,"line":293},11,[86,295,296],{"class":111},"        observer",[86,298,141],{"class":118},[86,300,301],{"class":107},"unobserve",[86,303,148],{"class":147},[86,305,306],{"class":111},"img",[86,308,126],{"class":147},[86,310,204],{"class":118},[86,312,314],{"class":88,"line":313},12,[86,315,316],{"class":118},"      }\n",[86,318,320,323,325],{"class":88,"line":319},13,[86,321,322],{"class":118},"    }",[86,324,126],{"class":147},[86,326,204],{"class":118},[86,328,330],{"class":88,"line":329},14,[86,331,332],{"class":118},"  },\n",[86,334,336,339,342,345,348,351,353],{"class":88,"line":335},15,[86,337,338],{"class":118},"  {",[86,340,341],{"class":147}," rootMargin",[86,343,344],{"class":118},":",[86,346,347],{"class":273}," \"",[86,349,350],{"class":277},"0px 0px 100px 0px",[86,352,274],{"class":273},[86,354,355],{"class":118}," },\n",[86,357,359,361,364],{"class":88,"line":358},16,[86,360,126],{"class":111},[86,362,363],{"class":118},";",[86,365,367],{"class":366},"sutJx"," \u002F\u002F 提前 100px 开始加载\n",[86,369,371,374,376,379,381,383,386,388,390,392,394,396,398,400,402,404,406,408,411,414],{"class":88,"line":370},17,[86,372,373],{"class":111},"document",[86,375,141],{"class":118},[86,377,378],{"class":107},"querySelectorAll",[86,380,148],{"class":111},[86,382,274],{"class":273},[86,384,385],{"class":277},"img[data-src]",[86,387,274],{"class":273},[86,389,126],{"class":111},[86,391,141],{"class":118},[86,393,144],{"class":107},[86,395,148],{"class":111},[86,397,148],{"class":118},[86,399,306],{"class":122},[86,401,126],{"class":118},[86,403,129],{"class":92},[86,405,97],{"class":111},[86,407,141],{"class":118},[86,409,410],{"class":107},"observe",[86,412,413],{"class":111},"(img))",[86,415,204],{"class":118},[50,417,419,420,423],{"id":418},"_2-封装为-vue-指令v-lazy","2. 封装为 Vue 指令（",[57,421,422],{},"v-lazy","）",[24,425,426],{},"为了方便复用，可以封装一个自定义指令，支持动态绑定、响应式更新。",[77,428,430],{"className":79,"code":429,"language":81,"meta":82,"style":82},"\u002F\u002F directives\u002Flazy.js\nexport default {\n  mounted(el, binding) {\n    \u002F\u002F 用 data-src 存储真实图片地址\n    el.dataset.src = binding.value;\n    const observer = new IntersectionObserver(\n      (entries) => {\n        if (entries[0].isIntersecting) {\n          el.src = el.dataset.src;\n          delete el.dataset.src;\n          observer.unobserve(el);\n        }\n      },\n      { rootMargin: \"0px 0px 100px 0px\" },\n    );\n    observer.observe(el);\n    \u002F\u002F 存储 observer 以便 unmounted 时断开\n    el._lazyObserver = observer;\n  },\n  unmounted(el) {\n    if (el._lazyObserver) {\n      el._lazyObserver.disconnect();\n      delete el._lazyObserver;\n    }\n  },\n};\n",[57,431,432,437,447,468,473,497,512,525,551,575,592,609,613,618,635,642,659,664,680,685,699,717,737,751,757,762],{"__ignoreMap":82},[86,433,434],{"class":88,"line":89},[86,435,436],{"class":366},"\u002F\u002F directives\u002Flazy.js\n",[86,438,439,442,445],{"class":88,"line":115},[86,440,441],{"class":165},"export",[86,443,444],{"class":165}," default",[86,446,132],{"class":118},[86,448,449,453,455,458,461,464,466],{"class":88,"line":135},[86,450,452],{"class":451},"sVXei","  mounted",[86,454,148],{"class":118},[86,456,457],{"class":122},"el",[86,459,460],{"class":118},",",[86,462,463],{"class":122}," binding",[86,465,126],{"class":118},[86,467,132],{"class":118},[86,469,470],{"class":88,"line":162},[86,471,472],{"class":366},"    \u002F\u002F 用 data-src 存储真实图片地址\n",[86,474,475,478,480,482,484,486,488,490,492,495],{"class":88,"line":185},[86,476,477],{"class":111},"    el",[86,479,141],{"class":118},[86,481,221],{"class":111},[86,483,141],{"class":118},[86,485,226],{"class":111},[86,487,101],{"class":100},[86,489,463],{"class":111},[86,491,141],{"class":118},[86,493,494],{"class":111},"value",[86,496,204],{"class":118},[86,498,499,502,504,506,508,510],{"class":88,"line":207},[86,500,501],{"class":92},"    const",[86,503,97],{"class":96},[86,505,101],{"class":100},[86,507,104],{"class":100},[86,509,108],{"class":107},[86,511,112],{"class":147},[86,513,514,517,519,521,523],{"class":88,"line":231},[86,515,516],{"class":118},"      (",[86,518,123],{"class":122},[86,520,126],{"class":118},[86,522,129],{"class":92},[86,524,132],{"class":118},[86,526,527,529,531,533,536,540,543,545,547,549],{"class":88,"line":245},[86,528,234],{"class":165},[86,530,169],{"class":147},[86,532,123],{"class":111},[86,534,535],{"class":147},"[",[86,537,539],{"class":538},"srdBf","0",[86,541,542],{"class":147},"]",[86,544,141],{"class":118},[86,546,176],{"class":111},[86,548,179],{"class":147},[86,550,182],{"class":118},[86,552,553,556,558,560,562,565,567,569,571,573],{"class":88,"line":261},[86,554,555],{"class":111},"          el",[86,557,141],{"class":118},[86,559,226],{"class":111},[86,561,101],{"class":100},[86,563,564],{"class":111}," el",[86,566,141],{"class":118},[86,568,221],{"class":111},[86,570,141],{"class":118},[86,572,226],{"class":111},[86,574,204],{"class":118},[86,576,577,580,582,584,586,588,590],{"class":88,"line":287},[86,578,579],{"class":100},"          delete",[86,581,564],{"class":111},[86,583,141],{"class":118},[86,585,221],{"class":111},[86,587,141],{"class":118},[86,589,226],{"class":111},[86,591,204],{"class":118},[86,593,594,597,599,601,603,605,607],{"class":88,"line":293},[86,595,596],{"class":111},"          observer",[86,598,141],{"class":118},[86,600,301],{"class":107},[86,602,148],{"class":147},[86,604,457],{"class":111},[86,606,126],{"class":147},[86,608,204],{"class":118},[86,610,611],{"class":88,"line":313},[86,612,290],{"class":118},[86,614,615],{"class":88,"line":319},[86,616,617],{"class":118},"      },\n",[86,619,620,623,625,627,629,631,633],{"class":88,"line":329},[86,621,622],{"class":118},"      {",[86,624,341],{"class":147},[86,626,344],{"class":118},[86,628,347],{"class":273},[86,630,350],{"class":277},[86,632,274],{"class":273},[86,634,355],{"class":118},[86,636,637,640],{"class":88,"line":335},[86,638,639],{"class":147},"    )",[86,641,204],{"class":118},[86,643,644,647,649,651,653,655,657],{"class":88,"line":358},[86,645,646],{"class":111},"    observer",[86,648,141],{"class":118},[86,650,410],{"class":107},[86,652,148],{"class":147},[86,654,457],{"class":111},[86,656,126],{"class":147},[86,658,204],{"class":118},[86,660,661],{"class":88,"line":370},[86,662,663],{"class":366},"    \u002F\u002F 存储 observer 以便 unmounted 时断开\n",[86,665,667,669,671,674,676,678],{"class":88,"line":666},18,[86,668,477],{"class":111},[86,670,141],{"class":118},[86,672,673],{"class":111},"_lazyObserver",[86,675,101],{"class":100},[86,677,97],{"class":111},[86,679,204],{"class":118},[86,681,683],{"class":88,"line":682},19,[86,684,332],{"class":118},[86,686,688,691,693,695,697],{"class":88,"line":687},20,[86,689,690],{"class":451},"  unmounted",[86,692,148],{"class":118},[86,694,457],{"class":122},[86,696,126],{"class":118},[86,698,132],{"class":118},[86,700,702,705,707,709,711,713,715],{"class":88,"line":701},21,[86,703,704],{"class":165},"    if",[86,706,169],{"class":147},[86,708,457],{"class":111},[86,710,141],{"class":118},[86,712,673],{"class":111},[86,714,179],{"class":147},[86,716,182],{"class":118},[86,718,720,723,725,727,729,732,735],{"class":88,"line":719},22,[86,721,722],{"class":111},"      el",[86,724,141],{"class":118},[86,726,673],{"class":111},[86,728,141],{"class":118},[86,730,731],{"class":107},"disconnect",[86,733,734],{"class":147},"()",[86,736,204],{"class":118},[86,738,740,743,745,747,749],{"class":88,"line":739},23,[86,741,742],{"class":100},"      delete",[86,744,564],{"class":111},[86,746,141],{"class":118},[86,748,673],{"class":111},[86,750,204],{"class":118},[86,752,754],{"class":88,"line":753},24,[86,755,756],{"class":118},"    }\n",[86,758,760],{"class":88,"line":759},25,[86,761,332],{"class":118},[86,763,765],{"class":88,"line":764},26,[86,766,767],{"class":118},"};\n",[24,769,770],{},"在组件中使用：",[77,772,775],{"className":773,"code":774,"language":12,"meta":82,"style":82},"language-vue shiki shiki-themes material-theme-lighter github-light github-dark","\u003Ctemplate>\n  \u003Cimg v-lazy=\"imageUrl\" alt=\"懒加载图片\" \u002F>\n\u003C\u002Ftemplate>\n",[57,776,777,789,825],{"__ignoreMap":82},[86,778,779,782,786],{"class":88,"line":89},[86,780,781],{"class":118},"\u003C",[86,783,785],{"class":784},"sQzsp","template",[86,787,788],{"class":118},">\n",[86,790,791,794,796,800,803,805,808,810,813,815,817,820,822],{"class":88,"line":115},[86,792,793],{"class":118},"  \u003C",[86,795,306],{"class":784},[86,797,799],{"class":798},"s9AJx"," v-lazy",[86,801,802],{"class":118},"=",[86,804,274],{"class":273},[86,806,807],{"class":277},"imageUrl",[86,809,274],{"class":273},[86,811,812],{"class":798}," alt",[86,814,802],{"class":118},[86,816,274],{"class":273},[86,818,819],{"class":277},"懒加载图片",[86,821,274],{"class":273},[86,823,824],{"class":118}," \u002F>\n",[86,826,827,830,832],{"class":88,"line":135},[86,828,829],{"class":118},"\u003C\u002F",[86,831,785],{"class":784},[86,833,788],{"class":118},[50,835,837],{"id":836},"_3-结合-vue-生态的现成方案","3. 结合 Vue 生态的现成方案",[839,840,841,851],"ul",{},[842,843,844,847,848,37],"li",{},[72,845,846],{},"vue-lazyload","：Vue 2 经典，Vue 3 有 ",[57,849,850],{},"vue-lazyload-next",[842,852,853,856],{},[72,854,855],{},"v-lazy-image"," 等轻量库。",[24,858,859,860,863,864,37],{},"对于背景图片懒加载，可设置 ",[57,861,862],{},"data-bg","，在 IntersectionObserver 中修改 ",[57,865,866],{},"el.style.backgroundImage",[42,868,870],{"id":869},"二第三方库按需引入与-tree-shaking","二、第三方库按需引入与 Tree Shaking",[24,872,873],{},"使用第三方库时，若全量引入会显著增加打包体积。按需引入 + Tree Shaking 可以只打包被使用的代码。",[50,875,877],{"id":876},"_1-使用按需导入的库如-lodash-es","1. 使用按需导入的库（如 lodash-es）",[839,879,880],{},[842,881,882,888,889,892,893,895],{},[72,883,884,885],{},"推荐使用 ",[57,886,887],{},"lodash-es","（ES 模块版本），而非 ",[57,890,891],{},"lodash","（CommonJS）。因为 ",[57,894,887],{}," 支持 Tree Shaking。",[77,897,899],{"className":79,"code":898,"language":81,"meta":82,"style":82},"\u002F\u002F ❌ 全量引入 lodash\nimport _ from 'lodash';\n_.debounce(...);\n\n\u002F\u002F ✅ 按需引入\nimport debounce from 'lodash-es\u002Fdebounce';\n\u002F\u002F 或\nimport { debounce } from 'lodash-es';\n",[57,900,901,906,927,946,952,957,975,980],{"__ignoreMap":82},[86,902,903],{"class":88,"line":89},[86,904,905],{"class":366},"\u002F\u002F ❌ 全量引入 lodash\n",[86,907,908,911,914,917,920,922,925],{"class":88,"line":115},[86,909,910],{"class":165},"import",[86,912,913],{"class":111}," _ ",[86,915,916],{"class":165},"from",[86,918,919],{"class":273}," '",[86,921,891],{"class":277},[86,923,924],{"class":273},"'",[86,926,204],{"class":118},[86,928,929,932,934,937,939,942,944],{"class":88,"line":135},[86,930,931],{"class":111},"_",[86,933,141],{"class":118},[86,935,936],{"class":107},"debounce",[86,938,148],{"class":111},[86,940,941],{"class":100},"...",[86,943,126],{"class":111},[86,945,204],{"class":118},[86,947,948],{"class":88,"line":162},[86,949,951],{"emptyLinePlaceholder":950},true,"\n",[86,953,954],{"class":88,"line":185},[86,955,956],{"class":366},"\u002F\u002F ✅ 按需引入\n",[86,958,959,961,964,966,968,971,973],{"class":88,"line":207},[86,960,910],{"class":165},[86,962,963],{"class":111}," debounce ",[86,965,916],{"class":165},[86,967,919],{"class":273},[86,969,970],{"class":277},"lodash-es\u002Fdebounce",[86,972,924],{"class":273},[86,974,204],{"class":118},[86,976,977],{"class":88,"line":231},[86,978,979],{"class":366},"\u002F\u002F 或\n",[86,981,982,984,987,990,993,996,998,1000,1002],{"class":88,"line":245},[86,983,910],{"class":165},[86,985,986],{"class":118}," {",[86,988,989],{"class":111}," debounce",[86,991,992],{"class":118}," }",[86,994,995],{"class":165}," from",[86,997,919],{"class":273},[86,999,887],{"class":277},[86,1001,924],{"class":273},[86,1003,204],{"class":118},[24,1005,1006,1007,1010,1011,37],{},"确保构建工具（Webpack、Vite）在生产模式开启 sideEffects 优化。Vite 默认支持，Webpack 需要 ",[57,1008,1009],{},"mode: 'production'"," 或显式配置 ",[57,1012,1013],{},"optimization.usedExports",[50,1015,1017],{"id":1016},"_2-ui-组件库按需引入-element-plus-为例","2. UI 组件库按需引入（ element-plus 为例）",[24,1019,1020,1021,1024,1025,1028,1029,1032],{},"大多数现代 UI 库都提供按需引入方式。例如 ",[57,1022,1023],{},"element-plus"," 推荐使用 ",[57,1026,1027],{},"unplugin-auto-import"," 和 ",[57,1030,1031],{},"unplugin-vue-components"," 插件自动按需引入。",[24,1034,1035,75],{},[72,1036,1037],{},"手动按需引入",[77,1039,1041],{"className":79,"code":1040,"language":81,"meta":82,"style":82},"import { ElButton, ElMessage } from \"element-plus\";\n\u002F\u002F 单独引入样式（组件库通常提供按需样式的插件）\nimport \"element-plus\u002Fes\u002Fcomponents\u002Fbutton\u002Fstyle\u002Fcss\";\n",[57,1042,1043,1069,1074],{"__ignoreMap":82},[86,1044,1045,1047,1049,1052,1054,1057,1059,1061,1063,1065,1067],{"class":88,"line":89},[86,1046,910],{"class":165},[86,1048,986],{"class":118},[86,1050,1051],{"class":111}," ElButton",[86,1053,460],{"class":118},[86,1055,1056],{"class":111}," ElMessage",[86,1058,992],{"class":118},[86,1060,995],{"class":165},[86,1062,347],{"class":273},[86,1064,1023],{"class":277},[86,1066,274],{"class":273},[86,1068,204],{"class":118},[86,1070,1071],{"class":88,"line":115},[86,1072,1073],{"class":366},"\u002F\u002F 单独引入样式（组件库通常提供按需样式的插件）\n",[86,1075,1076,1078,1080,1083,1085],{"class":88,"line":135},[86,1077,910],{"class":165},[86,1079,347],{"class":273},[86,1081,1082],{"class":277},"element-plus\u002Fes\u002Fcomponents\u002Fbutton\u002Fstyle\u002Fcss",[86,1084,274],{"class":273},[86,1086,204],{"class":118},[24,1088,1089,75],{},[72,1090,1091],{},"使用插件自动按需（推荐）",[77,1093,1095],{"className":79,"code":1094,"language":81,"meta":82,"style":82},"\u002F\u002F vite.config.js\nimport AutoImport from \"unplugin-auto-import\u002Fvite\";\nimport Components from \"unplugin-vue-components\u002Fvite\";\nimport { ElementPlusResolver } from \"unplugin-vue-components\u002Fresolvers\";\n\nexport default {\n  plugins: [\n    AutoImport({ resolvers: [ElementPlusResolver()] }),\n    Components({ resolvers: [ElementPlusResolver()] }),\n  ],\n};\n",[57,1096,1097,1102,1120,1138,1160,1164,1172,1182,1214,1239,1246],{"__ignoreMap":82},[86,1098,1099],{"class":88,"line":89},[86,1100,1101],{"class":366},"\u002F\u002F vite.config.js\n",[86,1103,1104,1106,1109,1111,1113,1116,1118],{"class":88,"line":115},[86,1105,910],{"class":165},[86,1107,1108],{"class":111}," AutoImport ",[86,1110,916],{"class":165},[86,1112,347],{"class":273},[86,1114,1115],{"class":277},"unplugin-auto-import\u002Fvite",[86,1117,274],{"class":273},[86,1119,204],{"class":118},[86,1121,1122,1124,1127,1129,1131,1134,1136],{"class":88,"line":135},[86,1123,910],{"class":165},[86,1125,1126],{"class":111}," Components ",[86,1128,916],{"class":165},[86,1130,347],{"class":273},[86,1132,1133],{"class":277},"unplugin-vue-components\u002Fvite",[86,1135,274],{"class":273},[86,1137,204],{"class":118},[86,1139,1140,1142,1144,1147,1149,1151,1153,1156,1158],{"class":88,"line":162},[86,1141,910],{"class":165},[86,1143,986],{"class":118},[86,1145,1146],{"class":111}," ElementPlusResolver",[86,1148,992],{"class":118},[86,1150,995],{"class":165},[86,1152,347],{"class":273},[86,1154,1155],{"class":277},"unplugin-vue-components\u002Fresolvers",[86,1157,274],{"class":273},[86,1159,204],{"class":118},[86,1161,1162],{"class":88,"line":185},[86,1163,951],{"emptyLinePlaceholder":950},[86,1165,1166,1168,1170],{"class":88,"line":207},[86,1167,441],{"class":165},[86,1169,444],{"class":165},[86,1171,132],{"class":118},[86,1173,1174,1177,1179],{"class":88,"line":231},[86,1175,1176],{"class":147},"  plugins",[86,1178,344],{"class":118},[86,1180,1181],{"class":111}," [\n",[86,1183,1184,1187,1189,1192,1195,1197,1200,1203,1206,1209,1211],{"class":88,"line":245},[86,1185,1186],{"class":107},"    AutoImport",[86,1188,148],{"class":111},[86,1190,1191],{"class":118},"{",[86,1193,1194],{"class":147}," resolvers",[86,1196,344],{"class":118},[86,1198,1199],{"class":111}," [",[86,1201,1202],{"class":107},"ElementPlusResolver",[86,1204,1205],{"class":111},"()] ",[86,1207,1208],{"class":118},"}",[86,1210,126],{"class":111},[86,1212,1213],{"class":118},",\n",[86,1215,1216,1219,1221,1223,1225,1227,1229,1231,1233,1235,1237],{"class":88,"line":261},[86,1217,1218],{"class":107},"    Components",[86,1220,148],{"class":111},[86,1222,1191],{"class":118},[86,1224,1194],{"class":147},[86,1226,344],{"class":118},[86,1228,1199],{"class":111},[86,1230,1202],{"class":107},[86,1232,1205],{"class":111},[86,1234,1208],{"class":118},[86,1236,126],{"class":111},[86,1238,1213],{"class":118},[86,1240,1241,1244],{"class":88,"line":287},[86,1242,1243],{"class":111},"  ]",[86,1245,1213],{"class":118},[86,1247,1248],{"class":88,"line":293},[86,1249,767],{"class":118},[24,1251,1252,1253,1256],{},"这样，模板中使用的 ",[57,1254,1255],{},"\u003Cel-button>"," 会自动引入对应组件和样式，且支持 Tree Shaking。",[50,1258,1260],{"id":1259},"_3-确保库支持-tree-shaking-的条件","3. 确保库支持 Tree Shaking 的条件",[839,1262,1263,1274,1281],{},[842,1264,1265,1266,1269,1270,1273],{},"库使用 ES 模块（ESM）格式，且 ",[57,1267,1268],{},"package.json"," 中 ",[57,1271,1272],{},"\"sideEffects\": false"," 或明确声明。",[842,1275,1276,1277,1280],{},"避免只在 ",[57,1278,1279],{},"index.js"," 中集中导出（二级导入通常可避免全量加载）。",[842,1282,1283,1284,1287],{},"使用构建工具的 ",[57,1285,1286],{},"optimization"," 选项（Webpack）或 Vite 默认配置。",[50,1289,1291],{"id":1290},"_4-工具排查未使用的依赖","4. 工具排查未使用的依赖",[839,1293,1294],{},[842,1295,1296,32,1299,32,1302,1305],{},[57,1297,1298],{},"webpack-bundle-analyzer",[57,1300,1301],{},"rollup-plugin-visualizer",[57,1303,1304],{},"vite-bundle-visualizer"," 生成体积报告，找出全量引入的库。",[42,1307,1309],{"id":1308},"三首屏性能监控lighthouse-web-vitals","三、首屏性能监控：Lighthouse & Web Vitals",[24,1311,1312],{},"性能监控是优化的前提，通过定量指标了解真实用户体验。",[50,1314,1316],{"id":1315},"_1-lighthouse实验室工具","1. Lighthouse（实验室工具）",[24,1318,1319],{},"Lighthouse 是 Google 出品的高仿真性能审计工具，可在 Chrome DevTools、PageSpeed Insights 或 CLI 中使用。它给出综合评分（性能、可访问性、SEO 等）并提供具体优化建议。",[24,1321,1322,1325],{},[72,1323,1324],{},"核心关注指标","（与 Web Vitals 重叠）：",[839,1327,1328,1334,1340,1346,1352],{},[842,1329,1330,1333],{},[72,1331,1332],{},"FCP","（First Contentful Paint）：首次内容绘制时间。",[842,1335,1336,1339],{},[72,1337,1338],{},"LCP","（Largest Contentful Paint）：最大内容绘制，衡量加载性能。",[842,1341,1342,1345],{},[72,1343,1344],{},"TBT","（Total Blocking Time）：总阻塞时间，交互准备度。",[842,1347,1348,1351],{},[72,1349,1350],{},"CLS","（Cumulative Layout Shift）：累计布局偏移，视觉稳定性。",[842,1353,1354,1357],{},[72,1355,1356],{},"Speed Index","：速度指数。",[24,1359,1360,75],{},[72,1361,1362],{},"分析流程",[1364,1365,1366,1369,1372],"ol",{},[842,1367,1368],{},"运行 Lighthouse（DevTools → Lighthouse 面板，选择 Mobile 或 Desktop）。",[842,1370,1371],{},"查看性能评分和 “Opportunities” 列表（例如 “减少未使用的 JavaScript”、 “适当调整图片大小”）。",[842,1373,1374],{},"按建议优化后重新测试。",[50,1376,1378],{"id":1377},"_2-web-vitals真实用户监控","2. Web Vitals（真实用户监控）",[24,1380,1381,1382,1385,1386,1389],{},"Web Vitals 是一组由 Google 定义的、衡量真实用户体验的核心指标，通常通过 ",[72,1383,1384],{},"现场数据","（CrUX 报告）或 ",[72,1387,1388],{},"实验室数据","（Lighthouse）获取。",[1391,1392,1393,1409],"table",{},[1394,1395,1396],"thead",{},[1397,1398,1399,1403,1406],"tr",{},[1400,1401,1402],"th",{},"指标",[1400,1404,1405],{},"含义",[1400,1407,1408],{},"良好阈值 (75分位)",[1410,1411,1412,1425,1439],"tbody",{},[1397,1413,1414,1419,1422],{},[1415,1416,1417],"td",{},[72,1418,1338],{},[1415,1420,1421],{},"最大内容元素渲染完成时间",[1415,1423,1424],{},"≤ 2.5秒",[1397,1426,1427,1433,1436],{},[1415,1428,1429,1432],{},[72,1430,1431],{},"INP"," (取代 FID)",[1415,1434,1435],{},"交互延迟（Next Paint），衡量响应性",[1415,1437,1438],{},"≤ 200毫秒",[1397,1440,1441,1445,1448],{},[1415,1442,1443],{},[72,1444,1350],{},[1415,1446,1447],{},"页面布局意外偏移程度",[1415,1449,1450],{},"≤ 0.1",[24,1452,1453,75],{},[72,1454,1455],{},"监控方式",[839,1457,1458,1464],{},[842,1459,1460,1463],{},[72,1461,1462],{},"Chrome 用户体验报告 (CrUX)","：基于真实 Chrome 用户的匿名数据，可通过 PageSpeed Insights 查看域名总体表现。",[842,1465,1466,1469,1470,1473],{},[72,1467,1468],{},"JavaScript API","：使用 ",[57,1471,1472],{},"web-vitals"," 库收集并上报。",[77,1475,1477],{"className":79,"code":1476,"language":81,"meta":82,"style":82},"import { onLCP, onINP, onCLS } from \"web-vitals\";\nonLCP(console.log);\nonINP(console.log);\nonCLS(console.log);\n",[57,1478,1479,1510,1525,1538],{"__ignoreMap":82},[86,1480,1481,1483,1485,1488,1490,1493,1495,1498,1500,1502,1504,1506,1508],{"class":88,"line":89},[86,1482,910],{"class":165},[86,1484,986],{"class":118},[86,1486,1487],{"class":111}," onLCP",[86,1489,460],{"class":118},[86,1491,1492],{"class":111}," onINP",[86,1494,460],{"class":118},[86,1496,1497],{"class":111}," onCLS",[86,1499,992],{"class":118},[86,1501,995],{"class":165},[86,1503,347],{"class":273},[86,1505,1472],{"class":277},[86,1507,274],{"class":273},[86,1509,204],{"class":118},[86,1511,1512,1515,1518,1520,1523],{"class":88,"line":115},[86,1513,1514],{"class":107},"onLCP",[86,1516,1517],{"class":111},"(console",[86,1519,141],{"class":118},[86,1521,1522],{"class":111},"log)",[86,1524,204],{"class":118},[86,1526,1527,1530,1532,1534,1536],{"class":88,"line":135},[86,1528,1529],{"class":107},"onINP",[86,1531,1517],{"class":111},[86,1533,141],{"class":118},[86,1535,1522],{"class":111},[86,1537,204],{"class":118},[86,1539,1540,1543,1545,1547,1549],{"class":88,"line":162},[86,1541,1542],{"class":107},"onCLS",[86,1544,1517],{"class":111},[86,1546,141],{"class":118},[86,1548,1522],{"class":111},[86,1550,204],{"class":118},[50,1552,1554],{"id":1553},"_3-基于指标的优化策略","3. 基于指标的优化策略",[1391,1556,1557,1570],{},[1394,1558,1559],{},[1397,1560,1561,1564,1567],{},[1400,1562,1563],{},"差指标",[1400,1565,1566],{},"常见原因",[1400,1568,1569],{},"优化策略",[1410,1571,1572,1585,1606,1635],{},[1397,1573,1574,1579,1582],{},[1415,1575,1576],{},[72,1577,1578],{},"LCP 慢",[1415,1580,1581],{},"主资源（hero image）加载慢，首屏阻塞",[1415,1583,1584],{},"优化图片（WebP、响应式）、预加载 LCP 元素、CDN、服务器端渲染(SSR)",[1397,1586,1587,1592,1595],{},[1415,1588,1589],{},[72,1590,1591],{},"INP 差",[1415,1593,1594],{},"长任务阻塞主线程、过多输入延迟",[1415,1596,1597,1598,1601,1602,1605],{},"拆分长任务（使用 ",[57,1599,1600],{},"setTimeout"," 或 ",[57,1603,1604],{},"scheduler.yield","）、避免繁重的 JS 执行、使用 Web Worker",[1397,1607,1608,1613,1616],{},[1415,1609,1610],{},[72,1611,1612],{},"CLS 高",[1415,1614,1615],{},"图片\u002Fiframe 无尺寸占位、动态注入内容",[1415,1617,1618,1619,1622,1623,1626,1627,1630,1631,1634],{},"为所有媒体元素设置 ",[57,1620,1621],{},"width","\u002F",[57,1624,1625],{},"height"," 或 CSS ",[57,1628,1629],{},"aspect-ratio","；预留广告位；字体使用 ",[57,1632,1633],{},"font-display: optional"," 减少 FOIT",[1397,1636,1637,1642,1645],{},[1415,1638,1639],{},[72,1640,1641],{},"TBT 高",[1415,1643,1644],{},"大量未使用\u002F未拆分的 JS",[1415,1646,1647],{},"代码分割、懒加载非关键脚本、减少 polyfill",[50,1649,1651],{"id":1650},"_4-性能预算","4. 性能预算",[24,1653,1654,1655,1658],{},"设定性能预算（比如 LCP \u003C 2s, JS 总体积 \u003C 200KB），并在 CI 中集成 Lighthouse CI 或 ",[57,1656,1657],{},"lighthouse-bot","，监控每次 PR 是否突破预算。",[42,1660,1661],{"id":1661},"总结",[1391,1663,1664,1677],{},[1394,1665,1666],{},[1397,1667,1668,1671,1674],{},[1400,1669,1670],{},"资源优化方面",[1400,1672,1673],{},"核心技术",[1400,1675,1676],{},"收益",[1410,1678,1679,1692,1705],{},[1397,1680,1681,1686,1689],{},[1415,1682,1683],{},[72,1684,1685],{},"图片懒加载",[1415,1687,1688],{},"IntersectionObserver + Vue 指令封装",[1415,1690,1691],{},"减少非可视区图片加载，提升首屏速度",[1397,1693,1694,1699,1702],{},[1415,1695,1696],{},[72,1697,1698],{},"按需引入与 Tree Shaking",[1415,1700,1701],{},"ESM 模块、自动按需插件、sideEffects 配置",[1415,1703,1704],{},"大幅减小打包体积，仅包含实际使用的代码",[1397,1706,1707,1712,1715],{},[1415,1708,1709],{},[72,1710,1711],{},"首屏性能监控",[1415,1713,1714],{},"Lighthouse（实验室）+ Web Vitals（真实）",[1415,1716,1717],{},"量化性能瓶颈，指导优化方向，跟踪迭代效果",[24,1719,1720],{},"通过以上资源优化手段，可以系统性地降低加载开销，并从数据层面持续提升用户体验。",[1722,1723,1724],"h2",{"id":1724},"参考文献",[24,1726,1727],{},"以下链接在编写时均可正常访问：",[1391,1729,1730,1740],{},[1394,1731,1732],{},[1397,1733,1734,1737],{},[1400,1735,1736],{},"资料",[1400,1738,1739],{},"说明",[1410,1741,1742,1755,1767,1779],{},[1397,1743,1744,1752],{},[1415,1745,1746],{},[28,1747,1751],{"href":1748,"rel":1749},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-CN\u002Fdocs\u002FWeb\u002FAPI\u002FIntersection_Observer_API",[1750],"nofollow","MDN：Intersection Observer",[1415,1753,1754],{},"懒加载基础",[1397,1756,1757,1764],{},[1415,1758,1759],{},[28,1760,1763],{"href":1761,"rel":1762},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-CN\u002Fdocs\u002FWeb\u002FHTML\u002FElement\u002Fimg#loading",[1750],"MDN：loading=lazy",[1415,1765,1766],{},"原生图片懒加载",[1397,1768,1769,1776],{},[1415,1770,1771],{},[28,1772,1775],{"href":1773,"rel":1774},"https:\u002F\u002Fweb.dev\u002Farticles\u002Flcp",[1750],"web.dev：LCP",[1415,1777,1778],{},"最大内容绘制（英文）",[1397,1780,1781,1788],{},[1415,1782,1783],{},[28,1784,1787],{"href":1785,"rel":1786},"https:\u002F\u002Fweb.dev\u002Farticles\u002Fcls",[1750],"web.dev：CLS",[1415,1789,1790],{},"布局偏移（英文）",[1792,1793,1794],"style",{},"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 .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--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 .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 .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 .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}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}",{"title":82,"searchDepth":115,"depth":115,"links":1796},[1797,1798,1799,1800,1801],{"id":44,"depth":135,"text":45},{"id":869,"depth":135,"text":870},{"id":1308,"depth":135,"text":1309},{"id":1661,"depth":135,"text":1661},{"id":1724,"depth":115,"text":1724},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FVue\u002Fperformance\u002Fassets","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FVue\u002Fperformance\u002Fassets.md","md",{"items":1807},[1808,1812,1816,1821,1825,1829],{"path":1809,"title":1810,"description":1811,"category":10,"score":261},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fbuild","编译与打包优化","构建阶段的代码分割、Tree Shaking 与 SFC 编译优化。运行时见 runtime。",{"path":1813,"title":1814,"description":1815,"category":10,"score":261},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fruntime","运行时优化","减少不必要的渲染与响应式开销。构建侧见 build。",{"path":1817,"title":1818,"description":1819,"category":1820,"score":207},"\u002Fknowledge\u002Fteam-documents\u002F19","GridView 宫格加载渲染优化","系统首页 GridView 宫格模块接口耗时不高，但首次进入总耗时接近 22s。复盘耗时分层定位过程、代码层面的瓶颈（约 2000 行、100 个 tab 重复节点）、优化手段与最终指标对比。","团队文档",{"path":1822,"title":1823,"description":1824,"category":1820,"score":207},"\u002Fknowledge\u002Fteam-documents\u002F13","新商家系统性能优化实践","商家系统新版本上线后，团队持续针对构建效率与用户体验做了一轮系统性优化。本文记录核心方案与落地结果，供后续版本复用。",{"path":1826,"title":1827,"description":1828,"category":1820,"score":207},"\u002Fknowledge\u002Fteam-documents\u002F04","老系统升级与兼容改造实践","公司原有业务系统采用 Node + jQuery 架构，长期运行后暴露出典型遗留系统问题：",{"path":1830,"title":1831,"description":1832,"category":1820,"score":207},"\u002Fknowledge\u002Fteam-documents\u002F02","防篡改水印","在各类管理后台、SaaS 平台或内部系统中，页面水印已经是非常常见的能力：一方面在截图时携带账号、姓名、时间等信息，降低截图外传的风险；另一方面在上传图片或文档预览时，通过水印标记上传者与时间，满足审计和合规需求。",{"series":1834,"title":13,"items":1835,"index":1841,"prev":1842,"next":1843},"booklet\u002Fframe\u002Fvue\u002Fperformance",[1836,1837,1839],{"path":7,"title":8,"date":15,"pinned":16},{"path":1809,"title":1810,"date":1838,"pinned":16},"2021-08-02",{"path":1813,"title":1814,"date":1840,"pinned":16},"2021-06-10",0,null,{"path":1809,"title":1810,"date":1838,"pinned":16}]