[{"data":1,"prerenderedAt":1255},["ShallowReactive",2],{"post-nuxt-content-lessons":3},{"id":4,"title":5,"author":6,"body":7,"description":1241,"draft":1242,"extension":1243,"image":1244,"meta":1245,"navigation":752,"path":1246,"pinned":1242,"published":1247,"seo":1248,"stem":1249,"tags":1250,"__hash__":1254},"posts\u002Fposts\u002Fnuxt-content-lessons\u002Findex.md","Nuxt Content v3 踩坑记录：目录、图片与那些反直觉的设计",null,{"type":8,"value":9,"toc":1231},"minimark",[10,14,18,21,25,29,42,45,52,140,147,150,157,449,459,463,466,469,495,509,512,537,543,546,549,552,557,564,798,803,847,851,854,861,864,920,926,1156,1166,1169,1221,1224,1227],[11,12,13],"h1",{"id":13},"背景",[15,16,17],"p",{},"最近在用 Nuxt 3 + Nuxt Content v3 搭建一个个人博客，参考了一个 SvelteKit 项目（svaf）的架构。原以为换个框架只是语法差异，没想到在几个关键功能上踩了不少坑。",[15,19,20],{},"这篇文章记录了三个核心问题的排查过程和最终方案。",[11,22,24],{"id":23},"问题一目录导航toc只有-h2","问题一：目录导航（TOC）只有 h2",[26,27,28],"h2",{"id":28},"现象",[15,30,31,32,36,37,41],{},"Nuxt Content 内置了 TOC 数据，挂在 ",[33,34,35],"code",{},"post.body.toc"," 上。直接用它渲染目录，发现",[38,39,40],"strong",{},"只有 h2 标题，h3 全部丢失","。",[26,43,44],{"id":44},"排查",[15,46,47,48,51],{},"查了官方文档，发现 ",[33,49,50],{},"build.markdown.toc.depth"," 可以控制深度：",[53,54,59],"pre",{"className":55,"code":56,"language":57,"meta":58,"style":58},"language-typescript shiki shiki-themes github-light github-dark","\u002F\u002F nuxt.config.ts\ncontent: {\n  build: {\n    markdown: {\n      toc: { depth: 3 }  \u002F\u002F 包含 h3\n    }\n  }\n}\n","typescript","",[33,60,61,70,81,89,97,122,128,134],{"__ignoreMap":58},[62,63,66],"span",{"class":64,"line":65},"line",1,[62,67,69],{"class":68},"sHbNN","\u002F\u002F nuxt.config.ts\n",[62,71,73,77],{"class":64,"line":72},2,[62,74,76],{"class":75},"sw2iP","content",[62,78,80],{"class":79},"sIX_F",": {\n",[62,82,84,87],{"class":64,"line":83},3,[62,85,86],{"class":75},"  build",[62,88,80],{"class":79},[62,90,92,95],{"class":64,"line":91},4,[62,93,94],{"class":75},"    markdown",[62,96,80],{"class":79},[62,98,100,103,106,109,112,116,119],{"class":64,"line":99},5,[62,101,102],{"class":75},"      toc",[62,104,105],{"class":79},": { ",[62,107,108],{"class":75},"depth",[62,110,111],{"class":79},": ",[62,113,115],{"class":114},"suQ91","3",[62,117,118],{"class":79}," }  ",[62,120,121],{"class":68},"\u002F\u002F 包含 h3\n",[62,123,125],{"class":64,"line":124},6,[62,126,127],{"class":79},"    }\n",[62,129,131],{"class":64,"line":130},7,[62,132,133],{"class":79},"  }\n",[62,135,137],{"class":64,"line":136},8,[62,138,139],{"class":79},"}\n",[15,141,142,143,146],{},"配置改了，清了缓存重启，",[38,144,145],{},"依然只有 h2","。这个配置似乎不起作用。",[26,148,149],{"id":149},"最终方案",[15,151,152,153,156],{},"放弃依赖 Nuxt Content 的 toc，直接从 ",[33,154,155],{},"post.body"," 的 AST 里递归提取所有标题：",[53,158,160],{"className":55,"code":159,"language":57,"meta":58,"style":58},"function extractHeadings(node: any, list: Heading[] = []): Heading[] {\n  if (!node) return list\n  if (Array.isArray(node)) {\n    \u002F\u002F minimark 格式：[\"h2\", {id: \"xxx\"}, \"文本\"]\n    if (typeof node[0] === 'string' && \u002F^h[1-6]$\u002F.test(node[0])) {\n      const level = Number(node[0].charAt(1))\n      const text = extractText(node.slice(2)).trim()\n      list.push({ id: node[1]?.id || slugify(text), text, level })\n    }\n    for (const child of node) extractHeadings(child, list)\n  }\n  return list\n}\n",[33,161,162,211,231,244,249,311,343,375,400,405,431,436,444],{"__ignoreMap":58},[62,163,164,168,171,174,178,181,184,187,190,192,195,198,201,204,206,208],{"class":64,"line":65},[62,165,167],{"class":166},"siTax","function",[62,169,170],{"class":75}," extractHeadings",[62,172,173],{"class":79},"(",[62,175,177],{"class":176},"sPK6S","node",[62,179,180],{"class":166},":",[62,182,183],{"class":114}," any",[62,185,186],{"class":79},", ",[62,188,189],{"class":176},"list",[62,191,180],{"class":166},[62,193,194],{"class":75}," Heading",[62,196,197],{"class":79},"[] ",[62,199,200],{"class":166},"=",[62,202,203],{"class":79}," [])",[62,205,180],{"class":166},[62,207,194],{"class":75},[62,209,210],{"class":79},"[] {\n",[62,212,213,216,219,222,225,228],{"class":64,"line":72},[62,214,215],{"class":166},"  if",[62,217,218],{"class":79}," (",[62,220,221],{"class":166},"!",[62,223,224],{"class":79},"node) ",[62,226,227],{"class":166},"return",[62,229,230],{"class":79}," list\n",[62,232,233,235,238,241],{"class":64,"line":83},[62,234,215],{"class":166},[62,236,237],{"class":79}," (Array.",[62,239,240],{"class":75},"isArray",[62,242,243],{"class":79},"(node)) {\n",[62,245,246],{"class":64,"line":91},[62,247,248],{"class":68},"    \u002F\u002F minimark 格式：[\"h2\", {id: \"xxx\"}, \"文本\"]\n",[62,250,251,254,256,259,262,265,268,271,275,278,281,284,288,291,294,297,300,303,306,308],{"class":64,"line":99},[62,252,253],{"class":166},"    if",[62,255,218],{"class":79},[62,257,258],{"class":166},"typeof",[62,260,261],{"class":79}," node[",[62,263,264],{"class":114},"0",[62,266,267],{"class":79},"] ",[62,269,270],{"class":166},"===",[62,272,274],{"class":273},"scXbn"," 'string'",[62,276,277],{"class":166}," &&",[62,279,280],{"class":273}," \u002F",[62,282,283],{"class":166},"^",[62,285,287],{"class":286},"sPKmS","h",[62,289,290],{"class":114},"[1-6]",[62,292,293],{"class":166},"$",[62,295,296],{"class":273},"\u002F",[62,298,299],{"class":79},".",[62,301,302],{"class":75},"test",[62,304,305],{"class":79},"(node[",[62,307,264],{"class":114},[62,309,310],{"class":79},"])) {\n",[62,312,313,316,319,322,325,327,329,332,335,337,340],{"class":64,"line":124},[62,314,315],{"class":166},"      const",[62,317,318],{"class":114}," level",[62,320,321],{"class":166}," =",[62,323,324],{"class":75}," Number",[62,326,305],{"class":79},[62,328,264],{"class":114},[62,330,331],{"class":79},"].",[62,333,334],{"class":75},"charAt",[62,336,173],{"class":79},[62,338,339],{"class":114},"1",[62,341,342],{"class":79},"))\n",[62,344,345,347,350,352,355,358,361,363,366,369,372],{"class":64,"line":130},[62,346,315],{"class":166},[62,348,349],{"class":114}," text",[62,351,321],{"class":166},[62,353,354],{"class":75}," extractText",[62,356,357],{"class":79},"(node.",[62,359,360],{"class":75},"slice",[62,362,173],{"class":79},[62,364,365],{"class":114},"2",[62,367,368],{"class":79},")).",[62,370,371],{"class":75},"trim",[62,373,374],{"class":79},"()\n",[62,376,377,380,383,386,388,391,394,397],{"class":64,"line":136},[62,378,379],{"class":79},"      list.",[62,381,382],{"class":75},"push",[62,384,385],{"class":79},"({ id: node[",[62,387,339],{"class":114},[62,389,390],{"class":79},"]?.id ",[62,392,393],{"class":166},"||",[62,395,396],{"class":75}," slugify",[62,398,399],{"class":79},"(text), text, level })\n",[62,401,403],{"class":64,"line":402},9,[62,404,127],{"class":79},[62,406,408,411,413,416,419,422,425,428],{"class":64,"line":407},10,[62,409,410],{"class":166},"    for",[62,412,218],{"class":79},[62,414,415],{"class":166},"const",[62,417,418],{"class":114}," child",[62,420,421],{"class":166}," of",[62,423,424],{"class":79}," node) ",[62,426,427],{"class":75},"extractHeadings",[62,429,430],{"class":79},"(child, list)\n",[62,432,434],{"class":64,"line":433},11,[62,435,133],{"class":79},[62,437,439,442],{"class":64,"line":438},12,[62,440,441],{"class":166},"  return",[62,443,230],{"class":79},[62,445,447],{"class":64,"line":446},13,[62,448,139],{"class":79},[15,450,451,452,454,455,458],{},"关键点：Nuxt Content v3 的 body 使用 ",[38,453,8],{}," 格式，标题节点是数组 ",[33,456,457],{},"[\"h2\", {id: \"xxx\"}, \"标题文本\"]","，不是对象。",[11,460,462],{"id":461},"问题二图片路径解析错误","问题二：图片路径解析错误",[26,464,28],{"id":465},"现象-1",[15,467,468],{},"markdown 里用相对路径引用图片：",[53,470,474],{"className":471,"code":472,"language":473,"meta":58,"style":58},"language-markdown shiki shiki-themes github-light github-dark","![SSHFS挂载效果](img\u002Fsshfs.avif)\n","markdown",[33,475,476],{"__ignoreMap":58},[62,477,478,481,485,488,492],{"class":64,"line":65},[62,479,480],{"class":79},"![",[62,482,484],{"class":483},"smiIW","SSHFS挂载效果",[62,486,487],{"class":79},"](",[62,489,491],{"class":490},"szbl0","img\u002Fsshfs.avif",[62,493,494],{"class":79},")\n",[15,496,497,498,501,502,505,506,41],{},"HTML 输出的 ",[33,499,500],{},"\u003Cimg src=\"img\u002Fsshfs.avif\">"," 看起来没问题，但浏览器请求的却是 ",[33,503,504],{},"\u002Fposts\u002Fimg\u002Fsshfs.avif"," 而不是 ",[33,507,508],{},"\u002Fposts\u002Fsshfs\u002Fimg\u002Fsshfs.avif",[26,510,44],{"id":511},"排查-1",[513,514,515,523,526,532],"ol",{},[516,517,518,519,522],"li",{},"检查了 ",[33,520,521],{},"\u003Cbase>"," 标签 —— 没有",[516,524,525],{},"检查了重定向 —— 没有",[516,527,528,529,531],{},"直接访问 ",[33,530,508],{}," —— 返回 200 OK",[516,533,528,534,536],{},[33,535,504],{}," —— 404",[15,538,539,540,41],{},"服务器路由没问题，问题是",[38,541,542],{},"浏览器解析相对路径时基于了错误的基准 URL",[15,544,545],{},"这可能和 Nuxt 的客户端路由有关：SPA 导航时，浏览器的\"当前页面\"和 Vue Router 的\"当前路由\"不同步，导致相对路径解析出错。",[26,547,149],{"id":548},"最终方案-1",[15,550,551],{},"两步解决：",[15,553,554],{},[38,555,556],{},"第一步：自定义 ProseImg 组件",[15,558,559,560,563],{},"创建 ",[33,561,562],{},"components\u002Fcontent\u002FProseImg.vue","，覆盖 Nuxt Content 默认的图片渲染：",[53,565,569],{"className":566,"code":567,"language":568,"meta":58,"style":58},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup>\nconst route = useRoute()\nconst resolvedSrc = computed(() => {\n  if (!props.src) return ''\n  if (props.src.startsWith('\u002F') || props.src.startsWith('http')) return props.src\n  const base = route.path.replace(\u002F\\\u002F$\u002F, '')\n  return `${base}\u002F${props.src}`\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cimg :src=\"resolvedSrc\" :alt=\"alt\" \u002F>\n\u003C\u002Ftemplate>\n","vue",[33,570,571,586,600,621,637,675,710,734,739,748,754,763,790],{"__ignoreMap":58},[62,572,573,576,580,583],{"class":64,"line":65},[62,574,575],{"class":79},"\u003C",[62,577,579],{"class":578},"sbB4o","script",[62,581,582],{"class":75}," setup",[62,584,585],{"class":79},">\n",[62,587,588,590,593,595,598],{"class":64,"line":72},[62,589,415],{"class":166},[62,591,592],{"class":114}," route",[62,594,321],{"class":166},[62,596,597],{"class":75}," useRoute",[62,599,374],{"class":79},[62,601,602,604,607,609,612,615,618],{"class":64,"line":83},[62,603,415],{"class":166},[62,605,606],{"class":114}," resolvedSrc",[62,608,321],{"class":166},[62,610,611],{"class":75}," computed",[62,613,614],{"class":79},"(() ",[62,616,617],{"class":166},"=>",[62,619,620],{"class":79}," {\n",[62,622,623,625,627,629,632,634],{"class":64,"line":91},[62,624,215],{"class":166},[62,626,218],{"class":79},[62,628,221],{"class":166},[62,630,631],{"class":79},"props.src) ",[62,633,227],{"class":166},[62,635,636],{"class":273}," ''\n",[62,638,639,641,644,647,649,652,655,657,660,662,664,667,670,672],{"class":64,"line":99},[62,640,215],{"class":166},[62,642,643],{"class":79}," (props.src.",[62,645,646],{"class":75},"startsWith",[62,648,173],{"class":79},[62,650,651],{"class":273},"'\u002F'",[62,653,654],{"class":79},") ",[62,656,393],{"class":166},[62,658,659],{"class":79}," props.src.",[62,661,646],{"class":75},[62,663,173],{"class":79},[62,665,666],{"class":273},"'http'",[62,668,669],{"class":79},")) ",[62,671,227],{"class":166},[62,673,674],{"class":79}," props.src\n",[62,676,677,680,683,685,688,691,693,695,699,701,703,705,708],{"class":64,"line":124},[62,678,679],{"class":166},"  const",[62,681,682],{"class":114}," base",[62,684,321],{"class":166},[62,686,687],{"class":79}," route.path.",[62,689,690],{"class":75},"replace",[62,692,173],{"class":79},[62,694,296],{"class":273},[62,696,698],{"class":697},"sCnZR","\\\u002F",[62,700,293],{"class":166},[62,702,296],{"class":273},[62,704,186],{"class":79},[62,706,707],{"class":273},"''",[62,709,494],{"class":79},[62,711,712,714,717,720,723,726,728,731],{"class":64,"line":130},[62,713,441],{"class":166},[62,715,716],{"class":273}," `${",[62,718,719],{"class":79},"base",[62,721,722],{"class":273},"}\u002F${",[62,724,725],{"class":79},"props",[62,727,299],{"class":273},[62,729,730],{"class":79},"src",[62,732,733],{"class":273},"}`\n",[62,735,736],{"class":64,"line":136},[62,737,738],{"class":79},"})\n",[62,740,741,744,746],{"class":64,"line":402},[62,742,743],{"class":79},"\u003C\u002F",[62,745,579],{"class":578},[62,747,585],{"class":79},[62,749,750],{"class":64,"line":407},[62,751,753],{"emptyLinePlaceholder":752},true,"\n",[62,755,756,758,761],{"class":64,"line":433},[62,757,575],{"class":79},[62,759,760],{"class":578},"template",[62,762,585],{"class":79},[62,764,765,768,771,774,776,779,782,784,787],{"class":64,"line":438},[62,766,767],{"class":79},"  \u003C",[62,769,770],{"class":578},"img",[62,772,773],{"class":75}," :src",[62,775,200],{"class":79},[62,777,778],{"class":273},"\"resolvedSrc\"",[62,780,781],{"class":75}," :alt",[62,783,200],{"class":79},[62,785,786],{"class":273},"\"alt\"",[62,788,789],{"class":79}," \u002F>\n",[62,791,792,794,796],{"class":64,"line":446},[62,793,743],{"class":79},[62,795,760],{"class":578},[62,797,585],{"class":79},[15,799,800],{},[38,801,802],{},"第二步：启用 MDC prose 组件",[53,804,806],{"className":55,"code":805,"language":57,"meta":58,"style":58},"\u002F\u002F nuxt.config.ts\nmdc: {\n  components: {\n    prose: true  \u002F\u002F 让 Nuxt Content 使用自定义 ProseImg\n  }\n}\n",[33,807,808,812,819,826,839,843],{"__ignoreMap":58},[62,809,810],{"class":64,"line":65},[62,811,69],{"class":68},[62,813,814,817],{"class":64,"line":72},[62,815,816],{"class":75},"mdc",[62,818,80],{"class":79},[62,820,821,824],{"class":64,"line":83},[62,822,823],{"class":75},"  components",[62,825,80],{"class":79},[62,827,828,831,833,836],{"class":64,"line":91},[62,829,830],{"class":75},"    prose",[62,832,111],{"class":79},[62,834,835],{"class":114},"true",[62,837,838],{"class":68},"  \u002F\u002F 让 Nuxt Content 使用自定义 ProseImg\n",[62,840,841],{"class":64,"line":99},[62,842,133],{"class":79},[62,844,845],{"class":64,"line":124},[62,846,139],{"class":79},[11,848,850],{"id":849},"问题三content-目录的图片如何服务","问题三：content 目录的图片如何服务",[26,852,28],{"id":853},"现象-2",[15,855,856,857,860],{},"原站（SvelteKit）的图片放在 ",[33,858,859],{},"content\u002Fposts\u002F{slug}\u002Fimg\u002F"," 下，markdown 用相对路径引用，一切正常。Nuxt 项目里，content 目录的文件不会被当成静态资源服务。",[26,862,863],{"id":863},"方案对比",[865,866,867,883],"table",{},[868,869,870],"thead",{},[871,872,873,877,880],"tr",{},[874,875,876],"th",{},"方案",[874,878,879],{},"优点",[874,881,882],{},"缺点",[884,885,886,898,909],"tbody",{},[871,887,888,892,895],{},[889,890,891],"td",{},"复制到 public\u002F",[889,893,894],{},"简单",[889,896,897],{},"文件重复，需同步",[871,899,900,903,906],{},[889,901,902],{},"Vite 插件拦截",[889,904,905],{},"原站方案",[889,907,908],{},"Nuxt 不直接支持",[871,910,911,914,917],{},[889,912,913],{},"Server Route",[889,915,916],{},"不复制文件",[889,918,919],{},"需要手动写路由",[15,921,922,923,925],{},"最终选择 ",[38,924,913],{},"，和原站的 Vite 插件思路一致：",[53,927,929],{"className":55,"code":928,"language":57,"meta":58,"style":58},"\u002F\u002F server\u002Froutes\u002Fposts\u002F[slug]\u002Fimg\u002F[...file].ts\nexport default defineEventHandler(async (event) => {\n  const url = getRequestURL(event)\n  const match = url.pathname.match(\u002F^\\\u002Fposts\\\u002F([^\u002F]+)\\\u002Fimg\\\u002F(.+)$\u002F)\n  if (!match) return\n\n  const [, slug, filename] = match\n  const filePath = resolve('content\u002Fposts', slug, 'img', filename)\n  const data = await readFile(filePath)\n  setResponseHeader(event, 'content-type', MIME[ext])\n  return data\n})\n",[33,930,931,936,963,978,1042,1056,1060,1082,1108,1126,1145,1152],{"__ignoreMap":58},[62,932,933],{"class":64,"line":65},[62,934,935],{"class":68},"\u002F\u002F server\u002Froutes\u002Fposts\u002F[slug]\u002Fimg\u002F[...file].ts\n",[62,937,938,941,944,947,949,952,954,957,959,961],{"class":64,"line":72},[62,939,940],{"class":166},"export",[62,942,943],{"class":166}," default",[62,945,946],{"class":75}," defineEventHandler",[62,948,173],{"class":79},[62,950,951],{"class":166},"async",[62,953,218],{"class":79},[62,955,956],{"class":176},"event",[62,958,654],{"class":79},[62,960,617],{"class":166},[62,962,620],{"class":79},[62,964,965,967,970,972,975],{"class":64,"line":83},[62,966,679],{"class":166},[62,968,969],{"class":114}," url",[62,971,321],{"class":166},[62,973,974],{"class":75}," getRequestURL",[62,976,977],{"class":79},"(event)\n",[62,979,980,982,985,987,990,993,995,997,999,1001,1004,1006,1008,1011,1013,1016,1019,1022,1024,1026,1028,1030,1032,1034,1036,1038,1040],{"class":64,"line":91},[62,981,679],{"class":166},[62,983,984],{"class":114}," match",[62,986,321],{"class":166},[62,988,989],{"class":79}," url.pathname.",[62,991,992],{"class":75},"match",[62,994,173],{"class":79},[62,996,296],{"class":273},[62,998,283],{"class":166},[62,1000,698],{"class":697},[62,1002,1003],{"class":286},"posts",[62,1005,698],{"class":697},[62,1007,173],{"class":286},[62,1009,1010],{"class":114},"[",[62,1012,283],{"class":166},[62,1014,1015],{"class":114},"\u002F]",[62,1017,1018],{"class":166},"+",[62,1020,1021],{"class":286},")",[62,1023,698],{"class":697},[62,1025,770],{"class":286},[62,1027,698],{"class":697},[62,1029,173],{"class":286},[62,1031,299],{"class":114},[62,1033,1018],{"class":166},[62,1035,1021],{"class":286},[62,1037,293],{"class":166},[62,1039,296],{"class":273},[62,1041,494],{"class":79},[62,1043,1044,1046,1048,1050,1053],{"class":64,"line":99},[62,1045,215],{"class":166},[62,1047,218],{"class":79},[62,1049,221],{"class":166},[62,1051,1052],{"class":79},"match) ",[62,1054,1055],{"class":166},"return\n",[62,1057,1058],{"class":64,"line":124},[62,1059,753],{"emptyLinePlaceholder":752},[62,1061,1062,1064,1067,1070,1072,1075,1077,1079],{"class":64,"line":130},[62,1063,679],{"class":166},[62,1065,1066],{"class":79}," [, ",[62,1068,1069],{"class":114},"slug",[62,1071,186],{"class":79},[62,1073,1074],{"class":114},"filename",[62,1076,267],{"class":79},[62,1078,200],{"class":166},[62,1080,1081],{"class":79}," match\n",[62,1083,1084,1086,1089,1091,1094,1096,1099,1102,1105],{"class":64,"line":136},[62,1085,679],{"class":166},[62,1087,1088],{"class":114}," filePath",[62,1090,321],{"class":166},[62,1092,1093],{"class":75}," resolve",[62,1095,173],{"class":79},[62,1097,1098],{"class":273},"'content\u002Fposts'",[62,1100,1101],{"class":79},", slug, ",[62,1103,1104],{"class":273},"'img'",[62,1106,1107],{"class":79},", filename)\n",[62,1109,1110,1112,1115,1117,1120,1123],{"class":64,"line":402},[62,1111,679],{"class":166},[62,1113,1114],{"class":114}," data",[62,1116,321],{"class":166},[62,1118,1119],{"class":166}," await",[62,1121,1122],{"class":75}," readFile",[62,1124,1125],{"class":79},"(filePath)\n",[62,1127,1128,1131,1134,1137,1139,1142],{"class":64,"line":407},[62,1129,1130],{"class":75},"  setResponseHeader",[62,1132,1133],{"class":79},"(event, ",[62,1135,1136],{"class":273},"'content-type'",[62,1138,186],{"class":79},[62,1140,1141],{"class":114},"MIME",[62,1143,1144],{"class":79},"[ext])\n",[62,1146,1147,1149],{"class":64,"line":433},[62,1148,441],{"class":166},[62,1150,1151],{"class":79}," data\n",[62,1153,1154],{"class":64,"line":438},[62,1155,738],{"class":79},[15,1157,1158,1159,1161,1162,1165],{},"请求 ",[33,1160,508],{}," → 读取 ",[33,1163,1164],{},"content\u002Fposts\u002Fsshfs\u002Fimg\u002Fsshfs.avif"," → 返回图片。",[11,1167,1168],{"id":1168},"总结",[865,1170,1171,1183],{},[868,1172,1173],{},[871,1174,1175,1178,1181],{},[874,1176,1177],{},"问题",[874,1179,1180],{},"根因",[874,1182,876],{},[884,1184,1185,1199,1210],{},[871,1186,1187,1190,1196],{},[889,1188,1189],{},"TOC 只有 h2",[889,1191,1192,1195],{},[33,1193,1194],{},"toc.depth"," 配置不生效",[889,1197,1198],{},"从 body AST 手动提取",[871,1200,1201,1204,1207],{},[889,1202,1203],{},"图片路径错误",[889,1205,1206],{},"SPA 路由导致相对路径解析基准错误",[889,1208,1209],{},"ProseImg 组件 + mdc.prose 配置",[871,1211,1212,1215,1218],{},[889,1213,1214],{},"content 图片 404",[889,1216,1217],{},"content 目录不暴露给浏览器",[889,1219,1220],{},"Server Route 直接读取返回",[15,1222,1223],{},"Nuxt Content v3 的设计理念是\"图片放 public\u002F，用绝对路径\"。如果想像 SvelteKit 项目那样把图片和文章放一起，需要额外做不少工作。",[15,1225,1226],{},"不过一旦这些坑都踩过，整体开发体验还是很流畅的。Nuxt 的生态系统、自动导入、模块系统都比 SvelteKit 成熟很多。",[1228,1229,1230],"style",{},"html pre.shiki code .sHbNN, html code.shiki .sHbNN{--shiki-light:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sw2iP, html code.shiki .sw2iP{--shiki-light:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sIX_F, html code.shiki .sIX_F{--shiki-light:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .suQ91, html code.shiki .suQ91{--shiki-light:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .siTax, html code.shiki .siTax{--shiki-light:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sPK6S, html code.shiki .sPK6S{--shiki-light:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .scXbn, html code.shiki .scXbn{--shiki-light:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sPKmS, html code.shiki .sPKmS{--shiki-light:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .sCnZR, html code.shiki .sCnZR{--shiki-light:#22863A;--shiki-light-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .smiIW, html code.shiki .smiIW{--shiki-light:#032F62;--shiki-light-text-decoration:underline;--shiki-dark:#DBEDFF;--shiki-dark-text-decoration:underline}html pre.shiki code .szbl0, html code.shiki .szbl0{--shiki-light:#24292E;--shiki-light-text-decoration:underline;--shiki-dark:#E1E4E8;--shiki-dark-text-decoration:underline}html pre.shiki code .sbB4o, html code.shiki .sbB4o{--shiki-light:#22863A;--shiki-dark:#85E89D}",{"title":58,"searchDepth":72,"depth":83,"links":1232},[1233,1234,1235,1236,1237,1238,1239,1240],{"id":28,"depth":72,"text":28},{"id":44,"depth":72,"text":44},{"id":149,"depth":72,"text":149},{"id":465,"depth":72,"text":28},{"id":511,"depth":72,"text":44},{"id":548,"depth":72,"text":149},{"id":853,"depth":72,"text":28},{"id":863,"depth":72,"text":863},"从 SvelteKit 迁移到 Nuxt 3 的过程中，遇到了不少 Nuxt Content v3 的坑。这篇文章记录了目录导航、图片路径、ProseImg 组件等问题的排查和解决过程。",false,"md","\u002Fposts\u002Fnuxt-content-lessons\u002Fimg\u002Fcover.jpg",{},"\u002Fposts\u002Fnuxt-content-lessons","2026-05-31T14:00:00",{"title":5,"description":1241},"posts\u002Fnuxt-content-lessons\u002Findex",[1251,1252,1253],"Nuxt","踩坑","Nuxt Content","18zpsOtiC1vLNeR654DuHTZR4shpuigkYAPQyuiYZmA",1780733791991]