news 2026/9/9 19:47:28

Slidev 可写 Monaco 编辑器:用 {monaco-write} 在幻灯片中现场改码并直接保存回源文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Slidev 可写 Monaco 编辑器:用 {monaco-write} 在幻灯片中现场改码并直接保存回源文件

Slidev 可写 Monaco 编辑器:用 {monaco-write} 在幻灯片中现场改码并直接保存回源文件

【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidev

可写 Monaco(Writable Monaco)是 Slidev 代码块增强体系中非常特别的一个能力:它把「展示代码」升级为「编辑真实文件」。通过在代码块上追加{monaco-write}指令,幻灯片内会渲染一个与磁盘文件双向关联的 Monaco 编辑器,演讲者可以在演示(live coding)过程中直接修改代码并按下Ctrl/Cmd + S将内容实时写回源文件。读完本文,你将掌握<<<导入语法与{monaco-write}的组合用法、可叠加的全部选项、其仅在开发服务器下生效的边界条件,以及从 Markdown 解析到 WebSocket 写盘这一完整链路的底层实现原理。

一、这是什么:从"只读展示"到"真写盘"

默认情况下,无论是```ts {monaco}内联代码块,还是<<<导入的代码片段,幻灯片里的 Monaco 编辑器都只是「读取文件内容后展示」,编辑内容不会触碰原始文件,刷新页面即还原。

{monaco-write}指令打破了这条边界。它的核心语义由官方文档定义为三条:

  • 将 Monaco 编辑器链接到文件系统上的真实文件(Links Monaco editor to actual file on filesystem);
  • 编辑器内的修改直接保存到该文件(Changes are saved directly to the file);
  • 该能力专为现场编码演示设计(Useful for live coding demonstrations)。

对应的功能说明见 docs/features/monaco-write.md,该特性以since: v0.49.5版本引入。结合 import-snippet 导入语法(since: v0.47.0),即可让一段代码同时具备「高亮展示 + 实时可编辑 + 可持久化」三重身份,非常适合技术演讲中的即兴改码、培训实操以及录屏演示。

二、快速上手:最小示例与运行前提

{monaco-write}并不能直接作用于```ts这种内联代码块,它必须搭配代码片段导入语法使用。在任意一张幻灯片里写入:

<<< ./some-file.ts {monaco-write}

然后以开发模式启动演示即可:

npm run dev

打开对应幻灯片,你会看到一个完整的 Monaco 编辑器,其中的代码正是some-file.ts的当前内容。直接修改任意代码,按Ctrl/Cmd + S,改动会被写回./some-file.ts;刷新浏览器、切换到其他幻灯片再回来,内容保持一致。

运行前提与生效范围

需要特别强调的是,这个特性只在开发服务器模式下生效,原因有三个,均可以从源码确认:

  1. 负责写盘的 Vite 插件slidev:monaco-write声明了apply: 'serve',即只挂载到开发服务器;构建/导出(slidev build、导出 PDF 等)产物中不存在该写盘通道。见 monacoWrite.ts。
  2. 客户端保存动作通过 HMR 通道import.meta.hot.send(...)发送,未运行 dev server 时不存在 HMR socket。见 Monaco.vue。
  3. 客户端可写开关本身由__DEV__编译宏约束:writable && !readonly && __DEV__三者同时满足才允许保存。见 Monaco.vue。

因此,{monaco-write}是为演讲现场(本地slidev开发预览)准备的,不要指望它作用于构建后的静态部署页面。

三、语法全解:路径、语言、region 与可叠加选项

由于{monaco-write}建立在<<<代码导入语法之上,它天然继承了 snippet 导入的全部书写能力。以下形态均可用:

1. 路径写法

  • 相对路径<<< ./some-file.ts {monaco-write},相对于当前slides.md所在目录解析;
  • 根目录别名<<< @/snippets/snippet.js {monaco-write}@指向幻灯片项目的根目录。官方建议把可写片段放在@/snippets下,便于与 Monaco 编辑器配合。

2. 显式指定语言

导入路径的扩展名会被自动识别为高亮语言,也可以手动覆盖:

<<< @/snippets/code.js ts {monaco-write}

3. 只取文件的某一段:region 过滤

如果源文件很大,只想让某一 region 出现在编辑器中(保存时仍写回整个文件),可以使用 VS Code 风格的#region标记:

<<< @/snippets/snippet.js#region-name {monaco-write}

解析器会从文件中裁剪出#region region-name#endregion region-name之间的内容作为编辑器初始代码,支持嵌套 region 与多种语言的注释风格(///* */#<!-- -->--#pragma(* *)等),对应实现见 snippet.ts 中的 markers 与 findRegion。

