news 2026/9/14 2:39:39

如何用 marimo islands 把交互式笔记本内容嵌入静态网页?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何用 marimo islands 把交互式笔记本内容嵌入静态网页?

如何用 marimo islands 把交互式笔记本内容嵌入静态网页?

【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo

你已有的静态页面(博客、教程、课程站点)想放进 marimo 笔记本里的单元格输出,并且希望读者打开页面后这些内容可以交互,而不是截图或死板的 HTML。marimo 的 islands 功能就是为此设计的:把 marimo 的输出和/或 Python 代码以自定义 HTML 标签的形式嵌入你的页面,页面加载后由 marimo 的响应式运行时接管,内容变为可交互(发布指南 将其列为自托管场景下“把单个单元格输出直接嵌入 HTML 页面”的选项)。注意文档标注:islands 仍是早期功能,API 预计不会变,但在被认定为稳定前可能还有改进。

islands 与常见的 iframe 嵌入不同:没有顶层 app,运行时拿不到父页面的 HTML,只在你用 marimo 自定义标签标出的“岛”上初始化(见 frontend/islands/development.md)。文档也提示这是一个面向集成的高级构建块,适合接到静态站点生成器或文档工具里。

主路径:从代码块生成 island HTML

docs/guides/exporting/webassembly_html.md 给出的核心做法是用MarimoIslandGenerator逐块添加代码、构建应用、再把渲染出的 HTML 拼进页面。文档示例:

import asyncio import sys from marimo import MarimoIslandGenerator if sys.platform == 'win32': asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) async def main(): generator = MarimoIslandGenerator() block1 = generator.add_code("import marimo as mo") block2 = generator.add_code("mo.md('Hello, islands!)' )") # Build the app app = await generator.build() # Render the app output = f""" <html> <head> {generator.render_head()} </head> <body> {block1.render(display_output=False)} {block2.render()} </body> </html> """ print(output) # Save the HTML to a file output_file = "output.html" with open(output_file, "w", encoding="utf-8") as f: f.write(output) if __name__ == '__main__': asyncio.run(main())

上例按文档整理(block2的代码以文档为准应为mo.md('Hello, islands!'),这里保留文档原文结构)。执行时要理解几个关键点:

  • add_code每个代码块调用一次,返回一个可render()的 stub。build()会在本地 marimo 会话中运行这些代码,拿到真实输出;文档源码明确说明build()只能调用一次(marimo/_islands/_island_generator.py)。
  • render_head()生成放进<head>的 JS/CSS 引用,默认使用当前安装的 marimo 版本(version_override参数可指定其他版本);它包含@marimo-team/islandsmain.jsstyle.css(jsDelivr CDN)、Google Fonts 预连接与 KaTeX 样式,这些都由render_head()自动生成,不需要手写。
  • 第一块是 import,没有展示价值,所以用block1.render(display_output=False)只保留代码;第二块正常render(),输出和隐藏代码一起渲染。
  • add_code还支持display_code(是否在 HTML 中展示代码)、display_outputis_reactive(该块是否通过 pyodide 在浏览器里运行)等参数,默认值见源码 docstring。

生成的output.html就是一份可以直接放进静态站点的完整页面。

替代路径:从已有的 marimo .py 笔记本生成

如果你已经有一个 marimo 笔记本文件(marimo 笔记本本身就是纯 Python 文件),可以用from_file直接生成:

from marimo import MarimoIslandGenerator # Create the generator from file generator = MarimoIslandGenerator.from_file("./<notebook-name>.py", display_code=False) # Generate and print the HTML without building # This will still work for basic rendering, though without running the cells html = generator.render_html(include_init_island=False) print(html) # Save the HTML to a file output_file = "output.html" with open(output_file, "w", encoding="utf-8") as f: f.write(html)

./<notebook-name>.py是文档中的占位符,替换成你的 marimo 笔记本文件路径即可。这条路径有一个明确的限制(文档原话):不经过build()的渲染是 basic rendering,单元格不会真正执行。也就是说,如果需要真实运行结果,走上一条含await generator.build()的路径;如果只需要静态渲染结构,from_file+render_html更省事。

