[{"data":1,"prerenderedAt":1911},["ShallowReactive",2],{"page-\u002Fvue\u002Fnuxt\u002Fnuxt4":3},{"id":4,"title":5,"body":6,"description":93,"extension":1905,"meta":1906,"navigation":940,"path":1907,"seo":1908,"stem":1909,"__hash__":1910},"content\u002FVue\u002FNuxt\u002FNuxt4.md","Nuxt4",{"type":7,"value":8,"toc":1883},"minimark",[9,14,22,42,49,60,63,68,71,84,87,145,148,151,186,189,192,195,198,201,214,225,232,242,253,272,275,278,285,288,291,301,304,307,310,313,316,319,322,325,375,379,382,451,455,461,464,467,470,473,476,572,575,670,673,677,680,744,817,852,855,858,868,874,884,893,906,917,1015,1033,1039,1177,1180,1298,1305,1308,1330,1340,1344,1355,1370,1461,1475,1478,1481,1484,1490,1497,1609,1626,1865,1869,1876,1879],[10,11,13],"h2",{"id":12},"usehead","useHead",[15,16,17,18,21],"p",{},"useHead是一个核心的组合式函数，用来程序化管理Html文档中的head部分，如果你想修改网页的标题、关键词、描述，或者引入外部的 CSS\u002FJS 文件，",[19,20,13],"code",{}," 就是你的首选工具。",[15,23,24,25,29,30,33,34,37,38,41],{},"他可以做到",[26,27,28],"strong",{},"管理元数据","，",[26,31,32],{},"注入外部资源","，也可以",[26,35,36],{},"传入响应式数据","，当你的业务数据发生变化时，",[26,39,40],{},"页面的标题或描述也会随之自动更新","，而无需手动操作 DOM。",[15,43,44,45,48],{},"它和SSR是兼容的，nxut在服务端渲染阶段就处理好了这些头信息，当浏览器接收到Html的时候，",[19,46,47],{},"\u003Chead>"," 里的内容就已经是完整的了。",[15,50,51,52,55,56,59],{},"我们可以通过useHead来设置",[19,53,54],{},"title","、",[19,57,58],{},"meta"," 标签（如描述、关键词、Open Graph 标签等）。这对于搜索引擎优化（SEO）至关重要，因为爬虫主要通过这些信息来了解你的网页内容。",[15,61,62],{},"很多的图标库或者UI框架式是都要使用sciprt和link的，一个是引入js，一个是引入css，因此我们可以在useHead里完成。",[64,65,67],"h3",{"id":66},"seo优化","SEO优化",[15,69,70],{},"nuxt推荐在大多数的SEO场景下使用useSeoMeta，它是useHead的快捷方式，专门为meta标签设定的，有很棒的ts自动补全，能防止写错og:title、twitter:card等属性。",[15,72,73,74,76,77,79,80,83],{},"虽然 ",[19,75,13],{}," 也可以通过 ",[19,78,58],{}," 数组来配置 SEO，但 ",[19,81,82],{},"useSeoMeta"," 专门为 SEO 场景做了优化，提供了更强大的类型提示和更简洁的语法。",[15,85,86],{},"用法如下：",[88,89,94],"pre",{"className":90,"code":91,"language":92,"meta":93,"style":93},"language-ts shiki shiki-themes github-light github-dark","useSeoMeta({ title: 'Kiriyama | 视觉开发与架构', ogTitle: 'Kiriyama 的个人技术空间', description: '专注于 .NET、C++ 图形学及现代前端技术的深度探索。', ogDescription: '在这里展示我的技术栈：C#, Skia, Nuxt4 以及更多艺术感与技术结合的项目。', ogImage: 'https:\u002F\u002Fexample.com\u002Fog-image.png', twitterCard: 'summary_large_image', })\n","ts","",[19,95,96],{"__ignoreMap":93},[97,98,101,104,108,112,115,118,121,124,127,130,133,136,139,142],"span",{"class":99,"line":100},"line",1,[97,102,82],{"class":103},"sScJk",[97,105,107],{"class":106},"sVt8B","({ title: ",[97,109,111],{"class":110},"sZZnC","'Kiriyama | 视觉开发与架构'",[97,113,114],{"class":106},", ogTitle: ",[97,116,117],{"class":110},"'Kiriyama 的个人技术空间'",[97,119,120],{"class":106},", description: ",[97,122,123],{"class":110},"'专注于 .NET、C++ 图形学及现代前端技术的深度探索。'",[97,125,126],{"class":106},", ogDescription: ",[97,128,129],{"class":110},"'在这里展示我的技术栈：C#, Skia, Nuxt4 以及更多艺术感与技术结合的项目。'",[97,131,132],{"class":106},", ogImage: ",[97,134,135],{"class":110},"'https:\u002F\u002Fexample.com\u002Fog-image.png'",[97,137,138],{"class":106},", twitterCard: ",[97,140,141],{"class":110},"'summary_large_image'",[97,143,144],{"class":106},", })\n",[15,146,147],{},"title是给浏览器和搜索引擎看的，而ogTitle是给社交媒体和分享卡片使用的",[15,149,150],{},"title的标签就是我们熟悉的title标签，但是ogTitle则是：",[88,152,156],{"className":153,"code":154,"language":155,"meta":93,"style":93},"language-html shiki shiki-themes github-light github-dark","\u003Cmeta property=\"og:title\" content=\"...\">\n","html",[19,157,158],{"__ignoreMap":93},[97,159,160,163,166,169,172,175,178,180,183],{"class":99,"line":100},[97,161,162],{"class":106},"\u003C",[97,164,58],{"class":165},"s9eBZ",[97,167,168],{"class":103}," property",[97,170,171],{"class":106},"=",[97,173,174],{"class":110},"\"og:title\"",[97,176,177],{"class":103}," content",[97,179,171],{"class":106},[97,181,182],{"class":110},"\"...\"",[97,184,185],{"class":106},">\n",[15,187,188],{},"显示的位置是: 微信、飞书、Discord、Facebook 等分享链接时的卡片，核心目的是点击率。",[10,190,191],{"id":191},"生命周期",[64,193,194],{"id":194},"水合",[15,196,197],{},"同构渲染一般叫做通用渲染，指的是代码可以同时运行在服务端和客户端的能力，同构渲染的工作模式为两个阶段，第一阶段是服务端渲染阶段，也可以叫做脱水。",[15,199,200],{},"当你第一次访问页面的时候，服务器执行vue代码，就爱那个数据填充进入组件，生成完整的html字符串给浏览器，用户立即就可以看到内容，SEO友好（搜索引擎爬虫能抓到完整的html）。",[202,203,204],"blockquote",{},[15,205,206,209,210,213],{},[97,207,208],{},"!tip"," 注意\n第一阶段生成的是静态html，组件树转换为纯字符串，服务器会将抓取到的数据序列化成一个 JSON 对象（通常挂载在 ",[19,211,212],{},"window.__NUXT__"," 下），随 HTML 一起发给浏览器。",[15,215,216,217,220,221,224],{},"第二阶段是",[26,218,219],{},"客户端激活，水合阶段","，浏览器解析完了html后，开始加载",[26,222,223],{},"nuxt的客户端脚本，vue接管控制器","，在浏览器中再次运行一次代码。",[15,226,227,228,231],{},"水合的过程中vue不会重新创建DOM，而是会去扫描现有的HTML，将虚拟DOM（VNDOE）和真实的DOM节点挂钩。绑定事件监听器，把 ",[19,229,230],{},"v-on:click"," 之类的交互逻辑关联到 DOM 元素上。",[15,233,234,237,238,241],{},[26,235,236],{},"客户端代码不会再次发送网络请求去拿首页数据","，而是",[26,239,240],{},"直接读取服务器传过来的 JSON 对象","（刚才说的“零件列表”）。",[15,243,244,245,248,249,252],{},"这个阶段很容易遇到水合错误，如果",[26,246,247],{},"第一阶段生成的东西","和",[26,250,251],{},"第二阶段扫描到的东西","对不上，Vue 就会报警告甚至崩溃。",[15,254,255,256,259,260,263,264,267,268,271],{},"典型的场景就比如服务器渲染时间，或者",[19,257,258],{},"\u003Cp>","标签里面塞div导致浏览器自动修复的时候发现DOM不一样；如果你在 ",[19,261,262],{},"setup"," 顶层直接用了 ",[19,265,266],{},"window"," 或 ",[19,269,270],{},"localStorage","。服务器端没有这些对象，渲染会失败；客户端有，导致两端结果不一致。",[10,273,274],{"id":274},"数据获取",[15,276,277],{},"数据获取是最核心的一环，如果你用传统的axios放在OnMounted里的话，爬虫得到是一个空壳，SEO是没用的，因此Nuxt会提供四个核心工具：useFetch，useAsyncData、$fetch和useLasyFetch四个工具。",[15,279,280,281,284],{},"useFetch 是SSR友好的封装，可以自动去重，用于获取初始页面的核心数据，useAsyncData是更加灵活的异步数据处理，",[26,282,283],{},"需要自定义请求逻辑或者复杂转换逻辑场景","。",[15,286,287],{},"它们两个都是服务端获取+客户端复用payload。 $fetch是纯http请求工具，用户交互触发的请求，可以用于点击、滚动加载等，仅限于客户端执行。",[15,289,290],{},"useLazyFetch工具的核心特征是useFetch但是表现为lazy模式，主要用于非首屏关键数据，不阻塞路由导航。",[15,292,293,294,300],{},"那么我们需要给BaseUrl配置代理，比如我这里的apifox mock接口的地址是：",[295,296,297],"a",{"href":297,"rel":298},"http:\u002F\u002F127.0.0.1:4523\u002Fm1\u002F6982732-6700215-default",[299],"nofollow","\napi是:\u002Fapi\u002Fuser。",[15,302,303],{},"那么我们可以在nuaxt.config.ts中去配置nitro。\nnitro有两个配置一个是devProxy，一个是routeRules，前者只在csr下生效，后者是ssr、csr上都生效。",[15,305,306],{},"![[Pasted image 20260424235322.png]]",[64,308,309],{"id":309},"pick",[15,311,312],{},"很多时候后端接口返回的对象很大，包含着create_time、update_time、author_email等，但是前端只需要title、content等，这时候可以使用pick来选择保留进入html的字段，但是pick只能选第一层。\n![[Pasted image 20260425131608.png]]",[64,314,315],{"id":315},"transform",[15,317,318],{},"对于大部分的api数据都是嵌套的格式，推荐使用transform来处理数据：\n![[Pasted image 20260425131614.png]]",[64,320,321],{"id":321},"refresh",[15,323,324],{},"useFetch的第一个参数是它的key，如果你在同一个页面措辞调用同一个接口，由于key相同，数据可能不会更新。",[88,326,328],{"className":90,"code":327,"language":92,"meta":93,"style":93},"const {data, refresh} = await useFetch(\"\u002Fapi\u002Farticle\")\n\u002F\u002F 点击刷新按钮调用refresh即可重新获取一次\n",[19,329,330,368],{"__ignoreMap":93},[97,331,332,336,339,343,346,348,351,353,356,359,362,365],{"class":99,"line":100},[97,333,335],{"class":334},"szBVR","const",[97,337,338],{"class":106}," {",[97,340,342],{"class":341},"sj4cs","data",[97,344,345],{"class":106},", ",[97,347,321],{"class":341},[97,349,350],{"class":106},"} ",[97,352,171],{"class":334},[97,354,355],{"class":334}," await",[97,357,358],{"class":103}," useFetch",[97,360,361],{"class":106},"(",[97,363,364],{"class":110},"\"\u002Fapi\u002Farticle\"",[97,366,367],{"class":106},")\n",[97,369,371],{"class":99,"line":370},2,[97,372,374],{"class":373},"sJ8bj","\u002F\u002F 点击刷新按钮调用refresh即可重新获取一次\n",[64,376,378],{"id":377},"useasyncdata","useAsyncData",[15,380,381],{},"如果接口需要复杂逻辑，比如需要判断参数，或者想把它放在一个函数里面，可以使用这个：",[88,383,385],{"className":90,"code":384,"language":92,"meta":93,"style":93},"async function getData(){\nconst {data, pending, error} = await useFetch\u003CBaseRespones\u003CArticleType>>(\"\u002Fapi\u002Farticle\u002Fdetail\")\n}\n",[19,386,387,401,445],{"__ignoreMap":93},[97,388,389,392,395,398],{"class":99,"line":100},[97,390,391],{"class":334},"async",[97,393,394],{"class":334}," function",[97,396,397],{"class":103}," getData",[97,399,400],{"class":106},"(){\n",[97,402,403,405,407,409,411,414,416,419,421,423,425,427,429,432,434,437,440,443],{"class":99,"line":370},[97,404,335],{"class":334},[97,406,338],{"class":106},[97,408,342],{"class":341},[97,410,345],{"class":106},[97,412,413],{"class":341},"pending",[97,415,345],{"class":106},[97,417,418],{"class":341},"error",[97,420,350],{"class":106},[97,422,171],{"class":334},[97,424,355],{"class":334},[97,426,358],{"class":103},[97,428,162],{"class":106},[97,430,431],{"class":103},"BaseRespones",[97,433,162],{"class":106},[97,435,436],{"class":103},"ArticleType",[97,438,439],{"class":106},">>(",[97,441,442],{"class":110},"\"\u002Fapi\u002Farticle\u002Fdetail\"",[97,444,367],{"class":106},[97,446,448],{"class":99,"line":447},3,[97,449,450],{"class":106},"}\n",[64,452,454],{"id":453},"uselazyfetch","useLazyFetch",[15,456,457,458,284],{},"这是useFetch的一个变体，它的逻辑是先跳页面，再加载数据，通常情况下，Nuxt的useFetch会阻塞路由跳转，也就是说：如果你在setup里面使用了await useFetch的话，用户点击链接，",[26,459,460],{},"浏览器就会卡在当前页，直到新页面的数据请求完成后再进行跳转",[15,462,463],{},"useLazyFetch则可以避免这种阻塞，useLazyFetch相当于执行了useFetch(url, {lazy: true}，它可以进行非阻塞跳转，路由会立即切换，组件立即挂载。前台异步：数据再后台抓取，抓取过程中页面已经展示出来了。响应式：返回的pending属性会从true变为fasle，可以根据这个展示加载动画Loading。",[15,465,466],{},"比如你有一个页面数据很大，很复杂的表，接口可能要2s，这时候可以用这个，页面秒开，并显示转圈\u002F骨架屏。",[15,468,469],{},"const {pending, data: post} = useLazyFetch('\u002Fapi\u002Fpost')",[64,471,472],{"id":472},"获取原始响应",[15,474,475],{},"如果你需要获取原始响应的数据，比如判断响应的http状态码、http响应头。有两种做法，一种是onResponse属性，一种是$fetch.rAw",[88,477,479],{"className":90,"code":478,"language":92,"meta":93,"style":93},"const {data, error} = await useFetch('\u002Fapi\u002Fcurrent\u002Fuser', {\n    onResponse ({response}){\n        const customHeader = reseponse.headers.get('x-custom-header')\n        console.log(customHeader)\n    }\n})\n",[19,480,481,509,524,548,560,566],{"__ignoreMap":93},[97,482,483,485,487,489,491,493,495,497,499,501,503,506],{"class":99,"line":100},[97,484,335],{"class":334},[97,486,338],{"class":106},[97,488,342],{"class":341},[97,490,345],{"class":106},[97,492,418],{"class":341},[97,494,350],{"class":106},[97,496,171],{"class":334},[97,498,355],{"class":334},[97,500,358],{"class":103},[97,502,361],{"class":106},[97,504,505],{"class":110},"'\u002Fapi\u002Fcurrent\u002Fuser'",[97,507,508],{"class":106},", {\n",[97,510,511,514,517,521],{"class":99,"line":370},[97,512,513],{"class":103},"    onResponse",[97,515,516],{"class":106}," ({",[97,518,520],{"class":519},"s4XuR","response",[97,522,523],{"class":106},"}){\n",[97,525,526,529,532,535,538,541,543,546],{"class":99,"line":447},[97,527,528],{"class":334},"        const",[97,530,531],{"class":341}," customHeader",[97,533,534],{"class":334}," =",[97,536,537],{"class":106}," reseponse.headers.",[97,539,540],{"class":103},"get",[97,542,361],{"class":106},[97,544,545],{"class":110},"'x-custom-header'",[97,547,367],{"class":106},[97,549,551,554,557],{"class":99,"line":550},4,[97,552,553],{"class":106},"        console.",[97,555,556],{"class":103},"log",[97,558,559],{"class":106},"(customHeader)\n",[97,561,563],{"class":99,"line":562},5,[97,564,565],{"class":106},"    }\n",[97,567,569],{"class":99,"line":568},6,[97,570,571],{"class":106},"})\n",[15,573,574],{},"然后是$fetch.raw",[88,576,578],{"className":90,"code":577,"language":92,"meta":93,"style":93},"const fetchUser = async()=>{\ntry{\nconst res = await $fetch.raw(\"\u002Fapi\u002Fcurrent\u002Fuser\")\nconst user = res._data\nconst serverTime = res.headers.get('data')\n}}\n",[19,579,580,601,608,632,644,665],{"__ignoreMap":93},[97,581,582,584,587,589,592,595,598],{"class":99,"line":100},[97,583,335],{"class":334},[97,585,586],{"class":103}," fetchUser",[97,588,534],{"class":334},[97,590,591],{"class":334}," async",[97,593,594],{"class":106},"()",[97,596,597],{"class":334},"=>",[97,599,600],{"class":106},"{\n",[97,602,603,606],{"class":99,"line":370},[97,604,605],{"class":334},"try",[97,607,600],{"class":106},[97,609,610,612,615,617,619,622,625,627,630],{"class":99,"line":447},[97,611,335],{"class":334},[97,613,614],{"class":341}," res",[97,616,534],{"class":334},[97,618,355],{"class":334},[97,620,621],{"class":106}," $fetch.",[97,623,624],{"class":103},"raw",[97,626,361],{"class":106},[97,628,629],{"class":110},"\"\u002Fapi\u002Fcurrent\u002Fuser\"",[97,631,367],{"class":106},[97,633,634,636,639,641],{"class":99,"line":550},[97,635,335],{"class":334},[97,637,638],{"class":341}," user",[97,640,534],{"class":334},[97,642,643],{"class":106}," res._data\n",[97,645,646,648,651,653,656,658,660,663],{"class":99,"line":562},[97,647,335],{"class":334},[97,649,650],{"class":341}," serverTime",[97,652,534],{"class":334},[97,654,655],{"class":106}," res.headers.",[97,657,540],{"class":103},[97,659,361],{"class":106},[97,661,662],{"class":110},"'data'",[97,664,367],{"class":106},[97,666,667],{"class":99,"line":568},[97,668,669],{"class":106},"}}\n",[10,671,672],{"id":672},"状态管理",[64,674,676],{"id":675},"usestate","useState",[15,678,679],{},"useState是nuxt 为SSR环境设计的响应式状态管理器，它能够在服务器和浏览器之间共享状态，可以确保每个用户的请求都有独立的状态，并且能解决水合不匹配。",[88,681,683],{"className":90,"code":682,"language":92,"meta":93,"style":93},"\u002F\u002F 服务端\nconst config = useState('app-config', () => ({\n  theme: 'dark',\n  language: 'zh-CN'\n}))\n\u002F\u002F 服务端渲染时：config 的值被序列化到 payload\n",[19,684,685,690,715,726,734,739],{"__ignoreMap":93},[97,686,687],{"class":99,"line":100},[97,688,689],{"class":373},"\u002F\u002F 服务端\n",[97,691,692,694,697,699,702,704,707,710,712],{"class":99,"line":370},[97,693,335],{"class":334},[97,695,696],{"class":341}," config",[97,698,534],{"class":334},[97,700,701],{"class":103}," useState",[97,703,361],{"class":106},[97,705,706],{"class":110},"'app-config'",[97,708,709],{"class":106},", () ",[97,711,597],{"class":334},[97,713,714],{"class":106}," ({\n",[97,716,717,720,723],{"class":99,"line":447},[97,718,719],{"class":106},"  theme: ",[97,721,722],{"class":110},"'dark'",[97,724,725],{"class":106},",\n",[97,727,728,731],{"class":99,"line":550},[97,729,730],{"class":106},"  language: ",[97,732,733],{"class":110},"'zh-CN'\n",[97,735,736],{"class":99,"line":562},[97,737,738],{"class":106},"}))\n",[97,740,741],{"class":99,"line":568},[97,742,743],{"class":373},"\u002F\u002F 服务端渲染时：config 的值被序列化到 payload\n",[88,745,747],{"className":153,"code":746,"language":155,"meta":93,"style":93},"\u003C!-- 生成的 HTML 中包含 -->\n\u003Cscript>\n  window.__NUXT__ = {\n    data: {\n      'app-config': { theme: 'dark', language: 'zh-CN' }\n    }\n  }\n\u003C\u002Fscript>\n",[19,748,749,754,763,773,778,797,801,807],{"__ignoreMap":93},[97,750,751],{"class":99,"line":100},[97,752,753],{"class":373},"\u003C!-- 生成的 HTML 中包含 -->\n",[97,755,756,758,761],{"class":99,"line":370},[97,757,162],{"class":106},[97,759,760],{"class":165},"script",[97,762,185],{"class":106},[97,764,765,768,770],{"class":99,"line":447},[97,766,767],{"class":106},"  window.__NUXT__ ",[97,769,171],{"class":334},[97,771,772],{"class":106}," {\n",[97,774,775],{"class":99,"line":550},[97,776,777],{"class":106},"    data: {\n",[97,779,780,783,786,788,791,794],{"class":99,"line":562},[97,781,782],{"class":110},"      'app-config'",[97,784,785],{"class":106},": { theme: ",[97,787,722],{"class":110},[97,789,790],{"class":106},", language: ",[97,792,793],{"class":110},"'zh-CN'",[97,795,796],{"class":106}," }\n",[97,798,799],{"class":99,"line":568},[97,800,565],{"class":106},[97,802,804],{"class":99,"line":803},7,[97,805,806],{"class":106},"  }\n",[97,808,810,813,815],{"class":99,"line":809},8,[97,811,812],{"class":106},"\u003C\u002F",[97,814,760],{"class":165},[97,816,185],{"class":106},[88,818,820],{"className":90,"code":819,"language":92,"meta":93,"style":93},"\u002F\u002F 客户端水合时\nconst config = useState('app-config')  \u002F\u002F 自动从 window.__NUXT__ 读取\n\u002F\u002F ✅ 值和服务端完全一致，无需重新请求或计算\n",[19,821,822,827,847],{"__ignoreMap":93},[97,823,824],{"class":99,"line":100},[97,825,826],{"class":373},"\u002F\u002F 客户端水合时\n",[97,828,829,831,833,835,837,839,841,844],{"class":99,"line":370},[97,830,335],{"class":334},[97,832,696],{"class":341},[97,834,534],{"class":334},[97,836,701],{"class":103},[97,838,361],{"class":106},[97,840,706],{"class":110},[97,842,843],{"class":106},")  ",[97,845,846],{"class":373},"\u002F\u002F 自动从 window.__NUXT__ 读取\n",[97,848,849],{"class":99,"line":447},[97,850,851],{"class":373},"\u002F\u002F ✅ 值和服务端完全一致，无需重新请求或计算\n",[15,853,854],{},"如果使用ref的话，会导致全局\u002F组件级别的跨请求共享，用户A会看到用户B的数据。",[64,856,857],{"id":857},"pinia",[15,859,860,863,864,284],{},[19,861,862],{},"@pinia\u002Fnuxt"," 模块为 Pinia 提供了开箱即用的 SSR 支持。它可以自动处理状态在服务端和客户端之间的序列化与“注水”（hydration），让你在 Nuxt 应用中能无缝地使用 Pinia 进行状态管理",[295,865],{"href":866,"rel":867},"https:\u002F\u002Fdeepwiki.com\u002Fvuejs\u002Fpinia\u002F4.2-nuxt-integration",[299],[15,869,870,871,284],{},"它做了几件关键的事情，但你安装并且配置了@pinia\u002Fnuxt模块之后，他就会自动在应用启动的时候创建 Pinia 实例，并将其与 Nuxt 的运行时环境连接起来",[295,872],{"href":866,"rel":873},[299],[15,875,876,877,880,881,284],{},"在服务端，他会捕获pinia在组件渲染过程中产生的所有状态变更，并自动将这些状态序列化到 Nuxt 的 payload (",[19,878,879],{},"nuxtApp.payload.pinia",") 中",[295,882],{"href":866,"rel":883},[299],[15,885,886,887,890,284],{},"在客户端，应用启动时，模块会读取 payload 中的状态，并直接将其作为 Pinia 的初始状态进行恢复。这样就",[26,888,889],{},"保证了服务端和客户端状态的完全一致，从而避免了水合不匹配的问题",[295,891],{"href":866,"rel":892},[299],[15,894,895,896,55,899,902,903,284],{},"模块会配置好自动导入功能，你可以在组件或者Composables中直接使用 ",[19,897,898],{},"defineStore()",[19,900,901],{},"storeToRefs()"," 等函数，无需手动导入",[295,904],{"href":866,"rel":905},[299],[15,907,908,909,912,913,284],{},"如果你的 Store 中确实需要存储这类无法被json序列化的数据，可以使用 Pinia 提供的 ",[19,910,911],{},"skipHydrate()"," 辅助函数来标记它们，告诉框架：“这个数据不需要被传递和激活”",[295,914],{"href":915,"rel":916},"https:\u002F\u002Fblog.csdn.net\u002Fgitblog_01060\u002Farticle\u002Fdetails\u002F151535419",[299],[88,918,920],{"className":90,"code":919,"language":92,"meta":93,"style":93},"import { skipHydrate } from 'pinia'\n\nexport const useMyStore = defineStore('my-store', () => {\n  \u002F\u002F 这个复杂实例不会被序列化，客户端会使用服务端计算后的新实例\n  const complexInstance = skipHydrate(new SomeComplexClass())\n  \n  return { complexInstance }\n})\n",[19,921,922,936,942,969,974,998,1003,1011],{"__ignoreMap":93},[97,923,924,927,930,933],{"class":99,"line":100},[97,925,926],{"class":334},"import",[97,928,929],{"class":106}," { skipHydrate } ",[97,931,932],{"class":334},"from",[97,934,935],{"class":110}," 'pinia'\n",[97,937,938],{"class":99,"line":370},[97,939,941],{"emptyLinePlaceholder":940},true,"\n",[97,943,944,947,950,953,955,958,960,963,965,967],{"class":99,"line":447},[97,945,946],{"class":334},"export",[97,948,949],{"class":334}," const",[97,951,952],{"class":341}," useMyStore",[97,954,534],{"class":334},[97,956,957],{"class":103}," defineStore",[97,959,361],{"class":106},[97,961,962],{"class":110},"'my-store'",[97,964,709],{"class":106},[97,966,597],{"class":334},[97,968,772],{"class":106},[97,970,971],{"class":99,"line":550},[97,972,973],{"class":373},"  \u002F\u002F 这个复杂实例不会被序列化，客户端会使用服务端计算后的新实例\n",[97,975,976,979,982,984,987,989,992,995],{"class":99,"line":562},[97,977,978],{"class":334},"  const",[97,980,981],{"class":341}," complexInstance",[97,983,534],{"class":334},[97,985,986],{"class":103}," skipHydrate",[97,988,361],{"class":106},[97,990,991],{"class":334},"new",[97,993,994],{"class":103}," SomeComplexClass",[97,996,997],{"class":106},"())\n",[97,999,1000],{"class":99,"line":568},[97,1001,1002],{"class":106},"  \n",[97,1004,1005,1008],{"class":99,"line":803},[97,1006,1007],{"class":334},"  return",[97,1009,1010],{"class":106}," { complexInstance }\n",[97,1012,1013],{"class":99,"line":809},[97,1014,571],{"class":106},[15,1016,1017,1020,1021,1028,1029,284],{},[26,1018,1019],{},"生产配置建议","：Pinia官方与Nuxt团队的集成中，",[26,1022,1023,1024,1027],{},"强烈建议保持 ",[19,1025,1026],{},"renderJsonPayloads"," 为默认开启状态","。关闭它可能会导致与Pinia SSR 相关的序列化警告",[295,1030],{"href":1031,"rel":1032},"https:\u002F\u002Fblog.gitcode.com\u002F38dd8771bae7d48517395c904c5d3947.html",[299],[15,1034,1035,1038],{},[26,1036,1037],{},"背后的机制","：",[88,1040,1042],{"className":90,"code":1041,"language":92,"meta":93,"style":93},"\u002F\u002F Nuxt 内部简化逻辑\nconst requestScopedState = new Map()  \u002F\u002F 每个请求一个独立 Map\nexport const useState = (key, init) => {\n  \u002F\u002F 当前请求的独立存储\n  if (!requestScopedState.has(key)) {\n    requestScopedState.set(key, init ? init() : null)\n  }\n  return requestScopedState.get(key)\n}\n\u002F\u002F 请求结束 → 清空 requestScopedState\n",[19,1043,1044,1049,1070,1098,1103,1122,1150,1154,1166,1171],{"__ignoreMap":93},[97,1045,1046],{"class":99,"line":100},[97,1047,1048],{"class":373},"\u002F\u002F Nuxt 内部简化逻辑\n",[97,1050,1051,1053,1056,1058,1061,1064,1067],{"class":99,"line":370},[97,1052,335],{"class":334},[97,1054,1055],{"class":341}," requestScopedState",[97,1057,534],{"class":334},[97,1059,1060],{"class":334}," new",[97,1062,1063],{"class":103}," Map",[97,1065,1066],{"class":106},"()  ",[97,1068,1069],{"class":373},"\u002F\u002F 每个请求一个独立 Map\n",[97,1071,1072,1074,1076,1078,1080,1083,1086,1088,1091,1094,1096],{"class":99,"line":447},[97,1073,946],{"class":334},[97,1075,949],{"class":334},[97,1077,701],{"class":103},[97,1079,534],{"class":334},[97,1081,1082],{"class":106}," (",[97,1084,1085],{"class":519},"key",[97,1087,345],{"class":106},[97,1089,1090],{"class":519},"init",[97,1092,1093],{"class":106},") ",[97,1095,597],{"class":334},[97,1097,772],{"class":106},[97,1099,1100],{"class":99,"line":550},[97,1101,1102],{"class":373},"  \u002F\u002F 当前请求的独立存储\n",[97,1104,1105,1108,1110,1113,1116,1119],{"class":99,"line":562},[97,1106,1107],{"class":334},"  if",[97,1109,1082],{"class":106},[97,1111,1112],{"class":334},"!",[97,1114,1115],{"class":106},"requestScopedState.",[97,1117,1118],{"class":103},"has",[97,1120,1121],{"class":106},"(key)) {\n",[97,1123,1124,1127,1130,1133,1136,1139,1142,1145,1148],{"class":99,"line":568},[97,1125,1126],{"class":106},"    requestScopedState.",[97,1128,1129],{"class":103},"set",[97,1131,1132],{"class":106},"(key, init ",[97,1134,1135],{"class":334},"?",[97,1137,1138],{"class":103}," init",[97,1140,1141],{"class":106},"() ",[97,1143,1144],{"class":334},":",[97,1146,1147],{"class":341}," null",[97,1149,367],{"class":106},[97,1151,1152],{"class":99,"line":803},[97,1153,806],{"class":106},[97,1155,1156,1158,1161,1163],{"class":99,"line":809},[97,1157,1007],{"class":334},[97,1159,1160],{"class":106}," requestScopedState.",[97,1162,540],{"class":103},[97,1164,1165],{"class":106},"(key)\n",[97,1167,1169],{"class":99,"line":1168},9,[97,1170,450],{"class":106},[97,1172,1174],{"class":99,"line":1173},10,[97,1175,1176],{"class":373},"\u002F\u002F 请求结束 → 清空 requestScopedState\n",[15,1178,1179],{},"但不要在useState中存储不可序列化的数据。",[88,1181,1183],{"className":90,"code":1182,"language":92,"meta":93,"style":93},"\u002F\u002F ❌ 错误：函数无法被序列化\nconst myFn = useState('fn', () => () => console.log('hi'))\n\n\u002F\u002F ❌ 错误：Symbol 无法被序列化\nconst sym = useState('sym', () => Symbol())\n\n\u002F\u002F ✅ 正确：基础类型、对象、数组\nconst config = useState('config', () => ({ theme: 'dark' }))\n",[19,1184,1185,1190,1228,1232,1237,1262,1266,1271],{"__ignoreMap":93},[97,1186,1187],{"class":99,"line":100},[97,1188,1189],{"class":373},"\u002F\u002F ❌ 错误：函数无法被序列化\n",[97,1191,1192,1194,1197,1199,1201,1203,1206,1208,1210,1213,1215,1218,1220,1222,1225],{"class":99,"line":370},[97,1193,335],{"class":334},[97,1195,1196],{"class":341}," myFn",[97,1198,534],{"class":334},[97,1200,701],{"class":103},[97,1202,361],{"class":106},[97,1204,1205],{"class":110},"'fn'",[97,1207,709],{"class":106},[97,1209,597],{"class":334},[97,1211,1212],{"class":106}," () ",[97,1214,597],{"class":334},[97,1216,1217],{"class":106}," console.",[97,1219,556],{"class":103},[97,1221,361],{"class":106},[97,1223,1224],{"class":110},"'hi'",[97,1226,1227],{"class":106},"))\n",[97,1229,1230],{"class":99,"line":447},[97,1231,941],{"emptyLinePlaceholder":940},[97,1233,1234],{"class":99,"line":550},[97,1235,1236],{"class":373},"\u002F\u002F ❌ 错误：Symbol 无法被序列化\n",[97,1238,1239,1241,1244,1246,1248,1250,1253,1255,1257,1260],{"class":99,"line":562},[97,1240,335],{"class":334},[97,1242,1243],{"class":341}," sym",[97,1245,534],{"class":334},[97,1247,701],{"class":103},[97,1249,361],{"class":106},[97,1251,1252],{"class":110},"'sym'",[97,1254,709],{"class":106},[97,1256,597],{"class":334},[97,1258,1259],{"class":103}," Symbol",[97,1261,997],{"class":106},[97,1263,1264],{"class":99,"line":568},[97,1265,941],{"emptyLinePlaceholder":940},[97,1267,1268],{"class":99,"line":803},[97,1269,1270],{"class":373},"\u002F\u002F ✅ 正确：基础类型、对象、数组\n",[97,1272,1273,1275,1277,1279,1281,1283,1286,1288,1290,1293,1295],{"class":99,"line":809},[97,1274,335],{"class":334},[97,1276,696],{"class":341},[97,1278,534],{"class":334},[97,1280,701],{"class":103},[97,1282,361],{"class":106},[97,1284,1285],{"class":110},"'config'",[97,1287,709],{"class":106},[97,1289,597],{"class":334},[97,1291,1292],{"class":106}," ({ theme: ",[97,1294,722],{"class":110},[97,1296,1297],{"class":106}," }))\n",[15,1299,1300,1301,1304],{},"并且当ref在顶层，比如说utils\u002F",[97,1302,1303],{},"...",".ts的时候会发生泄漏，但是如果是在setup里面的话，就很不会发生泄漏。泄漏的是“进程级共享内存”，不是“请求级实例内存”。",[15,1306,1307],{},"setup里面的ref属于组件实例，而ssr每个请求都会创建一套新的应用\u002F组件实例，因此数据就会跟着当前的组件实例走，本次的请求渲染完就结束，不会被下一个请求复用。",[1309,1310,1311],"ul",{},[1312,1313,1314,1315,1318,1319],"li",{},"模块顶层：",[19,1316,1317],{},"const x = ref(...)","（文件一加载就创建）\n",[1309,1320,1321,1324],{},[1312,1322,1323],{},"跟着 Node 进程走",[1312,1325,1326,1327],{},"后续所有请求都可能读写同一个 ",[19,1328,1329],{},"x",[15,1331,1332,1333,1335,1336,1339],{},"在 Nuxt SSR 里，每个请求都会重新跑页面 ",[19,1334,262],{},"（服务端那次），所以 setup 内状态天然是 request-scoped。",[1337,1338],"br",{},"\n而模块顶层代码通常只在进程启动\u002F首次 import 时执行一次，因此是 process-scoped，才会串用户。",[10,1341,1343],{"id":1342},"http处理文件分片","HTTP处理文件分片",[15,1345,1346,1347,1350,1351,1354],{},"这是HTTP协议中",[19,1348,1349],{},"multipart\u002Fform-data","格式的核心机制。当浏览器上传多个文件（或一个文件加其他表单字段）时，会",[26,1352,1353],{},"自动","使用这种格式进行打包。",[15,1356,1357,1358,1361,1362,1365,1366,1369],{},"假设你提交了一个包含两个文件（",[19,1359,1360],{},"avatar.jpg"," 和 ",[19,1363,1364],{},"resume.pdf","）和一个用户名（",[19,1367,1368],{},"name","）的表单，浏览器生成的请求体大致长成这样：",[88,1371,1375],{"className":1372,"code":1373,"language":1374,"meta":93,"style":93},"language-http shiki shiki-themes github-light github-dark","POST \u002Fupload HTTP\u002F1.1\nHost: example.com\nContent-Type: multipart\u002Fform-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Length: [自动计算的总长度]\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"name\"\n张三\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"avatar\"; filename=\"avatar.jpg\"\nContent-Type: image\u002Fjpeg\n[这里是一大段二进制的图片数据]\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"resume\"; filename=\"resume.pdf\"\nContent-Type: application\u002Fpdf\n[这里是一大段二进制的PDF数据]\n------WebKitFormBoundary7MA4YWxkTrZu0gW--\n","http",[19,1376,1377,1382,1387,1392,1397,1402,1407,1412,1416,1421,1426,1432,1437,1443,1449,1455],{"__ignoreMap":93},[97,1378,1379],{"class":99,"line":100},[97,1380,1381],{},"POST \u002Fupload HTTP\u002F1.1\n",[97,1383,1384],{"class":99,"line":370},[97,1385,1386],{},"Host: example.com\n",[97,1388,1389],{"class":99,"line":447},[97,1390,1391],{},"Content-Type: multipart\u002Fform-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\n",[97,1393,1394],{"class":99,"line":550},[97,1395,1396],{},"Content-Length: [自动计算的总长度]\n",[97,1398,1399],{"class":99,"line":562},[97,1400,1401],{},"------WebKitFormBoundary7MA4YWxkTrZu0gW\n",[97,1403,1404],{"class":99,"line":568},[97,1405,1406],{},"Content-Disposition: form-data; name=\"name\"\n",[97,1408,1409],{"class":99,"line":803},[97,1410,1411],{},"张三\n",[97,1413,1414],{"class":99,"line":809},[97,1415,1401],{},[97,1417,1418],{"class":99,"line":1168},[97,1419,1420],{},"Content-Disposition: form-data; name=\"avatar\"; filename=\"avatar.jpg\"\n",[97,1422,1423],{"class":99,"line":1173},[97,1424,1425],{},"Content-Type: image\u002Fjpeg\n",[97,1427,1429],{"class":99,"line":1428},11,[97,1430,1431],{},"[这里是一大段二进制的图片数据]\n",[97,1433,1435],{"class":99,"line":1434},12,[97,1436,1401],{},[97,1438,1440],{"class":99,"line":1439},13,[97,1441,1442],{},"Content-Disposition: form-data; name=\"resume\"; filename=\"resume.pdf\"\n",[97,1444,1446],{"class":99,"line":1445},14,[97,1447,1448],{},"Content-Type: application\u002Fpdf\n",[97,1450,1452],{"class":99,"line":1451},15,[97,1453,1454],{},"[这里是一大段二进制的PDF数据]\n",[97,1456,1458],{"class":99,"line":1457},16,[97,1459,1460],{},"------WebKitFormBoundary7MA4YWxkTrZu0gW--\n",[15,1462,1463,1466,1467,1470,1471,1474],{},[19,1464,1465],{},"boundary"," 是整个过程的核心。它本质上是一个",[26,1468,1469],{},"随机生成的、足够复杂的唯一字符串","（如上面的 ",[19,1472,1473],{},"----WebKitFormBoundary7MA4YWxkTrZu0gW","）。",[15,1476,1477],{},"它的工作就是充当分割线，他首先在Content-Type头部被声明，用于告诉服务器这个字符串来分割不同数据块。",[15,1479,1480],{},"无论是普通文本还是文件数据，开始之前都会加上-- 和 boundary，表明一个新的字段开始。",[15,1482,1483],{},"结束的时候所有字段最后都会加上 -- + boundary + -- 来表示整个请求体到此结束。",[15,1485,1486,1487],{},"服务器收到请求的时候会先读取Content-Type头部，",[26,1488,1489],{},"解析出boundary的值，然后按照这个规则去切片和解析请求体，从而还原出你上传的表单字段和文件。",[15,1491,1492,1493,1496],{},"这里需要澄清一个很常见的概念混淆，“浏览器自动处理为分片”",[26,1494,1495],{},"，和我们通常提到的","“文件分片上传”**，是两个完全不同的东西，只是名字里都有“分片”：",[1498,1499,1500,1522],"table",{},[1501,1502,1503],"thead",{},[1504,1505,1506,1510,1517],"tr",{},[1507,1508,1509],"th",{},"特性",[1507,1511,1512],{},[26,1513,1514,1516],{},[19,1515,1349],{}," 格式",[1507,1518,1519],{},[26,1520,1521],{},"大文件分片上传",[1523,1524,1525,1556,1576,1596],"tbody",{},[1504,1526,1527,1533,1550],{},[1528,1529,1530],"td",{},[26,1531,1532],{},"触发方式",[1528,1534,1535,1538,1539,1542,1543,1546,1547],{},[26,1536,1537],{},"浏览器自动","，只要你在 ",[19,1540,1541],{},"\u003Cform>"," 或 ",[19,1544,1545],{},"FormData"," 中设置了 ",[19,1548,1549],{},"type=\"file\"",[1528,1551,1552,1555],{},[26,1553,1554],{},"需要手动编码","实现，浏览器不会自动做",[1504,1557,1558,1563,1570],{},[1528,1559,1560],{},[26,1561,1562],{},"目的",[1528,1564,1565,1566,1569],{},"将",[26,1567,1568],{},"多个不同","的文件\u002F字段打包在一个请求中发送",[1528,1571,1565,1572,1575],{},[26,1573,1574],{},"一个巨大","的文件切成小块，分多次请求发送",[1504,1577,1578,1583,1590],{},[1528,1579,1580],{},[26,1581,1582],{},"行为",[1528,1584,1585,1586,1589],{},"所有部分（分片）在",[26,1587,1588],{},"同一个HTTP请求","里",[1528,1591,1592,1593],{},"每个分片是一个",[26,1594,1595],{},"独立的HTTP请求",[1504,1597,1598,1603,1606],{},[1528,1599,1600],{},[26,1601,1602],{},"分片大小",[1528,1604,1605],{},"没有固定大小，就是文件的完整数据",[1528,1607,1608],{},"由你控制（通常1MB-10MB）",[15,1610,1611,1612,1615,1616,1619,1620,1623,1624,1038],{},"当我们需要监听上传进度的时候，可以用原生",[19,1613,1614],{},"XMLHttpRequest"," 或者 ",[19,1617,1618],{},"axios"," 的 ",[19,1621,1622],{},"onUploadProgress"," 事件，结合 ",[19,1625,1545],{},[88,1627,1629],{"className":90,"code":1628,"language":92,"meta":93,"style":93},"\u002F\u002F 在 Vue 组件或 composable 中\nconst uploadFiles = async (files) => {\n  const formData = new FormData()\n  files.forEach(file => {\n    formData.append('files', file) \u002F\u002F 浏览器会自动处理 boundary\n  })\n  \u002F\u002F 使用 axios 并监听上传进度\n  const { data } = await axios.post('\u002Fapi\u002Fupload', formData, {\n    headers: {\n      'Content-Type': 'multipart\u002Fform-data' \u002F\u002F 注意：不设置 boundary，axios 会帮你生成\n    },\n    onUploadProgress: (progressEvent) => {\n      const percentCompleted = Math.round((progressEvent.loaded * 100) \u002F progressEvent.total)\n      console.log(`上传进度: ${percentCompleted}%`)\n      \u002F\u002F 更新你的响应式状态\n    }\n  })\n}\n",[19,1630,1631,1636,1658,1675,1693,1712,1717,1722,1752,1757,1771,1776,1793,1826,1846,1851,1855,1860],{"__ignoreMap":93},[97,1632,1633],{"class":99,"line":100},[97,1634,1635],{"class":373},"\u002F\u002F 在 Vue 组件或 composable 中\n",[97,1637,1638,1640,1643,1645,1647,1649,1652,1654,1656],{"class":99,"line":370},[97,1639,335],{"class":334},[97,1641,1642],{"class":103}," uploadFiles",[97,1644,534],{"class":334},[97,1646,591],{"class":334},[97,1648,1082],{"class":106},[97,1650,1651],{"class":519},"files",[97,1653,1093],{"class":106},[97,1655,597],{"class":334},[97,1657,772],{"class":106},[97,1659,1660,1662,1665,1667,1669,1672],{"class":99,"line":447},[97,1661,978],{"class":334},[97,1663,1664],{"class":341}," formData",[97,1666,534],{"class":334},[97,1668,1060],{"class":334},[97,1670,1671],{"class":103}," FormData",[97,1673,1674],{"class":106},"()\n",[97,1676,1677,1680,1683,1685,1688,1691],{"class":99,"line":550},[97,1678,1679],{"class":106},"  files.",[97,1681,1682],{"class":103},"forEach",[97,1684,361],{"class":106},[97,1686,1687],{"class":519},"file",[97,1689,1690],{"class":334}," =>",[97,1692,772],{"class":106},[97,1694,1695,1698,1701,1703,1706,1709],{"class":99,"line":562},[97,1696,1697],{"class":106},"    formData.",[97,1699,1700],{"class":103},"append",[97,1702,361],{"class":106},[97,1704,1705],{"class":110},"'files'",[97,1707,1708],{"class":106},", file) ",[97,1710,1711],{"class":373},"\u002F\u002F 浏览器会自动处理 boundary\n",[97,1713,1714],{"class":99,"line":568},[97,1715,1716],{"class":106},"  })\n",[97,1718,1719],{"class":99,"line":803},[97,1720,1721],{"class":373},"  \u002F\u002F 使用 axios 并监听上传进度\n",[97,1723,1724,1726,1729,1731,1734,1736,1738,1741,1744,1746,1749],{"class":99,"line":809},[97,1725,978],{"class":334},[97,1727,1728],{"class":106}," { ",[97,1730,342],{"class":341},[97,1732,1733],{"class":106}," } ",[97,1735,171],{"class":334},[97,1737,355],{"class":334},[97,1739,1740],{"class":106}," axios.",[97,1742,1743],{"class":103},"post",[97,1745,361],{"class":106},[97,1747,1748],{"class":110},"'\u002Fapi\u002Fupload'",[97,1750,1751],{"class":106},", formData, {\n",[97,1753,1754],{"class":99,"line":1168},[97,1755,1756],{"class":106},"    headers: {\n",[97,1758,1759,1762,1765,1768],{"class":99,"line":1173},[97,1760,1761],{"class":110},"      'Content-Type'",[97,1763,1764],{"class":106},": ",[97,1766,1767],{"class":110},"'multipart\u002Fform-data'",[97,1769,1770],{"class":373}," \u002F\u002F 注意：不设置 boundary，axios 会帮你生成\n",[97,1772,1773],{"class":99,"line":1428},[97,1774,1775],{"class":106},"    },\n",[97,1777,1778,1781,1784,1787,1789,1791],{"class":99,"line":1434},[97,1779,1780],{"class":103},"    onUploadProgress",[97,1782,1783],{"class":106},": (",[97,1785,1786],{"class":519},"progressEvent",[97,1788,1093],{"class":106},[97,1790,597],{"class":334},[97,1792,772],{"class":106},[97,1794,1795,1798,1801,1803,1806,1809,1812,1815,1818,1820,1823],{"class":99,"line":1439},[97,1796,1797],{"class":334},"      const",[97,1799,1800],{"class":341}," percentCompleted",[97,1802,534],{"class":334},[97,1804,1805],{"class":106}," Math.",[97,1807,1808],{"class":103},"round",[97,1810,1811],{"class":106},"((progressEvent.loaded ",[97,1813,1814],{"class":334},"*",[97,1816,1817],{"class":341}," 100",[97,1819,1093],{"class":106},[97,1821,1822],{"class":334},"\u002F",[97,1824,1825],{"class":106}," progressEvent.total)\n",[97,1827,1828,1831,1833,1835,1838,1841,1844],{"class":99,"line":1445},[97,1829,1830],{"class":106},"      console.",[97,1832,556],{"class":103},[97,1834,361],{"class":106},[97,1836,1837],{"class":110},"`上传进度: ${",[97,1839,1840],{"class":106},"percentCompleted",[97,1842,1843],{"class":110},"}%`",[97,1845,367],{"class":106},[97,1847,1848],{"class":99,"line":1451},[97,1849,1850],{"class":373},"      \u002F\u002F 更新你的响应式状态\n",[97,1852,1853],{"class":99,"line":1457},[97,1854,565],{"class":106},[97,1856,1858],{"class":99,"line":1857},17,[97,1859,1716],{"class":106},[97,1861,1863],{"class":99,"line":1862},18,[97,1864,450],{"class":106},[10,1866,1868],{"id":1867},"cookie扩展工具","Cookie扩展工具",[15,1870,1871,1872,1875],{},"useCookie是nuxt提供的SSR友好类型的Cookie管理工具，他可以在浏览器和服务器之间同步Cookie，就算用户刷新页面或者关闭浏览器，数据依然是存在的，这个是SSR安全的，",[26,1873,1874],{},"浏览器端渲染的时候","，他可以自动去读请求头里的Cookie，在客户端又可以通过Js去读写。",[15,1877,1878],{},"我们会在存储用户的token和存储用户的个性化配置的时候使用。",[1880,1881,1882],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":93,"searchDepth":370,"depth":370,"links":1884},[1885,1888,1891,1899,1903,1904],{"id":12,"depth":370,"text":13,"children":1886},[1887],{"id":66,"depth":447,"text":67},{"id":191,"depth":370,"text":191,"children":1889},[1890],{"id":194,"depth":447,"text":194},{"id":274,"depth":370,"text":274,"children":1892},[1893,1894,1895,1896,1897,1898],{"id":309,"depth":447,"text":309},{"id":315,"depth":447,"text":315},{"id":321,"depth":447,"text":321},{"id":377,"depth":447,"text":378},{"id":453,"depth":447,"text":454},{"id":472,"depth":447,"text":472},{"id":672,"depth":370,"text":672,"children":1900},[1901,1902],{"id":675,"depth":447,"text":676},{"id":857,"depth":447,"text":857},{"id":1342,"depth":370,"text":1343},{"id":1867,"depth":370,"text":1868},"md",{},"\u002Fvue\u002Fnuxt\u002Fnuxt4",{"description":93},"Vue\u002FNuxt\u002FNuxt4","q3gBSdYhVw08fPBWsjYgpC9hYwrRa3PgQCLPDCpWzFM",1791042467638]