4. 与其他代码块特性叠加

{monaco-write}会从 meta 中被单独剥离,其余代码块选项原样传给 Monaco 组件,因此可以自由组合:

<<< @/snippets/snippet.js {monaco-write}{lines:true}{height:auto}
  • {lines:true}:为编辑器开启行号(其余行为遵循全局lineNumbersheadmatter);
  • {height:'auto'}:编辑器高度随输入内容自动增长,也可用{height:'300px'}{height:'100%'}等 CSS 单位固定高度;
  • 更多编辑器层面的配置可参考 配置 Monaco 编辑器。

叠加顺序不限:{monaco-write}被识别后即从 meta 中移除,剩余 JSON 通过v-bind绑定到<Monaco>组件,见 snippet.ts 的实现。

5. 与普通 Monaco 的对照

  • ```ts {monaco}:只读编辑体验,改动不落盘,用于展示与现场尝试;
  • ```ts {monaco-diff}:双代码块差异对比编辑,用~~~分隔原始与修改版本;
  • <<< path {monaco-write}:真正的"改真文件"模式,三种能力的对比如下表:
指令来源可编辑保存回文件适用场景
{monaco}内联代码块内容是(仅内存)现场演示、代码讲解
{monaco-diff}内联代码块内~~~两侧是(仅内存)展示改动前后差异
{monaco-write}<<<导入的真实文件现场改码、边讲边改并落地

四、底层原理:从 Markdown 行到磁盘写入的完整链路

理解{monaco-write}的完整行为,需要沿一条调用链走通四个环节,对应仓库中的三处核心源码。

第 1 步:MarkdownIt 插件识别并改写代码块

Markdown 解析阶段由 markdown-it 插件MarkdownItSnippet处理(snippet.ts)。当某行匹配^<<<导入语法(正则RE_SNIPPET_IMPORT)时,插件先按规则解析出文件路径、region、语言与 meta;随后判断 meta 中是否包含{monaco-write}

  • 若包含,则执行三件事:把该文件路径登记进monacoWriterWhitelist白名单;将文件内容经lz-stringcompressToBase64压缩编码;最后向文档流插入一段自定义 HTML token:<Monaco writable="..." code-lz="..." lang="..." v-bind="..." />。也就是说,{monaco-write}实际是语法糖,最终会编译为一个携带可写文件路径的 Monaco 组件。

对应实现见 snippet.ts。代码以 Base64 压缩串内联进 HTML 是刻意的设计——编辑器即使尚未与文件系统交互,也能立即渲染初始内容,不依赖额外请求。

第 2 步:客户端 Monaco 组件注册保存动作

浏览器侧,内置组件 Monaco.vue 负责渲染编辑器并接管交互:

  • 组件 Props 中包含writable?: string(要写回的文件路径)与readonly
  • 挂载后,编辑器注册了一个名为slidev-save的动作,绑定Ctrl/Cmd + S快捷键(Monaco.vue);
  • 触发保存时,若isWritable(即writable && !readonly && __DEV__)不成立,会输出警告this monaco editor is not writable, save action is ignored并忽略本次操作;
  • 条件满足则通过 HMR 通道向开发服务器发送自定义消息:import.meta.hot.send('slidev:monaco-write', { file: writable, content: editor.getValue() }),其中file是 slides 里书写的文件路径,content是编辑器当前全部文本。

需要留意编辑器自动高度与行号等选项同样作用于该组件,见 Monaco.vue 的 Props 定义与默认值。

第 3 步:服务端插件校验并写盘

开发服务器一侧,Vite 插件 monacoWrite.ts 中名为slidev:monaco-write的插件通过configureServer监听每个 WebSocket 连接上的消息(monacoWrite.ts):

  1. 白名单校验:消息必须携带{ file, content },且file必须出现在monacoWriterWhitelist中——该集合只由解析阶段使用过{monaco-write}的文件填充,从而保证只有幻灯片里显式声明过的文件才允许被写入,非法文件会被拒绝并打印[Slidev] Unauthorized file write
  2. 越界防护:将路径path.resolve(userRoot, file)解析后,用path.relative(userRoot, filepath)判定是否仍在项目根目录内;若rel..开头或为绝对路径(即解析后逃逸出项目根目录),则拒绝写入并打印Refusing monaco write outside project root
  3. 真实写盘:通过校验后打印[Slidev] Writing file: <path>日志,并以utf-8编码调用fs.writeFile将内容写回磁盘。

第 4 步:插件注册与生命周期

