首頁 > web前端 > js教程 > 主體

Markdown 中的互動元件

Barbara Streisand
發布: 2024-10-30 15:27:02
原創
364 人瀏覽過

最近我實現了一個日曆,結果非常好,我想記錄該方法並與更廣泛的受眾分享。我真的很想在文章中得到我的工作的實際可測試結果。

我已經調整我的網站一段時間了,這個想法開始了下一次迭代。最終導致了另一個全面重建,但這個故事並不是關於我與完美主義的鬥爭。這是關於轉動這個:

HTML comes with a lot of ready to use and flexible elements, but date selector
has a handful of limitations and the need to write your own calendar / date
input emerges sooner rather than later. In this tutorial I'll walk you through
implementing a calendar view and show how you can extend its functionality to fit
your booking widget or dashboard filter.

Here's how the final result might look like:

<!--render:custom-calendar-component/CalendarExample.ssi.tsx-->
登入後複製
登入後複製

進入此:

Interactive Components in Markdown

設定

我的網站在 Deno 上運行,並從最近開始使用 Hono 和 Hono/JSX,但該方法適用於任何基於 JS 的運行時和 JSX。

正如您已經注意到的,部落格文章是帶有屬性的 Markdown 文件,這些屬性在構建時使用 Marked 和 Front Matter 轉換為靜態 HTML。

經過一番反覆思考,我決定了這個工作流程:

  • 我用markdown寫文章
  • 我在與文章相同的資料夾中建立了一個 JSX 元件
  • 我使用 HTML 註解在 Markdown 中「導入」元件
  • 它的神奇功效

註解需要某種前綴,例如渲染並且基本上只是該元件的路徑:

<!--render:custom-calendar-component/CalendarExample.ssi.tsx-->
登入後複製
登入後複製

也可以在路徑後添加道具,但對於我的用例來說,不需要它,所以我跳過了這一部分。

渲染 HTML

在瀏覽器上新增任何內容之前,我們需要從 JSX 元件渲染 HTML。為此,我們「只」需要使用自訂渲染器覆蓋 HTML 渲染邏輯:

export default class Renderer extends Marked.Renderer {
  constructor(private baseUrl: string, options?: Marked.marked.MarkedOptions) {
    super(options);
  }

  override html(html: string): string {
    const ssiMatch = /<!--render:(.+)-->/.exec(html);
    if (ssiMatch?.length) {
      const filename = ssiMatch[1];
      const ssi = SSIComponents.get(filename);
      if (!ssi) return html;
      const content = render(createElement(ssi.Component, {}));
      return [
        content,
        `<script type="module" src="${ssi.script}"></script>`,
      ].join("");
    }
    return html;
  }
}
登入後複製
登入後複製

邏輯非常簡單:檢查 html 字串是否符合 // 然後渲染 JSX。如果您手邊有組件,那就很容易了。

編譯元件

我的部落格內容是靜態產生的,所以我很自然地採用了相同的方法:

  • 掃描內容資料夾中的 *.ssi.tsx 元件(因此有字尾)
  • 建立一個檔案來匯入它們並將其新增到地圖中,以便我可以輕鬆地透過路徑檢索它們

這是我的建置腳本的樣子:

const rawContent = await readDir("./content");
const content: Record<string, Article> = {};
const ssi: Array<string> = [];

for (const pathname in rawContent) {
  if (pathname.endsWith(".ssi.tsx")) {
    ssi.push(pathname);
    continue;
  }
}

const scripts = await compileSSI(ssi.map((name) => `./content/${name}`));

const ssiContents = `
import type { FC } from 'hono/jsx';
const SSIComponents = new Map<string,{ Component: FC, script: string }>();
${
  scripts
    ? ssi
        .map(
          (pathname, i) =>
            `SSIComponents.set("${pathname}", { Component: (await import("./${pathname}")).default, script: "${scripts[i]}" })`
        )
        .join("\n")
    : ""
}
export default SSIComponents;
`;

await Deno.writeFile("./content/ssi.ts", new TextEncoder().encode(ssiContents));
登入後複製

不要太執著於 Deno 特定功能,它可以輕鬆地用 Node 或其他任何東西重寫。

神奇之處在於編寫類似 JavaScript 程式碼的文字檔。

這個腳本:

const ssiContents = `
import type { FC } from 'hono/jsx';
const SSIComponents = new Map<string,{ Component: FC, script: string }>();
${
  scripts
    ? ssi
        .map(
          (pathname, i) =>
            `SSIComponents.set("${pathname}", { Component: (await import("./${pathname}")).default, script: "${scripts[i]}" })`
        )
        .join("\n")
    : ""
}
export default SSIComponents;
`;
登入後複製

傳回以下字串:

import type { FC } from 'hono/jsx';
const SSIComponents = new Map<string,{ Component: FC, script: string }>();
SSIComponents.set("custom-calendar-component/CalendarExample.ssi.tsx", { Component: (await import("./custom-calendar-component/CalendarExample.ssi.tsx")).default, script: "/content/custom-calendar-component/CalendarExample.ssi.js" })
export default SSIComponents;

登入後複製

然後可以在渲染器中匯入和使用:)

寫程式碼的程式碼!魔法!並且在過程中沒有任何人工智慧受到傷害:只是你的老式元程式設計。

最後,最後一個難題是滋潤前端的組件。我使用 esbuild,但我個人打算將其切換到 Vite 或 HMR 附帶的任何其他工具。

儘管如此,它看起來是這樣的:

HTML comes with a lot of ready to use and flexible elements, but date selector
has a handful of limitations and the need to write your own calendar / date
input emerges sooner rather than later. In this tutorial I'll walk you through
implementing a calendar view and show how you can extend its functionality to fit
your booking widget or dashboard filter.

Here's how the final result might look like:

<!--render:custom-calendar-component/CalendarExample.ssi.tsx-->
登入後複製
登入後複製

您可以注意到一個虛擬入口點,其值為零,但會強制 esbuild 在自己的資料夾中建立檔案並具有可預測的路徑。

且 ssi-tsconfig.json 非常通用:

<!--render:custom-calendar-component/CalendarExample.ssi.tsx-->
登入後複製
登入後複製

在實際的前端水合作用中,我選擇了簡單的方法,並將其添加到我的 .ssi.tsx 檔案的底部:

export default class Renderer extends Marked.Renderer {
  constructor(private baseUrl: string, options?: Marked.marked.MarkedOptions) {
    super(options);
  }

  override html(html: string): string {
    const ssiMatch = /<!--render:(.+)-->/.exec(html);
    if (ssiMatch?.length) {
      const filename = ssiMatch[1];
      const ssi = SSIComponents.get(filename);
      if (!ssi) return html;
      const content = render(createElement(ssi.Component, {}));
      return [
        content,
        `<script type="module" src="${ssi.script}"></script>`,
      ].join("");
    }
    return html;
  }
}
登入後複製
登入後複製

我相信讀者會找到一種更優雅的方式來做到這一點,但僅此而已!

隨意調整程式碼(以下是儲存庫的連結),添加您自己的風格並分享您的想法!

Interactive Components in Markdown 瓦萊裡婭·VG / valeriavg.dev

以上是Markdown 中的互動元件的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板