e0a5f57793
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as a monorepo subdirectory under desktop/. Source only — node_modules, dist, Cargo target and the upstream .git history are excluded. desktop/ keeps its own independent Cargo workspace (desktop/src-tauri with crates/*); it is not part of the backend boosapi crate, so a `cargo check` at the metapi root still compiles only the backend. Build the desktop app from desktop/src-tauri per desktop/README.md. Note: app icons are still placeholder assets pending the final BoosAPI logo. The updater signing keys live outside the repo and are not included.
151 lines
4.2 KiB
Markdown
151 lines
4.2 KiB
Markdown
description: 在 Nuxt 3 SSR 中使用 markstream-vue,获得服务端 HTML、稳定 fallback,以及重节点的客户端增强。
|
||
---
|
||
|
||
# Nuxt 3 SSR 用法示例
|
||
|
||
> For the English version, see [nuxt-ssr.md](../nuxt-ssr.md).
|
||
|
||
`markstream-vue` 现在可以直接在 Nuxt SSR 中产出服务端 HTML。为了“能跑”而把整个 `<MarkdownRender>` 包进 `<ClientOnly>`,已经不是默认推荐做法。
|
||
|
||
当前的 SSR 模型是:
|
||
|
||
- 普通 markdown、HTML、链接、表格、脚注、图片会直接在服务端渲染。
|
||
- 代码块会先输出稳定的服务端 `<pre><code>` fallback,再在客户端增强成 Monaco。
|
||
- 数学公式在提供同步 KaTeX loader 时,可以直接在服务端输出真正的 KaTeX HTML。
|
||
- Mermaid、D2、Infographic 会先输出可读的 SSR fallback,再在客户端增强。
|
||
- scoped 自定义组件覆盖、自定义 node type、以及 `customHtmlTags` 也都能参与服务端渲染。
|
||
|
||
## 最小 SSR 页面
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import MarkdownRender from 'markstream-vue'
|
||
import { ref } from 'vue'
|
||
|
||
const markdown = ref(`
|
||
# 来自 Nuxt SSR 的问候
|
||
|
||
行内公式:$E = mc^2$
|
||
|
||
\`\`\`ts
|
||
export const greet = (name: string) => \`hello \${name}\`
|
||
\`\`\`
|
||
|
||
\`\`\`mermaid
|
||
graph TD
|
||
SSR --> Hydration
|
||
\`\`\`
|
||
`.trim())
|
||
</script>
|
||
|
||
<template>
|
||
<MarkdownRender :content="markdown" :final="true" />
|
||
</template>
|
||
```
|
||
|
||
这样首包就已经有服务端 HTML;重节点仍然会在 hydration 后增强,但不会先给你一个空壳。
|
||
|
||
## 服务端渲染数学公式
|
||
|
||
如果你希望 SSR 响应里直接带 KaTeX HTML,可以在 Nuxt 应用里注册同步 loader:
|
||
|
||
```ts
|
||
import katex from 'katex'
|
||
import { enableKatex } from 'markstream-vue'
|
||
|
||
enableKatex(() => katex)
|
||
```
|
||
|
||
如果没有这个 loader,数学公式也仍然是 SSR-safe,只是会稳定回退成可读原文。
|
||
|
||
## 预解析 nodes 与自定义组件
|
||
|
||
除了直接传 `content`,你也可以在 SSR 中传 `nodes`,或者配合 scoped custom components / trusted custom tags 一起使用。
|
||
|
||
```ts
|
||
import { setCustomComponents } from 'markstream-vue'
|
||
import { defineComponent, h } from 'vue'
|
||
|
||
setCustomComponents('docs-ssr', {
|
||
thinking: defineComponent({
|
||
props: {
|
||
node: { type: Object, required: true },
|
||
},
|
||
setup(props) {
|
||
return () => h('aside', { 'data-ssr-thinking': '1' }, String((props.node as any).content ?? ''))
|
||
},
|
||
}),
|
||
})
|
||
```
|
||
|
||
```vue
|
||
<template>
|
||
<MarkdownRender
|
||
custom-id="docs-ssr"
|
||
content="<thinking>服务端自定义标签</thinking>"
|
||
:custom-html-tags="['thinking']"
|
||
:final="true"
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
如果你的服务端已经拿到了 AST,优先用 `nodes + final`,这样可以避免浏览器端重复解析,SSR 输出也更可控。
|
||
|
||
## 什么时候还需要 `<ClientOnly>`
|
||
|
||
通常不是给渲染器本身用的。
|
||
|
||
只有当你自己的页面逻辑本身依赖浏览器 API,或者你明确想让一整块区域跳过 SSR 时,才需要 `<ClientOnly>`。
|
||
|
||
## Nuxt playground
|
||
|
||
仓库里已经提供了完整的 Nuxt playground,路径是 `playground-nuxt/`:
|
||
|
||
```bash
|
||
pnpm install
|
||
pnpm play:nuxt
|
||
```
|
||
|
||
专门的 SSR 验证路由是:
|
||
|
||
- `/ssr-lab`
|
||
|
||
这个页面里有固定矩阵,用来覆盖:
|
||
|
||
- 基础 markdown + HTML 的 SSR
|
||
- 重节点的 SSR fallback
|
||
- hydration 后的增强路径
|
||
- 显式禁用增强时的稳定 fallback
|
||
- 自定义标签与 scoped override 的验证
|
||
|
||
## SSR 验收命令
|
||
|
||
Nuxt SSR 的验收 e2e 命令是:
|
||
|
||
```bash
|
||
pnpm test:e2e:nuxt-ssr
|
||
```
|
||
|
||
它会同时验证:
|
||
|
||
- `nuxt dev`
|
||
- `nuxt build && nuxt preview`
|
||
|
||
每种模式都会先检查 `/ssr-lab` 的原始 HTTP HTML,再打开浏览器验证 hydration、增强、reload 和路由往返后的稳定性。
|
||
|
||
## 库级 SSR 回归覆盖
|
||
|
||
仓库里还有一层直接针对渲染器的 `renderToString` 回归测试:
|
||
|
||
```bash
|
||
pnpm test --run test/ssr-render-to-string.test.ts test/ssr-import.test.ts
|
||
```
|
||
|
||
这层测试显式覆盖了:
|
||
|
||
- 轻节点 SSR 组件矩阵,例如 heading、paragraph、行内格式、link、list、table、footnote、admonition、`vmr_container`
|
||
- 独立的 `MarkdownCodeBlockNode` SSR shell
|
||
- 通过 `setCustomComponents` 做 scoped 内置覆盖
|
||
- 直接自定义 node type 的 SSR
|
||
- 通过 `customHtmlTags` 接入的可信自定义标签 SSR
|