createMonacoWriterPlugin被统一挂载在 vite/index.ts 返回的插件数组里,与 Monaco 类型加载、远程资源、HMR 补丁等插件并列。由于插件设置了apply: 'serve',因此只参与 dev server 的插件管线——这再次印证了第一节提到的"仅开发模式生效"约束。

五、安全须知与实用建议

写入是立即且直接落盘的

与「保存到内存、刷新即丢」的普通 Monaco 不同,{monaco-write}一旦保存成功,源文件立刻被覆盖,不经过暂存区,也没有撤销通道。官方文档给出的警告非常明确:使用前务必备份相关文件(Back up files before using - changes are saved directly)。对于参加现场演示的版本库,建议:

  • 演示前对要修改的文件建立副本或提交一次 git commit,便于一键还原;
  • 演示用的片段与正式代码尽量隔离,例如单独维护@/snippets/下的演示文件;
  • 明确告知听众/协作者保存动作会真实改动工作区,避免误操作污染共享分支。

服务端做了哪些保护(可以从源码确认)

  • 白名单机制:只有写入了{monaco-write}指令、并成功解析出的文件才可被写入,见 monacoWrite.ts 与 snippet.ts;
  • 目录逃逸防护:写盘目标被强制约束在userRoot(项目根)之内,无法通过路径拼接写任意位置;
  • 保存被readonly覆盖:若同一代码块同时带只读选项,isWritable计算会将其关闭,保存动作被静默忽略(仅在控制台告警)。

常见问题排查

  • Ctrl/Cmd + S没有反应:确认正在运行slidev开发服务器(非构建产物预览)、编辑器未处于只读状态、且代码块确实通过<<<语法导入而非内联```书写;
  • 想让它自动保存而无需按键:当前实现为手动Ctrl/Cmd + S触发,源码中没有自动定时写回逻辑,如需此能力可在社区讨论或自行扩展客户端 action;
  • 只想展示、禁止误改:去掉{monaco-write}改回{monaco}即可恢复只读内存态。

六、总结

{monaco-write}是 Slidev 把「编辑器」与「代码仓库」打通的关键特性,适合一切需要真实改码的演示与教学场景。它由三部分协作完成:MarkdownIt 阶段的<<<导入与白名单登记、客户端 Monaco 的Ctrl/Cmd + S保存动作与 HMR 消息发送、服务端插件的白名单/越界校验与最终落盘。理解这条链路,你就能在幻灯片里安全、可靠地做 live coding,同时清楚何时不可用(构建产物)、如何防误改(备份与只读)。

延伸阅读(仓库内):可写编辑器的功能说明见 docs/features/monaco-write.md;<<<导入语法全解见 docs/features/import-snippet.md;普通 Monaco 与 diff 编辑器见 docs/features/monaco-editor.md;编辑器全局配置见 docs/custom/config-monaco.md。

【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidev

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

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

将内容导入 Open Notebook:添加 Source 的完整实战指南

将内容导入 Open Notebook&#xff1a;添加 Source 的完整实战指南 【免费下载链接】open-notebook An Open Source implementation of Notebook LM with more flexibility and features 项目地址: https://gitcode.com/GitHub_Trending/op/open-notebook 把文档、网页、…

作者头像 李华
网站建设 2026/9/9 19:44:38

毕业设计指南:基于SpringBoot+Vue的车间管理系统开发全流程

1. 选题定调&#xff1a;为什么车间管理系统是性价比最高的毕设方向之一每年到了毕设选题季&#xff0c;后台都会收到一堆类似的私信&#xff1a;老师给的题目列表里全是"基于XX的XX管理系统"&#xff0c;看着没新意&#xff0c;但又怕选难了做不出来。如果你现在正处…

作者头像 李华
网站建设 2026/9/9 19:43:51

Ince-Gaussian光束的涡旋阵列生成:理论仿真与实验全流程解析

做结构光场的人&#xff0c;近几年大概都会听到一个绕不开的名字——Ince-Gaussian光束。如果你正好拿到这个课题&#xff0c;要看明白涡旋阵列是怎么从这类光束里“长”出来的&#xff0c;我的建议是别急着上手调光路&#xff0c;先花两天把Ince-Gaussian的理论框架和仿真吃透…

作者头像 李华
网站建设 2026/9/9 19:41:24

Cena 0.8.2评测软件实战:从编译配置到SPJ实现的全流程指南

简介&#xff1a;Cena评测软件0.8.2是一款面向C、C和Pascal编程学习与竞赛场景的自动评测工具&#xff0c;可检查代码正确性、运行效率及编码规范&#xff0c;适合从初学者到竞赛选手各层次用户快速获得反馈。压缩包共446个文件&#xff0c;约10.68MB&#xff0c;主要包含h头文…

作者头像 李华