最近、カレンダーを実装したところ、非常にうまくいったので、そのアプローチを文書化して、より幅広い視聴者と共有したいと思いました。そして、実際に自分の作業のテスト可能な結果を記事内に載せたかったのです。
私はしばらくウェブサイトを微調整してきましたが、このアイデアをきっかけに次のイテレーションが始まりました。そして最終的にはさらに完全な再構築につながりましたが、この物語は私と完璧主義との戦いについての話ではありません。これを変えることです:
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-->
これに:
私の Web サイトは Deno で実行されており、最近から Hono と Hono/JSX を使用していますが、このアプローチはあらゆる JS ベースのランタイムと JSX で機能します。
すでにお気づきのとおり、ブログ投稿は、Marked および Front Matter を使用してビルド時に静的 HTML に変換される属性を持つマークダウン ファイルです。
少し行ったり来たりした後、次のワークフローに落ち着きました。
コメントには何らかの接頭辞が必要です。レンダリングと基本的にはこのコンポーネントへの単なるパス:
<!--render:custom-calendar-component/CalendarExample.ssi.tsx-->
パスの後にプロパティを追加することもできますが、私のユースケースでは必要なかったので、その部分はスキップしました。
ブラウザ上で何かをハイドレートする前に、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 をレンダリングします。コンポーネントが手元にあれば、それは簡単です。
私のブログ コンテンツは静的に生成されるため、当然のことながら同じアプローチを採用しました。
私のビルド スクリプトは次のようになります:
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 固有の関数にこだわりすぎないでください。ノードやその他のもので簡単に書き換えることができます。
魔法は、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;
これをインポートしてレンダラーで使用できます :)
コードを記述するコード!魔法!そして、その過程で AI が被害を受けることはありません。昔ながらのメタプログラミングだけです。
そして最後に、パズルの最後のピースは、フロントエンドのコンポーネントをハイドレートすることです。私は 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; } }
読者ならもっとエレガントな方法を見つけられると思いますが、ほとんどこれで終わりです。
自由にコードを調整し (リポジトリへのリンクは下にあります)、独自のセンスを追加して、意見を共有してください。
以上がマークダウンのインタラクティブコンポーネントの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。