[{"data":1,"prerenderedAt":5182},["ShallowReactive",2],{"home-posts":3},[4,2674,5128],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"date":11,"body":12,"_type":2667,"_id":2668,"_source":2669,"_file":2670,"_stem":2671,"_extension":2672,"code":2673},"\u002Fblog\u002F3-post","blog",false,"","Nuxt 4 博客重构实战：实现动态文章目录与阅读进度条","使用 Intersection Observer 与 requestAnimationFrame 优化长文阅读体验","2026-06-17",{"type":13,"children":14,"toc":2656},"root",[15,23,37,56,63,68,75,103,994,1000,1020,1528,1570,1576,1581,1587,1607,2221,2227,2240,2520,2528,2567,2573,2585,2630,2646,2651],{"type":16,"tag":17,"props":18,"children":20},"element","h1",{"id":19},"nuxt-4-博客重构实战实现动态文章目录与阅读进度条",[21],{"type":22,"value":9},"text",{"type":16,"tag":24,"props":25,"children":26},"p",{},[27,29,35],{"type":22,"value":28},"在技术博客的重构过程中，内容固然重要，但",{"type":16,"tag":30,"props":31,"children":32},"strong",{},[33],{"type":22,"value":34},"阅读体验",{"type":22,"value":36},"往往决定了读者能否耐心看完一篇长文。作为一个前端开发者，我始终认为博客不仅是内容的载体，更是前端技术的试验田。",{"type":16,"tag":24,"props":38,"children":39},{},[40,42,54],{"type":22,"value":41},"在将博客迁移到 Nuxt 4 的过程中，我决定为文章页加入两个核心体验优化：",{"type":16,"tag":30,"props":43,"children":44},{},[45,47,52],{"type":22,"value":46},"动态文章目录（TOC）",{"type":16,"tag":30,"props":48,"children":49},{},[50],{"type":22,"value":51},"和",{"type":22,"value":53},"沉浸式阅读进度条",{"type":22,"value":55},"。本文将记录这两个功能的实现思路，以及在 SSR 环境下遇到的一些坑。",{"type":16,"tag":57,"props":58,"children":60},"h2",{"id":59},"一-动态文章目录toc的实现",[61],{"type":22,"value":62},"一、 动态文章目录（TOC）的实现",{"type":16,"tag":24,"props":64,"children":65},{},[66],{"type":22,"value":67},"对于包含大量代码和层级标题的技术文章，侧边目录是刚需。我们需要实现两个功能：解析标题生成目录、滚动时高亮当前章节。",{"type":16,"tag":69,"props":70,"children":72},"h3",{"id":71},"_1-提取文章标题",[73],{"type":22,"value":74},"1. 提取文章标题",{"type":16,"tag":24,"props":76,"children":77},{},[78,80,87,89,94,96,101],{"type":22,"value":79},"在 Nuxt 中，Markdown 渲染后通常会包裹在一个特定的容器内（例如 ",{"type":16,"tag":81,"props":82,"children":84},"code",{"className":83},[],[85],{"type":22,"value":86},".article-content",{"type":22,"value":88},"）。我们可以在组件挂载后，通过 DOM 查询提取所有的 ",{"type":16,"tag":81,"props":90,"children":92},{"className":91},[],[93],{"type":22,"value":57},{"type":22,"value":95}," 和 ",{"type":16,"tag":81,"props":97,"children":99},{"className":98},[],[100],{"type":22,"value":69},{"type":22,"value":102}," 标签。",{"type":16,"tag":104,"props":105,"children":109},"pre",{"className":106,"code":107,"language":108,"meta":8,"style":8},"language-typescript shiki shiki-themes github-light github-dark","\u002F\u002F composables\u002FuseToc.ts\nimport { ref, onMounted, onUnmounted } from 'vue'\n\ninterface TocItem {\n  id: string\n  text: string\n  level: number\n}\n\nexport function useToc(containerSelector = '.article-content') {\n  const headings = ref\u003CTocItem[]>([])\n  const activeId = ref('')\n  let observer: IntersectionObserver | null = null\n\n  const generateToc = () => {\n    \u002F\u002F 仅在客户端执行 DOM 操作\n    if (import.meta.server) return\n    \n    const container = document.querySelector(containerSelector)\n    if (!container) return\n\n    const elements = container.querySelectorAll('h2, h3')\n    headings.value = Array.from(elements).map((el) => ({\n      id: el.id || `heading-${Math.random().toString(36).substr(2, 9)}`,\n      text: el.textContent || '',\n      level: parseInt(el.tagName.substring(1)),\n    }))\n\n    \u002F\u002F 确保标题有 id，方便锚点跳转\n    elements.forEach((el, index) => {\n      if (!el.id) el.id = headings.value[index].id\n    })\n    \n    initObserver()\n  }\n\n  \u002F\u002F ... 初始化 IntersectionObserver 的逻辑\n}\n","typescript",[110],{"type":16,"tag":81,"props":111,"children":112},{"__ignoreMap":8},[113,125,152,162,182,203,220,238,247,255,299,337,372,414,422,453,462,500,509,542,568,576,616,673,769,791,829,838,846,855,898,929,938,946,960,969,977,986],{"type":16,"tag":114,"props":115,"children":118},"span",{"class":116,"line":117},"line",1,[119],{"type":16,"tag":114,"props":120,"children":122},{"style":121},"--shiki-default:#6A737D;--shiki-dark:#6A737D",[123],{"type":22,"value":124},"\u002F\u002F composables\u002FuseToc.ts\n",{"type":16,"tag":114,"props":126,"children":128},{"class":116,"line":127},2,[129,135,141,146],{"type":16,"tag":114,"props":130,"children":132},{"style":131},"--shiki-default:#D73A49;--shiki-dark:#F97583",[133],{"type":22,"value":134},"import",{"type":16,"tag":114,"props":136,"children":138},{"style":137},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[139],{"type":22,"value":140}," { ref, onMounted, onUnmounted } ",{"type":16,"tag":114,"props":142,"children":143},{"style":131},[144],{"type":22,"value":145},"from",{"type":16,"tag":114,"props":147,"children":149},{"style":148},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[150],{"type":22,"value":151}," 'vue'\n",{"type":16,"tag":114,"props":153,"children":155},{"class":116,"line":154},3,[156],{"type":16,"tag":114,"props":157,"children":159},{"emptyLinePlaceholder":158},true,[160],{"type":22,"value":161},"\n",{"type":16,"tag":114,"props":163,"children":165},{"class":116,"line":164},4,[166,171,177],{"type":16,"tag":114,"props":167,"children":168},{"style":131},[169],{"type":22,"value":170},"interface",{"type":16,"tag":114,"props":172,"children":174},{"style":173},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[175],{"type":22,"value":176}," TocItem",{"type":16,"tag":114,"props":178,"children":179},{"style":137},[180],{"type":22,"value":181}," {\n",{"type":16,"tag":114,"props":183,"children":185},{"class":116,"line":184},5,[186,192,197],{"type":16,"tag":114,"props":187,"children":189},{"style":188},"--shiki-default:#E36209;--shiki-dark:#FFAB70",[190],{"type":22,"value":191},"  id",{"type":16,"tag":114,"props":193,"children":194},{"style":131},[195],{"type":22,"value":196},":",{"type":16,"tag":114,"props":198,"children":200},{"style":199},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[201],{"type":22,"value":202}," string\n",{"type":16,"tag":114,"props":204,"children":206},{"class":116,"line":205},6,[207,212,216],{"type":16,"tag":114,"props":208,"children":209},{"style":188},[210],{"type":22,"value":211},"  text",{"type":16,"tag":114,"props":213,"children":214},{"style":131},[215],{"type":22,"value":196},{"type":16,"tag":114,"props":217,"children":218},{"style":199},[219],{"type":22,"value":202},{"type":16,"tag":114,"props":221,"children":223},{"class":116,"line":222},7,[224,229,233],{"type":16,"tag":114,"props":225,"children":226},{"style":188},[227],{"type":22,"value":228},"  level",{"type":16,"tag":114,"props":230,"children":231},{"style":131},[232],{"type":22,"value":196},{"type":16,"tag":114,"props":234,"children":235},{"style":199},[236],{"type":22,"value":237}," number\n",{"type":16,"tag":114,"props":239,"children":241},{"class":116,"line":240},8,[242],{"type":16,"tag":114,"props":243,"children":244},{"style":137},[245],{"type":22,"value":246},"}\n",{"type":16,"tag":114,"props":248,"children":250},{"class":116,"line":249},9,[251],{"type":16,"tag":114,"props":252,"children":253},{"emptyLinePlaceholder":158},[254],{"type":22,"value":161},{"type":16,"tag":114,"props":256,"children":258},{"class":116,"line":257},10,[259,264,269,274,279,284,289,294],{"type":16,"tag":114,"props":260,"children":261},{"style":131},[262],{"type":22,"value":263},"export",{"type":16,"tag":114,"props":265,"children":266},{"style":131},[267],{"type":22,"value":268}," function",{"type":16,"tag":114,"props":270,"children":271},{"style":173},[272],{"type":22,"value":273}," useToc",{"type":16,"tag":114,"props":275,"children":276},{"style":137},[277],{"type":22,"value":278},"(",{"type":16,"tag":114,"props":280,"children":281},{"style":188},[282],{"type":22,"value":283},"containerSelector",{"type":16,"tag":114,"props":285,"children":286},{"style":131},[287],{"type":22,"value":288}," =",{"type":16,"tag":114,"props":290,"children":291},{"style":148},[292],{"type":22,"value":293}," '.article-content'",{"type":16,"tag":114,"props":295,"children":296},{"style":137},[297],{"type":22,"value":298},") {\n",{"type":16,"tag":114,"props":300,"children":302},{"class":116,"line":301},11,[303,308,313,317,322,327,332],{"type":16,"tag":114,"props":304,"children":305},{"style":131},[306],{"type":22,"value":307},"  const",{"type":16,"tag":114,"props":309,"children":310},{"style":199},[311],{"type":22,"value":312}," headings",{"type":16,"tag":114,"props":314,"children":315},{"style":131},[316],{"type":22,"value":288},{"type":16,"tag":114,"props":318,"children":319},{"style":173},[320],{"type":22,"value":321}," ref",{"type":16,"tag":114,"props":323,"children":324},{"style":137},[325],{"type":22,"value":326},"\u003C",{"type":16,"tag":114,"props":328,"children":329},{"style":173},[330],{"type":22,"value":331},"TocItem",{"type":16,"tag":114,"props":333,"children":334},{"style":137},[335],{"type":22,"value":336},"[]>([])\n",{"type":16,"tag":114,"props":338,"children":340},{"class":116,"line":339},12,[341,345,350,354,358,362,367],{"type":16,"tag":114,"props":342,"children":343},{"style":131},[344],{"type":22,"value":307},{"type":16,"tag":114,"props":346,"children":347},{"style":199},[348],{"type":22,"value":349}," activeId",{"type":16,"tag":114,"props":351,"children":352},{"style":131},[353],{"type":22,"value":288},{"type":16,"tag":114,"props":355,"children":356},{"style":173},[357],{"type":22,"value":321},{"type":16,"tag":114,"props":359,"children":360},{"style":137},[361],{"type":22,"value":278},{"type":16,"tag":114,"props":363,"children":364},{"style":148},[365],{"type":22,"value":366},"''",{"type":16,"tag":114,"props":368,"children":369},{"style":137},[370],{"type":22,"value":371},")\n",{"type":16,"tag":114,"props":373,"children":375},{"class":116,"line":374},13,[376,381,386,390,395,400,405,409],{"type":16,"tag":114,"props":377,"children":378},{"style":131},[379],{"type":22,"value":380},"  let",{"type":16,"tag":114,"props":382,"children":383},{"style":137},[384],{"type":22,"value":385}," observer",{"type":16,"tag":114,"props":387,"children":388},{"style":131},[389],{"type":22,"value":196},{"type":16,"tag":114,"props":391,"children":392},{"style":173},[393],{"type":22,"value":394}," IntersectionObserver",{"type":16,"tag":114,"props":396,"children":397},{"style":131},[398],{"type":22,"value":399}," |",{"type":16,"tag":114,"props":401,"children":402},{"style":199},[403],{"type":22,"value":404}," null",{"type":16,"tag":114,"props":406,"children":407},{"style":131},[408],{"type":22,"value":288},{"type":16,"tag":114,"props":410,"children":411},{"style":199},[412],{"type":22,"value":413}," null\n",{"type":16,"tag":114,"props":415,"children":417},{"class":116,"line":416},14,[418],{"type":16,"tag":114,"props":419,"children":420},{"emptyLinePlaceholder":158},[421],{"type":22,"value":161},{"type":16,"tag":114,"props":423,"children":425},{"class":116,"line":424},15,[426,430,435,439,444,449],{"type":16,"tag":114,"props":427,"children":428},{"style":131},[429],{"type":22,"value":307},{"type":16,"tag":114,"props":431,"children":432},{"style":173},[433],{"type":22,"value":434}," generateToc",{"type":16,"tag":114,"props":436,"children":437},{"style":131},[438],{"type":22,"value":288},{"type":16,"tag":114,"props":440,"children":441},{"style":137},[442],{"type":22,"value":443}," () ",{"type":16,"tag":114,"props":445,"children":446},{"style":131},[447],{"type":22,"value":448},"=>",{"type":16,"tag":114,"props":450,"children":451},{"style":137},[452],{"type":22,"value":181},{"type":16,"tag":114,"props":454,"children":456},{"class":116,"line":455},16,[457],{"type":16,"tag":114,"props":458,"children":459},{"style":121},[460],{"type":22,"value":461},"    \u002F\u002F 仅在客户端执行 DOM 操作\n",{"type":16,"tag":114,"props":463,"children":465},{"class":116,"line":464},17,[466,471,476,480,485,490,495],{"type":16,"tag":114,"props":467,"children":468},{"style":131},[469],{"type":22,"value":470},"    if",{"type":16,"tag":114,"props":472,"children":473},{"style":137},[474],{"type":22,"value":475}," (",{"type":16,"tag":114,"props":477,"children":478},{"style":131},[479],{"type":22,"value":134},{"type":16,"tag":114,"props":481,"children":482},{"style":137},[483],{"type":22,"value":484},".",{"type":16,"tag":114,"props":486,"children":487},{"style":199},[488],{"type":22,"value":489},"meta",{"type":16,"tag":114,"props":491,"children":492},{"style":137},[493],{"type":22,"value":494},".server) ",{"type":16,"tag":114,"props":496,"children":497},{"style":131},[498],{"type":22,"value":499},"return\n",{"type":16,"tag":114,"props":501,"children":503},{"class":116,"line":502},18,[504],{"type":16,"tag":114,"props":505,"children":506},{"style":137},[507],{"type":22,"value":508},"    \n",{"type":16,"tag":114,"props":510,"children":512},{"class":116,"line":511},19,[513,518,523,527,532,537],{"type":16,"tag":114,"props":514,"children":515},{"style":131},[516],{"type":22,"value":517},"    const",{"type":16,"tag":114,"props":519,"children":520},{"style":199},[521],{"type":22,"value":522}," container",{"type":16,"tag":114,"props":524,"children":525},{"style":131},[526],{"type":22,"value":288},{"type":16,"tag":114,"props":528,"children":529},{"style":137},[530],{"type":22,"value":531}," document.",{"type":16,"tag":114,"props":533,"children":534},{"style":173},[535],{"type":22,"value":536},"querySelector",{"type":16,"tag":114,"props":538,"children":539},{"style":137},[540],{"type":22,"value":541},"(containerSelector)\n",{"type":16,"tag":114,"props":543,"children":545},{"class":116,"line":544},20,[546,550,554,559,564],{"type":16,"tag":114,"props":547,"children":548},{"style":131},[549],{"type":22,"value":470},{"type":16,"tag":114,"props":551,"children":552},{"style":137},[553],{"type":22,"value":475},{"type":16,"tag":114,"props":555,"children":556},{"style":131},[557],{"type":22,"value":558},"!",{"type":16,"tag":114,"props":560,"children":561},{"style":137},[562],{"type":22,"value":563},"container) ",{"type":16,"tag":114,"props":565,"children":566},{"style":131},[567],{"type":22,"value":499},{"type":16,"tag":114,"props":569,"children":571},{"class":116,"line":570},21,[572],{"type":16,"tag":114,"props":573,"children":574},{"emptyLinePlaceholder":158},[575],{"type":22,"value":161},{"type":16,"tag":114,"props":577,"children":579},{"class":116,"line":578},22,[580,584,589,593,598,603,607,612],{"type":16,"tag":114,"props":581,"children":582},{"style":131},[583],{"type":22,"value":517},{"type":16,"tag":114,"props":585,"children":586},{"style":199},[587],{"type":22,"value":588}," elements",{"type":16,"tag":114,"props":590,"children":591},{"style":131},[592],{"type":22,"value":288},{"type":16,"tag":114,"props":594,"children":595},{"style":137},[596],{"type":22,"value":597}," container.",{"type":16,"tag":114,"props":599,"children":600},{"style":173},[601],{"type":22,"value":602},"querySelectorAll",{"type":16,"tag":114,"props":604,"children":605},{"style":137},[606],{"type":22,"value":278},{"type":16,"tag":114,"props":608,"children":609},{"style":148},[610],{"type":22,"value":611},"'h2, h3'",{"type":16,"tag":114,"props":613,"children":614},{"style":137},[615],{"type":22,"value":371},{"type":16,"tag":114,"props":617,"children":619},{"class":116,"line":618},23,[620,625,630,635,639,644,649,654,659,664,668],{"type":16,"tag":114,"props":621,"children":622},{"style":137},[623],{"type":22,"value":624},"    headings.value ",{"type":16,"tag":114,"props":626,"children":627},{"style":131},[628],{"type":22,"value":629},"=",{"type":16,"tag":114,"props":631,"children":632},{"style":137},[633],{"type":22,"value":634}," Array.",{"type":16,"tag":114,"props":636,"children":637},{"style":173},[638],{"type":22,"value":145},{"type":16,"tag":114,"props":640,"children":641},{"style":137},[642],{"type":22,"value":643},"(elements).",{"type":16,"tag":114,"props":645,"children":646},{"style":173},[647],{"type":22,"value":648},"map",{"type":16,"tag":114,"props":650,"children":651},{"style":137},[652],{"type":22,"value":653},"((",{"type":16,"tag":114,"props":655,"children":656},{"style":188},[657],{"type":22,"value":658},"el",{"type":16,"tag":114,"props":660,"children":661},{"style":137},[662],{"type":22,"value":663},") ",{"type":16,"tag":114,"props":665,"children":666},{"style":131},[667],{"type":22,"value":448},{"type":16,"tag":114,"props":669,"children":670},{"style":137},[671],{"type":22,"value":672}," ({\n",{"type":16,"tag":114,"props":674,"children":676},{"class":116,"line":675},24,[677,682,687,692,697,701,706,711,716,720,725,730,735,739,744,749,754,759,764],{"type":16,"tag":114,"props":678,"children":679},{"style":137},[680],{"type":22,"value":681},"      id: el.id ",{"type":16,"tag":114,"props":683,"children":684},{"style":131},[685],{"type":22,"value":686},"||",{"type":16,"tag":114,"props":688,"children":689},{"style":148},[690],{"type":22,"value":691}," `heading-${",{"type":16,"tag":114,"props":693,"children":694},{"style":137},[695],{"type":22,"value":696},"Math",{"type":16,"tag":114,"props":698,"children":699},{"style":148},[700],{"type":22,"value":484},{"type":16,"tag":114,"props":702,"children":703},{"style":173},[704],{"type":22,"value":705},"random",{"type":16,"tag":114,"props":707,"children":708},{"style":148},[709],{"type":22,"value":710},"().",{"type":16,"tag":114,"props":712,"children":713},{"style":173},[714],{"type":22,"value":715},"toString",{"type":16,"tag":114,"props":717,"children":718},{"style":148},[719],{"type":22,"value":278},{"type":16,"tag":114,"props":721,"children":722},{"style":199},[723],{"type":22,"value":724},"36",{"type":16,"tag":114,"props":726,"children":727},{"style":148},[728],{"type":22,"value":729},").",{"type":16,"tag":114,"props":731,"children":732},{"style":173},[733],{"type":22,"value":734},"substr",{"type":16,"tag":114,"props":736,"children":737},{"style":148},[738],{"type":22,"value":278},{"type":16,"tag":114,"props":740,"children":741},{"style":199},[742],{"type":22,"value":743},"2",{"type":16,"tag":114,"props":745,"children":746},{"style":148},[747],{"type":22,"value":748},", ",{"type":16,"tag":114,"props":750,"children":751},{"style":199},[752],{"type":22,"value":753},"9",{"type":16,"tag":114,"props":755,"children":756},{"style":148},[757],{"type":22,"value":758},")",{"type":16,"tag":114,"props":760,"children":761},{"style":148},[762],{"type":22,"value":763},"}`",{"type":16,"tag":114,"props":765,"children":766},{"style":137},[767],{"type":22,"value":768},",\n",{"type":16,"tag":114,"props":770,"children":772},{"class":116,"line":771},25,[773,778,782,787],{"type":16,"tag":114,"props":774,"children":775},{"style":137},[776],{"type":22,"value":777},"      text: el.textContent ",{"type":16,"tag":114,"props":779,"children":780},{"style":131},[781],{"type":22,"value":686},{"type":16,"tag":114,"props":783,"children":784},{"style":148},[785],{"type":22,"value":786}," ''",{"type":16,"tag":114,"props":788,"children":789},{"style":137},[790],{"type":22,"value":768},{"type":16,"tag":114,"props":792,"children":794},{"class":116,"line":793},26,[795,800,805,810,815,819,824],{"type":16,"tag":114,"props":796,"children":797},{"style":137},[798],{"type":22,"value":799},"      level: ",{"type":16,"tag":114,"props":801,"children":802},{"style":173},[803],{"type":22,"value":804},"parseInt",{"type":16,"tag":114,"props":806,"children":807},{"style":137},[808],{"type":22,"value":809},"(el.tagName.",{"type":16,"tag":114,"props":811,"children":812},{"style":173},[813],{"type":22,"value":814},"substring",{"type":16,"tag":114,"props":816,"children":817},{"style":137},[818],{"type":22,"value":278},{"type":16,"tag":114,"props":820,"children":821},{"style":199},[822],{"type":22,"value":823},"1",{"type":16,"tag":114,"props":825,"children":826},{"style":137},[827],{"type":22,"value":828},")),\n",{"type":16,"tag":114,"props":830,"children":832},{"class":116,"line":831},27,[833],{"type":16,"tag":114,"props":834,"children":835},{"style":137},[836],{"type":22,"value":837},"    }))\n",{"type":16,"tag":114,"props":839,"children":841},{"class":116,"line":840},28,[842],{"type":16,"tag":114,"props":843,"children":844},{"emptyLinePlaceholder":158},[845],{"type":22,"value":161},{"type":16,"tag":114,"props":847,"children":849},{"class":116,"line":848},29,[850],{"type":16,"tag":114,"props":851,"children":852},{"style":121},[853],{"type":22,"value":854},"    \u002F\u002F 确保标题有 id，方便锚点跳转\n",{"type":16,"tag":114,"props":856,"children":858},{"class":116,"line":857},30,[859,864,869,873,877,881,886,890,894],{"type":16,"tag":114,"props":860,"children":861},{"style":137},[862],{"type":22,"value":863},"    elements.",{"type":16,"tag":114,"props":865,"children":866},{"style":173},[867],{"type":22,"value":868},"forEach",{"type":16,"tag":114,"props":870,"children":871},{"style":137},[872],{"type":22,"value":653},{"type":16,"tag":114,"props":874,"children":875},{"style":188},[876],{"type":22,"value":658},{"type":16,"tag":114,"props":878,"children":879},{"style":137},[880],{"type":22,"value":748},{"type":16,"tag":114,"props":882,"children":883},{"style":188},[884],{"type":22,"value":885},"index",{"type":16,"tag":114,"props":887,"children":888},{"style":137},[889],{"type":22,"value":663},{"type":16,"tag":114,"props":891,"children":892},{"style":131},[893],{"type":22,"value":448},{"type":16,"tag":114,"props":895,"children":896},{"style":137},[897],{"type":22,"value":181},{"type":16,"tag":114,"props":899,"children":901},{"class":116,"line":900},31,[902,907,911,915,920,924],{"type":16,"tag":114,"props":903,"children":904},{"style":131},[905],{"type":22,"value":906},"      if",{"type":16,"tag":114,"props":908,"children":909},{"style":137},[910],{"type":22,"value":475},{"type":16,"tag":114,"props":912,"children":913},{"style":131},[914],{"type":22,"value":558},{"type":16,"tag":114,"props":916,"children":917},{"style":137},[918],{"type":22,"value":919},"el.id) el.id ",{"type":16,"tag":114,"props":921,"children":922},{"style":131},[923],{"type":22,"value":629},{"type":16,"tag":114,"props":925,"children":926},{"style":137},[927],{"type":22,"value":928}," headings.value[index].id\n",{"type":16,"tag":114,"props":930,"children":932},{"class":116,"line":931},32,[933],{"type":16,"tag":114,"props":934,"children":935},{"style":137},[936],{"type":22,"value":937},"    })\n",{"type":16,"tag":114,"props":939,"children":941},{"class":116,"line":940},33,[942],{"type":16,"tag":114,"props":943,"children":944},{"style":137},[945],{"type":22,"value":508},{"type":16,"tag":114,"props":947,"children":949},{"class":116,"line":948},34,[950,955],{"type":16,"tag":114,"props":951,"children":952},{"style":173},[953],{"type":22,"value":954},"    initObserver",{"type":16,"tag":114,"props":956,"children":957},{"style":137},[958],{"type":22,"value":959},"()\n",{"type":16,"tag":114,"props":961,"children":963},{"class":116,"line":962},35,[964],{"type":16,"tag":114,"props":965,"children":966},{"style":137},[967],{"type":22,"value":968},"  }\n",{"type":16,"tag":114,"props":970,"children":972},{"class":116,"line":971},36,[973],{"type":16,"tag":114,"props":974,"children":975},{"emptyLinePlaceholder":158},[976],{"type":22,"value":161},{"type":16,"tag":114,"props":978,"children":980},{"class":116,"line":979},37,[981],{"type":16,"tag":114,"props":982,"children":983},{"style":121},[984],{"type":22,"value":985},"  \u002F\u002F ... 初始化 IntersectionObserver 的逻辑\n",{"type":16,"tag":114,"props":987,"children":989},{"class":116,"line":988},38,[990],{"type":16,"tag":114,"props":991,"children":992},{"style":137},[993],{"type":22,"value":246},{"type":16,"tag":69,"props":995,"children":997},{"id":996},"_2-滚动高亮当前章节",[998],{"type":22,"value":999},"2. 滚动高亮当前章节",{"type":16,"tag":24,"props":1001,"children":1002},{},[1003,1005,1011,1013,1018],{"type":22,"value":1004},"早期我使用监听 ",{"type":16,"tag":81,"props":1006,"children":1008},{"className":1007},[],[1009],{"type":22,"value":1010},"scroll",{"type":22,"value":1012}," 事件来计算标题位置，但在长文章中会导致严重的性能问题。后来改用 ",{"type":16,"tag":30,"props":1014,"children":1015},{},[1016],{"type":22,"value":1017},"Intersection Observer API",{"type":22,"value":1019},"，由浏览器底层异步监听，性能极佳。",{"type":16,"tag":104,"props":1021,"children":1023},{"className":106,"code":1022,"language":108,"meta":8,"style":8},"  const initObserver = () => {\n    if (observer) observer.disconnect()\n\n    observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          \u002F\u002F 当标题进入视口时，更新激活状态\n          if (entry.isIntersecting) {\n            activeId.value = entry.target.id\n          }\n        })\n      },\n      {\n        \u002F\u002F 调整根容器的边距，让标题在滚动到视口顶部 20% 时就触发高亮\n        rootMargin: '-20% 0px -80% 0px', \n        threshold: 0,\n      }\n    )\n\n    headings.value.forEach((item) => {\n      const el = document.getElementById(item.id)\n      if (el) observer?.observe(el)\n    })\n  }\n\n  onMounted(() => {\n    generateToc()\n  })\n\n  onUnmounted(() => {\n    observer?.disconnect()\n  })\n\n  return { headings, activeId }\n}\n",[1024],{"type":16,"tag":81,"props":1025,"children":1026},{"__ignoreMap":8},[1027,1055,1076,1083,1109,1134,1167,1175,1188,1205,1213,1221,1229,1237,1245,1263,1280,1288,1296,1303,1336,1367,1389,1396,1403,1410,1431,1443,1451,1458,1478,1494,1501,1508,1521],{"type":16,"tag":114,"props":1028,"children":1029},{"class":116,"line":117},[1030,1034,1039,1043,1047,1051],{"type":16,"tag":114,"props":1031,"children":1032},{"style":131},[1033],{"type":22,"value":307},{"type":16,"tag":114,"props":1035,"children":1036},{"style":173},[1037],{"type":22,"value":1038}," initObserver",{"type":16,"tag":114,"props":1040,"children":1041},{"style":131},[1042],{"type":22,"value":288},{"type":16,"tag":114,"props":1044,"children":1045},{"style":137},[1046],{"type":22,"value":443},{"type":16,"tag":114,"props":1048,"children":1049},{"style":131},[1050],{"type":22,"value":448},{"type":16,"tag":114,"props":1052,"children":1053},{"style":137},[1054],{"type":22,"value":181},{"type":16,"tag":114,"props":1056,"children":1057},{"class":116,"line":127},[1058,1062,1067,1072],{"type":16,"tag":114,"props":1059,"children":1060},{"style":131},[1061],{"type":22,"value":470},{"type":16,"tag":114,"props":1063,"children":1064},{"style":137},[1065],{"type":22,"value":1066}," (observer) observer.",{"type":16,"tag":114,"props":1068,"children":1069},{"style":173},[1070],{"type":22,"value":1071},"disconnect",{"type":16,"tag":114,"props":1073,"children":1074},{"style":137},[1075],{"type":22,"value":959},{"type":16,"tag":114,"props":1077,"children":1078},{"class":116,"line":154},[1079],{"type":16,"tag":114,"props":1080,"children":1081},{"emptyLinePlaceholder":158},[1082],{"type":22,"value":161},{"type":16,"tag":114,"props":1084,"children":1085},{"class":116,"line":164},[1086,1091,1095,1100,1104],{"type":16,"tag":114,"props":1087,"children":1088},{"style":137},[1089],{"type":22,"value":1090},"    observer ",{"type":16,"tag":114,"props":1092,"children":1093},{"style":131},[1094],{"type":22,"value":629},{"type":16,"tag":114,"props":1096,"children":1097},{"style":131},[1098],{"type":22,"value":1099}," new",{"type":16,"tag":114,"props":1101,"children":1102},{"style":173},[1103],{"type":22,"value":394},{"type":16,"tag":114,"props":1105,"children":1106},{"style":137},[1107],{"type":22,"value":1108},"(\n",{"type":16,"tag":114,"props":1110,"children":1111},{"class":116,"line":184},[1112,1117,1122,1126,1130],{"type":16,"tag":114,"props":1113,"children":1114},{"style":137},[1115],{"type":22,"value":1116},"      (",{"type":16,"tag":114,"props":1118,"children":1119},{"style":188},[1120],{"type":22,"value":1121},"entries",{"type":16,"tag":114,"props":1123,"children":1124},{"style":137},[1125],{"type":22,"value":663},{"type":16,"tag":114,"props":1127,"children":1128},{"style":131},[1129],{"type":22,"value":448},{"type":16,"tag":114,"props":1131,"children":1132},{"style":137},[1133],{"type":22,"value":181},{"type":16,"tag":114,"props":1135,"children":1136},{"class":116,"line":205},[1137,1142,1146,1150,1155,1159,1163],{"type":16,"tag":114,"props":1138,"children":1139},{"style":137},[1140],{"type":22,"value":1141},"        entries.",{"type":16,"tag":114,"props":1143,"children":1144},{"style":173},[1145],{"type":22,"value":868},{"type":16,"tag":114,"props":1147,"children":1148},{"style":137},[1149],{"type":22,"value":653},{"type":16,"tag":114,"props":1151,"children":1152},{"style":188},[1153],{"type":22,"value":1154},"entry",{"type":16,"tag":114,"props":1156,"children":1157},{"style":137},[1158],{"type":22,"value":663},{"type":16,"tag":114,"props":1160,"children":1161},{"style":131},[1162],{"type":22,"value":448},{"type":16,"tag":114,"props":1164,"children":1165},{"style":137},[1166],{"type":22,"value":181},{"type":16,"tag":114,"props":1168,"children":1169},{"class":116,"line":222},[1170],{"type":16,"tag":114,"props":1171,"children":1172},{"style":121},[1173],{"type":22,"value":1174},"          \u002F\u002F 当标题进入视口时，更新激活状态\n",{"type":16,"tag":114,"props":1176,"children":1177},{"class":116,"line":240},[1178,1183],{"type":16,"tag":114,"props":1179,"children":1180},{"style":131},[1181],{"type":22,"value":1182},"          if",{"type":16,"tag":114,"props":1184,"children":1185},{"style":137},[1186],{"type":22,"value":1187}," (entry.isIntersecting) {\n",{"type":16,"tag":114,"props":1189,"children":1190},{"class":116,"line":249},[1191,1196,1200],{"type":16,"tag":114,"props":1192,"children":1193},{"style":137},[1194],{"type":22,"value":1195},"            activeId.value ",{"type":16,"tag":114,"props":1197,"children":1198},{"style":131},[1199],{"type":22,"value":629},{"type":16,"tag":114,"props":1201,"children":1202},{"style":137},[1203],{"type":22,"value":1204}," entry.target.id\n",{"type":16,"tag":114,"props":1206,"children":1207},{"class":116,"line":257},[1208],{"type":16,"tag":114,"props":1209,"children":1210},{"style":137},[1211],{"type":22,"value":1212},"          }\n",{"type":16,"tag":114,"props":1214,"children":1215},{"class":116,"line":301},[1216],{"type":16,"tag":114,"props":1217,"children":1218},{"style":137},[1219],{"type":22,"value":1220},"        })\n",{"type":16,"tag":114,"props":1222,"children":1223},{"class":116,"line":339},[1224],{"type":16,"tag":114,"props":1225,"children":1226},{"style":137},[1227],{"type":22,"value":1228},"      },\n",{"type":16,"tag":114,"props":1230,"children":1231},{"class":116,"line":374},[1232],{"type":16,"tag":114,"props":1233,"children":1234},{"style":137},[1235],{"type":22,"value":1236},"      {\n",{"type":16,"tag":114,"props":1238,"children":1239},{"class":116,"line":416},[1240],{"type":16,"tag":114,"props":1241,"children":1242},{"style":121},[1243],{"type":22,"value":1244},"        \u002F\u002F 调整根容器的边距，让标题在滚动到视口顶部 20% 时就触发高亮\n",{"type":16,"tag":114,"props":1246,"children":1247},{"class":116,"line":424},[1248,1253,1258],{"type":16,"tag":114,"props":1249,"children":1250},{"style":137},[1251],{"type":22,"value":1252},"        rootMargin: ",{"type":16,"tag":114,"props":1254,"children":1255},{"style":148},[1256],{"type":22,"value":1257},"'-20% 0px -80% 0px'",{"type":16,"tag":114,"props":1259,"children":1260},{"style":137},[1261],{"type":22,"value":1262},", \n",{"type":16,"tag":114,"props":1264,"children":1265},{"class":116,"line":455},[1266,1271,1276],{"type":16,"tag":114,"props":1267,"children":1268},{"style":137},[1269],{"type":22,"value":1270},"        threshold: ",{"type":16,"tag":114,"props":1272,"children":1273},{"style":199},[1274],{"type":22,"value":1275},"0",{"type":16,"tag":114,"props":1277,"children":1278},{"style":137},[1279],{"type":22,"value":768},{"type":16,"tag":114,"props":1281,"children":1282},{"class":116,"line":464},[1283],{"type":16,"tag":114,"props":1284,"children":1285},{"style":137},[1286],{"type":22,"value":1287},"      }\n",{"type":16,"tag":114,"props":1289,"children":1290},{"class":116,"line":502},[1291],{"type":16,"tag":114,"props":1292,"children":1293},{"style":137},[1294],{"type":22,"value":1295},"    )\n",{"type":16,"tag":114,"props":1297,"children":1298},{"class":116,"line":511},[1299],{"type":16,"tag":114,"props":1300,"children":1301},{"emptyLinePlaceholder":158},[1302],{"type":22,"value":161},{"type":16,"tag":114,"props":1304,"children":1305},{"class":116,"line":544},[1306,1311,1315,1319,1324,1328,1332],{"type":16,"tag":114,"props":1307,"children":1308},{"style":137},[1309],{"type":22,"value":1310},"    headings.value.",{"type":16,"tag":114,"props":1312,"children":1313},{"style":173},[1314],{"type":22,"value":868},{"type":16,"tag":114,"props":1316,"children":1317},{"style":137},[1318],{"type":22,"value":653},{"type":16,"tag":114,"props":1320,"children":1321},{"style":188},[1322],{"type":22,"value":1323},"item",{"type":16,"tag":114,"props":1325,"children":1326},{"style":137},[1327],{"type":22,"value":663},{"type":16,"tag":114,"props":1329,"children":1330},{"style":131},[1331],{"type":22,"value":448},{"type":16,"tag":114,"props":1333,"children":1334},{"style":137},[1335],{"type":22,"value":181},{"type":16,"tag":114,"props":1337,"children":1338},{"class":116,"line":570},[1339,1344,1349,1353,1357,1362],{"type":16,"tag":114,"props":1340,"children":1341},{"style":131},[1342],{"type":22,"value":1343},"      const",{"type":16,"tag":114,"props":1345,"children":1346},{"style":199},[1347],{"type":22,"value":1348}," el",{"type":16,"tag":114,"props":1350,"children":1351},{"style":131},[1352],{"type":22,"value":288},{"type":16,"tag":114,"props":1354,"children":1355},{"style":137},[1356],{"type":22,"value":531},{"type":16,"tag":114,"props":1358,"children":1359},{"style":173},[1360],{"type":22,"value":1361},"getElementById",{"type":16,"tag":114,"props":1363,"children":1364},{"style":137},[1365],{"type":22,"value":1366},"(item.id)\n",{"type":16,"tag":114,"props":1368,"children":1369},{"class":116,"line":578},[1370,1374,1379,1384],{"type":16,"tag":114,"props":1371,"children":1372},{"style":131},[1373],{"type":22,"value":906},{"type":16,"tag":114,"props":1375,"children":1376},{"style":137},[1377],{"type":22,"value":1378}," (el) observer?.",{"type":16,"tag":114,"props":1380,"children":1381},{"style":173},[1382],{"type":22,"value":1383},"observe",{"type":16,"tag":114,"props":1385,"children":1386},{"style":137},[1387],{"type":22,"value":1388},"(el)\n",{"type":16,"tag":114,"props":1390,"children":1391},{"class":116,"line":618},[1392],{"type":16,"tag":114,"props":1393,"children":1394},{"style":137},[1395],{"type":22,"value":937},{"type":16,"tag":114,"props":1397,"children":1398},{"class":116,"line":675},[1399],{"type":16,"tag":114,"props":1400,"children":1401},{"style":137},[1402],{"type":22,"value":968},{"type":16,"tag":114,"props":1404,"children":1405},{"class":116,"line":771},[1406],{"type":16,"tag":114,"props":1407,"children":1408},{"emptyLinePlaceholder":158},[1409],{"type":22,"value":161},{"type":16,"tag":114,"props":1411,"children":1412},{"class":116,"line":793},[1413,1418,1423,1427],{"type":16,"tag":114,"props":1414,"children":1415},{"style":173},[1416],{"type":22,"value":1417},"  onMounted",{"type":16,"tag":114,"props":1419,"children":1420},{"style":137},[1421],{"type":22,"value":1422},"(() ",{"type":16,"tag":114,"props":1424,"children":1425},{"style":131},[1426],{"type":22,"value":448},{"type":16,"tag":114,"props":1428,"children":1429},{"style":137},[1430],{"type":22,"value":181},{"type":16,"tag":114,"props":1432,"children":1433},{"class":116,"line":831},[1434,1439],{"type":16,"tag":114,"props":1435,"children":1436},{"style":173},[1437],{"type":22,"value":1438},"    generateToc",{"type":16,"tag":114,"props":1440,"children":1441},{"style":137},[1442],{"type":22,"value":959},{"type":16,"tag":114,"props":1444,"children":1445},{"class":116,"line":840},[1446],{"type":16,"tag":114,"props":1447,"children":1448},{"style":137},[1449],{"type":22,"value":1450},"  })\n",{"type":16,"tag":114,"props":1452,"children":1453},{"class":116,"line":848},[1454],{"type":16,"tag":114,"props":1455,"children":1456},{"emptyLinePlaceholder":158},[1457],{"type":22,"value":161},{"type":16,"tag":114,"props":1459,"children":1460},{"class":116,"line":857},[1461,1466,1470,1474],{"type":16,"tag":114,"props":1462,"children":1463},{"style":173},[1464],{"type":22,"value":1465},"  onUnmounted",{"type":16,"tag":114,"props":1467,"children":1468},{"style":137},[1469],{"type":22,"value":1422},{"type":16,"tag":114,"props":1471,"children":1472},{"style":131},[1473],{"type":22,"value":448},{"type":16,"tag":114,"props":1475,"children":1476},{"style":137},[1477],{"type":22,"value":181},{"type":16,"tag":114,"props":1479,"children":1480},{"class":116,"line":900},[1481,1486,1490],{"type":16,"tag":114,"props":1482,"children":1483},{"style":137},[1484],{"type":22,"value":1485},"    observer?.",{"type":16,"tag":114,"props":1487,"children":1488},{"style":173},[1489],{"type":22,"value":1071},{"type":16,"tag":114,"props":1491,"children":1492},{"style":137},[1493],{"type":22,"value":959},{"type":16,"tag":114,"props":1495,"children":1496},{"class":116,"line":931},[1497],{"type":16,"tag":114,"props":1498,"children":1499},{"style":137},[1500],{"type":22,"value":1450},{"type":16,"tag":114,"props":1502,"children":1503},{"class":116,"line":940},[1504],{"type":16,"tag":114,"props":1505,"children":1506},{"emptyLinePlaceholder":158},[1507],{"type":22,"value":161},{"type":16,"tag":114,"props":1509,"children":1510},{"class":116,"line":948},[1511,1516],{"type":16,"tag":114,"props":1512,"children":1513},{"style":131},[1514],{"type":22,"value":1515},"  return",{"type":16,"tag":114,"props":1517,"children":1518},{"style":137},[1519],{"type":22,"value":1520}," { headings, activeId }\n",{"type":16,"tag":114,"props":1522,"children":1523},{"class":116,"line":962},[1524],{"type":16,"tag":114,"props":1525,"children":1526},{"style":137},[1527],{"type":22,"value":246},{"type":16,"tag":24,"props":1529,"children":1530},{},[1531,1536,1538,1544,1546,1552,1554,1560,1562,1568],{"type":16,"tag":30,"props":1532,"children":1533},{},[1534],{"type":22,"value":1535},"💡 踩坑记录：",{"type":22,"value":1537},"\n在 Nuxt 的 SSR 模式下，直接在 ",{"type":16,"tag":81,"props":1539,"children":1541},{"className":1540},[],[1542],{"type":22,"value":1543},"setup",{"type":22,"value":1545}," 阶段访问 ",{"type":16,"tag":81,"props":1547,"children":1549},{"className":1548},[],[1550],{"type":22,"value":1551},"document",{"type":22,"value":1553}," 会报错。必须确保 DOM 操作在 ",{"type":16,"tag":81,"props":1555,"children":1557},{"className":1556},[],[1558],{"type":22,"value":1559},"onMounted",{"type":22,"value":1561}," 钩子中执行，或者使用 ",{"type":16,"tag":81,"props":1563,"children":1565},{"className":1564},[],[1566],{"type":22,"value":1567},"import.meta.client",{"type":22,"value":1569}," 进行环境判断。",{"type":16,"tag":57,"props":1571,"children":1573},{"id":1572},"二-沉浸式阅读进度条",[1574],{"type":22,"value":1575},"二、 沉浸式阅读进度条",{"type":16,"tag":24,"props":1577,"children":1578},{},[1579],{"type":22,"value":1580},"进度条能给读者一个明确的心理预期：“这篇文章还有多少没看完”。实现思路很简单：计算当前滚动高度占页面总高度的百分比。",{"type":16,"tag":69,"props":1582,"children":1584},{"id":1583},"_1-基础逻辑与性能优化",[1585],{"type":22,"value":1586},"1. 基础逻辑与性能优化",{"type":16,"tag":24,"props":1588,"children":1589},{},[1590,1592,1597,1599,1605],{"type":22,"value":1591},"直接监听 ",{"type":16,"tag":81,"props":1593,"children":1595},{"className":1594},[],[1596],{"type":22,"value":1010},{"type":22,"value":1598}," 事件并频繁修改响应式数据会导致页面卡顿。我们需要结合 ",{"type":16,"tag":81,"props":1600,"children":1602},{"className":1601},[],[1603],{"type":22,"value":1604},"requestAnimationFrame",{"type":22,"value":1606}," 进行节流优化。",{"type":16,"tag":104,"props":1608,"children":1610},{"className":106,"code":1609,"language":108,"meta":8,"style":8},"\u002F\u002F composables\u002FuseReadingProgress.ts\nimport { ref, onMounted, onUnmounted } from 'vue'\n\nexport function useReadingProgress() {\n  const progress = ref(0)\n  let ticking = false\n\n  const updateProgress = () => {\n    const scrollTop = window.scrollY || document.documentElement.scrollTop\n    const docHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight\n    \n    \u002F\u002F 防止除以 0 或负数\n    progress.value = docHeight > 0 ? Math.min(100, (scrollTop \u002F docHeight) * 100) : 0\n    ticking = false\n  }\n\n  const onScroll = () => {\n    if (!ticking) {\n      window.requestAnimationFrame(updateProgress)\n      ticking = true\n    }\n  }\n\n  onMounted(() => {\n    window.addEventListener('scroll', onScroll, { passive: true })\n    updateProgress() \u002F\u002F 初始化\n  })\n\n  onUnmounted(() => {\n    window.removeEventListener('scroll', onScroll)\n  })\n\n  return { progress }\n}\n",[1611],{"type":16,"tag":81,"props":1612,"children":1613},{"__ignoreMap":8},[1614,1622,1641,1648,1669,1701,1722,1729,1757,1787,1818,1825,1833,1922,1938,1945,1952,1980,2000,2017,2034,2042,2049,2056,2075,2112,2130,2137,2144,2163,2188,2195,2202,2214],{"type":16,"tag":114,"props":1615,"children":1616},{"class":116,"line":117},[1617],{"type":16,"tag":114,"props":1618,"children":1619},{"style":121},[1620],{"type":22,"value":1621},"\u002F\u002F composables\u002FuseReadingProgress.ts\n",{"type":16,"tag":114,"props":1623,"children":1624},{"class":116,"line":127},[1625,1629,1633,1637],{"type":16,"tag":114,"props":1626,"children":1627},{"style":131},[1628],{"type":22,"value":134},{"type":16,"tag":114,"props":1630,"children":1631},{"style":137},[1632],{"type":22,"value":140},{"type":16,"tag":114,"props":1634,"children":1635},{"style":131},[1636],{"type":22,"value":145},{"type":16,"tag":114,"props":1638,"children":1639},{"style":148},[1640],{"type":22,"value":151},{"type":16,"tag":114,"props":1642,"children":1643},{"class":116,"line":154},[1644],{"type":16,"tag":114,"props":1645,"children":1646},{"emptyLinePlaceholder":158},[1647],{"type":22,"value":161},{"type":16,"tag":114,"props":1649,"children":1650},{"class":116,"line":164},[1651,1655,1659,1664],{"type":16,"tag":114,"props":1652,"children":1653},{"style":131},[1654],{"type":22,"value":263},{"type":16,"tag":114,"props":1656,"children":1657},{"style":131},[1658],{"type":22,"value":268},{"type":16,"tag":114,"props":1660,"children":1661},{"style":173},[1662],{"type":22,"value":1663}," useReadingProgress",{"type":16,"tag":114,"props":1665,"children":1666},{"style":137},[1667],{"type":22,"value":1668},"() {\n",{"type":16,"tag":114,"props":1670,"children":1671},{"class":116,"line":184},[1672,1676,1681,1685,1689,1693,1697],{"type":16,"tag":114,"props":1673,"children":1674},{"style":131},[1675],{"type":22,"value":307},{"type":16,"tag":114,"props":1677,"children":1678},{"style":199},[1679],{"type":22,"value":1680}," progress",{"type":16,"tag":114,"props":1682,"children":1683},{"style":131},[1684],{"type":22,"value":288},{"type":16,"tag":114,"props":1686,"children":1687},{"style":173},[1688],{"type":22,"value":321},{"type":16,"tag":114,"props":1690,"children":1691},{"style":137},[1692],{"type":22,"value":278},{"type":16,"tag":114,"props":1694,"children":1695},{"style":199},[1696],{"type":22,"value":1275},{"type":16,"tag":114,"props":1698,"children":1699},{"style":137},[1700],{"type":22,"value":371},{"type":16,"tag":114,"props":1702,"children":1703},{"class":116,"line":205},[1704,1708,1713,1717],{"type":16,"tag":114,"props":1705,"children":1706},{"style":131},[1707],{"type":22,"value":380},{"type":16,"tag":114,"props":1709,"children":1710},{"style":137},[1711],{"type":22,"value":1712}," ticking ",{"type":16,"tag":114,"props":1714,"children":1715},{"style":131},[1716],{"type":22,"value":629},{"type":16,"tag":114,"props":1718,"children":1719},{"style":199},[1720],{"type":22,"value":1721}," false\n",{"type":16,"tag":114,"props":1723,"children":1724},{"class":116,"line":222},[1725],{"type":16,"tag":114,"props":1726,"children":1727},{"emptyLinePlaceholder":158},[1728],{"type":22,"value":161},{"type":16,"tag":114,"props":1730,"children":1731},{"class":116,"line":240},[1732,1736,1741,1745,1749,1753],{"type":16,"tag":114,"props":1733,"children":1734},{"style":131},[1735],{"type":22,"value":307},{"type":16,"tag":114,"props":1737,"children":1738},{"style":173},[1739],{"type":22,"value":1740}," updateProgress",{"type":16,"tag":114,"props":1742,"children":1743},{"style":131},[1744],{"type":22,"value":288},{"type":16,"tag":114,"props":1746,"children":1747},{"style":137},[1748],{"type":22,"value":443},{"type":16,"tag":114,"props":1750,"children":1751},{"style":131},[1752],{"type":22,"value":448},{"type":16,"tag":114,"props":1754,"children":1755},{"style":137},[1756],{"type":22,"value":181},{"type":16,"tag":114,"props":1758,"children":1759},{"class":116,"line":249},[1760,1764,1769,1773,1778,1782],{"type":16,"tag":114,"props":1761,"children":1762},{"style":131},[1763],{"type":22,"value":517},{"type":16,"tag":114,"props":1765,"children":1766},{"style":199},[1767],{"type":22,"value":1768}," scrollTop",{"type":16,"tag":114,"props":1770,"children":1771},{"style":131},[1772],{"type":22,"value":288},{"type":16,"tag":114,"props":1774,"children":1775},{"style":137},[1776],{"type":22,"value":1777}," window.scrollY ",{"type":16,"tag":114,"props":1779,"children":1780},{"style":131},[1781],{"type":22,"value":686},{"type":16,"tag":114,"props":1783,"children":1784},{"style":137},[1785],{"type":22,"value":1786}," document.documentElement.scrollTop\n",{"type":16,"tag":114,"props":1788,"children":1789},{"class":116,"line":257},[1790,1794,1799,1803,1808,1813],{"type":16,"tag":114,"props":1791,"children":1792},{"style":131},[1793],{"type":22,"value":517},{"type":16,"tag":114,"props":1795,"children":1796},{"style":199},[1797],{"type":22,"value":1798}," docHeight",{"type":16,"tag":114,"props":1800,"children":1801},{"style":131},[1802],{"type":22,"value":288},{"type":16,"tag":114,"props":1804,"children":1805},{"style":137},[1806],{"type":22,"value":1807}," document.documentElement.scrollHeight ",{"type":16,"tag":114,"props":1809,"children":1810},{"style":131},[1811],{"type":22,"value":1812},"-",{"type":16,"tag":114,"props":1814,"children":1815},{"style":137},[1816],{"type":22,"value":1817}," document.documentElement.clientHeight\n",{"type":16,"tag":114,"props":1819,"children":1820},{"class":116,"line":301},[1821],{"type":16,"tag":114,"props":1822,"children":1823},{"style":137},[1824],{"type":22,"value":508},{"type":16,"tag":114,"props":1826,"children":1827},{"class":116,"line":339},[1828],{"type":16,"tag":114,"props":1829,"children":1830},{"style":121},[1831],{"type":22,"value":1832},"    \u002F\u002F 防止除以 0 或负数\n",{"type":16,"tag":114,"props":1834,"children":1835},{"class":116,"line":374},[1836,1841,1845,1850,1855,1860,1865,1870,1875,1879,1884,1889,1894,1899,1904,1909,1913,1917],{"type":16,"tag":114,"props":1837,"children":1838},{"style":137},[1839],{"type":22,"value":1840},"    progress.value ",{"type":16,"tag":114,"props":1842,"children":1843},{"style":131},[1844],{"type":22,"value":629},{"type":16,"tag":114,"props":1846,"children":1847},{"style":137},[1848],{"type":22,"value":1849}," docHeight ",{"type":16,"tag":114,"props":1851,"children":1852},{"style":131},[1853],{"type":22,"value":1854},">",{"type":16,"tag":114,"props":1856,"children":1857},{"style":199},[1858],{"type":22,"value":1859}," 0",{"type":16,"tag":114,"props":1861,"children":1862},{"style":131},[1863],{"type":22,"value":1864}," ?",{"type":16,"tag":114,"props":1866,"children":1867},{"style":137},[1868],{"type":22,"value":1869}," Math.",{"type":16,"tag":114,"props":1871,"children":1872},{"style":173},[1873],{"type":22,"value":1874},"min",{"type":16,"tag":114,"props":1876,"children":1877},{"style":137},[1878],{"type":22,"value":278},{"type":16,"tag":114,"props":1880,"children":1881},{"style":199},[1882],{"type":22,"value":1883},"100",{"type":16,"tag":114,"props":1885,"children":1886},{"style":137},[1887],{"type":22,"value":1888},", (scrollTop ",{"type":16,"tag":114,"props":1890,"children":1891},{"style":131},[1892],{"type":22,"value":1893},"\u002F",{"type":16,"tag":114,"props":1895,"children":1896},{"style":137},[1897],{"type":22,"value":1898}," docHeight) ",{"type":16,"tag":114,"props":1900,"children":1901},{"style":131},[1902],{"type":22,"value":1903},"*",{"type":16,"tag":114,"props":1905,"children":1906},{"style":199},[1907],{"type":22,"value":1908}," 100",{"type":16,"tag":114,"props":1910,"children":1911},{"style":137},[1912],{"type":22,"value":663},{"type":16,"tag":114,"props":1914,"children":1915},{"style":131},[1916],{"type":22,"value":196},{"type":16,"tag":114,"props":1918,"children":1919},{"style":199},[1920],{"type":22,"value":1921}," 0\n",{"type":16,"tag":114,"props":1923,"children":1924},{"class":116,"line":416},[1925,1930,1934],{"type":16,"tag":114,"props":1926,"children":1927},{"style":137},[1928],{"type":22,"value":1929},"    ticking ",{"type":16,"tag":114,"props":1931,"children":1932},{"style":131},[1933],{"type":22,"value":629},{"type":16,"tag":114,"props":1935,"children":1936},{"style":199},[1937],{"type":22,"value":1721},{"type":16,"tag":114,"props":1939,"children":1940},{"class":116,"line":424},[1941],{"type":16,"tag":114,"props":1942,"children":1943},{"style":137},[1944],{"type":22,"value":968},{"type":16,"tag":114,"props":1946,"children":1947},{"class":116,"line":455},[1948],{"type":16,"tag":114,"props":1949,"children":1950},{"emptyLinePlaceholder":158},[1951],{"type":22,"value":161},{"type":16,"tag":114,"props":1953,"children":1954},{"class":116,"line":464},[1955,1959,1964,1968,1972,1976],{"type":16,"tag":114,"props":1956,"children":1957},{"style":131},[1958],{"type":22,"value":307},{"type":16,"tag":114,"props":1960,"children":1961},{"style":173},[1962],{"type":22,"value":1963}," onScroll",{"type":16,"tag":114,"props":1965,"children":1966},{"style":131},[1967],{"type":22,"value":288},{"type":16,"tag":114,"props":1969,"children":1970},{"style":137},[1971],{"type":22,"value":443},{"type":16,"tag":114,"props":1973,"children":1974},{"style":131},[1975],{"type":22,"value":448},{"type":16,"tag":114,"props":1977,"children":1978},{"style":137},[1979],{"type":22,"value":181},{"type":16,"tag":114,"props":1981,"children":1982},{"class":116,"line":502},[1983,1987,1991,1995],{"type":16,"tag":114,"props":1984,"children":1985},{"style":131},[1986],{"type":22,"value":470},{"type":16,"tag":114,"props":1988,"children":1989},{"style":137},[1990],{"type":22,"value":475},{"type":16,"tag":114,"props":1992,"children":1993},{"style":131},[1994],{"type":22,"value":558},{"type":16,"tag":114,"props":1996,"children":1997},{"style":137},[1998],{"type":22,"value":1999},"ticking) {\n",{"type":16,"tag":114,"props":2001,"children":2002},{"class":116,"line":511},[2003,2008,2012],{"type":16,"tag":114,"props":2004,"children":2005},{"style":137},[2006],{"type":22,"value":2007},"      window.",{"type":16,"tag":114,"props":2009,"children":2010},{"style":173},[2011],{"type":22,"value":1604},{"type":16,"tag":114,"props":2013,"children":2014},{"style":137},[2015],{"type":22,"value":2016},"(updateProgress)\n",{"type":16,"tag":114,"props":2018,"children":2019},{"class":116,"line":544},[2020,2025,2029],{"type":16,"tag":114,"props":2021,"children":2022},{"style":137},[2023],{"type":22,"value":2024},"      ticking ",{"type":16,"tag":114,"props":2026,"children":2027},{"style":131},[2028],{"type":22,"value":629},{"type":16,"tag":114,"props":2030,"children":2031},{"style":199},[2032],{"type":22,"value":2033}," true\n",{"type":16,"tag":114,"props":2035,"children":2036},{"class":116,"line":570},[2037],{"type":16,"tag":114,"props":2038,"children":2039},{"style":137},[2040],{"type":22,"value":2041},"    }\n",{"type":16,"tag":114,"props":2043,"children":2044},{"class":116,"line":578},[2045],{"type":16,"tag":114,"props":2046,"children":2047},{"style":137},[2048],{"type":22,"value":968},{"type":16,"tag":114,"props":2050,"children":2051},{"class":116,"line":618},[2052],{"type":16,"tag":114,"props":2053,"children":2054},{"emptyLinePlaceholder":158},[2055],{"type":22,"value":161},{"type":16,"tag":114,"props":2057,"children":2058},{"class":116,"line":675},[2059,2063,2067,2071],{"type":16,"tag":114,"props":2060,"children":2061},{"style":173},[2062],{"type":22,"value":1417},{"type":16,"tag":114,"props":2064,"children":2065},{"style":137},[2066],{"type":22,"value":1422},{"type":16,"tag":114,"props":2068,"children":2069},{"style":131},[2070],{"type":22,"value":448},{"type":16,"tag":114,"props":2072,"children":2073},{"style":137},[2074],{"type":22,"value":181},{"type":16,"tag":114,"props":2076,"children":2077},{"class":116,"line":771},[2078,2083,2088,2092,2097,2102,2107],{"type":16,"tag":114,"props":2079,"children":2080},{"style":137},[2081],{"type":22,"value":2082},"    window.",{"type":16,"tag":114,"props":2084,"children":2085},{"style":173},[2086],{"type":22,"value":2087},"addEventListener",{"type":16,"tag":114,"props":2089,"children":2090},{"style":137},[2091],{"type":22,"value":278},{"type":16,"tag":114,"props":2093,"children":2094},{"style":148},[2095],{"type":22,"value":2096},"'scroll'",{"type":16,"tag":114,"props":2098,"children":2099},{"style":137},[2100],{"type":22,"value":2101},", onScroll, { passive: ",{"type":16,"tag":114,"props":2103,"children":2104},{"style":199},[2105],{"type":22,"value":2106},"true",{"type":16,"tag":114,"props":2108,"children":2109},{"style":137},[2110],{"type":22,"value":2111}," })\n",{"type":16,"tag":114,"props":2113,"children":2114},{"class":116,"line":793},[2115,2120,2125],{"type":16,"tag":114,"props":2116,"children":2117},{"style":173},[2118],{"type":22,"value":2119},"    updateProgress",{"type":16,"tag":114,"props":2121,"children":2122},{"style":137},[2123],{"type":22,"value":2124},"() ",{"type":16,"tag":114,"props":2126,"children":2127},{"style":121},[2128],{"type":22,"value":2129},"\u002F\u002F 初始化\n",{"type":16,"tag":114,"props":2131,"children":2132},{"class":116,"line":831},[2133],{"type":16,"tag":114,"props":2134,"children":2135},{"style":137},[2136],{"type":22,"value":1450},{"type":16,"tag":114,"props":2138,"children":2139},{"class":116,"line":840},[2140],{"type":16,"tag":114,"props":2141,"children":2142},{"emptyLinePlaceholder":158},[2143],{"type":22,"value":161},{"type":16,"tag":114,"props":2145,"children":2146},{"class":116,"line":848},[2147,2151,2155,2159],{"type":16,"tag":114,"props":2148,"children":2149},{"style":173},[2150],{"type":22,"value":1465},{"type":16,"tag":114,"props":2152,"children":2153},{"style":137},[2154],{"type":22,"value":1422},{"type":16,"tag":114,"props":2156,"children":2157},{"style":131},[2158],{"type":22,"value":448},{"type":16,"tag":114,"props":2160,"children":2161},{"style":137},[2162],{"type":22,"value":181},{"type":16,"tag":114,"props":2164,"children":2165},{"class":116,"line":857},[2166,2170,2175,2179,2183],{"type":16,"tag":114,"props":2167,"children":2168},{"style":137},[2169],{"type":22,"value":2082},{"type":16,"tag":114,"props":2171,"children":2172},{"style":173},[2173],{"type":22,"value":2174},"removeEventListener",{"type":16,"tag":114,"props":2176,"children":2177},{"style":137},[2178],{"type":22,"value":278},{"type":16,"tag":114,"props":2180,"children":2181},{"style":148},[2182],{"type":22,"value":2096},{"type":16,"tag":114,"props":2184,"children":2185},{"style":137},[2186],{"type":22,"value":2187},", onScroll)\n",{"type":16,"tag":114,"props":2189,"children":2190},{"class":116,"line":900},[2191],{"type":16,"tag":114,"props":2192,"children":2193},{"style":137},[2194],{"type":22,"value":1450},{"type":16,"tag":114,"props":2196,"children":2197},{"class":116,"line":931},[2198],{"type":16,"tag":114,"props":2199,"children":2200},{"emptyLinePlaceholder":158},[2201],{"type":22,"value":161},{"type":16,"tag":114,"props":2203,"children":2204},{"class":116,"line":940},[2205,2209],{"type":16,"tag":114,"props":2206,"children":2207},{"style":131},[2208],{"type":22,"value":1515},{"type":16,"tag":114,"props":2210,"children":2211},{"style":137},[2212],{"type":22,"value":2213}," { progress }\n",{"type":16,"tag":114,"props":2215,"children":2216},{"class":116,"line":948},[2217],{"type":16,"tag":114,"props":2218,"children":2219},{"style":137},[2220],{"type":22,"value":246},{"type":16,"tag":69,"props":2222,"children":2224},{"id":2223},"_2-结合-tailwind-css-v4-实现丝滑动画",[2225],{"type":22,"value":2226},"2. 结合 Tailwind CSS v4 实现丝滑动画",{"type":16,"tag":24,"props":2228,"children":2229},{},[2230,2232,2238],{"type":22,"value":2231},"在 Nuxt 4 中，我们可以直接使用 Tailwind CSS v4 来渲染进度条。利用 CSS 变量和 ",{"type":16,"tag":81,"props":2233,"children":2235},{"className":2234},[],[2236],{"type":22,"value":2237},"transition",{"type":22,"value":2239},"，可以让进度条的变化更加平滑。",{"type":16,"tag":104,"props":2241,"children":2245},{"className":2242,"code":2243,"language":2244,"meta":8,"style":8},"language-vue shiki shiki-themes github-light github-dark","\u003Ctemplate>\n  \u003Cdiv class=\"fixed top-0 left-0 w-full h-1 z-50 bg-gray-200 dark:bg-gray-800\">\n    \u003Cdiv \n      class=\"h-full bg-blue-500 transition-all duration-150 ease-out\"\n      :style=\"{ width: `${progress}%` }\"\n    \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup lang=\"ts\">\nconst { progress } = useReadingProgress()\n\u003C\u002Fscript>\n","vue",[2246],{"type":16,"tag":81,"props":2247,"children":2248},{"__ignoreMap":8},[2249,2267,2298,2315,2332,2384,2397,2413,2429,2436,2471,2505],{"type":16,"tag":114,"props":2250,"children":2251},{"class":116,"line":117},[2252,2256,2262],{"type":16,"tag":114,"props":2253,"children":2254},{"style":137},[2255],{"type":22,"value":326},{"type":16,"tag":114,"props":2257,"children":2259},{"style":2258},"--shiki-default:#22863A;--shiki-dark:#85E89D",[2260],{"type":22,"value":2261},"template",{"type":16,"tag":114,"props":2263,"children":2264},{"style":137},[2265],{"type":22,"value":2266},">\n",{"type":16,"tag":114,"props":2268,"children":2269},{"class":116,"line":127},[2270,2275,2280,2285,2289,2294],{"type":16,"tag":114,"props":2271,"children":2272},{"style":137},[2273],{"type":22,"value":2274},"  \u003C",{"type":16,"tag":114,"props":2276,"children":2277},{"style":2258},[2278],{"type":22,"value":2279},"div",{"type":16,"tag":114,"props":2281,"children":2282},{"style":173},[2283],{"type":22,"value":2284}," class",{"type":16,"tag":114,"props":2286,"children":2287},{"style":137},[2288],{"type":22,"value":629},{"type":16,"tag":114,"props":2290,"children":2291},{"style":148},[2292],{"type":22,"value":2293},"\"fixed top-0 left-0 w-full h-1 z-50 bg-gray-200 dark:bg-gray-800\"",{"type":16,"tag":114,"props":2295,"children":2296},{"style":137},[2297],{"type":22,"value":2266},{"type":16,"tag":114,"props":2299,"children":2300},{"class":116,"line":154},[2301,2306,2310],{"type":16,"tag":114,"props":2302,"children":2303},{"style":137},[2304],{"type":22,"value":2305},"    \u003C",{"type":16,"tag":114,"props":2307,"children":2308},{"style":2258},[2309],{"type":22,"value":2279},{"type":16,"tag":114,"props":2311,"children":2312},{"style":137},[2313],{"type":22,"value":2314}," \n",{"type":16,"tag":114,"props":2316,"children":2317},{"class":116,"line":164},[2318,2323,2327],{"type":16,"tag":114,"props":2319,"children":2320},{"style":173},[2321],{"type":22,"value":2322},"      class",{"type":16,"tag":114,"props":2324,"children":2325},{"style":137},[2326],{"type":22,"value":629},{"type":16,"tag":114,"props":2328,"children":2329},{"style":148},[2330],{"type":22,"value":2331},"\"h-full bg-blue-500 transition-all duration-150 ease-out\"\n",{"type":16,"tag":114,"props":2333,"children":2334},{"class":116,"line":184},[2335,2340,2345,2349,2354,2359,2364,2369,2374,2379],{"type":16,"tag":114,"props":2336,"children":2337},{"style":137},[2338],{"type":22,"value":2339},"      :",{"type":16,"tag":114,"props":2341,"children":2342},{"style":173},[2343],{"type":22,"value":2344},"style",{"type":16,"tag":114,"props":2346,"children":2347},{"style":137},[2348],{"type":22,"value":629},{"type":16,"tag":114,"props":2350,"children":2351},{"style":148},[2352],{"type":22,"value":2353},"\"",{"type":16,"tag":114,"props":2355,"children":2356},{"style":137},[2357],{"type":22,"value":2358},"{ width: ",{"type":16,"tag":114,"props":2360,"children":2361},{"style":148},[2362],{"type":22,"value":2363},"`${",{"type":16,"tag":114,"props":2365,"children":2366},{"style":137},[2367],{"type":22,"value":2368},"progress",{"type":16,"tag":114,"props":2370,"children":2371},{"style":148},[2372],{"type":22,"value":2373},"}%`",{"type":16,"tag":114,"props":2375,"children":2376},{"style":137},[2377],{"type":22,"value":2378}," }",{"type":16,"tag":114,"props":2380,"children":2381},{"style":148},[2382],{"type":22,"value":2383},"\"\n",{"type":16,"tag":114,"props":2385,"children":2386},{"class":116,"line":205},[2387,2393],{"type":16,"tag":114,"props":2388,"children":2390},{"style":2389},"--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic",[2391],{"type":22,"value":2392},"    \u002F",{"type":16,"tag":114,"props":2394,"children":2395},{"style":137},[2396],{"type":22,"value":2266},{"type":16,"tag":114,"props":2398,"children":2399},{"class":116,"line":222},[2400,2405,2409],{"type":16,"tag":114,"props":2401,"children":2402},{"style":137},[2403],{"type":22,"value":2404},"  \u003C\u002F",{"type":16,"tag":114,"props":2406,"children":2407},{"style":2258},[2408],{"type":22,"value":2279},{"type":16,"tag":114,"props":2410,"children":2411},{"style":137},[2412],{"type":22,"value":2266},{"type":16,"tag":114,"props":2414,"children":2415},{"class":116,"line":240},[2416,2421,2425],{"type":16,"tag":114,"props":2417,"children":2418},{"style":137},[2419],{"type":22,"value":2420},"\u003C\u002F",{"type":16,"tag":114,"props":2422,"children":2423},{"style":2258},[2424],{"type":22,"value":2261},{"type":16,"tag":114,"props":2426,"children":2427},{"style":137},[2428],{"type":22,"value":2266},{"type":16,"tag":114,"props":2430,"children":2431},{"class":116,"line":249},[2432],{"type":16,"tag":114,"props":2433,"children":2434},{"emptyLinePlaceholder":158},[2435],{"type":22,"value":161},{"type":16,"tag":114,"props":2437,"children":2438},{"class":116,"line":257},[2439,2443,2448,2453,2458,2462,2467],{"type":16,"tag":114,"props":2440,"children":2441},{"style":137},[2442],{"type":22,"value":326},{"type":16,"tag":114,"props":2444,"children":2445},{"style":2258},[2446],{"type":22,"value":2447},"script",{"type":16,"tag":114,"props":2449,"children":2450},{"style":173},[2451],{"type":22,"value":2452}," setup",{"type":16,"tag":114,"props":2454,"children":2455},{"style":173},[2456],{"type":22,"value":2457}," lang",{"type":16,"tag":114,"props":2459,"children":2460},{"style":137},[2461],{"type":22,"value":629},{"type":16,"tag":114,"props":2463,"children":2464},{"style":148},[2465],{"type":22,"value":2466},"\"ts\"",{"type":16,"tag":114,"props":2468,"children":2469},{"style":137},[2470],{"type":22,"value":2266},{"type":16,"tag":114,"props":2472,"children":2473},{"class":116,"line":301},[2474,2479,2484,2488,2493,2497,2501],{"type":16,"tag":114,"props":2475,"children":2476},{"style":131},[2477],{"type":22,"value":2478},"const",{"type":16,"tag":114,"props":2480,"children":2481},{"style":137},[2482],{"type":22,"value":2483}," { ",{"type":16,"tag":114,"props":2485,"children":2486},{"style":199},[2487],{"type":22,"value":2368},{"type":16,"tag":114,"props":2489,"children":2490},{"style":137},[2491],{"type":22,"value":2492}," } ",{"type":16,"tag":114,"props":2494,"children":2495},{"style":131},[2496],{"type":22,"value":629},{"type":16,"tag":114,"props":2498,"children":2499},{"style":173},[2500],{"type":22,"value":1663},{"type":16,"tag":114,"props":2502,"children":2503},{"style":137},[2504],{"type":22,"value":959},{"type":16,"tag":114,"props":2506,"children":2507},{"class":116,"line":339},[2508,2512,2516],{"type":16,"tag":114,"props":2509,"children":2510},{"style":137},[2511],{"type":22,"value":2420},{"type":16,"tag":114,"props":2513,"children":2514},{"style":2258},[2515],{"type":22,"value":2447},{"type":16,"tag":114,"props":2517,"children":2518},{"style":137},[2519],{"type":22,"value":2266},{"type":16,"tag":24,"props":2521,"children":2522},{},[2523],{"type":16,"tag":30,"props":2524,"children":2525},{},[2526],{"type":22,"value":2527},"💡 优化细节：",{"type":16,"tag":2529,"props":2530,"children":2531},"ol",{},[2532,2554],{"type":16,"tag":2533,"props":2534,"children":2535},"li",{},[2536,2538,2544,2546,2552],{"type":22,"value":2537},"使用 ",{"type":16,"tag":81,"props":2539,"children":2541},{"className":2540},[],[2542],{"type":22,"value":2543},"passive: true",{"type":22,"value":2545}," 监听滚动事件，告诉浏览器不会调用 ",{"type":16,"tag":81,"props":2547,"children":2549},{"className":2548},[],[2550],{"type":22,"value":2551},"preventDefault()",{"type":22,"value":2553},"，从而提升滚动性能。",{"type":16,"tag":2533,"props":2555,"children":2556},{},[2557,2559,2565],{"type":22,"value":2558},"进度条使用 ",{"type":16,"tag":81,"props":2560,"children":2562},{"className":2561},[],[2563],{"type":22,"value":2564},"transition-all duration-150",{"type":22,"value":2566},"，避免数值跳变带来的视觉突兀感。",{"type":16,"tag":57,"props":2568,"children":2570},{"id":2569},"三-总结",[2571],{"type":22,"value":2572},"三、 总结",{"type":16,"tag":24,"props":2574,"children":2575},{},[2576,2578,2583],{"type":22,"value":2577},"通过这次重构，我深刻体会到：",{"type":16,"tag":30,"props":2579,"children":2580},{},[2581],{"type":22,"value":2582},"优秀的阅读体验是由无数个细节堆砌而成的",{"type":22,"value":2584},"。",{"type":16,"tag":2586,"props":2587,"children":2588},"ul",{},[2589,2599,2611],{"type":16,"tag":2533,"props":2590,"children":2591},{},[2592,2597],{"type":16,"tag":30,"props":2593,"children":2594},{},[2595],{"type":22,"value":2596},"Intersection Observer",{"type":22,"value":2598}," 替代传统的滚动监听，是处理目录高亮的最佳实践。",{"type":16,"tag":2533,"props":2600,"children":2601},{},[2602,2604,2609],{"type":22,"value":2603},"在 SSR 框架（如 Nuxt）中，时刻牢记",{"type":16,"tag":30,"props":2605,"children":2606},{},[2607],{"type":22,"value":2608},"客户端与服务端的环境差异",{"type":22,"value":2610},"，避免 hydration mismatch（水合不匹配）错误。",{"type":16,"tag":2533,"props":2612,"children":2613},{},[2614,2616,2621,2623,2628],{"type":22,"value":2615},"善用 ",{"type":16,"tag":81,"props":2617,"children":2619},{"className":2618},[],[2620],{"type":22,"value":1604},{"type":22,"value":2622}," 和 CSS ",{"type":16,"tag":81,"props":2624,"children":2626},{"className":2625},[],[2627],{"type":22,"value":2237},{"type":22,"value":2629},"，可以用极低的成本换取丝滑的视觉体验。",{"type":16,"tag":24,"props":2631,"children":2632},{},[2633,2635,2644],{"type":22,"value":2634},"目前这两个功能已经在 ",{"type":16,"tag":2636,"props":2637,"children":2641},"a",{"href":2638,"rel":2639},"https:\u002F\u002Fninglab.top",[2640],"nofollow",[2642],{"type":22,"value":2643},"ninglab.top",{"type":22,"value":2645}," 上线，阅读体验有了显著提升。下一步，我计划引入 Giscus 评论系统，让博客真正“活”起来。",{"type":16,"tag":24,"props":2647,"children":2648},{},[2649],{"type":22,"value":2650},"如果你也在重构博客，或者对 Nuxt 4 的某些特性有疑问，欢迎在评论区交流！",{"type":16,"tag":2344,"props":2652,"children":2653},{},[2654],{"type":22,"value":2655},"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);}",{"title":8,"searchDepth":127,"depth":127,"links":2657},[2658,2662,2666],{"id":59,"depth":127,"text":62,"children":2659},[2660,2661],{"id":71,"depth":154,"text":74},{"id":996,"depth":154,"text":999},{"id":1572,"depth":127,"text":1575,"children":2663},[2664,2665],{"id":1583,"depth":154,"text":1586},{"id":2223,"depth":154,"text":2226},{"id":2569,"depth":127,"text":2572},"markdown","content:blog:3-post.md","content","blog\u002F3-post.md","blog\u002F3-post","md","2LK7M1",{"_path":2675,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":2676,"description":2677,"date":2678,"code":2679,"body":2680,"_type":2667,"_id":5125,"_source":2669,"_file":5126,"_stem":5127,"_extension":2672},"\u002Fblog\u002F2-post","FastAPI + Vue3：轻量级全栈项目实战","前后端目录规范与依赖管理","2026-05-08","D4E5F6",{"type":13,"children":2681,"toc":5118},[2682,2687,2702,2708,2716,2724,2905,2911,3156,3513,3519,3915,4237,4243,4258,4450,4465,4631,4637,4928,5068,5076,5114],{"type":16,"tag":17,"props":2683,"children":2685},{"id":2684},"fastapi-vue3轻量级全栈项目实战",[2686],{"type":22,"value":2676},{"type":16,"tag":2688,"props":2689,"children":2690},"blockquote",{},[2691],{"type":16,"tag":24,"props":2692,"children":2693},{},[2694,2696,2700],{"type":22,"value":2695},"适用场景：快速原型 \u002F 内部工具 \u002F 个人项目",{"type":16,"tag":2697,"props":2698,"children":2699},"br",{},[],{"type":22,"value":2701},"\n技术栈：FastAPI + Vue3 + Pinia + Axios + Docker",{"type":16,"tag":57,"props":2703,"children":2705},{"id":2704},"_1-目录规范与依赖管理",[2706],{"type":22,"value":2707},"1. 目录规范与依赖管理",{"type":16,"tag":104,"props":2709,"children":2711},{"code":2710},"project\u002F\n├── backend\u002F          # Python 后端\n│   ├── app\u002F\n│   │   ├── main.py\n│   │   ├── routers\u002F\n│   │   ├── models\u002F   # SQLAlchemy \u002F Pydantic\n│   │   └── core\u002F     # Config, Security\n│   └── requirements.txt\n└── frontend\u002F         # Vue3 前端\n    ├── src\u002F\n    │   ├── api\u002F\n    │   ├── stores\u002F   # Pinia\n    │   └── composables\u002F\n    └── package.json\n",[2712],{"type":16,"tag":81,"props":2713,"children":2714},{"__ignoreMap":8},[2715],{"type":22,"value":2710},{"type":16,"tag":24,"props":2717,"children":2718},{},[2719],{"type":16,"tag":30,"props":2720,"children":2721},{},[2722],{"type":22,"value":2723},"初始化",{"type":16,"tag":104,"props":2725,"children":2729},{"code":2726,"language":2727,"meta":8,"className":2728,"style":8},"# Backend\npython -m venv .venv && source .venv\u002Fbin\u002Factivate\npip install \"fastapi[standard]\" pydantic passlib[bcrypt] python-jose[cryptography]\n\n# Frontend\nnpm create vite@latest frontend -- --template vue-ts\ncd frontend && npm i axios pinia vue-router\n","bash","language-bash shiki shiki-themes github-light github-dark",[2730],{"type":16,"tag":81,"props":2731,"children":2732},{"__ignoreMap":8},[2733,2741,2779,2812,2819,2827,2865],{"type":16,"tag":114,"props":2734,"children":2735},{"class":116,"line":117},[2736],{"type":16,"tag":114,"props":2737,"children":2738},{"style":121},[2739],{"type":22,"value":2740},"# Backend\n",{"type":16,"tag":114,"props":2742,"children":2743},{"class":116,"line":127},[2744,2749,2754,2759,2764,2769,2774],{"type":16,"tag":114,"props":2745,"children":2746},{"style":173},[2747],{"type":22,"value":2748},"python",{"type":16,"tag":114,"props":2750,"children":2751},{"style":199},[2752],{"type":22,"value":2753}," -m",{"type":16,"tag":114,"props":2755,"children":2756},{"style":148},[2757],{"type":22,"value":2758}," venv",{"type":16,"tag":114,"props":2760,"children":2761},{"style":148},[2762],{"type":22,"value":2763}," .venv",{"type":16,"tag":114,"props":2765,"children":2766},{"style":137},[2767],{"type":22,"value":2768}," && ",{"type":16,"tag":114,"props":2770,"children":2771},{"style":199},[2772],{"type":22,"value":2773},"source",{"type":16,"tag":114,"props":2775,"children":2776},{"style":148},[2777],{"type":22,"value":2778}," .venv\u002Fbin\u002Factivate\n",{"type":16,"tag":114,"props":2780,"children":2781},{"class":116,"line":154},[2782,2787,2792,2797,2802,2807],{"type":16,"tag":114,"props":2783,"children":2784},{"style":173},[2785],{"type":22,"value":2786},"pip",{"type":16,"tag":114,"props":2788,"children":2789},{"style":148},[2790],{"type":22,"value":2791}," install",{"type":16,"tag":114,"props":2793,"children":2794},{"style":148},[2795],{"type":22,"value":2796}," \"fastapi[standard]\"",{"type":16,"tag":114,"props":2798,"children":2799},{"style":148},[2800],{"type":22,"value":2801}," pydantic",{"type":16,"tag":114,"props":2803,"children":2804},{"style":148},[2805],{"type":22,"value":2806}," passlib[bcrypt]",{"type":16,"tag":114,"props":2808,"children":2809},{"style":148},[2810],{"type":22,"value":2811}," python-jose[cryptography]\n",{"type":16,"tag":114,"props":2813,"children":2814},{"class":116,"line":164},[2815],{"type":16,"tag":114,"props":2816,"children":2817},{"emptyLinePlaceholder":158},[2818],{"type":22,"value":161},{"type":16,"tag":114,"props":2820,"children":2821},{"class":116,"line":184},[2822],{"type":16,"tag":114,"props":2823,"children":2824},{"style":121},[2825],{"type":22,"value":2826},"# Frontend\n",{"type":16,"tag":114,"props":2828,"children":2829},{"class":116,"line":205},[2830,2835,2840,2845,2850,2855,2860],{"type":16,"tag":114,"props":2831,"children":2832},{"style":173},[2833],{"type":22,"value":2834},"npm",{"type":16,"tag":114,"props":2836,"children":2837},{"style":148},[2838],{"type":22,"value":2839}," create",{"type":16,"tag":114,"props":2841,"children":2842},{"style":148},[2843],{"type":22,"value":2844}," vite@latest",{"type":16,"tag":114,"props":2846,"children":2847},{"style":148},[2848],{"type":22,"value":2849}," frontend",{"type":16,"tag":114,"props":2851,"children":2852},{"style":199},[2853],{"type":22,"value":2854}," --",{"type":16,"tag":114,"props":2856,"children":2857},{"style":199},[2858],{"type":22,"value":2859}," --template",{"type":16,"tag":114,"props":2861,"children":2862},{"style":148},[2863],{"type":22,"value":2864}," vue-ts\n",{"type":16,"tag":114,"props":2866,"children":2867},{"class":116,"line":222},[2868,2873,2877,2881,2885,2890,2895,2900],{"type":16,"tag":114,"props":2869,"children":2870},{"style":199},[2871],{"type":22,"value":2872},"cd",{"type":16,"tag":114,"props":2874,"children":2875},{"style":148},[2876],{"type":22,"value":2849},{"type":16,"tag":114,"props":2878,"children":2879},{"style":137},[2880],{"type":22,"value":2768},{"type":16,"tag":114,"props":2882,"children":2883},{"style":173},[2884],{"type":22,"value":2834},{"type":16,"tag":114,"props":2886,"children":2887},{"style":148},[2888],{"type":22,"value":2889}," i",{"type":16,"tag":114,"props":2891,"children":2892},{"style":148},[2893],{"type":22,"value":2894}," axios",{"type":16,"tag":114,"props":2896,"children":2897},{"style":148},[2898],{"type":22,"value":2899}," pinia",{"type":16,"tag":114,"props":2901,"children":2902},{"style":148},[2903],{"type":22,"value":2904}," vue-router\n",{"type":16,"tag":57,"props":2906,"children":2908},{"id":2907},"_2-fastapi-路由pydantic-校验与-jwt",[2909],{"type":22,"value":2910},"2. FastAPI 路由、Pydantic 校验与 JWT",{"type":16,"tag":104,"props":2912,"children":2915},{"code":2913,"language":2748,"meta":8,"className":2914,"style":8},"# backend\u002Fapp\u002Fcore\u002Fsecurity.py\nfrom datetime import datetime, timedelta\nfrom jose import jwt\n\nSECRET_KEY = \"your-secret\"\nALGORITHM = \"HS256\"\n\ndef create_token(data: dict, expires_delta: timedelta = timedelta(hours=24)):\n    to_encode = data.copy()\n    to_encode.update({\"exp\": datetime.utcnow() + expires_delta})\n    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)\n","language-python shiki shiki-themes github-light github-dark",[2916],{"type":16,"tag":81,"props":2917,"children":2918},{"__ignoreMap":8},[2919,2927,2948,2969,2976,2993,3010,3017,3073,3090,3118],{"type":16,"tag":114,"props":2920,"children":2921},{"class":116,"line":117},[2922],{"type":16,"tag":114,"props":2923,"children":2924},{"style":121},[2925],{"type":22,"value":2926},"# backend\u002Fapp\u002Fcore\u002Fsecurity.py\n",{"type":16,"tag":114,"props":2928,"children":2929},{"class":116,"line":127},[2930,2934,2939,2943],{"type":16,"tag":114,"props":2931,"children":2932},{"style":131},[2933],{"type":22,"value":145},{"type":16,"tag":114,"props":2935,"children":2936},{"style":137},[2937],{"type":22,"value":2938}," datetime ",{"type":16,"tag":114,"props":2940,"children":2941},{"style":131},[2942],{"type":22,"value":134},{"type":16,"tag":114,"props":2944,"children":2945},{"style":137},[2946],{"type":22,"value":2947}," datetime, timedelta\n",{"type":16,"tag":114,"props":2949,"children":2950},{"class":116,"line":154},[2951,2955,2960,2964],{"type":16,"tag":114,"props":2952,"children":2953},{"style":131},[2954],{"type":22,"value":145},{"type":16,"tag":114,"props":2956,"children":2957},{"style":137},[2958],{"type":22,"value":2959}," jose ",{"type":16,"tag":114,"props":2961,"children":2962},{"style":131},[2963],{"type":22,"value":134},{"type":16,"tag":114,"props":2965,"children":2966},{"style":137},[2967],{"type":22,"value":2968}," jwt\n",{"type":16,"tag":114,"props":2970,"children":2971},{"class":116,"line":164},[2972],{"type":16,"tag":114,"props":2973,"children":2974},{"emptyLinePlaceholder":158},[2975],{"type":22,"value":161},{"type":16,"tag":114,"props":2977,"children":2978},{"class":116,"line":184},[2979,2984,2988],{"type":16,"tag":114,"props":2980,"children":2981},{"style":199},[2982],{"type":22,"value":2983},"SECRET_KEY",{"type":16,"tag":114,"props":2985,"children":2986},{"style":131},[2987],{"type":22,"value":288},{"type":16,"tag":114,"props":2989,"children":2990},{"style":148},[2991],{"type":22,"value":2992}," \"your-secret\"\n",{"type":16,"tag":114,"props":2994,"children":2995},{"class":116,"line":205},[2996,3001,3005],{"type":16,"tag":114,"props":2997,"children":2998},{"style":199},[2999],{"type":22,"value":3000},"ALGORITHM",{"type":16,"tag":114,"props":3002,"children":3003},{"style":131},[3004],{"type":22,"value":288},{"type":16,"tag":114,"props":3006,"children":3007},{"style":148},[3008],{"type":22,"value":3009}," \"HS256\"\n",{"type":16,"tag":114,"props":3011,"children":3012},{"class":116,"line":222},[3013],{"type":16,"tag":114,"props":3014,"children":3015},{"emptyLinePlaceholder":158},[3016],{"type":22,"value":161},{"type":16,"tag":114,"props":3018,"children":3019},{"class":116,"line":240},[3020,3025,3030,3035,3040,3045,3049,3054,3059,3063,3068],{"type":16,"tag":114,"props":3021,"children":3022},{"style":131},[3023],{"type":22,"value":3024},"def",{"type":16,"tag":114,"props":3026,"children":3027},{"style":173},[3028],{"type":22,"value":3029}," create_token",{"type":16,"tag":114,"props":3031,"children":3032},{"style":137},[3033],{"type":22,"value":3034},"(data: ",{"type":16,"tag":114,"props":3036,"children":3037},{"style":199},[3038],{"type":22,"value":3039},"dict",{"type":16,"tag":114,"props":3041,"children":3042},{"style":137},[3043],{"type":22,"value":3044},", expires_delta: timedelta ",{"type":16,"tag":114,"props":3046,"children":3047},{"style":131},[3048],{"type":22,"value":629},{"type":16,"tag":114,"props":3050,"children":3051},{"style":137},[3052],{"type":22,"value":3053}," timedelta(",{"type":16,"tag":114,"props":3055,"children":3056},{"style":188},[3057],{"type":22,"value":3058},"hours",{"type":16,"tag":114,"props":3060,"children":3061},{"style":131},[3062],{"type":22,"value":629},{"type":16,"tag":114,"props":3064,"children":3065},{"style":199},[3066],{"type":22,"value":3067},"24",{"type":16,"tag":114,"props":3069,"children":3070},{"style":137},[3071],{"type":22,"value":3072},")):\n",{"type":16,"tag":114,"props":3074,"children":3075},{"class":116,"line":249},[3076,3081,3085],{"type":16,"tag":114,"props":3077,"children":3078},{"style":137},[3079],{"type":22,"value":3080},"    to_encode ",{"type":16,"tag":114,"props":3082,"children":3083},{"style":131},[3084],{"type":22,"value":629},{"type":16,"tag":114,"props":3086,"children":3087},{"style":137},[3088],{"type":22,"value":3089}," data.copy()\n",{"type":16,"tag":114,"props":3091,"children":3092},{"class":116,"line":257},[3093,3098,3103,3108,3113],{"type":16,"tag":114,"props":3094,"children":3095},{"style":137},[3096],{"type":22,"value":3097},"    to_encode.update({",{"type":16,"tag":114,"props":3099,"children":3100},{"style":148},[3101],{"type":22,"value":3102},"\"exp\"",{"type":16,"tag":114,"props":3104,"children":3105},{"style":137},[3106],{"type":22,"value":3107},": datetime.utcnow() ",{"type":16,"tag":114,"props":3109,"children":3110},{"style":131},[3111],{"type":22,"value":3112},"+",{"type":16,"tag":114,"props":3114,"children":3115},{"style":137},[3116],{"type":22,"value":3117}," expires_delta})\n",{"type":16,"tag":114,"props":3119,"children":3120},{"class":116,"line":301},[3121,3126,3131,3135,3139,3144,3148,3152],{"type":16,"tag":114,"props":3122,"children":3123},{"style":131},[3124],{"type":22,"value":3125},"    return",{"type":16,"tag":114,"props":3127,"children":3128},{"style":137},[3129],{"type":22,"value":3130}," jwt.encode(to_encode, ",{"type":16,"tag":114,"props":3132,"children":3133},{"style":199},[3134],{"type":22,"value":2983},{"type":16,"tag":114,"props":3136,"children":3137},{"style":137},[3138],{"type":22,"value":748},{"type":16,"tag":114,"props":3140,"children":3141},{"style":188},[3142],{"type":22,"value":3143},"algorithm",{"type":16,"tag":114,"props":3145,"children":3146},{"style":131},[3147],{"type":22,"value":629},{"type":16,"tag":114,"props":3149,"children":3150},{"style":199},[3151],{"type":22,"value":3000},{"type":16,"tag":114,"props":3153,"children":3154},{"style":137},[3155],{"type":22,"value":371},{"type":16,"tag":104,"props":3157,"children":3159},{"code":3158,"language":2748,"meta":8,"className":2914,"style":8},"# backend\u002Fapp\u002Frouters\u002Fauth.py\nfrom fastapi import APIRouter, Depends, HTTPException\nfrom pydantic import BaseModel\nfrom app.core.security import create_token\n\nrouter = APIRouter(prefix=\"\u002Fauth\", tags=[\"auth\"])\n\nclass LoginReq(BaseModel):\n    username: str\n    password: str\n\n@router.post(\"\u002Flogin\")\ndef login(payload: LoginReq):\n    if payload.username != \"admin\" or payload.password != \"123\":\n        raise HTTPException(401, \"Invalid credentials\")\n    return {\"access_token\": create_token({\"sub\": payload.username})}\n",[3160],{"type":16,"tag":81,"props":3161,"children":3162},{"__ignoreMap":8},[3163,3171,3192,3213,3234,3241,3300,3307,3334,3347,3359,3366,3387,3404,3450,3481],{"type":16,"tag":114,"props":3164,"children":3165},{"class":116,"line":117},[3166],{"type":16,"tag":114,"props":3167,"children":3168},{"style":121},[3169],{"type":22,"value":3170},"# backend\u002Fapp\u002Frouters\u002Fauth.py\n",{"type":16,"tag":114,"props":3172,"children":3173},{"class":116,"line":127},[3174,3178,3183,3187],{"type":16,"tag":114,"props":3175,"children":3176},{"style":131},[3177],{"type":22,"value":145},{"type":16,"tag":114,"props":3179,"children":3180},{"style":137},[3181],{"type":22,"value":3182}," fastapi ",{"type":16,"tag":114,"props":3184,"children":3185},{"style":131},[3186],{"type":22,"value":134},{"type":16,"tag":114,"props":3188,"children":3189},{"style":137},[3190],{"type":22,"value":3191}," APIRouter, Depends, HTTPException\n",{"type":16,"tag":114,"props":3193,"children":3194},{"class":116,"line":154},[3195,3199,3204,3208],{"type":16,"tag":114,"props":3196,"children":3197},{"style":131},[3198],{"type":22,"value":145},{"type":16,"tag":114,"props":3200,"children":3201},{"style":137},[3202],{"type":22,"value":3203}," pydantic ",{"type":16,"tag":114,"props":3205,"children":3206},{"style":131},[3207],{"type":22,"value":134},{"type":16,"tag":114,"props":3209,"children":3210},{"style":137},[3211],{"type":22,"value":3212}," BaseModel\n",{"type":16,"tag":114,"props":3214,"children":3215},{"class":116,"line":164},[3216,3220,3225,3229],{"type":16,"tag":114,"props":3217,"children":3218},{"style":131},[3219],{"type":22,"value":145},{"type":16,"tag":114,"props":3221,"children":3222},{"style":137},[3223],{"type":22,"value":3224}," app.core.security ",{"type":16,"tag":114,"props":3226,"children":3227},{"style":131},[3228],{"type":22,"value":134},{"type":16,"tag":114,"props":3230,"children":3231},{"style":137},[3232],{"type":22,"value":3233}," create_token\n",{"type":16,"tag":114,"props":3235,"children":3236},{"class":116,"line":184},[3237],{"type":16,"tag":114,"props":3238,"children":3239},{"emptyLinePlaceholder":158},[3240],{"type":22,"value":161},{"type":16,"tag":114,"props":3242,"children":3243},{"class":116,"line":205},[3244,3249,3253,3258,3263,3267,3272,3276,3281,3285,3290,3295],{"type":16,"tag":114,"props":3245,"children":3246},{"style":137},[3247],{"type":22,"value":3248},"router ",{"type":16,"tag":114,"props":3250,"children":3251},{"style":131},[3252],{"type":22,"value":629},{"type":16,"tag":114,"props":3254,"children":3255},{"style":137},[3256],{"type":22,"value":3257}," APIRouter(",{"type":16,"tag":114,"props":3259,"children":3260},{"style":188},[3261],{"type":22,"value":3262},"prefix",{"type":16,"tag":114,"props":3264,"children":3265},{"style":131},[3266],{"type":22,"value":629},{"type":16,"tag":114,"props":3268,"children":3269},{"style":148},[3270],{"type":22,"value":3271},"\"\u002Fauth\"",{"type":16,"tag":114,"props":3273,"children":3274},{"style":137},[3275],{"type":22,"value":748},{"type":16,"tag":114,"props":3277,"children":3278},{"style":188},[3279],{"type":22,"value":3280},"tags",{"type":16,"tag":114,"props":3282,"children":3283},{"style":131},[3284],{"type":22,"value":629},{"type":16,"tag":114,"props":3286,"children":3287},{"style":137},[3288],{"type":22,"value":3289},"[",{"type":16,"tag":114,"props":3291,"children":3292},{"style":148},[3293],{"type":22,"value":3294},"\"auth\"",{"type":16,"tag":114,"props":3296,"children":3297},{"style":137},[3298],{"type":22,"value":3299},"])\n",{"type":16,"tag":114,"props":3301,"children":3302},{"class":116,"line":222},[3303],{"type":16,"tag":114,"props":3304,"children":3305},{"emptyLinePlaceholder":158},[3306],{"type":22,"value":161},{"type":16,"tag":114,"props":3308,"children":3309},{"class":116,"line":240},[3310,3315,3320,3324,3329],{"type":16,"tag":114,"props":3311,"children":3312},{"style":131},[3313],{"type":22,"value":3314},"class",{"type":16,"tag":114,"props":3316,"children":3317},{"style":173},[3318],{"type":22,"value":3319}," LoginReq",{"type":16,"tag":114,"props":3321,"children":3322},{"style":137},[3323],{"type":22,"value":278},{"type":16,"tag":114,"props":3325,"children":3326},{"style":173},[3327],{"type":22,"value":3328},"BaseModel",{"type":16,"tag":114,"props":3330,"children":3331},{"style":137},[3332],{"type":22,"value":3333},"):\n",{"type":16,"tag":114,"props":3335,"children":3336},{"class":116,"line":249},[3337,3342],{"type":16,"tag":114,"props":3338,"children":3339},{"style":137},[3340],{"type":22,"value":3341},"    username: ",{"type":16,"tag":114,"props":3343,"children":3344},{"style":199},[3345],{"type":22,"value":3346},"str\n",{"type":16,"tag":114,"props":3348,"children":3349},{"class":116,"line":257},[3350,3355],{"type":16,"tag":114,"props":3351,"children":3352},{"style":137},[3353],{"type":22,"value":3354},"    password: ",{"type":16,"tag":114,"props":3356,"children":3357},{"style":199},[3358],{"type":22,"value":3346},{"type":16,"tag":114,"props":3360,"children":3361},{"class":116,"line":301},[3362],{"type":16,"tag":114,"props":3363,"children":3364},{"emptyLinePlaceholder":158},[3365],{"type":22,"value":161},{"type":16,"tag":114,"props":3367,"children":3368},{"class":116,"line":339},[3369,3374,3378,3383],{"type":16,"tag":114,"props":3370,"children":3371},{"style":173},[3372],{"type":22,"value":3373},"@router.post",{"type":16,"tag":114,"props":3375,"children":3376},{"style":137},[3377],{"type":22,"value":278},{"type":16,"tag":114,"props":3379,"children":3380},{"style":148},[3381],{"type":22,"value":3382},"\"\u002Flogin\"",{"type":16,"tag":114,"props":3384,"children":3385},{"style":137},[3386],{"type":22,"value":371},{"type":16,"tag":114,"props":3388,"children":3389},{"class":116,"line":374},[3390,3394,3399],{"type":16,"tag":114,"props":3391,"children":3392},{"style":131},[3393],{"type":22,"value":3024},{"type":16,"tag":114,"props":3395,"children":3396},{"style":173},[3397],{"type":22,"value":3398}," login",{"type":16,"tag":114,"props":3400,"children":3401},{"style":137},[3402],{"type":22,"value":3403},"(payload: LoginReq):\n",{"type":16,"tag":114,"props":3405,"children":3406},{"class":116,"line":416},[3407,3411,3416,3421,3426,3431,3436,3440,3445],{"type":16,"tag":114,"props":3408,"children":3409},{"style":131},[3410],{"type":22,"value":470},{"type":16,"tag":114,"props":3412,"children":3413},{"style":137},[3414],{"type":22,"value":3415}," payload.username ",{"type":16,"tag":114,"props":3417,"children":3418},{"style":131},[3419],{"type":22,"value":3420},"!=",{"type":16,"tag":114,"props":3422,"children":3423},{"style":148},[3424],{"type":22,"value":3425}," \"admin\"",{"type":16,"tag":114,"props":3427,"children":3428},{"style":131},[3429],{"type":22,"value":3430}," or",{"type":16,"tag":114,"props":3432,"children":3433},{"style":137},[3434],{"type":22,"value":3435}," payload.password ",{"type":16,"tag":114,"props":3437,"children":3438},{"style":131},[3439],{"type":22,"value":3420},{"type":16,"tag":114,"props":3441,"children":3442},{"style":148},[3443],{"type":22,"value":3444}," \"123\"",{"type":16,"tag":114,"props":3446,"children":3447},{"style":137},[3448],{"type":22,"value":3449},":\n",{"type":16,"tag":114,"props":3451,"children":3452},{"class":116,"line":424},[3453,3458,3463,3468,3472,3477],{"type":16,"tag":114,"props":3454,"children":3455},{"style":131},[3456],{"type":22,"value":3457},"        raise",{"type":16,"tag":114,"props":3459,"children":3460},{"style":137},[3461],{"type":22,"value":3462}," HTTPException(",{"type":16,"tag":114,"props":3464,"children":3465},{"style":199},[3466],{"type":22,"value":3467},"401",{"type":16,"tag":114,"props":3469,"children":3470},{"style":137},[3471],{"type":22,"value":748},{"type":16,"tag":114,"props":3473,"children":3474},{"style":148},[3475],{"type":22,"value":3476},"\"Invalid credentials\"",{"type":16,"tag":114,"props":3478,"children":3479},{"style":137},[3480],{"type":22,"value":371},{"type":16,"tag":114,"props":3482,"children":3483},{"class":116,"line":455},[3484,3488,3493,3498,3503,3508],{"type":16,"tag":114,"props":3485,"children":3486},{"style":131},[3487],{"type":22,"value":3125},{"type":16,"tag":114,"props":3489,"children":3490},{"style":137},[3491],{"type":22,"value":3492}," {",{"type":16,"tag":114,"props":3494,"children":3495},{"style":148},[3496],{"type":22,"value":3497},"\"access_token\"",{"type":16,"tag":114,"props":3499,"children":3500},{"style":137},[3501],{"type":22,"value":3502},": create_token({",{"type":16,"tag":114,"props":3504,"children":3505},{"style":148},[3506],{"type":22,"value":3507},"\"sub\"",{"type":16,"tag":114,"props":3509,"children":3510},{"style":137},[3511],{"type":22,"value":3512},": payload.username})}\n",{"type":16,"tag":57,"props":3514,"children":3516},{"id":3515},"_3-vue3-请求封装与拦截器",[3517],{"type":22,"value":3518},"3. Vue3 请求封装与拦截器",{"type":16,"tag":104,"props":3520,"children":3524},{"code":3521,"language":3522,"meta":8,"className":3523,"style":8},"\u002F\u002F frontend\u002Fsrc\u002Fapi\u002Fclient.ts\nimport axios from \"axios\"\nimport { useAuthStore } from \"@\u002Fstores\u002Fauth\"\n\nconst api = axios.create({ baseURL: \"\u002Fapi\", timeout: 5000 })\n\napi.interceptors.request.use(cfg => {\n  const token = useAuthStore().token\n  if (token) cfg.headers.Authorization = `Bearer ${token}`\n  return cfg\n})\n\napi.interceptors.response.use(\n  res => res.data,\n  err => {\n    if (err.response?.status === 401) useAuthStore().logout()\n    return Promise.reject(err)\n  }\n)\n\nexport default api\n","ts","language-ts shiki shiki-themes github-light github-dark",[3525],{"type":16,"tag":81,"props":3526,"children":3527},{"__ignoreMap":8},[3528,3536,3557,3578,3585,3635,3642,3673,3699,3731,3743,3751,3758,3774,3791,3807,3851,3877,3884,3891,3898],{"type":16,"tag":114,"props":3529,"children":3530},{"class":116,"line":117},[3531],{"type":16,"tag":114,"props":3532,"children":3533},{"style":121},[3534],{"type":22,"value":3535},"\u002F\u002F frontend\u002Fsrc\u002Fapi\u002Fclient.ts\n",{"type":16,"tag":114,"props":3537,"children":3538},{"class":116,"line":127},[3539,3543,3548,3552],{"type":16,"tag":114,"props":3540,"children":3541},{"style":131},[3542],{"type":22,"value":134},{"type":16,"tag":114,"props":3544,"children":3545},{"style":137},[3546],{"type":22,"value":3547}," axios ",{"type":16,"tag":114,"props":3549,"children":3550},{"style":131},[3551],{"type":22,"value":145},{"type":16,"tag":114,"props":3553,"children":3554},{"style":148},[3555],{"type":22,"value":3556}," \"axios\"\n",{"type":16,"tag":114,"props":3558,"children":3559},{"class":116,"line":154},[3560,3564,3569,3573],{"type":16,"tag":114,"props":3561,"children":3562},{"style":131},[3563],{"type":22,"value":134},{"type":16,"tag":114,"props":3565,"children":3566},{"style":137},[3567],{"type":22,"value":3568}," { useAuthStore } ",{"type":16,"tag":114,"props":3570,"children":3571},{"style":131},[3572],{"type":22,"value":145},{"type":16,"tag":114,"props":3574,"children":3575},{"style":148},[3576],{"type":22,"value":3577}," \"@\u002Fstores\u002Fauth\"\n",{"type":16,"tag":114,"props":3579,"children":3580},{"class":116,"line":164},[3581],{"type":16,"tag":114,"props":3582,"children":3583},{"emptyLinePlaceholder":158},[3584],{"type":22,"value":161},{"type":16,"tag":114,"props":3586,"children":3587},{"class":116,"line":184},[3588,3592,3597,3601,3606,3611,3616,3621,3626,3631],{"type":16,"tag":114,"props":3589,"children":3590},{"style":131},[3591],{"type":22,"value":2478},{"type":16,"tag":114,"props":3593,"children":3594},{"style":199},[3595],{"type":22,"value":3596}," api",{"type":16,"tag":114,"props":3598,"children":3599},{"style":131},[3600],{"type":22,"value":288},{"type":16,"tag":114,"props":3602,"children":3603},{"style":137},[3604],{"type":22,"value":3605}," axios.",{"type":16,"tag":114,"props":3607,"children":3608},{"style":173},[3609],{"type":22,"value":3610},"create",{"type":16,"tag":114,"props":3612,"children":3613},{"style":137},[3614],{"type":22,"value":3615},"({ baseURL: ",{"type":16,"tag":114,"props":3617,"children":3618},{"style":148},[3619],{"type":22,"value":3620},"\"\u002Fapi\"",{"type":16,"tag":114,"props":3622,"children":3623},{"style":137},[3624],{"type":22,"value":3625},", timeout: ",{"type":16,"tag":114,"props":3627,"children":3628},{"style":199},[3629],{"type":22,"value":3630},"5000",{"type":16,"tag":114,"props":3632,"children":3633},{"style":137},[3634],{"type":22,"value":2111},{"type":16,"tag":114,"props":3636,"children":3637},{"class":116,"line":205},[3638],{"type":16,"tag":114,"props":3639,"children":3640},{"emptyLinePlaceholder":158},[3641],{"type":22,"value":161},{"type":16,"tag":114,"props":3643,"children":3644},{"class":116,"line":222},[3645,3650,3655,3659,3664,3669],{"type":16,"tag":114,"props":3646,"children":3647},{"style":137},[3648],{"type":22,"value":3649},"api.interceptors.request.",{"type":16,"tag":114,"props":3651,"children":3652},{"style":173},[3653],{"type":22,"value":3654},"use",{"type":16,"tag":114,"props":3656,"children":3657},{"style":137},[3658],{"type":22,"value":278},{"type":16,"tag":114,"props":3660,"children":3661},{"style":188},[3662],{"type":22,"value":3663},"cfg",{"type":16,"tag":114,"props":3665,"children":3666},{"style":131},[3667],{"type":22,"value":3668}," =>",{"type":16,"tag":114,"props":3670,"children":3671},{"style":137},[3672],{"type":22,"value":181},{"type":16,"tag":114,"props":3674,"children":3675},{"class":116,"line":240},[3676,3680,3685,3689,3694],{"type":16,"tag":114,"props":3677,"children":3678},{"style":131},[3679],{"type":22,"value":307},{"type":16,"tag":114,"props":3681,"children":3682},{"style":199},[3683],{"type":22,"value":3684}," token",{"type":16,"tag":114,"props":3686,"children":3687},{"style":131},[3688],{"type":22,"value":288},{"type":16,"tag":114,"props":3690,"children":3691},{"style":173},[3692],{"type":22,"value":3693}," useAuthStore",{"type":16,"tag":114,"props":3695,"children":3696},{"style":137},[3697],{"type":22,"value":3698},"().token\n",{"type":16,"tag":114,"props":3700,"children":3701},{"class":116,"line":249},[3702,3707,3712,3716,3721,3726],{"type":16,"tag":114,"props":3703,"children":3704},{"style":131},[3705],{"type":22,"value":3706},"  if",{"type":16,"tag":114,"props":3708,"children":3709},{"style":137},[3710],{"type":22,"value":3711}," (token) cfg.headers.Authorization ",{"type":16,"tag":114,"props":3713,"children":3714},{"style":131},[3715],{"type":22,"value":629},{"type":16,"tag":114,"props":3717,"children":3718},{"style":148},[3719],{"type":22,"value":3720}," `Bearer ${",{"type":16,"tag":114,"props":3722,"children":3723},{"style":137},[3724],{"type":22,"value":3725},"token",{"type":16,"tag":114,"props":3727,"children":3728},{"style":148},[3729],{"type":22,"value":3730},"}`\n",{"type":16,"tag":114,"props":3732,"children":3733},{"class":116,"line":257},[3734,3738],{"type":16,"tag":114,"props":3735,"children":3736},{"style":131},[3737],{"type":22,"value":1515},{"type":16,"tag":114,"props":3739,"children":3740},{"style":137},[3741],{"type":22,"value":3742}," cfg\n",{"type":16,"tag":114,"props":3744,"children":3745},{"class":116,"line":301},[3746],{"type":16,"tag":114,"props":3747,"children":3748},{"style":137},[3749],{"type":22,"value":3750},"})\n",{"type":16,"tag":114,"props":3752,"children":3753},{"class":116,"line":339},[3754],{"type":16,"tag":114,"props":3755,"children":3756},{"emptyLinePlaceholder":158},[3757],{"type":22,"value":161},{"type":16,"tag":114,"props":3759,"children":3760},{"class":116,"line":374},[3761,3766,3770],{"type":16,"tag":114,"props":3762,"children":3763},{"style":137},[3764],{"type":22,"value":3765},"api.interceptors.response.",{"type":16,"tag":114,"props":3767,"children":3768},{"style":173},[3769],{"type":22,"value":3654},{"type":16,"tag":114,"props":3771,"children":3772},{"style":137},[3773],{"type":22,"value":1108},{"type":16,"tag":114,"props":3775,"children":3776},{"class":116,"line":416},[3777,3782,3786],{"type":16,"tag":114,"props":3778,"children":3779},{"style":188},[3780],{"type":22,"value":3781},"  res",{"type":16,"tag":114,"props":3783,"children":3784},{"style":131},[3785],{"type":22,"value":3668},{"type":16,"tag":114,"props":3787,"children":3788},{"style":137},[3789],{"type":22,"value":3790}," res.data,\n",{"type":16,"tag":114,"props":3792,"children":3793},{"class":116,"line":424},[3794,3799,3803],{"type":16,"tag":114,"props":3795,"children":3796},{"style":188},[3797],{"type":22,"value":3798},"  err",{"type":16,"tag":114,"props":3800,"children":3801},{"style":131},[3802],{"type":22,"value":3668},{"type":16,"tag":114,"props":3804,"children":3805},{"style":137},[3806],{"type":22,"value":181},{"type":16,"tag":114,"props":3808,"children":3809},{"class":116,"line":455},[3810,3814,3819,3824,3829,3833,3838,3842,3847],{"type":16,"tag":114,"props":3811,"children":3812},{"style":131},[3813],{"type":22,"value":470},{"type":16,"tag":114,"props":3815,"children":3816},{"style":137},[3817],{"type":22,"value":3818}," (err.response?.status ",{"type":16,"tag":114,"props":3820,"children":3821},{"style":131},[3822],{"type":22,"value":3823},"===",{"type":16,"tag":114,"props":3825,"children":3826},{"style":199},[3827],{"type":22,"value":3828}," 401",{"type":16,"tag":114,"props":3830,"children":3831},{"style":137},[3832],{"type":22,"value":663},{"type":16,"tag":114,"props":3834,"children":3835},{"style":173},[3836],{"type":22,"value":3837},"useAuthStore",{"type":16,"tag":114,"props":3839,"children":3840},{"style":137},[3841],{"type":22,"value":710},{"type":16,"tag":114,"props":3843,"children":3844},{"style":173},[3845],{"type":22,"value":3846},"logout",{"type":16,"tag":114,"props":3848,"children":3849},{"style":137},[3850],{"type":22,"value":959},{"type":16,"tag":114,"props":3852,"children":3853},{"class":116,"line":464},[3854,3858,3863,3867,3872],{"type":16,"tag":114,"props":3855,"children":3856},{"style":131},[3857],{"type":22,"value":3125},{"type":16,"tag":114,"props":3859,"children":3860},{"style":199},[3861],{"type":22,"value":3862}," Promise",{"type":16,"tag":114,"props":3864,"children":3865},{"style":137},[3866],{"type":22,"value":484},{"type":16,"tag":114,"props":3868,"children":3869},{"style":173},[3870],{"type":22,"value":3871},"reject",{"type":16,"tag":114,"props":3873,"children":3874},{"style":137},[3875],{"type":22,"value":3876},"(err)\n",{"type":16,"tag":114,"props":3878,"children":3879},{"class":116,"line":502},[3880],{"type":16,"tag":114,"props":3881,"children":3882},{"style":137},[3883],{"type":22,"value":968},{"type":16,"tag":114,"props":3885,"children":3886},{"class":116,"line":511},[3887],{"type":16,"tag":114,"props":3888,"children":3889},{"style":137},[3890],{"type":22,"value":371},{"type":16,"tag":114,"props":3892,"children":3893},{"class":116,"line":544},[3894],{"type":16,"tag":114,"props":3895,"children":3896},{"emptyLinePlaceholder":158},[3897],{"type":22,"value":161},{"type":16,"tag":114,"props":3899,"children":3900},{"class":116,"line":570},[3901,3905,3910],{"type":16,"tag":114,"props":3902,"children":3903},{"style":131},[3904],{"type":22,"value":263},{"type":16,"tag":114,"props":3906,"children":3907},{"style":131},[3908],{"type":22,"value":3909}," default",{"type":16,"tag":114,"props":3911,"children":3912},{"style":137},[3913],{"type":22,"value":3914}," api\n",{"type":16,"tag":104,"props":3916,"children":3918},{"code":3917,"language":3522,"meta":8,"className":3523,"style":8},"\u002F\u002F frontend\u002Fsrc\u002Fstores\u002Fauth.ts\nimport { defineStore } from \"pinia\"\nimport { ref } from \"vue\"\n\nexport const useAuthStore = defineStore(\"auth\", () => {\n  const token = ref\u003Cstring | null>(localStorage.getItem(\"token\"))\n  const setToken = (t: string) => { token.value = t; localStorage.setItem(\"token\", t) }\n  const logout = () => { token.value = null; localStorage.removeItem(\"token\") }\n  return { token, setToken, logout }\n})\n",[3919],{"type":16,"tag":81,"props":3920,"children":3921},{"__ignoreMap":8},[3922,3930,3951,3972,3979,4025,4085,4159,4218,4230],{"type":16,"tag":114,"props":3923,"children":3924},{"class":116,"line":117},[3925],{"type":16,"tag":114,"props":3926,"children":3927},{"style":121},[3928],{"type":22,"value":3929},"\u002F\u002F frontend\u002Fsrc\u002Fstores\u002Fauth.ts\n",{"type":16,"tag":114,"props":3931,"children":3932},{"class":116,"line":127},[3933,3937,3942,3946],{"type":16,"tag":114,"props":3934,"children":3935},{"style":131},[3936],{"type":22,"value":134},{"type":16,"tag":114,"props":3938,"children":3939},{"style":137},[3940],{"type":22,"value":3941}," { defineStore } ",{"type":16,"tag":114,"props":3943,"children":3944},{"style":131},[3945],{"type":22,"value":145},{"type":16,"tag":114,"props":3947,"children":3948},{"style":148},[3949],{"type":22,"value":3950}," \"pinia\"\n",{"type":16,"tag":114,"props":3952,"children":3953},{"class":116,"line":154},[3954,3958,3963,3967],{"type":16,"tag":114,"props":3955,"children":3956},{"style":131},[3957],{"type":22,"value":134},{"type":16,"tag":114,"props":3959,"children":3960},{"style":137},[3961],{"type":22,"value":3962}," { ref } ",{"type":16,"tag":114,"props":3964,"children":3965},{"style":131},[3966],{"type":22,"value":145},{"type":16,"tag":114,"props":3968,"children":3969},{"style":148},[3970],{"type":22,"value":3971}," \"vue\"\n",{"type":16,"tag":114,"props":3973,"children":3974},{"class":116,"line":164},[3975],{"type":16,"tag":114,"props":3976,"children":3977},{"emptyLinePlaceholder":158},[3978],{"type":22,"value":161},{"type":16,"tag":114,"props":3980,"children":3981},{"class":116,"line":184},[3982,3986,3991,3995,3999,4004,4008,4012,4017,4021],{"type":16,"tag":114,"props":3983,"children":3984},{"style":131},[3985],{"type":22,"value":263},{"type":16,"tag":114,"props":3987,"children":3988},{"style":131},[3989],{"type":22,"value":3990}," const",{"type":16,"tag":114,"props":3992,"children":3993},{"style":199},[3994],{"type":22,"value":3693},{"type":16,"tag":114,"props":3996,"children":3997},{"style":131},[3998],{"type":22,"value":288},{"type":16,"tag":114,"props":4000,"children":4001},{"style":173},[4002],{"type":22,"value":4003}," defineStore",{"type":16,"tag":114,"props":4005,"children":4006},{"style":137},[4007],{"type":22,"value":278},{"type":16,"tag":114,"props":4009,"children":4010},{"style":148},[4011],{"type":22,"value":3294},{"type":16,"tag":114,"props":4013,"children":4014},{"style":137},[4015],{"type":22,"value":4016},", () ",{"type":16,"tag":114,"props":4018,"children":4019},{"style":131},[4020],{"type":22,"value":448},{"type":16,"tag":114,"props":4022,"children":4023},{"style":137},[4024],{"type":22,"value":181},{"type":16,"tag":114,"props":4026,"children":4027},{"class":116,"line":205},[4028,4032,4036,4040,4044,4048,4053,4057,4061,4066,4071,4075,4080],{"type":16,"tag":114,"props":4029,"children":4030},{"style":131},[4031],{"type":22,"value":307},{"type":16,"tag":114,"props":4033,"children":4034},{"style":199},[4035],{"type":22,"value":3684},{"type":16,"tag":114,"props":4037,"children":4038},{"style":131},[4039],{"type":22,"value":288},{"type":16,"tag":114,"props":4041,"children":4042},{"style":173},[4043],{"type":22,"value":321},{"type":16,"tag":114,"props":4045,"children":4046},{"style":137},[4047],{"type":22,"value":326},{"type":16,"tag":114,"props":4049,"children":4050},{"style":199},[4051],{"type":22,"value":4052},"string",{"type":16,"tag":114,"props":4054,"children":4055},{"style":131},[4056],{"type":22,"value":399},{"type":16,"tag":114,"props":4058,"children":4059},{"style":199},[4060],{"type":22,"value":404},{"type":16,"tag":114,"props":4062,"children":4063},{"style":137},[4064],{"type":22,"value":4065},">(localStorage.",{"type":16,"tag":114,"props":4067,"children":4068},{"style":173},[4069],{"type":22,"value":4070},"getItem",{"type":16,"tag":114,"props":4072,"children":4073},{"style":137},[4074],{"type":22,"value":278},{"type":16,"tag":114,"props":4076,"children":4077},{"style":148},[4078],{"type":22,"value":4079},"\"token\"",{"type":16,"tag":114,"props":4081,"children":4082},{"style":137},[4083],{"type":22,"value":4084},"))\n",{"type":16,"tag":114,"props":4086,"children":4087},{"class":116,"line":222},[4088,4092,4097,4101,4105,4110,4114,4119,4123,4127,4132,4136,4141,4146,4150,4154],{"type":16,"tag":114,"props":4089,"children":4090},{"style":131},[4091],{"type":22,"value":307},{"type":16,"tag":114,"props":4093,"children":4094},{"style":173},[4095],{"type":22,"value":4096}," setToken",{"type":16,"tag":114,"props":4098,"children":4099},{"style":131},[4100],{"type":22,"value":288},{"type":16,"tag":114,"props":4102,"children":4103},{"style":137},[4104],{"type":22,"value":475},{"type":16,"tag":114,"props":4106,"children":4107},{"style":188},[4108],{"type":22,"value":4109},"t",{"type":16,"tag":114,"props":4111,"children":4112},{"style":131},[4113],{"type":22,"value":196},{"type":16,"tag":114,"props":4115,"children":4116},{"style":199},[4117],{"type":22,"value":4118}," string",{"type":16,"tag":114,"props":4120,"children":4121},{"style":137},[4122],{"type":22,"value":663},{"type":16,"tag":114,"props":4124,"children":4125},{"style":131},[4126],{"type":22,"value":448},{"type":16,"tag":114,"props":4128,"children":4129},{"style":137},[4130],{"type":22,"value":4131}," { token.value ",{"type":16,"tag":114,"props":4133,"children":4134},{"style":131},[4135],{"type":22,"value":629},{"type":16,"tag":114,"props":4137,"children":4138},{"style":137},[4139],{"type":22,"value":4140}," t; localStorage.",{"type":16,"tag":114,"props":4142,"children":4143},{"style":173},[4144],{"type":22,"value":4145},"setItem",{"type":16,"tag":114,"props":4147,"children":4148},{"style":137},[4149],{"type":22,"value":278},{"type":16,"tag":114,"props":4151,"children":4152},{"style":148},[4153],{"type":22,"value":4079},{"type":16,"tag":114,"props":4155,"children":4156},{"style":137},[4157],{"type":22,"value":4158},", t) }\n",{"type":16,"tag":114,"props":4160,"children":4161},{"class":116,"line":240},[4162,4166,4171,4175,4179,4183,4187,4191,4195,4200,4205,4209,4213],{"type":16,"tag":114,"props":4163,"children":4164},{"style":131},[4165],{"type":22,"value":307},{"type":16,"tag":114,"props":4167,"children":4168},{"style":173},[4169],{"type":22,"value":4170}," logout",{"type":16,"tag":114,"props":4172,"children":4173},{"style":131},[4174],{"type":22,"value":288},{"type":16,"tag":114,"props":4176,"children":4177},{"style":137},[4178],{"type":22,"value":443},{"type":16,"tag":114,"props":4180,"children":4181},{"style":131},[4182],{"type":22,"value":448},{"type":16,"tag":114,"props":4184,"children":4185},{"style":137},[4186],{"type":22,"value":4131},{"type":16,"tag":114,"props":4188,"children":4189},{"style":131},[4190],{"type":22,"value":629},{"type":16,"tag":114,"props":4192,"children":4193},{"style":199},[4194],{"type":22,"value":404},{"type":16,"tag":114,"props":4196,"children":4197},{"style":137},[4198],{"type":22,"value":4199},"; localStorage.",{"type":16,"tag":114,"props":4201,"children":4202},{"style":173},[4203],{"type":22,"value":4204},"removeItem",{"type":16,"tag":114,"props":4206,"children":4207},{"style":137},[4208],{"type":22,"value":278},{"type":16,"tag":114,"props":4210,"children":4211},{"style":148},[4212],{"type":22,"value":4079},{"type":16,"tag":114,"props":4214,"children":4215},{"style":137},[4216],{"type":22,"value":4217},") }\n",{"type":16,"tag":114,"props":4219,"children":4220},{"class":116,"line":249},[4221,4225],{"type":16,"tag":114,"props":4222,"children":4223},{"style":131},[4224],{"type":22,"value":1515},{"type":16,"tag":114,"props":4226,"children":4227},{"style":137},[4228],{"type":22,"value":4229}," { token, setToken, logout }\n",{"type":16,"tag":114,"props":4231,"children":4232},{"class":116,"line":257},[4233],{"type":16,"tag":114,"props":4234,"children":4235},{"style":137},[4236],{"type":22,"value":3750},{"type":16,"tag":57,"props":4238,"children":4240},{"id":4239},"_4-本地开发与跨域配置",[4241],{"type":22,"value":4242},"4. 本地开发与跨域配置",{"type":16,"tag":24,"props":4244,"children":4245},{},[4246],{"type":16,"tag":30,"props":4247,"children":4248},{},[4249,4251,4257],{"type":22,"value":4250},"Vite 代理 (",{"type":16,"tag":81,"props":4252,"children":4254},{"className":4253},[],[4255],{"type":22,"value":4256},"frontend\u002Fvite.config.ts",{"type":22,"value":758},{"type":16,"tag":104,"props":4259,"children":4261},{"code":4260,"language":3522,"meta":8,"className":3523,"style":8},"export default defineConfig({\n  server: {\n    proxy: {\n      \"\u002Fapi\": {\n        target: \"http:\u002F\u002F127.0.0.1:8000\",\n        changeOrigin: true,\n        rewrite: path => path.replace(\u002F^\\\u002Fapi\u002F, \"\")\n      }\n    }\n  }\n})\n",[4262],{"type":16,"tag":81,"props":4263,"children":4264},{"__ignoreMap":8},[4265,4286,4294,4302,4315,4332,4348,4422,4429,4436,4443],{"type":16,"tag":114,"props":4266,"children":4267},{"class":116,"line":117},[4268,4272,4276,4281],{"type":16,"tag":114,"props":4269,"children":4270},{"style":131},[4271],{"type":22,"value":263},{"type":16,"tag":114,"props":4273,"children":4274},{"style":131},[4275],{"type":22,"value":3909},{"type":16,"tag":114,"props":4277,"children":4278},{"style":173},[4279],{"type":22,"value":4280}," defineConfig",{"type":16,"tag":114,"props":4282,"children":4283},{"style":137},[4284],{"type":22,"value":4285},"({\n",{"type":16,"tag":114,"props":4287,"children":4288},{"class":116,"line":127},[4289],{"type":16,"tag":114,"props":4290,"children":4291},{"style":137},[4292],{"type":22,"value":4293},"  server: {\n",{"type":16,"tag":114,"props":4295,"children":4296},{"class":116,"line":154},[4297],{"type":16,"tag":114,"props":4298,"children":4299},{"style":137},[4300],{"type":22,"value":4301},"    proxy: {\n",{"type":16,"tag":114,"props":4303,"children":4304},{"class":116,"line":164},[4305,4310],{"type":16,"tag":114,"props":4306,"children":4307},{"style":148},[4308],{"type":22,"value":4309},"      \"\u002Fapi\"",{"type":16,"tag":114,"props":4311,"children":4312},{"style":137},[4313],{"type":22,"value":4314},": {\n",{"type":16,"tag":114,"props":4316,"children":4317},{"class":116,"line":184},[4318,4323,4328],{"type":16,"tag":114,"props":4319,"children":4320},{"style":137},[4321],{"type":22,"value":4322},"        target: ",{"type":16,"tag":114,"props":4324,"children":4325},{"style":148},[4326],{"type":22,"value":4327},"\"http:\u002F\u002F127.0.0.1:8000\"",{"type":16,"tag":114,"props":4329,"children":4330},{"style":137},[4331],{"type":22,"value":768},{"type":16,"tag":114,"props":4333,"children":4334},{"class":116,"line":205},[4335,4340,4344],{"type":16,"tag":114,"props":4336,"children":4337},{"style":137},[4338],{"type":22,"value":4339},"        changeOrigin: ",{"type":16,"tag":114,"props":4341,"children":4342},{"style":199},[4343],{"type":22,"value":2106},{"type":16,"tag":114,"props":4345,"children":4346},{"style":137},[4347],{"type":22,"value":768},{"type":16,"tag":114,"props":4349,"children":4350},{"class":116,"line":222},[4351,4356,4361,4366,4370,4375,4380,4384,4388,4393,4399,4405,4409,4413,4418],{"type":16,"tag":114,"props":4352,"children":4353},{"style":173},[4354],{"type":22,"value":4355},"        rewrite",{"type":16,"tag":114,"props":4357,"children":4358},{"style":137},[4359],{"type":22,"value":4360},": ",{"type":16,"tag":114,"props":4362,"children":4363},{"style":188},[4364],{"type":22,"value":4365},"path",{"type":16,"tag":114,"props":4367,"children":4368},{"style":131},[4369],{"type":22,"value":3668},{"type":16,"tag":114,"props":4371,"children":4372},{"style":137},[4373],{"type":22,"value":4374}," path.",{"type":16,"tag":114,"props":4376,"children":4377},{"style":173},[4378],{"type":22,"value":4379},"replace",{"type":16,"tag":114,"props":4381,"children":4382},{"style":137},[4383],{"type":22,"value":278},{"type":16,"tag":114,"props":4385,"children":4386},{"style":148},[4387],{"type":22,"value":1893},{"type":16,"tag":114,"props":4389,"children":4390},{"style":131},[4391],{"type":22,"value":4392},"^",{"type":16,"tag":114,"props":4394,"children":4396},{"style":4395},"--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold",[4397],{"type":22,"value":4398},"\\\u002F",{"type":16,"tag":114,"props":4400,"children":4402},{"style":4401},"--shiki-default:#032F62;--shiki-dark:#DBEDFF",[4403],{"type":22,"value":4404},"api",{"type":16,"tag":114,"props":4406,"children":4407},{"style":148},[4408],{"type":22,"value":1893},{"type":16,"tag":114,"props":4410,"children":4411},{"style":137},[4412],{"type":22,"value":748},{"type":16,"tag":114,"props":4414,"children":4415},{"style":148},[4416],{"type":22,"value":4417},"\"\"",{"type":16,"tag":114,"props":4419,"children":4420},{"style":137},[4421],{"type":22,"value":371},{"type":16,"tag":114,"props":4423,"children":4424},{"class":116,"line":240},[4425],{"type":16,"tag":114,"props":4426,"children":4427},{"style":137},[4428],{"type":22,"value":1287},{"type":16,"tag":114,"props":4430,"children":4431},{"class":116,"line":249},[4432],{"type":16,"tag":114,"props":4433,"children":4434},{"style":137},[4435],{"type":22,"value":2041},{"type":16,"tag":114,"props":4437,"children":4438},{"class":116,"line":257},[4439],{"type":16,"tag":114,"props":4440,"children":4441},{"style":137},[4442],{"type":22,"value":968},{"type":16,"tag":114,"props":4444,"children":4445},{"class":116,"line":301},[4446],{"type":16,"tag":114,"props":4447,"children":4448},{"style":137},[4449],{"type":22,"value":3750},{"type":16,"tag":24,"props":4451,"children":4452},{},[4453],{"type":16,"tag":30,"props":4454,"children":4455},{},[4456,4458,4464],{"type":22,"value":4457},"FastAPI CORS (",{"type":16,"tag":81,"props":4459,"children":4461},{"className":4460},[],[4462],{"type":22,"value":4463},"backend\u002Fapp\u002Fmain.py",{"type":22,"value":758},{"type":16,"tag":104,"props":4466,"children":4468},{"code":4467,"language":2748,"meta":8,"className":2914,"style":8},"from fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\n\napp = FastAPI()\napp.add_middleware(CORSMiddleware, allow_origins=[\"*\"], allow_credentials=True,\n                   allow_methods=[\"*\"], allow_headers=[\"*\"])\n",[4469],{"type":16,"tag":81,"props":4470,"children":4471},{"__ignoreMap":8},[4472,4492,4513,4520,4537,4586],{"type":16,"tag":114,"props":4473,"children":4474},{"class":116,"line":117},[4475,4479,4483,4487],{"type":16,"tag":114,"props":4476,"children":4477},{"style":131},[4478],{"type":22,"value":145},{"type":16,"tag":114,"props":4480,"children":4481},{"style":137},[4482],{"type":22,"value":3182},{"type":16,"tag":114,"props":4484,"children":4485},{"style":131},[4486],{"type":22,"value":134},{"type":16,"tag":114,"props":4488,"children":4489},{"style":137},[4490],{"type":22,"value":4491}," FastAPI\n",{"type":16,"tag":114,"props":4493,"children":4494},{"class":116,"line":127},[4495,4499,4504,4508],{"type":16,"tag":114,"props":4496,"children":4497},{"style":131},[4498],{"type":22,"value":145},{"type":16,"tag":114,"props":4500,"children":4501},{"style":137},[4502],{"type":22,"value":4503}," fastapi.middleware.cors ",{"type":16,"tag":114,"props":4505,"children":4506},{"style":131},[4507],{"type":22,"value":134},{"type":16,"tag":114,"props":4509,"children":4510},{"style":137},[4511],{"type":22,"value":4512}," CORSMiddleware\n",{"type":16,"tag":114,"props":4514,"children":4515},{"class":116,"line":154},[4516],{"type":16,"tag":114,"props":4517,"children":4518},{"emptyLinePlaceholder":158},[4519],{"type":22,"value":161},{"type":16,"tag":114,"props":4521,"children":4522},{"class":116,"line":164},[4523,4528,4532],{"type":16,"tag":114,"props":4524,"children":4525},{"style":137},[4526],{"type":22,"value":4527},"app ",{"type":16,"tag":114,"props":4529,"children":4530},{"style":131},[4531],{"type":22,"value":629},{"type":16,"tag":114,"props":4533,"children":4534},{"style":137},[4535],{"type":22,"value":4536}," FastAPI()\n",{"type":16,"tag":114,"props":4538,"children":4539},{"class":116,"line":184},[4540,4545,4550,4554,4558,4563,4568,4573,4577,4582],{"type":16,"tag":114,"props":4541,"children":4542},{"style":137},[4543],{"type":22,"value":4544},"app.add_middleware(CORSMiddleware, ",{"type":16,"tag":114,"props":4546,"children":4547},{"style":188},[4548],{"type":22,"value":4549},"allow_origins",{"type":16,"tag":114,"props":4551,"children":4552},{"style":131},[4553],{"type":22,"value":629},{"type":16,"tag":114,"props":4555,"children":4556},{"style":137},[4557],{"type":22,"value":3289},{"type":16,"tag":114,"props":4559,"children":4560},{"style":148},[4561],{"type":22,"value":4562},"\"*\"",{"type":16,"tag":114,"props":4564,"children":4565},{"style":137},[4566],{"type":22,"value":4567},"], ",{"type":16,"tag":114,"props":4569,"children":4570},{"style":188},[4571],{"type":22,"value":4572},"allow_credentials",{"type":16,"tag":114,"props":4574,"children":4575},{"style":131},[4576],{"type":22,"value":629},{"type":16,"tag":114,"props":4578,"children":4579},{"style":199},[4580],{"type":22,"value":4581},"True",{"type":16,"tag":114,"props":4583,"children":4584},{"style":137},[4585],{"type":22,"value":768},{"type":16,"tag":114,"props":4587,"children":4588},{"class":116,"line":205},[4589,4594,4598,4602,4606,4610,4615,4619,4623,4627],{"type":16,"tag":114,"props":4590,"children":4591},{"style":188},[4592],{"type":22,"value":4593},"                   allow_methods",{"type":16,"tag":114,"props":4595,"children":4596},{"style":131},[4597],{"type":22,"value":629},{"type":16,"tag":114,"props":4599,"children":4600},{"style":137},[4601],{"type":22,"value":3289},{"type":16,"tag":114,"props":4603,"children":4604},{"style":148},[4605],{"type":22,"value":4562},{"type":16,"tag":114,"props":4607,"children":4608},{"style":137},[4609],{"type":22,"value":4567},{"type":16,"tag":114,"props":4611,"children":4612},{"style":188},[4613],{"type":22,"value":4614},"allow_headers",{"type":16,"tag":114,"props":4616,"children":4617},{"style":131},[4618],{"type":22,"value":629},{"type":16,"tag":114,"props":4620,"children":4621},{"style":137},[4622],{"type":22,"value":3289},{"type":16,"tag":114,"props":4624,"children":4625},{"style":148},[4626],{"type":22,"value":4562},{"type":16,"tag":114,"props":4628,"children":4629},{"style":137},[4630],{"type":22,"value":3299},{"type":16,"tag":57,"props":4632,"children":4634},{"id":4633},"_5-docker-compose-编排与-nginx",[4635],{"type":22,"value":4636},"5. Docker Compose 编排与 Nginx",{"type":16,"tag":104,"props":4638,"children":4642},{"code":4639,"language":4640,"meta":8,"className":4641,"style":8},"# docker-compose.yml\nservices:\n  backend:\n    build: .\u002Fbackend\n    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload\n    volumes: [.\u002Fbackend:\u002Fapp]\n  frontend:\n    build: .\u002Ffrontend\n    command: npm run dev -- --host\n    volumes: [.\u002Ffrontend:\u002Fapp, \u002Fapp\u002Fnode_modules]\n    ports: [\"5173:5173\"]\n  nginx:\n    image: nginx:alpine\n    ports: [\"80:80\"]\n    volumes: [\".\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf:ro\"]\n    depends_on: [backend, frontend]\n","yaml","language-yaml shiki shiki-themes github-light github-dark",[4643],{"type":16,"tag":81,"props":4644,"children":4645},{"__ignoreMap":8},[4646,4654,4666,4678,4695,4712,4735,4747,4763,4779,4808,4829,4841,4858,4878,4898],{"type":16,"tag":114,"props":4647,"children":4648},{"class":116,"line":117},[4649],{"type":16,"tag":114,"props":4650,"children":4651},{"style":121},[4652],{"type":22,"value":4653},"# docker-compose.yml\n",{"type":16,"tag":114,"props":4655,"children":4656},{"class":116,"line":127},[4657,4662],{"type":16,"tag":114,"props":4658,"children":4659},{"style":2258},[4660],{"type":22,"value":4661},"services",{"type":16,"tag":114,"props":4663,"children":4664},{"style":137},[4665],{"type":22,"value":3449},{"type":16,"tag":114,"props":4667,"children":4668},{"class":116,"line":154},[4669,4674],{"type":16,"tag":114,"props":4670,"children":4671},{"style":2258},[4672],{"type":22,"value":4673},"  backend",{"type":16,"tag":114,"props":4675,"children":4676},{"style":137},[4677],{"type":22,"value":3449},{"type":16,"tag":114,"props":4679,"children":4680},{"class":116,"line":164},[4681,4686,4690],{"type":16,"tag":114,"props":4682,"children":4683},{"style":2258},[4684],{"type":22,"value":4685},"    build",{"type":16,"tag":114,"props":4687,"children":4688},{"style":137},[4689],{"type":22,"value":4360},{"type":16,"tag":114,"props":4691,"children":4692},{"style":148},[4693],{"type":22,"value":4694},".\u002Fbackend\n",{"type":16,"tag":114,"props":4696,"children":4697},{"class":116,"line":184},[4698,4703,4707],{"type":16,"tag":114,"props":4699,"children":4700},{"style":2258},[4701],{"type":22,"value":4702},"    command",{"type":16,"tag":114,"props":4704,"children":4705},{"style":137},[4706],{"type":22,"value":4360},{"type":16,"tag":114,"props":4708,"children":4709},{"style":148},[4710],{"type":22,"value":4711},"uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload\n",{"type":16,"tag":114,"props":4713,"children":4714},{"class":116,"line":205},[4715,4720,4725,4730],{"type":16,"tag":114,"props":4716,"children":4717},{"style":2258},[4718],{"type":22,"value":4719},"    volumes",{"type":16,"tag":114,"props":4721,"children":4722},{"style":137},[4723],{"type":22,"value":4724},": [",{"type":16,"tag":114,"props":4726,"children":4727},{"style":148},[4728],{"type":22,"value":4729},".\u002Fbackend:\u002Fapp",{"type":16,"tag":114,"props":4731,"children":4732},{"style":137},[4733],{"type":22,"value":4734},"]\n",{"type":16,"tag":114,"props":4736,"children":4737},{"class":116,"line":222},[4738,4743],{"type":16,"tag":114,"props":4739,"children":4740},{"style":2258},[4741],{"type":22,"value":4742},"  frontend",{"type":16,"tag":114,"props":4744,"children":4745},{"style":137},[4746],{"type":22,"value":3449},{"type":16,"tag":114,"props":4748,"children":4749},{"class":116,"line":240},[4750,4754,4758],{"type":16,"tag":114,"props":4751,"children":4752},{"style":2258},[4753],{"type":22,"value":4685},{"type":16,"tag":114,"props":4755,"children":4756},{"style":137},[4757],{"type":22,"value":4360},{"type":16,"tag":114,"props":4759,"children":4760},{"style":148},[4761],{"type":22,"value":4762},".\u002Ffrontend\n",{"type":16,"tag":114,"props":4764,"children":4765},{"class":116,"line":249},[4766,4770,4774],{"type":16,"tag":114,"props":4767,"children":4768},{"style":2258},[4769],{"type":22,"value":4702},{"type":16,"tag":114,"props":4771,"children":4772},{"style":137},[4773],{"type":22,"value":4360},{"type":16,"tag":114,"props":4775,"children":4776},{"style":148},[4777],{"type":22,"value":4778},"npm run dev -- --host\n",{"type":16,"tag":114,"props":4780,"children":4781},{"class":116,"line":257},[4782,4786,4790,4795,4799,4804],{"type":16,"tag":114,"props":4783,"children":4784},{"style":2258},[4785],{"type":22,"value":4719},{"type":16,"tag":114,"props":4787,"children":4788},{"style":137},[4789],{"type":22,"value":4724},{"type":16,"tag":114,"props":4791,"children":4792},{"style":148},[4793],{"type":22,"value":4794},".\u002Ffrontend:\u002Fapp",{"type":16,"tag":114,"props":4796,"children":4797},{"style":137},[4798],{"type":22,"value":748},{"type":16,"tag":114,"props":4800,"children":4801},{"style":148},[4802],{"type":22,"value":4803},"\u002Fapp\u002Fnode_modules",{"type":16,"tag":114,"props":4805,"children":4806},{"style":137},[4807],{"type":22,"value":4734},{"type":16,"tag":114,"props":4809,"children":4810},{"class":116,"line":301},[4811,4816,4820,4825],{"type":16,"tag":114,"props":4812,"children":4813},{"style":2258},[4814],{"type":22,"value":4815},"    ports",{"type":16,"tag":114,"props":4817,"children":4818},{"style":137},[4819],{"type":22,"value":4724},{"type":16,"tag":114,"props":4821,"children":4822},{"style":148},[4823],{"type":22,"value":4824},"\"5173:5173\"",{"type":16,"tag":114,"props":4826,"children":4827},{"style":137},[4828],{"type":22,"value":4734},{"type":16,"tag":114,"props":4830,"children":4831},{"class":116,"line":339},[4832,4837],{"type":16,"tag":114,"props":4833,"children":4834},{"style":2258},[4835],{"type":22,"value":4836},"  nginx",{"type":16,"tag":114,"props":4838,"children":4839},{"style":137},[4840],{"type":22,"value":3449},{"type":16,"tag":114,"props":4842,"children":4843},{"class":116,"line":374},[4844,4849,4853],{"type":16,"tag":114,"props":4845,"children":4846},{"style":2258},[4847],{"type":22,"value":4848},"    image",{"type":16,"tag":114,"props":4850,"children":4851},{"style":137},[4852],{"type":22,"value":4360},{"type":16,"tag":114,"props":4854,"children":4855},{"style":148},[4856],{"type":22,"value":4857},"nginx:alpine\n",{"type":16,"tag":114,"props":4859,"children":4860},{"class":116,"line":416},[4861,4865,4869,4874],{"type":16,"tag":114,"props":4862,"children":4863},{"style":2258},[4864],{"type":22,"value":4815},{"type":16,"tag":114,"props":4866,"children":4867},{"style":137},[4868],{"type":22,"value":4724},{"type":16,"tag":114,"props":4870,"children":4871},{"style":148},[4872],{"type":22,"value":4873},"\"80:80\"",{"type":16,"tag":114,"props":4875,"children":4876},{"style":137},[4877],{"type":22,"value":4734},{"type":16,"tag":114,"props":4879,"children":4880},{"class":116,"line":424},[4881,4885,4889,4894],{"type":16,"tag":114,"props":4882,"children":4883},{"style":2258},[4884],{"type":22,"value":4719},{"type":16,"tag":114,"props":4886,"children":4887},{"style":137},[4888],{"type":22,"value":4724},{"type":16,"tag":114,"props":4890,"children":4891},{"style":148},[4892],{"type":22,"value":4893},"\".\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf:ro\"",{"type":16,"tag":114,"props":4895,"children":4896},{"style":137},[4897],{"type":22,"value":4734},{"type":16,"tag":114,"props":4899,"children":4900},{"class":116,"line":455},[4901,4906,4910,4915,4919,4924],{"type":16,"tag":114,"props":4902,"children":4903},{"style":2258},[4904],{"type":22,"value":4905},"    depends_on",{"type":16,"tag":114,"props":4907,"children":4908},{"style":137},[4909],{"type":22,"value":4724},{"type":16,"tag":114,"props":4911,"children":4912},{"style":148},[4913],{"type":22,"value":4914},"backend",{"type":16,"tag":114,"props":4916,"children":4917},{"style":137},[4918],{"type":22,"value":748},{"type":16,"tag":114,"props":4920,"children":4921},{"style":148},[4922],{"type":22,"value":4923},"frontend",{"type":16,"tag":114,"props":4925,"children":4926},{"style":137},[4927],{"type":22,"value":4734},{"type":16,"tag":104,"props":4929,"children":4933},{"code":4930,"language":4931,"meta":8,"className":4932,"style":8},"# nginx.conf\nserver {\n  listen 80;\n  location \u002F {\n    proxy_pass http:\u002F\u002Ffrontend:5173;\n  }\n  location \u002Fapi\u002F {\n    proxy_pass http:\u002F\u002Fbackend:8000\u002Fapi\u002F;\n    proxy_set_header Host $host;\n  }\n}\n","nginx","language-nginx shiki shiki-themes github-light github-dark",[4934],{"type":16,"tag":81,"props":4935,"children":4936},{"__ignoreMap":8},[4937,4945,4957,4975,4993,5006,5013,5029,5041,5054,5061],{"type":16,"tag":114,"props":4938,"children":4939},{"class":116,"line":117},[4940],{"type":16,"tag":114,"props":4941,"children":4942},{"style":121},[4943],{"type":22,"value":4944},"# nginx.conf\n",{"type":16,"tag":114,"props":4946,"children":4947},{"class":116,"line":127},[4948,4953],{"type":16,"tag":114,"props":4949,"children":4950},{"style":131},[4951],{"type":22,"value":4952},"server",{"type":16,"tag":114,"props":4954,"children":4955},{"style":137},[4956],{"type":22,"value":181},{"type":16,"tag":114,"props":4958,"children":4959},{"class":116,"line":154},[4960,4965,4970],{"type":16,"tag":114,"props":4961,"children":4962},{"style":131},[4963],{"type":22,"value":4964},"  listen ",{"type":16,"tag":114,"props":4966,"children":4967},{"style":199},[4968],{"type":22,"value":4969},"80",{"type":16,"tag":114,"props":4971,"children":4972},{"style":137},[4973],{"type":22,"value":4974},";\n",{"type":16,"tag":114,"props":4976,"children":4977},{"class":116,"line":164},[4978,4983,4988],{"type":16,"tag":114,"props":4979,"children":4980},{"style":131},[4981],{"type":22,"value":4982},"  location",{"type":16,"tag":114,"props":4984,"children":4985},{"style":173},[4986],{"type":22,"value":4987}," \u002F ",{"type":16,"tag":114,"props":4989,"children":4990},{"style":137},[4991],{"type":22,"value":4992},"{\n",{"type":16,"tag":114,"props":4994,"children":4995},{"class":116,"line":184},[4996,5001],{"type":16,"tag":114,"props":4997,"children":4998},{"style":131},[4999],{"type":22,"value":5000},"    proxy_pass ",{"type":16,"tag":114,"props":5002,"children":5003},{"style":137},[5004],{"type":22,"value":5005},"http:\u002F\u002Ffrontend:5173;\n",{"type":16,"tag":114,"props":5007,"children":5008},{"class":116,"line":205},[5009],{"type":16,"tag":114,"props":5010,"children":5011},{"style":137},[5012],{"type":22,"value":968},{"type":16,"tag":114,"props":5014,"children":5015},{"class":116,"line":222},[5016,5020,5025],{"type":16,"tag":114,"props":5017,"children":5018},{"style":131},[5019],{"type":22,"value":4982},{"type":16,"tag":114,"props":5021,"children":5022},{"style":173},[5023],{"type":22,"value":5024}," \u002Fapi\u002F ",{"type":16,"tag":114,"props":5026,"children":5027},{"style":137},[5028],{"type":22,"value":4992},{"type":16,"tag":114,"props":5030,"children":5031},{"class":116,"line":240},[5032,5036],{"type":16,"tag":114,"props":5033,"children":5034},{"style":131},[5035],{"type":22,"value":5000},{"type":16,"tag":114,"props":5037,"children":5038},{"style":137},[5039],{"type":22,"value":5040},"http:\u002F\u002Fbackend:8000\u002Fapi\u002F;\n",{"type":16,"tag":114,"props":5042,"children":5043},{"class":116,"line":249},[5044,5049],{"type":16,"tag":114,"props":5045,"children":5046},{"style":131},[5047],{"type":22,"value":5048},"    proxy_set_header ",{"type":16,"tag":114,"props":5050,"children":5051},{"style":137},[5052],{"type":22,"value":5053},"Host $host;\n",{"type":16,"tag":114,"props":5055,"children":5056},{"class":116,"line":257},[5057],{"type":16,"tag":114,"props":5058,"children":5059},{"style":137},[5060],{"type":22,"value":968},{"type":16,"tag":114,"props":5062,"children":5063},{"class":116,"line":301},[5064],{"type":16,"tag":114,"props":5065,"children":5066},{"style":137},[5067],{"type":22,"value":246},{"type":16,"tag":24,"props":5069,"children":5070},{},[5071],{"type":16,"tag":30,"props":5072,"children":5073},{},[5074],{"type":22,"value":5075},"启动",{"type":16,"tag":104,"props":5077,"children":5079},{"code":5078,"language":2727,"meta":8,"className":2728,"style":8},"docker compose up -d\n# 访问 http:\u002F\u002Flocalhost\n",[5080],{"type":16,"tag":81,"props":5081,"children":5082},{"__ignoreMap":8},[5083,5106],{"type":16,"tag":114,"props":5084,"children":5085},{"class":116,"line":117},[5086,5091,5096,5101],{"type":16,"tag":114,"props":5087,"children":5088},{"style":173},[5089],{"type":22,"value":5090},"docker",{"type":16,"tag":114,"props":5092,"children":5093},{"style":148},[5094],{"type":22,"value":5095}," compose",{"type":16,"tag":114,"props":5097,"children":5098},{"style":148},[5099],{"type":22,"value":5100}," up",{"type":16,"tag":114,"props":5102,"children":5103},{"style":199},[5104],{"type":22,"value":5105}," -d\n",{"type":16,"tag":114,"props":5107,"children":5108},{"class":116,"line":127},[5109],{"type":16,"tag":114,"props":5110,"children":5111},{"style":121},[5112],{"type":22,"value":5113},"# 访问 http:\u002F\u002Flocalhost\n",{"type":16,"tag":2344,"props":5115,"children":5116},{},[5117],{"type":22,"value":2655},{"title":8,"searchDepth":127,"depth":127,"links":5119},[5120,5121,5122,5123,5124],{"id":2704,"depth":127,"text":2707},{"id":2907,"depth":127,"text":2910},{"id":3515,"depth":127,"text":3518},{"id":4239,"depth":127,"text":4242},{"id":4633,"depth":127,"text":4636},"content:blog:2-post.md","blog\u002F2-post.md","blog\u002F2-post",{"_path":5129,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":5130,"description":5131,"date":5132,"code":5133,"body":5134,"_type":2667,"_id":5179,"_source":2669,"_file":5180,"_stem":5181,"_extension":2672},"\u002Fblog\u002Ffirst-post","开始使用 Nuxt 4 重构博客","记录从旧项目迁移到 Nuxt 4 的过程与心得。","2026-05-06","D4E5F7",{"type":13,"children":5135,"toc":5176},[5136,5142,5147,5152],{"type":16,"tag":17,"props":5137,"children":5139},{"id":5138},"你好nuxt-4",[5140],{"type":22,"value":5141},"你好，Nuxt 4",{"type":16,"tag":24,"props":5143,"children":5144},{},[5145],{"type":22,"value":5146},"这是第一篇测试文章。Nuxt 4 带来了更清晰的目录结构和更好的性能。",{"type":16,"tag":57,"props":5148,"children":5150},{"id":5149},"主要变化",[5151],{"type":22,"value":5149},{"type":16,"tag":2586,"props":5153,"children":5154},{},[5155,5166,5171],{"type":16,"tag":2533,"props":5156,"children":5157},{},[5158,5164],{"type":16,"tag":81,"props":5159,"children":5161},{"className":5160},[],[5162],{"type":22,"value":5163},"app\u002F",{"type":22,"value":5165}," 目录成为默认应用代码存放处",{"type":16,"tag":2533,"props":5167,"children":5168},{},[5169],{"type":22,"value":5170},"更高效的构建工具链",{"type":16,"tag":2533,"props":5172,"children":5173},{},[5174],{"type":22,"value":5175},"...",{"title":8,"searchDepth":127,"depth":127,"links":5177},[5178],{"id":5149,"depth":127,"text":5149},"content:blog:first-post.md","blog\u002Ffirst-post.md","blog\u002Ffirst-post",1788161223942]