[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fecology":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fecology":1315,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fecology":1341},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":1311,"stem":1312,"id":1313,"extension":1314},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fecology","Vue 生态关键库深度集成","VueUse、Vite 与 Nuxt 是 Vue 3 生态中常用的效率与工程化方案。概览见 Vue 基础。","技能小册",[12,13],"vue","ecology","2025-08-26",false,{"type":17,"value":18,"toc":1294},"minimark",[19,23,33,38,46,51,61,66,213,219,223,607,618,622,626,633,637,644,682,695,699,702,731,735,752,756,760,763,767,774,810,814,824,854,870,874,877,891,906,1037,1041,1062,1065,1128,1131,1176,1179,1202,1205,1226,1229,1232,1291],[20,21,8],"h1",{"id":22},"vue-生态关键库深度集成",[24,25,26,27,32],"p",{},"VueUse、Vite 与 Nuxt 是 Vue 3 生态中常用的效率与工程化方案。概览见 ",[28,29,31],"a",{"href":30},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fbasics","Vue 基础","。",[34,35,37],"h2",{"id":36},"vueuse组合式工具库","VueUse：组合式工具库",[24,39,40,41,45],{},"VueUse 提供大量基于 Composition API 的 ",[42,43,44],"code",{},"useXxx"," 函数，涵盖状态、浏览器 API、动画等场景。",[47,48,50],"h3",{"id":49},"一常用函数索引","一、常用函数索引",[24,52,53,54,60],{},"VueUse 是基于 Composition API 的实用工具集合，提供超过 200 个开箱即用的 composable 函数（具体数量以 ",[28,55,59],{"href":56,"rel":57},"https:\u002F\u002Fvueuse.org\u002F",[58],"nofollow","官方站点"," 实时统计为准），涵盖动画、传感器、状态、浏览器 API 等几乎所有前端场景。它最大的价值在于封装了复杂的响应式逻辑，让开发者可以“只用不造”，大幅提升开发效率。",[62,63,65],"h4",{"id":64},"常用函数快速索引","🔌 常用函数快速索引",[67,68,69,85],"table",{},[70,71,72],"thead",{},[73,74,75,79,82],"tr",{},[76,77,78],"th",{},"类别",[76,80,81],{},"典型函数",[76,83,84],{},"一句话作用",[86,87,88,106,122,141,160,179,200],"tbody",{},[73,89,90,94,103],{},[91,92,93],"td",{},"状态",[91,95,96,99,100],{},[42,97,98],{},"useLocalStorage",", ",[42,101,102],{},"useSessionStorage",[91,104,105],{},"与存储同步的响应式状态",[73,107,108,111,119],{},[91,109,110],{},"动画",[91,112,113,99,116],{},[42,114,115],{},"useTransition",[42,117,118],{},"useRafFn",[91,120,121],{},"数值过渡、rAF 循环",[73,123,124,127,138],{},[91,125,126],{},"传感器",[91,128,129,99,132,99,135],{},[42,130,131],{},"useMouse",[42,133,134],{},"useElementSize",[42,136,137],{},"useOnline",[91,139,140],{},"鼠标、尺寸、网络状态",[73,142,143,146,157],{},[91,144,145],{},"交互",[91,147,148,99,151,99,154],{},[42,149,150],{},"useDark",[42,152,153],{},"useToggle",[42,155,156],{},"onClickOutside",[91,158,159],{},"主题、切换、点击外部",[73,161,162,165,176],{},[91,163,164],{},"工具",[91,166,167,99,170,99,173],{},[42,168,169],{},"useDebounceFn",[42,171,172],{},"useThrottleFn",[42,174,175],{},"useAsyncState",[91,177,178],{},"防抖、节流、异步状态",[73,180,181,184,189],{},[91,182,183],{},"网络",[91,185,186],{},[42,187,188],{},"useFetch",[91,190,191,192,196,197,199],{},"请求封装；",[193,194,195],"strong",{},"Nuxt 项目","须与内置 ",[42,198,188],{}," 区分",[73,201,202,205,210],{},[91,203,204],{},"历史",[91,206,207],{},[42,208,209],{},"useRefHistory",[91,211,212],{},"ref 变更历史（撤销\u002F重做）",[24,214,215,216,218],{},"这些函数都遵循 Vue 官方组合式函数的最佳实践：通过 ",[42,217,44],{}," 命名保持统一，内部自动处理副作用清理防止内存泄漏，并提供了完整的 TypeScript 支持。",[47,220,222],{"id":221},"实战示例组合多个-vueuse-函数","实战示例：组合多个 VueUse 函数",[224,225,229],"pre",{"className":226,"code":227,"language":12,"meta":228,"style":228},"language-vue shiki shiki-themes material-theme-lighter github-light github-dark","\u003Cscript setup lang=\"ts\">\nimport { useDark, useToggle, useLocalStorage, useDebounceFn } from \"@vueuse\u002Fcore\";\n\n\u002F\u002F 深色模式：自动读写 localStorage 与 \u003Chtml class=\"dark\">\nconst isDark = useDark();\nconst toggleDark = useToggle(isDark);\n\n\u002F\u002F 与 localStorage 双向同步的响应式状态，刷新页面也不丢失\nconst draft = useLocalStorage(\"editor-draft\", \"\");\n\n\u002F\u002F 防抖保存，300ms 内的连续输入只会触发一次真正的保存\nconst save = useDebounceFn((content: string) => {\n    console.log(\"saving...\", content);\n}, 300);\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003Cbutton @click=\"toggleDark()\">切换主题\u003C\u002Fbutton>\n    \u003Ctextarea v-model=\"draft\" @input=\"save(draft)\" \u002F>\n\u003C\u002Ftemplate>\n","",[42,230,231,267,314,321,328,351,368,373,379,411,416,422,457,488,502,512,517,527,560,598],{"__ignoreMap":228},[232,233,236,240,244,248,251,254,258,262,264],"span",{"class":234,"line":235},"line",1,[232,237,239],{"class":238},"sP7_E","\u003C",[232,241,243],{"class":242},"sQzsp","script",[232,245,247],{"class":246},"s9AJx"," setup",[232,249,250],{"class":246}," lang",[232,252,253],{"class":238},"=",[232,255,257],{"class":256},"sjJ54","\"",[232,259,261],{"class":260},"s_sjI","ts",[232,263,257],{"class":256},[232,265,266],{"class":238},">\n",[232,268,270,274,277,281,284,287,289,292,294,297,300,303,306,309,311],{"class":234,"line":269},2,[232,271,273],{"class":272},"sVHd0","import",[232,275,276],{"class":238}," {",[232,278,280],{"class":279},"su5hD"," useDark",[232,282,283],{"class":238},",",[232,285,286],{"class":279}," useToggle",[232,288,283],{"class":238},[232,290,291],{"class":279}," useLocalStorage",[232,293,283],{"class":238},[232,295,296],{"class":279}," useDebounceFn",[232,298,299],{"class":238}," }",[232,301,302],{"class":272}," from",[232,304,305],{"class":256}," \"",[232,307,308],{"class":260},"@vueuse\u002Fcore",[232,310,257],{"class":256},[232,312,313],{"class":238},";\n",[232,315,317],{"class":234,"line":316},3,[232,318,320],{"emptyLinePlaceholder":319},true,"\n",[232,322,324],{"class":234,"line":323},4,[232,325,327],{"class":326},"sutJx","\u002F\u002F 深色模式：自动读写 localStorage 与 \u003Chtml class=\"dark\">\n",[232,329,331,335,339,343,346,349],{"class":234,"line":330},5,[232,332,334],{"class":333},"sbsja","const",[232,336,338],{"class":337},"s_hVV"," isDark",[232,340,342],{"class":341},"smGrS"," =",[232,344,280],{"class":345},"sGLFI",[232,347,348],{"class":279},"()",[232,350,313],{"class":238},[232,352,354,356,359,361,363,366],{"class":234,"line":353},6,[232,355,334],{"class":333},[232,357,358],{"class":337}," toggleDark",[232,360,342],{"class":341},[232,362,286],{"class":345},[232,364,365],{"class":279},"(isDark)",[232,367,313],{"class":238},[232,369,371],{"class":234,"line":370},7,[232,372,320],{"emptyLinePlaceholder":319},[232,374,376],{"class":234,"line":375},8,[232,377,378],{"class":326},"\u002F\u002F 与 localStorage 双向同步的响应式状态，刷新页面也不丢失\n",[232,380,382,384,387,389,391,394,396,399,401,403,406,409],{"class":234,"line":381},9,[232,383,334],{"class":333},[232,385,386],{"class":337}," draft",[232,388,342],{"class":341},[232,390,291],{"class":345},[232,392,393],{"class":279},"(",[232,395,257],{"class":256},[232,397,398],{"class":260},"editor-draft",[232,400,257],{"class":256},[232,402,283],{"class":238},[232,404,405],{"class":256}," \"\"",[232,407,408],{"class":279},")",[232,410,313],{"class":238},[232,412,414],{"class":234,"line":413},10,[232,415,320],{"emptyLinePlaceholder":319},[232,417,419],{"class":234,"line":418},11,[232,420,421],{"class":326},"\u002F\u002F 防抖保存，300ms 内的连续输入只会触发一次真正的保存\n",[232,423,425,427,430,432,434,436,438,442,445,449,451,454],{"class":234,"line":424},12,[232,426,334],{"class":333},[232,428,429],{"class":337}," save",[232,431,342],{"class":341},[232,433,296],{"class":345},[232,435,393],{"class":279},[232,437,393],{"class":238},[232,439,441],{"class":440},"s99_P","content",[232,443,444],{"class":341},":",[232,446,448],{"class":447},"sZMiF"," string",[232,450,408],{"class":238},[232,452,453],{"class":333}," =>",[232,455,456],{"class":238}," {\n",[232,458,460,463,466,469,472,474,477,479,481,484,486],{"class":234,"line":459},13,[232,461,462],{"class":279},"    console",[232,464,465],{"class":238},".",[232,467,468],{"class":345},"log",[232,470,393],{"class":471},"skxfh",[232,473,257],{"class":256},[232,475,476],{"class":260},"saving...",[232,478,257],{"class":256},[232,480,283],{"class":238},[232,482,483],{"class":279}," content",[232,485,408],{"class":471},[232,487,313],{"class":238},[232,489,491,494,498,500],{"class":234,"line":490},14,[232,492,493],{"class":238},"},",[232,495,497],{"class":496},"srdBf"," 300",[232,499,408],{"class":279},[232,501,313],{"class":238},[232,503,505,508,510],{"class":234,"line":504},15,[232,506,507],{"class":238},"\u003C\u002F",[232,509,243],{"class":242},[232,511,266],{"class":238},[232,513,515],{"class":234,"line":514},16,[232,516,320],{"emptyLinePlaceholder":319},[232,518,520,522,525],{"class":234,"line":519},17,[232,521,239],{"class":238},[232,523,524],{"class":242},"template",[232,526,266],{"class":238},[232,528,530,533,536,539,541,543,546,548,551,554,556,558],{"class":234,"line":529},18,[232,531,532],{"class":238},"    \u003C",[232,534,535],{"class":242},"button",[232,537,538],{"class":246}," @click",[232,540,253],{"class":238},[232,542,257],{"class":256},[232,544,545],{"class":260},"toggleDark()",[232,547,257],{"class":256},[232,549,550],{"class":238},">",[232,552,553],{"class":279},"切换主题",[232,555,507],{"class":238},[232,557,535],{"class":242},[232,559,266],{"class":238},[232,561,563,565,568,571,573,575,578,580,583,585,587,590,592,596],{"class":234,"line":562},19,[232,564,532],{"class":238},[232,566,567],{"class":242},"textarea",[232,569,570],{"class":246}," v-model",[232,572,253],{"class":238},[232,574,257],{"class":256},[232,576,577],{"class":260},"draft",[232,579,257],{"class":256},[232,581,582],{"class":246}," @input",[232,584,253],{"class":238},[232,586,257],{"class":256},[232,588,589],{"class":260},"save(draft)",[232,591,257],{"class":256},[232,593,595],{"class":594},"svtyB"," \u002F",[232,597,266],{"class":238},[232,599,601,603,605],{"class":234,"line":600},20,[232,602,507],{"class":238},[232,604,524],{"class":242},[232,606,266],{"class":238},[24,608,609,610,613,614,617],{},"按需引入即可，无需手动处理 ",[42,611,612],{},"window","\u002F",[42,615,616],{},"localStorage"," 兼容性和清理逻辑，VueUse 内部已经在组件卸载时自动移除监听器。",[34,619,621],{"id":620},"vite-与-vue-的集成","Vite 与 Vue 的集成",[47,623,625],{"id":624},"二vitejsplugin-vue","二、@vitejs\u002Fplugin-vue",[24,627,628,629,632],{},"Vite 作为构建工具，它与 Vue 的深度集成主要体现在 ",[42,630,631],{},"@vitejs\u002Fplugin-vue"," 插件上。",[62,634,636],{"id":635},"️-核心机制","🏗️ 核心机制",[24,638,639,640,643],{},"插件的核心任务是将 ",[42,641,642],{},".vue"," 文件编译成浏览器可执行的 JavaScript 模块。它主要做了三件事：",[645,646,647,666,676],"ul",{},[648,649,650,653,654,657,658,661,662,665],"li",{},[193,651,652],{},"编译模板","：调用 ",[42,655,656],{},"@vue\u002Fcompiler-sfc"," 将 ",[42,659,660],{},"\u003Ctemplate>"," 转换为 ",[42,663,664],{},"render"," 函数。",[648,667,668,671,672,675],{},[193,669,670],{},"处理脚本","：解析 ",[42,673,674],{},"\u003Cscript setup>"," 等语法，并提取出模块的导出。",[648,677,678,681],{},[193,679,680],{},"生成模块","：生成一个“门面模块”，它将组件的脚本、模板、样式等部分分别导入，然后再组装成一个完整的 Vue 组件对象。",[24,683,684,685,687,688,690,691,694],{},"这种设计使得修改 ",[42,686,243],{},"、",[42,689,524],{}," 或 ",[42,692,693],{},"style"," 可以在不影响其他部分的情况下单独更新。",[62,696,698],{"id":697},"热模块替换-hmr","🔥 热模块替换 (HMR)",[24,700,701],{},"Vite 的 HMR 机制非常精细。它通过维护一个描述符缓存，当我们修改一个 SFC 文件时，系统会通过比较算法判断变更范围，从而确定最小更新集合：",[645,703,704,715,723],{},[648,705,706,711,712,714],{},[193,707,708,709],{},"修改 ",[42,710,660],{},"：仅替换组件的 ",[42,713,664],{}," 函数，组件状态得以保留。",[648,716,717,722],{},[193,718,708,719],{},[42,720,721],{},"\u003Cstyle>","：只更新 CSS 内容，组件不会重新渲染。",[648,724,725,730],{},[193,726,708,727],{},[42,728,729],{},"\u003Cscript>","：逻辑变更会影响状态，这时会触发组件的“软更新”。",[62,732,734],{"id":733},"请求拦截与重写","🚀 请求拦截与重写",[24,736,737,738,740,741,744,745,687,748,751],{},"当浏览器请求一个 ",[42,739,642],{}," 文件时，Vite 的插件会拦截请求，并重写到对应的模块上，例如将 ",[42,742,743],{},"App.vue"," 的请求重写为对 ",[42,746,747],{},"App.vue?vue&type=script",[42,749,750],{},"App.vue?vue&type=template"," 等资源的依赖请求。这使得 Vite 无需像 Webpack 那样将所有模块打包，天生就能实现按需编译和极快的冷启动速度。",[34,753,755],{"id":754},"nuxtjs-概览","Nuxt.js 概览",[47,757,759],{"id":758},"三nuxt-3","三、Nuxt 3",[24,761,762],{},"Nuxt 是一个构建于 Vue 之上的全栈框架，它通过约定优于配置的方式，解决了 Vue 项目在路由、SSR、构建等方面的诸多痛点。",[62,764,766],{"id":765},"路由文件即路由","📜 路由：文件即路由",[24,768,769,770,773],{},"Nuxt 的核心特性之一是基于文件系统的自动路由。",[42,771,772],{},"pages\u002F"," 目录下的结构直接映射为应用的路由。",[645,775,776,784,792,801],{},[648,777,778,781,782],{},[42,779,780],{},"\u002Fpages\u002Findex.vue"," → 路由 ",[42,783,613],{},[648,785,786,781,789],{},[42,787,788],{},"\u002Fpages\u002Fabout.vue",[42,790,791],{},"\u002Fabout",[648,793,794,797,798],{},[42,795,796],{},"\u002Fpages\u002Fuser\u002F[id].vue"," → 动态路由 ",[42,799,800],{},"\u002Fuser\u002F:id",[648,802,803,806,807],{},[42,804,805],{},"\u002Fpages\u002Fuser\u002F[id]\u002Fsettings.vue"," → 嵌套路由 ",[42,808,809],{},"\u002Fuser\u002F:id\u002Fsettings",[62,811,813],{"id":812},"数据usefetch-useasyncdata","🚀 数据：useFetch & useAsyncData",[24,815,816,817,820,821,823],{},"Nuxt 3 提供了强大的数据获取方式，核心是 ",[42,818,819],{},"useAsyncData"," 和 ",[42,822,188],{},"，它们专为 SSR 设计和优化。",[825,826,827,835,842],"ol",{},[648,828,829,834],{},[193,830,831],{},[42,832,833],{},"$fetch","：底层 HTTP 请求库，它是同构的，可以在客户端和服务器端使用，但本身不处理 SSR 数据同步。",[648,836,837,841],{},[193,838,839],{},[42,840,819],{},"：通用异步数据获取工具，可以处理任何异步操作，并能将服务器端获取的数据序列化后传给客户端，实现数据同步。",[648,843,844,848,849,820,851,853],{},[193,845,846],{},[42,847,188],{},"：它是 ",[42,850,819],{},[42,852,833],{}," 组合而成的语法糖，专门用于从 API 端点获取数据，是最简单的数据获取方式。",[24,855,856,857,687,860,687,863,687,866,869],{},"使用这些 composables 时，它们会返回 ",[42,858,859],{},"data",[42,861,862],{},"pending",[42,864,865],{},"error",[42,867,868],{},"refresh"," 等响应式属性，便于处理数据加载的各个阶段。",[62,871,873],{"id":872},"️-渲染ssr-vs-ssg","🏎️ 渲染：SSR vs SSG",[24,875,876],{},"Nuxt 提供了混合渲染模式，支持在同一项目中为不同路由配置不同的渲染策略。",[645,878,879,885],{},[648,880,881,884],{},[193,882,883],{},"服务端渲染 (SSR)","：每个请求在服务端实时渲染 HTML 后返回，优势在于 SEO 友好和首屏加载快，但需要服务器资源。",[648,886,887,890],{},[193,888,889],{},"静态站点生成 (SSG)","：在构建时生成所有页面的静态 HTML，优势是性能极佳且可部署在 CDN 上，但构建时间会随页面数量线性增长。",[24,892,893,894,897,898,901,902,905],{},"Nuxt 3 底层的 ",[193,895,896],{},"Nitro 引擎"," 提供了更易配置的混合渲染能力。你可以在 ",[42,899,900],{},"nuxt.config.ts"," 中使用 ",[42,903,904],{},"routeRules"," 按路径配置，灵活性极高。",[224,907,910],{"className":908,"code":909,"language":261,"meta":228,"style":228},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n    routeRules: {\n        \"\u002F\": { prerender: true }, \u002F\u002F 首页：构建时预渲染\n        \"\u002Fblog\u002F**\": { swr: 3600 }, \u002F\u002F 博客：增量静态再生，1 小时后台重新生成\n        \"\u002Fadmin\u002F**\": { ssr: false }, \u002F\u002F 后台：纯客户端渲染，跳过 SSR\n    },\n});\n",[42,911,912,917,933,942,971,997,1023,1028],{"__ignoreMap":228},[232,913,914],{"class":234,"line":235},[232,915,916],{"class":326},"\u002F\u002F nuxt.config.ts\n",[232,918,919,922,925,928,930],{"class":234,"line":269},[232,920,921],{"class":272},"export",[232,923,924],{"class":272}," default",[232,926,927],{"class":345}," defineNuxtConfig",[232,929,393],{"class":279},[232,931,932],{"class":238},"{\n",[232,934,935,938,940],{"class":234,"line":316},[232,936,937],{"class":471},"    routeRules",[232,939,444],{"class":238},[232,941,456],{"class":238},[232,943,944,947,950,952,954,956,959,961,965,968],{"class":234,"line":323},[232,945,946],{"class":256},"        \"",[232,948,613],{"class":949},"sZUrc",[232,951,257],{"class":256},[232,953,444],{"class":238},[232,955,276],{"class":238},[232,957,958],{"class":471}," prerender",[232,960,444],{"class":238},[232,962,964],{"class":963},"syTEX"," true",[232,966,967],{"class":238}," },",[232,969,970],{"class":326}," \u002F\u002F 首页：构建时预渲染\n",[232,972,973,975,978,980,982,984,987,989,992,994],{"class":234,"line":330},[232,974,946],{"class":256},[232,976,977],{"class":949},"\u002Fblog\u002F**",[232,979,257],{"class":256},[232,981,444],{"class":238},[232,983,276],{"class":238},[232,985,986],{"class":471}," swr",[232,988,444],{"class":238},[232,990,991],{"class":496}," 3600",[232,993,967],{"class":238},[232,995,996],{"class":326}," \u002F\u002F 博客：增量静态再生，1 小时后台重新生成\n",[232,998,999,1001,1004,1006,1008,1010,1013,1015,1018,1020],{"class":234,"line":353},[232,1000,946],{"class":256},[232,1002,1003],{"class":949},"\u002Fadmin\u002F**",[232,1005,257],{"class":256},[232,1007,444],{"class":238},[232,1009,276],{"class":238},[232,1011,1012],{"class":471}," ssr",[232,1014,444],{"class":238},[232,1016,1017],{"class":963}," false",[232,1019,967],{"class":238},[232,1021,1022],{"class":326}," \u002F\u002F 后台：纯客户端渲染，跳过 SSR\n",[232,1024,1025],{"class":234,"line":370},[232,1026,1027],{"class":238},"    },\n",[232,1029,1030,1033,1035],{"class":234,"line":375},[232,1031,1032],{"class":238},"}",[232,1034,408],{"class":279},[232,1036,313],{"class":238},[62,1038,1040],{"id":1039},"模块生态","📦 模块生态",[24,1042,1043,1044,1046,1047,1050,1051,1054,1055,687,1058,1061],{},"Nuxt 的另一大优势是丰富的官方\u002F社区模块（",[42,1045,900],{}," 的 ",[42,1048,1049],{},"modules"," 字段），例如 ",[42,1052,1053],{},"@nuxt\u002Fcontent","（本站文档系统基于此构建）、",[42,1056,1057],{},"@nuxt\u002Fimage",[42,1059,1060],{},"@pinia\u002Fnuxt"," 等，安装后通常零配置即可使用对应的 composables 和构建优化。",[34,1063,1064],{"id":1064},"常见坑",[645,1066,1067,1085,1097,1115],{},[648,1068,1069,1072,1073,687,1075,1077,1078,1081,1082,32],{},[193,1070,1071],{},"VueUse 的浏览器 API 在 SSR 下报错","：如 ",[42,1074,98],{},[42,1076,612],{}," 相关函数在服务端渲染阶段不可用，需配合 ",[42,1079,1080],{},"\u003CClientOnly>"," 或检查 ",[42,1083,1084],{},"import.meta.client",[648,1086,1087,1092,1093,1096],{},[193,1088,1089,1091],{},[42,1090,631],{}," 与旧版 Babel 插件混用","：不要同时引入 ",[42,1094,1095],{},"vue-loader","\u002FBabel 的 Vue 转换逻辑，Vite 项目应完全交给该插件处理 SFC 编译。",[648,1098,1099,1105,1106,1108,1109,1112,1113,32],{},[193,1100,1101,1102,1104],{},"Nuxt ",[42,1103,188],{}," 重复请求","：在多个组件里对同一个 URL 调用 ",[42,1107,188],{}," 若不传 ",[42,1110,1111],{},"key","，可能因缓存 key 冲突或不去重导致意外的多次请求，建议显式传入 ",[42,1114,1111],{},[648,1116,1117,1120,1121,1123,1124,1127],{},[193,1118,1119],{},"混合渲染规则顺序","：",[42,1122,904],{}," 按路径最长匹配优先，规则冲突时容易得到与预期不同的渲染模式，改动后建议用 ",[42,1125,1126],{},"nuxi analyze"," 或构建日志核对。",[34,1129,1130],{"id":1130},"最佳实践",[825,1132,1133,1142,1153,1169],{},[648,1134,1135,1136,1139,1140,32],{},"VueUse 函数优先按需自动导入（Nuxt 默认支持，Vite 项目可用 ",[42,1137,1138],{},"unplugin-auto-import","），避免手写大量 ",[42,1141,273],{},[648,1143,1144,1145,1148,1149,1152],{},"生产构建前用 ",[42,1146,1147],{},"vite build --mode production"," 结合 ",[42,1150,1151],{},"rollup-plugin-visualizer"," 一类工具检查产物体积。",[648,1154,1155,1156,613,1158,1160,1161,1164,1165,1168],{},"Nuxt 项目中数据获取统一走 ",[42,1157,188],{},[42,1159,819],{},"，避免在 ",[42,1162,1163],{},"onMounted"," 里裸调 ",[42,1166,1167],{},"fetch"," 导致 SSR\u002FCSR 数据不一致（Hydration Mismatch）。",[648,1170,1171,1172,1175],{},"对无需 SEO、纯交互的路由（如后台管理系统）用 ",[42,1173,1174],{},"ssr: false"," 换取更简单的部署与调试体验。",[34,1177,1178],{"id":1178},"总结",[645,1180,1181,1187,1193],{},[648,1182,1183,1186],{},[193,1184,1185],{},"VueUse","：组合式工具函数集合，按需引入，注意 SSR 环境下的浏览器 API 限制。",[648,1188,1189,1192],{},[193,1190,1191],{},"Vite + @vitejs\u002Fplugin-vue","：开发与生产构建，SFC 与细粒度 HMR。",[648,1194,1195,1198,1199,1201],{},[193,1196,1197],{},"Nuxt","：基于 Vue 的全栈框架，支持 SSR\u002FSSG 与 ",[42,1200,904],{}," 混合渲染，模块生态丰富。",[34,1203,1204],{"id":1204},"延伸阅读",[645,1206,1207,1212,1219],{},[648,1208,1209,1211],{},[28,1210,31],{"href":30},"：组合式 API、响应式基础。",[648,1213,1214,1218],{},[28,1215,1217],{"href":1216},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Frender","Vue 渲染机制","：理解 Vite HMR 背后的组件更新过程。",[648,1220,1221,1225],{},[28,1222,1224],{"href":1223},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fbuild","Vue 性能优化：构建","：进一步的 Vite 构建调优。",[34,1227,1228],{"id":1228},"参考文献",[24,1230,1231],{},"以下链接在编写时均可正常访问：",[67,1233,1234,1244],{},[70,1235,1236],{},[73,1237,1238,1241],{},[76,1239,1240],{},"资料",[76,1242,1243],{},"说明",[86,1245,1246,1257,1269,1280],{},[73,1247,1248,1254],{},[91,1249,1250],{},[28,1251,1253],{"href":56,"rel":1252},[58],"VueUse 文档",[91,1255,1256],{},"函数索引",[73,1258,1259,1266],{},[91,1260,1261],{},[28,1262,1265],{"href":1263,"rel":1264},"https:\u002F\u002Fcn.vite.dev\u002F",[58],"Vite 文档",[91,1267,1268],{},"中文",[73,1270,1271,1277],{},[91,1272,1273],{},[28,1274,631],{"href":1275,"rel":1276},"https:\u002F\u002Fgithub.com\u002Fvitejs\u002Fvite-plugin-vue",[58],[91,1278,1279],{},"Vue 插件",[73,1281,1282,1289],{},[91,1283,1284],{},[28,1285,1288],{"href":1286,"rel":1287},"https:\u002F\u002Fnuxt.com.cn\u002F",[58],"Nuxt 文档",[91,1290,1268],{},[693,1292,1293],{},"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 .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 .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .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 .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .svtyB, html code.shiki .svtyB{--shiki-light:#39ADB5;--shiki-light-font-style:inherit;--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}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 .sZUrc, html code.shiki .sZUrc{--shiki-light:#E53935;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":228,"searchDepth":269,"depth":269,"links":1295},[1296,1300,1303,1306,1307,1308,1309,1310],{"id":36,"depth":269,"text":37,"children":1297},[1298,1299],{"id":49,"depth":316,"text":50},{"id":221,"depth":316,"text":222},{"id":620,"depth":269,"text":621,"children":1301},[1302],{"id":624,"depth":316,"text":625},{"id":754,"depth":269,"text":755,"children":1304},[1305],{"id":758,"depth":316,"text":759},{"id":1064,"depth":269,"text":1064},{"id":1130,"depth":269,"text":1130},{"id":1178,"depth":269,"text":1178},{"id":1204,"depth":269,"text":1204},{"id":1228,"depth":269,"text":1228},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FVue\u002Fecology","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FVue\u002Fecology.md","md",{"items":1316},[1317,1321,1325,1329,1333,1337],{"path":1318,"title":1319,"description":1320,"category":10,"score":353},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets","资源优化","图片懒加载、按需引入与 Web Vitals 监控。见 HTML 图片、CSS 字体优化。",{"path":1322,"title":1323,"description":1324,"category":10,"score":353},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fstatus","状态管理：Pinia 与 Vuex","Pinia 是 Vue 官方推荐的状态管理库（Vue 3 新项目默认选型）。Vuex 4 仍可与 Vue 3 配合，用于维护旧项目。见 Vue 基础。",{"path":1326,"title":1327,"description":1328,"category":10,"score":353},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Ftrack","依赖收集与派发更新机制","说明 Vue 3 中 track \u002F trigger 与 targetMap 的工作方式。前置：v2-v3 差异。",{"path":1330,"title":1331,"description":1332,"category":10,"score":353},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Ftest","测试策略","Vue 应用常用 Vitest + @vue\u002Ftest-utils 做单元\u002F组件测试，Playwright 或 Cypress 做 E2E。见 Vue 基础。",{"path":1334,"title":1335,"description":1336,"category":10,"score":353},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fdesign","组合式 API 与逻辑复用设计模式","组合式 API 与 Composables 是 Vue 3 推荐的逻辑组织方式。概览见 Vue 基础。",{"path":1338,"title":1339,"description":1340,"category":10,"score":353},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Ftypescript","TypeScript 深度集成","Vue 3 源码使用 TypeScript 编写， 可获得良好的类型推导。见 Vue 基础。",{"series":1342,"title":12,"items":1343,"index":323,"prev":1389,"next":1390},"booklet\u002Fframe\u002Fvue",[1344,1346,1348,1351,1353,1354,1358,1362,1366,1369,1373,1377,1379,1381,1383,1385],{"path":1318,"title":1319,"date":1345,"pinned":15},"2025-11-06",{"path":30,"title":31,"date":1347,"pinned":15},"2021-07-29",{"path":1223,"title":1349,"date":1350,"pinned":15},"编译与打包优化","2021-08-02",{"path":1334,"title":1335,"date":1352,"pinned":15},"2024-06-16",{"path":7,"title":8,"date":14,"pinned":15},{"path":1355,"title":1356,"date":1357,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fproject","工程化实践","2021-07-20",{"path":1359,"title":1360,"date":1361,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fprops","组件通信与跨层级设计模式","2023-03-23",{"path":1363,"title":1364,"date":1365,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fproxy","响应式进阶","2023-03-21",{"path":1216,"title":1367,"date":1368,"pinned":15},"渲染机制与编译原理","2021-09-23",{"path":1370,"title":1371,"date":1372,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Frouter","路由与权限控制","2021-09-12",{"path":1374,"title":1375,"date":1376,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fruntime","运行时优化","2021-06-10",{"path":1322,"title":1323,"date":1378,"pinned":15},"2025-10-11",{"path":1330,"title":1331,"date":1380,"pinned":15},"2024-10-23",{"path":1326,"title":1327,"date":1382,"pinned":15},"2025-01-13",{"path":1338,"title":1339,"date":1384,"pinned":15},"2023-11-29",{"path":1386,"title":1387,"date":1388,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fv2-v3","Vue 2 与 Vue 3 响应式差异","2022-07-06",{"path":1334,"title":1335,"date":1352,"pinned":15},{"path":1355,"title":1356,"date":1357,"pinned":15}]