最近我实现了一个日历,结果非常好,我想记录该方法并与更广泛的受众分享。我真的很想在文章中得到我的工作的实际可测试结果。
我已经调整我的网站一段时间了,这个想法开始了下一次迭代。最终导致了又一次全面重建,但这个故事并不是关于我与完美主义的斗争。这是关于转动这个:
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-->
进入此:
我的网站在 Deno 上运行,并从最近开始使用 Hono 和 Hono/JSX,但该方法适用于任何基于 JS 的运行时和 JSX。
正如您已经注意到的,博客文章是带有属性的 Markdown 文件,这些属性在构建时使用 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 特定功能,它可以轻松地用 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; } }
我相信读者会找到一种更优雅的方式来做到这一点,但仅此而已!
随意调整代码(下面是存储库的链接),添加您自己的风格并分享您的想法!
以上是Markdown 中的交互组件的详细内容。更多信息请关注PHP中文网其他相关文章!