You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt如何基于Markdown生成内嵌真实内容的SEO友好静态HTML

Nuxt 基于Markdown生成内嵌真实内容的静态HTML实现方案

原实现的问题根因

你当前的实现最终导出的HTML没有内嵌Markdown内容,核心来自两个流程偏差:

  • 没有在静态生成阶段完成Markdown到HTML字符串的转换,反而把原始Markdown文本、Markdown解析逻辑全部打包进了客户端JS bundle
  • Nuxt 静态生成时的路由参数传递机制里,自定义挂在路由对象上的content字段不会自动注入到asyncData的上下文里,导致构建运行阶段asyncData拿不到传入的Markdown内容,初始渲染的HTML自然是空的,只能等客户端JS加载完成后临时渲染内容

分步骤正确实现

1. 提前封装仅构建端使用的Markdown渲染工具

这个工具仅在Node构建环境下运行,不会被打入客户端包:

// utils/markdown.js
import MarkdownIt from 'markdown-it'
const md = new MarkdownIt({
  html: true,
  linkify: true,
  typographer: true
})

export function renderMarkdown(rawMd) {
  return md.render(rawMd)
}

2. 修正构建路由的生成逻辑

在生成路由的阶段就提前读取Markdown文件、完成HTML转换,并把最终渲染好的内容放到Nuxt规定的payload字段里——这个字段的内容会在静态导出时直接序列化内嵌到HTML文件中,不会被打包进客户端JS:

// nuxt.config.js
import fs from 'fs'
import path from 'path'
import { renderMarkdown } from './utils/markdown'

const markdownDir = path.resolve(__dirname, 'markdownDir')

export default {
  generate: {
    routes: () => {
      const mdFiles = fs.readdirSync(markdownDir).filter(name => name.endsWith('.md'))
      return mdFiles.map(filename => {
        const slug = path.basename(filename, '.md')
        // 构建阶段就读取原始Markdown,提前转换为可直接渲染的HTML字符串
        const rawMd = fs.readFileSync(path.join(markdownDir, filename), 'utf-8')
        const renderedHtml = renderMarkdown(rawMd)
        return {
          route: `/articles/${slug}`,
          // payload内容会直接内嵌到静态HTML的初始状态里
          payload: {
            articleHtml: renderedHtml,
            slug
          }
        }
      })
    }
  }
}

3. 修正详情页的数据取值逻辑

asyncData直接读取构建阶段传入的payload内容,不要在客户端运行时才执行Markdown转换逻辑:

// pages/articles/_slug.vue
<template>
  <div class="markdown-body" v-html="articleHtml"></div>
</template>

<script>
export default {
  async asyncData({ payload, params }) {
    // 静态生产环境直接读取已经渲染好的HTML内容
    if (payload?.articleHtml) {
      return {
        articleHtml: payload.articleHtml,
        slug: payload.slug
      }
    }
    // dev环境下的兜底逻辑,动态引入Node端依赖,不会被打入生产客户端包
    const fs = (await import('fs')).default
    const path = await import('path')
    const mdPath = path.resolve(process.cwd(), 'markdownDir', `${params.slug}.md`)
    const rawMd = fs.readFileSync(mdPath, 'utf-8')
    const { renderMarkdown } = await import('../../utils/markdown')
    return {
      articleHtml: renderMarkdown(rawMd),
      slug: params.slug
    }
  }
}
</script>

导出效果验证

执行nuxt generate生成静态文件后,直接打开dist目录下对应的文章HTML文件,可以看到:

  • 渲染完成的Markdown DOM结构(标题、段落、列表、代码块等内容)直接内嵌在<div class="markdown-body">标签内部,不需要等待JS加载执行就能被爬虫识别
  • 页面渲染所需的初始数据会以JSON格式序列化存在HTML底部的全局状态里, hydration过程会直接复用这份内容,不会重复渲染

常见坑提醒

  • 不要把自定义的Markdown内容直接挂在路由对象的非标准字段上,只有Nuxt规定的payload字段才会在静态导出时被传递到页面上下文中
  • Markdown解析的逻辑一定要在构建阶段提前执行,不要放到asyncData里再实时调用,否则解析逻辑、原始Markdown文本会被全部打入客户端JS包,违背静态生成的初衷
  • 涉及Node端文件操作的逻辑一定要用动态import引入,避免把fs、path等Node模块打进生产客户端包增加体积

内容的提问来源于stack exchange,提问作者injoker1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 19:45:07