Rumah  >  Artikel  >  hujung hadapan web  >  Komponen Interaktif dalam Markdown

Komponen Interaktif dalam Markdown

Barbara Streisand
Barbara Streisandasal
2024-10-30 15:27:02297semak imbas

Baru-baru ini saya melaksanakan Kalendar dan ternyata saya mahu mendokumentasikan pendekatan itu dan berkongsi dengan khalayak yang lebih luas. Dan saya benar-benar mahu mendapatkan hasil kerja saya yang boleh diuji sebenarnya di dalam artikel itu.

Saya telah mengubah suai tapak web saya untuk seketika sekarang dan tendangan idea ini memulakan lelaran seterusnya. Dan akhirnya membawa kepada satu lagi pembinaan semula penuh, tetapi cerita ini bukan tentang pertempuran saya dengan perfeksionisme. Ini tentang mengubah ini:

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-->

Ke dalam ini:

Interactive Components in Markdown

Persediaan

Tapak web saya berjalan di Deno dan menggunakan Hono dan Hono/JSX sejak baru-baru ini, tetapi pendekatan ini berfungsi dengan mana-mana masa jalan dan JSX berasaskan JS.

Siaran blog, seperti yang telah anda perhatikan, ialah fail penurunan nilai dengan atribut yang ditukar kepada HTML statik pada binaan menggunakan Marked and Front Matter.

Selepas beberapa kali berulang-alik saya telah menyelesaikan aliran kerja ini:

  • Saya menulis artikel dalam penurunan harga
  • Saya mencipta komponen JSX dalam folder yang sama dengan artikel
  • Saya "import" komponen dalam markdown menggunakan ulasan HTML
  • Ia berkesan secara ajaib

Ulasan itu memerlukan semacam awalan, mis. render dan pada dasarnya hanya laluan ke komponen ini:

<!--render:custom-calendar-component/CalendarExample.ssi.tsx-->

Seseorang juga boleh menambah props demi laluan, tetapi untuk kes penggunaan saya ia tidak diperlukan jadi saya melangkau bahagian itu.

Membuat HTML

Sebelum kami boleh menghidratkan apa-apa sahaja pada penyemak imbas, kami perlu memaparkan HTML daripada komponen JSX. Untuk berbuat demikian, saya "hanya" perlu mengatasi logik pemaparan HTML menggunakan Renderer tersuai:

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;
  }
}

Logik agak mudah: semak sama ada rentetan html sepadan // dan kemudian render JSX. Mudah-peasy, jika anda mempunyai komponen di tangan itu.

Menyusun komponen

Kandungan blog saya dijana secara statik jadi secara semula jadi saya telah menggunakan pendekatan yang sama:

  • Imbas folder kandungan untuk *.ssi.tsx komponen (oleh itu akhiran)
  • Buat fail yang akan mengimportnya dan menambah pada Peta supaya saya boleh mendapatkannya dengan mudah melalui laluan ke

Begini rupa skrip binaan saya:

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));

Jangan terlalu terikat dengan fungsi khusus Deno, ia boleh ditulis semula dengan mudah dengan nod atau apa-apa lagi.

Keajaibannya ialah menulis fail teks yang menyerupai kod JavaScript.

Skrip ini:

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;
`;

Mengembalikan rentetan seperti ini:

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;

Yang kemudiannya boleh diimport dan digunakan dalam Renderer :)

Kod yang menulis kod! Sihir! Dan tiada AI telah dicederakan dalam proses: hanya pengaturcaraan meta sekolah lama anda.

Dan akhirnya, bahagian terakhir teka-teki adalah menghidratkan komponen pada bahagian hadapan. Saya menggunakan esbuild untuknya, tetapi secara peribadi saya merancang untuk menukarnya kepada Vite atau apa-apa lagi yang disertakan dengan HMR.

Walau bagaimanapun, beginilah rupanya:

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-->

Anda boleh melihat titik masuk tiruan yang mempunyai nilai sifar, tetapi memaksa esbuild untuk mencipta fail dalam folder mereka sendiri dan mempunyai laluan yang boleh diramal.

Dan ssi-tsconfig.json agak generik:

<!--render:custom-calendar-component/CalendarExample.ssi.tsx-->

Setakat penghidratan bahagian hadapan sebenar, saya memilih cara yang mudah dan hanya menambah ini di bahagian bawah fail .ssi.tsx saya:

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;
  }
}

Saya pasti pembaca akan menemui cara yang lebih elegan untuk melakukannya, tetapi itu sahaja!

Sila menala kod (pautan ke repo ada di bawah), tambah bakat anda sendiri dan kongsi pendapat anda!

Interactive Components in Markdown ValeriaVG / valeriavg.dev

Atas ialah kandungan terperinci Komponen Interaktif dalam Markdown. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn