Heim > Web-Frontend > js-Tutorial > Hauptteil

Interaktive Komponenten in Markdown

Barbara Streisand
Freigeben: 2024-10-30 15:27:02
Original
296 Leute haben es durchsucht

Kürzlich habe ich einen Kalender implementiert und er hat so gut geklappt, dass ich den Ansatz dokumentieren und mit einem breiteren Publikum teilen wollte. Und ich wollte unbedingt direkt im Artikel ein tatsächlich überprüfbares Ergebnis meiner Arbeit haben.

Ich arbeite schon seit einiger Zeit an meiner Website und dieser Ideenschub startete die nächste Iteration. Und letztendlich zu einem weiteren kompletten Neuaufbau geführt, aber in dieser Geschichte geht es nicht um meinen Kampf mit dem Perfektionismus. Es geht darum, dies umzudrehen:

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-->
Nach dem Login kopieren
Nach dem Login kopieren

Dazu:

Interactive Components in Markdown

Aufstellen

Meine Website läuft auf Deno und verwendet seit kurzem Hono und Hono/JSX, aber der Ansatz funktioniert mit jeder JS-basierten Laufzeit und JSX.

Blog-Beiträge sind, wie Sie bereits bemerkt haben, Markdown-Dateien mit Attributen, die beim Erstellen mit Marked und Front Matter in statisches HTML konvertiert werden.

Nach einigem Hin und Her habe ich mich für diesen Workflow entschieden:

  • Ich schreibe Artikel im Markdown
  • Ich erstelle eine JSX-Komponente im selben Ordner wie der Artikel
  • Ich „importiere“ die Komponente im Markdown mithilfe eines HTML-Kommentars
  • Es funktioniert auf magische Weise

Der Kommentar würde eine Art Präfix benötigen, z.B. render und im Grunde nur ein Pfad zu dieser Komponente:

<!--render:custom-calendar-component/CalendarExample.ssi.tsx-->
Nach dem Login kopieren
Nach dem Login kopieren

Man könnte nach dem Pfad auch Requisiten hinzufügen, aber für meinen Anwendungsfall war das nicht nötig, also habe ich diesen Teil übersprungen.

HTML rendern

Bevor wir etwas im Browser hydrieren können, müssen wir HTML aus der JSX-Komponente rendern. Dazu müssen wir „nur“ die HTML-Rendering-Logik mit einem benutzerdefinierten Renderer überschreiben:

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;
  }
}
Nach dem Login kopieren
Nach dem Login kopieren

Die Logik ist ganz einfach: Überprüfen Sie, ob die HTML-Zeichenfolge mit // übereinstimmt, und rendern Sie dann JSX. Ganz einfach, wenn man die Komponente zur Hand hat.

Kompilieren von Komponenten

Mein Blog-Inhalt wird statisch generiert, daher habe ich mich natürlich für den gleichen Ansatz entschieden:

  • Scannen Sie den Inhaltsordner nach *.ssi.tsx-Komponenten (daher das Suffix)
  • Erstellen Sie eine Datei, die sie importiert und zu einer Karte hinzufügt, damit ich sie einfach über den Pfad abrufen kann

So sieht mein Build-Skript aus:

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));
Nach dem Login kopieren

Lassen Sie sich nicht zu sehr an Deno-spezifische Funktionen hängen, es kann leicht mit Node oder etwas anderem umgeschrieben werden.

Die Magie liegt darin, eine Textdatei zu schreiben, die JavaScript-Code ähnelt.

Dieses Skript:

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;
`;
Nach dem Login kopieren

Gibt eine Zeichenfolge wie diese zurück:

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;

Nach dem Login kopieren

Die dann importiert und im Renderer verwendet werden können :)

Code, der den Code schreibt! Magie! Und keine KI wurde dabei verletzt: nur Ihre Metaprogrammierung der alten Schule.

Und schließlich besteht das letzte Puzzleteil darin, die Komponente im Frontend mit Feuchtigkeit zu versorgen. Ich habe dafür esbuild verwendet, aber ich persönlich plane, es auf Vite oder irgendetwas anderes umzustellen, das mit HMR geliefert wird.

Trotzdem sieht es so aus:

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-->
Nach dem Login kopieren
Nach dem Login kopieren

Sie können einen Dummy-Einstiegspunkt bemerken, der den Wert Null hat, aber esbuild zwingt, Dateien in seinen eigenen Ordnern zu erstellen und vorhersehbare Pfade zu haben.

Und ssi-tsconfig.json ist ziemlich allgemein gehalten:

<!--render:custom-calendar-component/CalendarExample.ssi.tsx-->
Nach dem Login kopieren
Nach dem Login kopieren

Was die eigentliche Frontend-Hydratisierung angeht, habe ich den einfachen Weg gewählt und einfach Folgendes am Ende meiner .ssi.tsx-Datei hinzugefügt:

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;
  }
}
Nach dem Login kopieren
Nach dem Login kopieren

Ich bin mir sicher, dass der Leser einen eleganteren Weg finden würde, aber das ist auch schon alles!

Fühlen Sie sich frei, den Code zu optimieren (Link zum Repo finden Sie unten), fügen Sie Ihre eigene Note hinzu und teilen Sie Ihre Gedanken mit!

Interactive Components in Markdown ValeriaVG / valeriavg.dev

Das obige ist der detaillierte Inhalt vonInteraktive Komponenten in Markdown. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!