# Fumadocs (Framework Mode, Waku): 本機 HTML
URL: /zh-TW/docs/waku/integrations/content/local-html
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/(framework)/[framework]/integrations/content/local-html.zh-TW.mdx

適用於本機 HTML 檔案的內容來源。

## 簡介 [#introduction]

`@fumadocs/local-html` 是用於本機 HTML 檔案的內容來源。它在設計上就不需要打包，因此能完全在執行階段運作。

它專為以 HTML 形式取得、而非由您自行撰寫的頁面而設計，例如匯出的簡報、報告，以及其他工具產生的輸出。每個檔案都會依照您的文件主題進行調整，而不是照原樣算繪。

與[本機 Markdown](/docs/waku/integrations/content/local-md) 不同，HTML 在這裡被視為需要調整的資料，而非用來撰寫內容的文件格式。

### 限制 [#limitations]

* 指令碼會被移除，原始頁面的互動行為不會保留。
* 內容中無法使用 MDX 元件，但您可以在算繪階段將 HTML 標籤對應到元件。
* 處理過程只是轉換，而非淨化 (sanitization) 。所有可執行的內容都會被移除，但其餘標記會原封不動地算繪，而其中引用的資源 (尤其是圖片) 會在每位使用者開啟頁面時被載入。請確保內容目錄中只放您信任的檔案，例如您自己的 Markdown 內容。

## 設定 [#setup]

請安裝此套件：

```package-install
@fumadocs/local-html
```

建立 `localHtml` 實例，並將它串接到 Fumadocs：

```ts title="lib/source.ts"
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()` 回傳的來源是一般的[內容載入器](/docs/headless/source-api)實例。

## 使用方式 [#usage]

`page.data.load()` 會回傳已處理內容的算繪器。

在頁面中算繪：

```tsx tab="src/pages/docs/_layout.tsx"
import type { ReactNode } from 'react';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { getSource } from '@/lib/source';

export default async function Layout({ children }: { children: ReactNode }) {
  const source = await getSource();

  return <DocsLayout tree={source.getPageTree()}>{children}</DocsLayout>;
}
```

```tsx tab="src/pages/docs/[...slugs].tsx"
import type { PageProps } from 'waku/router';
import { unstable_notFound } from 'waku/router/server';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { getSource } from '@/lib/source';

export default async function Page({ slugs }: PageProps<'/docs/[...slugs]'>) {
  const source = await getSource();
  const page = source.getPage(slugs);
  if (!page) unstable_notFound();

  const renderer = await page.data.load();
  const { body, toc } = await renderer.render();

  return (
    <DocsPage toc={toc}>
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsDescription>{page.data.description}</DocsDescription>
      <DocsBody>{body}</DocsBody>
    </DocsPage>
  );
}

export const getConfig = async () => ({ render: 'dynamic' }) as const;
```

將元件傳入 `render()`，即可將 HTML 標籤對應到您自訂的元件，例如帶有錨點的標題：

```tsx
import { getMDXComponents } from '@/components/mdx';

const { body, toc } = await renderer.render(getMDXComponents());
```

### 頁面中繼資料 [#page-metadata]

每個頁面的標題與描述會從該頁面的 `<head>` 讀取：

* `<title>` 會作為頁面標題。
* `<meta name="description">` 會作為頁面描述。

您可以使用 `fumadocs:` meta 標籤覆寫這些值 (也可以設定圖示) ，而無須更動頁面上顯示的內容：

```html
<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` 進行驗證。

### 內容選項 [#content-options]

頁面只會保留其內容元素：優先使用唯一的 `<main>`，其次是唯一的 `<article>`，若都沒有則退回使用 `<body>`，並移除 `<header>` 與 `<nav>` 等頁面外框元素。該元素的 `class` 與 `style` 屬性會被移除，改由您主題的 prose 樣式接手；其中的標題則會加上 id，供目錄與搜尋功能使用。

以上每個部分都可以設定：

```ts
import { localHtml } from '@fumadocs/local-html';

const pages = localHtml({
  dir: 'content/pages',
  // 保留原始的 class/style 屬性，不配合您的主題調整樣式
  adaptStyles: false,
  // 從內容中額外移除指定的標籤
  exclude: ['aside'],
  // 自訂挑選頁面內容所在元素的邏輯
  selectContent: (root) => {
    // 回傳 hast 元素，或回傳 undefined 以使用預設行為
  },
});
```

### 語法突顯 [#syntax-highlighting]

`<pre><code>` 區塊會使用 [Shiki](https://shiki.style) 進行語法突顯，選項與[本機 Markdown](/docs/waku/integrations/content/local-md) 相同：

```ts
const pages = localHtml({
  dir: 'content/pages',
  rehypeCodeOptions: {
    themes: {
      light: 'github-light',
      dark: 'github-dark',
    },
  },
});
```

語言會從程式碼區塊的 `language-*` class 讀取，該 class 可位於 `<code>` 或其外層的 `<pre>` 上：

```html
<pre><code class="language-ts">const answer: number = 42;</code></pre>
```

未指定語言的區塊會以純文字呈現。若要停用語法突顯，請傳入 `rehypeCodeOptions: false`。

### 熱重載 [#hot-reload]

`@fumadocs/local-html` 會在執行階段讀取檔案，因此預設不會監看這些檔案。

開發期間，Vite 外掛會透過 Vite 的檔案監看器讓來源失效，不需要額外的行程或連接埠。請將外掛加入您的設定，並連接來源：

```ts tab="waku.config.ts"
import { defineConfig } from 'waku/config';
import { localContentPlugin } from '@fumadocs/local-html/dev/vite';

export default defineConfig({
  vite: {
    plugins: [localContentPlugin()],
  },
});
```

```ts tab="lib/source.ts"
import { watchWithVite } from '@fumadocs/local-html/dev/vite';

if (import.meta.env.DEV) {
  watchWithVite(pages);
}
```

若想改用您自己的監看器，請在檔案變更時呼叫 `pages.invalidateFile(path)`。

### 停用重新驗證 [#disable-revalidation]

若您只需要一次性的快照，不需要重新驗證，可以使用 `staticSource()`。

它可搭配一般的 `loader()` 使用，不必侷限於 `dynamicLoader()`：

```ts title="lib/source.ts"
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',
});
```