render_html/render_body还有几个影响产物的开关:

  • include_init_island(默认True):在内容前加一个显示 “Initializing...” 的加载器,Pyodide 加载完成、内核就绪后消失。
  • include_payload(默认False):在 body 末尾追加一段 JSON payload 脚本标签,供 islands 运行时用来水合(hydrate)页面。
  • max_width/margin/style:控制 body 包裹容器的 CSS,style会覆盖前两者。

生成的 HTML 长什么样

无论哪条路径,产物结构一致:<head>里是 marimo JS/CSS 与字体引用,<body>里是用自定义标签定义的岛屿。文档给出的模板(其中<version>需替换为实际版本,与render_head()自动填充的版本保持一致):

<head> <!-- marimo js/ccs --> <script type="module" src="https://cdn.jsdelivr.net/npm/@marimo-team/islands@<version>/dist/main.js"></script> <link href="https://cdn.jsdelivr.net/npm/@marimo-team/islands@<version>/dist/style.css" rel="stylesheet" crossorigin="anonymous" /> <!-- fonts --> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link href="https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500;700&amp;family=Lora&amp;family=PT+Sans:wght@400;700&amp;display=swap" rel="stylesheet" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous" /> </head> <body> <marimo-island><script type="application/vnd.marimo.islands+json">{"schemaVersion":1,"appId":"main","cells":[{"cellId":"cell-1","code":"mo.md('Hello, islands!')","outputHtml":"\u003cspan\u003eHello, islands!\u003c/span\u003e","outputMimetype":"text/markdown","reactive":true,"displayCode":false,"displayOutput":true}]}</script>

payload 保存了每个单元格的代码、渲染后的输出 HTML、输出 MIME 类型和显示设置;DOM 提供可见的岛屿槽位,payload 提供运行时代码与元数据。文档特别强调:如果你要后处理 island HTML,必须原样保留这个type="application/vnd.marimo.islands+json"的 script 标签及其内容,否则会破坏运行时水合。

验证与限制

  • 成功判据来自文档站自带的 islands 示例页(docs/guides/island_example.md):页面打开时先展示预渲染输出;marimo 运行时初始化完成后,“内容变为可交互”(示例页中一个mo.ui.slider拖动后下方 Markdown 会跟着变化)。include_init_island=True时还会先看到 “Initializing...” 加载器,内核就绪后消失。可以拿这份示例页对照你生成页面的行为。
  • 版本一致性:手动维护 head 模板时,<version>要与生成端一致;用render_head()输出则版本自动跟随安装的 marimo(version_override可覆盖),文档示例页固定用的是0.5.0
  • 稳定性:文档两处标注 islands 是 early feature、尚未稳定,API 预计不变但仍有改进计划。
  • from_file路径的渲染不执行单元格,只适合 basic rendering;需要真实输出时用build()路径。
  • 生成 HTML 后如需本地联调,仓库内 frontend/islands/development.md 描述了pnpm dev:islands的本地开发/生产生成流程(面向 marimo 前端开发者)。

如果你的目标平台是 Jupyter Book,文档给出了现成的集成方式:jupyter-book-marimo插件会把 MyST 的{marimo}指令渲染为 hydrated marimo islands(见 docs/guides/exporting/jupyter_book.md),不必手写上面这套 HTML 拼装逻辑。

【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 2:36:36

工业级可燃气体变送器原理与实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 2:34:33

把小爱音箱接入 ChatGPT:MiGPT 语音助手 10 分钟部署教程

把小爱音箱接入 ChatGPT&#xff1a;MiGPT 语音助手 10 分钟部署教程 【免费下载链接】mi-gpt &#x1f3e0; 将小爱音箱接入 ChatGPT 和豆包&#xff0c;改造成你的专属语音助手。 项目地址: https://gitcode.com/GitHub_Trending/mi/mi-gpt MiGPT 是一个把小米小爱音箱…

作者头像 李华
网站建设 2026/9/14 2:33:43

Obsidian多端同步难题破解:五大方案实测与选型指南

我在Obsidian上折腾同步已经有8年了&#xff0c;从最早的移动硬盘手动拷贝&#xff0c;到后来的各种插件、网盘、Git仓库&#xff0c;几乎把市面上能用的方案都试了一遍。写这篇东西的起因很简单&#xff1a;前几天帮我朋友从Notion迁到Obsidian&#xff0c;第一句话就问“多端…

作者头像 李华
网站建设 2026/9/14 2:30:13

IPC设备P2P技术与NAT穿透原理详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华