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