本機 HTML
適用於本機 HTML 檔案的內容來源。
簡介
@fumadocs/local-html 是用於本機 HTML 檔案的內容來源。它在設計上就不需要打包,因此能完全在執行階段運作。
它專為以 HTML 形式取得、而非由您自行撰寫的頁面而設計,例如匯出的簡報、報告,以及其他工具產生的輸出。每個檔案都會依照您的文件主題進行調整,而不是照原樣算繪。
與本機 Markdown 不同,HTML 在這裡被視為需要調整的資料,而非用來撰寫內容的文件格式。
限制
- 指令碼會被移除,原始頁面的互動行為不會保留。
- 內容中無法使用 MDX 元件,但您可以在算繪階段將 HTML 標籤對應到元件。
- 處理過程只是轉換,而非淨化 (sanitization) 。所有可執行的內容都會被移除,但其餘標記會原封不動地算繪,而其中引用的資源 (尤其是圖片) 會在每位使用者開啟頁面時被載入。請確保內容目錄中只放您信任的檔案,例如您自己的 Markdown 內容。
設定
請安裝此套件:
npm install @fumadocs/local-html建立 localHtml 實例,並將它串接到 Fumadocs:
import { dynamicLoader } from 'fumadocs-core/source';
import { localHtml } from '@fumadocs/local-html';
const pages = localHtml({
dir: 'content/pages',
// 選項
});
const pagesLoader = dynamicLoader(pages.dynamicSource(), {
baseUrl: '/docs',
});
export async function getSource() {
return pagesLoader.get();
}pagesLoader.get() 回傳的來源是一般的內容載入器實例。
使用方式
page.data.load() 會回傳已處理內容的算繪器。
在載入器中將其序列化,再於用戶端算繪:
import { createFileRoute, notFound } from '@tanstack/react-router';
import { createServerFn } from '@tanstack/react-start';
import { useMemo } from 'react';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { useFumadocsLoader } from 'fumadocs-core/source/client';
import { rendererFromSerialized } from '@fumadocs/local-html/client';
import type { HtmlRendererOptions } from '@fumadocs/local-html';
import { getSource } from '@/lib/source';
// server function 結果的型別層級檢查無法驗證語法樹,
// 因此需將其註冊為可序列化(若無法解析 `@tanstack/router-core`,請先安裝)
declare module '@tanstack/router-core' {
interface SerializableExtensions {
fumadocsHtmlRenderer: HtmlRendererOptions;
}
}
export const Route = createFileRoute('/docs/$')({
component: Page,
loader: ({ params }) => serverLoader({ data: params._splat?.split('/') ?? [] }),
});
const serverLoader = createServerFn({
method: 'GET',
})
.validator((slugs: string[]) => slugs)
.handler(async ({ data: slugs }) => {
const source = await getSource();
const page = source.getPage(slugs);
if (!page) throw notFound();
const renderer = await page.data.load();
return {
title: page.data.title,
description: page.data.description,
// 語法樹,會在用戶端算繪
render: renderer.serialize(),
pageTree: await source.serializePageTree(source.getPageTree()),
};
});
function Page() {
const { title, description, render, pageTree } = useFumadocsLoader(Route.useLoaderData());
const renderer = useMemo(() => rendererFromSerialized(render), [render]);
const { body, toc } = renderer.renderSync();
return (
<DocsLayout tree={pageTree}>
<DocsPage toc={toc}>
<DocsTitle>{title}</DocsTitle>
<DocsDescription>{description}</DocsDescription>
<DocsBody>{body}</DocsBody>
</DocsPage>
</DocsLayout>
);
}將元件傳入 renderSync(),即可將 HTML 標籤對應到您自訂的元件,例如帶有錨點的標題:
import { getMDXComponents } from '@/components/mdx';
const { body, toc } = renderer.renderSync(getMDXComponents());頁面中繼資料
每個頁面的標題與描述會從該頁面的 <head> 讀取:
<title>會作為頁面標題。<meta name="description">會作為頁面描述。
您可以使用 fumadocs: meta 標籤覆寫這些值 (也可以設定圖示) ,而無須更動頁面上顯示的內容:
<meta name="fumadocs:title" content="My Page" />
<meta name="fumadocs:description" content="A short description." />
<meta name="fumadocs:icon" content="SquareBookmark" />文件中每一組 <meta name>/content 也都可以透過 page.data.metadata 取得。
與其他本機來源一樣,meta.json 檔案可用來自訂頁面樹,並透過 metaSchema 進行驗證。
內容選項
頁面只會保留其內容元素:優先使用唯一的 <main>,其次是唯一的 <article>,若都沒有則退回使用 <body>,並移除 <header> 與 <nav> 等頁面外框元素。該元素的 class 與 style 屬性會被移除,改由您主題的 prose 樣式接手;其中的標題則會加上 id,供目錄與搜尋功能使用。
以上每個部分都可以設定:
import { localHtml } from '@fumadocs/local-html';
const pages = localHtml({
dir: 'content/pages',
// 保留原始的 class/style 屬性,不配合您的主題調整樣式
adaptStyles: false,
// 從內容中額外移除指定的標籤
exclude: ['aside'],
// 自訂挑選頁面內容所在元素的邏輯
selectContent: (root) => {
// 回傳 hast 元素,或回傳 undefined 以使用預設行為
},
});語法突顯
<pre><code> 區塊會使用 Shiki 進行語法突顯,選項與本機 Markdown 相同:
const pages = localHtml({
dir: 'content/pages',
rehypeCodeOptions: {
themes: {
light: 'github-light',
dark: 'github-dark',
},
},
});語言會從程式碼區塊的 language-* class 讀取,該 class 可位於 <code> 或其外層的 <pre> 上:
<pre><code class="language-ts">const answer: number = 42;</code></pre>未指定語言的區塊會以純文字呈現。若要停用語法突顯,請傳入 rehypeCodeOptions: false。
熱重載
@fumadocs/local-html 會在執行階段讀取檔案,因此預設不會監看這些檔案。
開發期間,Vite 外掛會透過 Vite 的檔案監看器讓來源失效,不需要額外的行程或連接埠。請將外掛加入您的設定,並連接來源:
import { defineConfig } from 'vite';
import { localContentPlugin } from '@fumadocs/local-html/dev/vite';
export default defineConfig({
plugins: [localContentPlugin()],
});若想改用您自己的監看器,請在檔案變更時呼叫 pages.invalidateFile(path)。
停用重新驗證
若您只需要一次性的快照,不需要重新驗證,可以使用 staticSource()。
它可搭配一般的 loader() 使用,不必侷限於 dynamicLoader():
import { loader } from 'fumadocs-core/source';
import { localHtml } from '@fumadocs/local-html';
const pages = localHtml({
dir: 'content/pages',
});
export const source = loader(await pages.staticSource(), {
baseUrl: '/docs',
});How is this guide?
最後更新於
