ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Slidev 实战:用 vue-runner Demo 为 Monaco Runner 扩展 Vue SFC 实时运行能力

Slidev 实战:用 vue-runner Demo 为 Monaco Runner 扩展 Vue SFC 实时运行能力 Slidev 实战用 vue-runner Demo 为 Monaco Runner 扩展 Vue SFC 实时运行能力【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidev本文以仓库中的 demo/vue-runner 演示工程为主线完整讲解 Slidev 的{monaco-run}代码块如何把代码变成可运行编辑器并逐行剖析其中的 Vue SFC Code Runner 实现——从vue/compiler-sfc编译、import 重写、new Function求值到createApp挂载。读完后你可以掌握自定义 Code Runner 的完整机制并能在自己的 Slidev 演示中运行任意自定义语言代码。Demo 项目做了什么vue-runner 是 Slidev 仓库自带的一个最小演示工程只有一页幻灯片加一个自定义 runner 实现目的如第 32 行的原文所述This is a demo to prove the extensibility of Slidev Code Runners——证明 Code Runners 机制的可扩展性。工程结构非常精简demo/vue-runner/ ├── setup/ │ ├── code-runners.ts # 核心Vue SFC Runner 的实现 │ └── shiki.ts # 补充 vue 语言的 shiki 高亮 ├── slides.md # 演示幻灯片 └── package.json # 依赖与脚本第一页幻灯片里只有一个vue {monaco-run}代码块见 slides.md内容是一个完整的 Vue 单文件组件script setup import { computed, ref } from vue const counter ref(1) const doubled computed(() counter.value * 2) function inc() { counter.value } /script template div classselect-none text-lg flex gap-4 items-center span classtext-gray text-lg span classtext-orange{{ counter }}/span * 2 span classtext-green{{ doubled }}/span /span button classborder border-main p2 rounded clickinc1/button button classborder border-main p2 rounded clickcounter - 1-1/button /div /template当monaco功能开启时这个代码块会被转换成 Monaco 编辑器观众不仅可以在演示中直接编辑这个 SFC还能点击运行按钮把整个组件实时挂载到幻灯片上看到计数器工作、doubled计算属性随counter变化——这就是本 demo 想展示的“在幻灯片里跑一个真实 Vue 组件”。package.json中依赖了两样关键东西vue和vue/compiler-sfc见 demo/vue-runner/package.json分别供运行时创建组件和编译 SFC 使用。本地运行该 demo 可参考其中的脚本例如pnpm dev使用 nodemon 监听packages/slidev/dist变化后启动slidev ./slides.md、pnpm build、pnpm export等。顺带一提slides.md 第 34 行提示读者Refer to./setup/monaco-runner.ts但仓库中实际的实现文件是 setup/code-runners.ts这是 demo 中一处遗留的命名不一致阅读源码时以实际文件为准。{monaco-run}代码块的底层机制在动手写 Vue Runner 之前先弄清{monaco-run}是如何被解析和渲染的。语法如何变成 Monaco 组件代码块标记的解析发生在 Vite 插件侧的 markdown-it 语法转换层。packages/slidev/node/syntax/codeblock/monaco.ts 定义了核心正则const RE_MONACO /^([\w-])?\s*\{(monaco[\w-]*)\}\s*(\{[^}]*\})?(.*)$/它依次捕获语言标识如vue、ts、{monaco...}变体、以及可选的属性对象如{autorun:false}。转换器随后检查全局配置中config.monaco是否启用config.monaco true || config.monaco mode用 lz-string 将代码compressToBase64压缩避免把大段代码原文写进模板当变体为monaco-run时输出runnable属性最终生成形如Monaco v-bind{autorun:false} runnable langvue code-lz... /也就是说{monaco-run}与{monaco}的区别仅在于是否携带runnable标记真正决定能不能跑的是运行时的 runner 注册表。CodeRunner 组件Run 按钮、自动运行与输出展示携带runnable属性的代码块会挂上 packages/client/internals/CodeRunner.vue 组件它的行为细节包括运行入口模板右下角的播放按钮触发triggerRun内部是debounce(200, ...)防止连续点击造成并发执行autorun 语义props 中autorun支持boolean | once。演示模式下autorun为真时会对code建立watch(..., { immediate: true })即修改代码即重新求值打印模式下则强制为once只跑一次保证截图/导出结果稳定模式限制disabled仅在slide、presenter上下文以及嵌入模式下的overview为false在其他渲染上下文中会显示Code is disabledshowOutputAt与v-click相同的取值方式组件通过$clicksContext.calculate注册点击节点输出区域只在对应点击步激活时显示见 docs/features/monaco-run.md 中的{showOutputAt:1}示例结果渲染runner 的返回值可以是 HTML 字符串、错误文本、带高亮语言的文本或一个 DOM 元素element通过DomElement.vue挂载——vue-runner 正是利用了最后一种。核心实现写一个 Vue SFC Code Runner打开 demo/vue-runner/setup/code-runners.ts整个 Vue Runner 只有 38 行。它按 Slidev 约定的入口导出import { defineCodeRunnersSetup } from slidev/types export default defineCodeRunnersSetup(() { return { async vue(code) { // ... }, } })约定本身定义在 docs/custom/config-code-runners.md在项目根目录创建./setup/code-runners.ts返回对象中每个 key 是一种语言 id对应一个(code, ctx) outputs的函数。这里以vue为 key意味着vue {monaco-run}代码块会把 SFC 源码交给它。下面按实现顺序拆解这个函数体完整代码见 setup/code-runners.ts1. 按需引入编译工具链const Vue await import(vue) const { parse, compileScript } await import(vue/compiler-sfc)vue与vue/compiler-sfc都是浏览器环境可用的包这也是 demo 的package.json把二者列为 devDependencies 的原因parse负责解析 SFC 结构compileScript负责把script setup与模板编译为可执行的组件导出。2. 编译 SFC内联模板 命名导出const sfc parse(code) let scripts compileScript(sfc.descriptor, { id: sfc.descriptor.filename, genDefaultAs: __Component, inlineTemplate: true, }).content两个编译选项是关键inlineTemplate: true把template编译出的渲染函数直接内联进脚本而不是引用外部的template对象。这样编译产物才是自包含的无需再单独处理模板编译结果genDefaultAs: __Componentscript setup编译后的默认导出会被命名为__Component方便后续拼上return __Component。demo 原文也注明Compile the script, note this demo does not handle Vue styles——style块在这条链路上是被忽略的样式需写在模板的 class 中demo 用了 UnoCSS 原子类flex、p2、rounded等这些由 Slidev 全局样式提供。3. 重写 Vue 的 import 语句scripts scripts.replace( /import (\{[^}]\}) from []vue[]/g, (_, imports) const ${imports.replace(/\sas\s/g, :)} Vue, ) scripts \nreturn __ComponentcompileScript的产物仍是 ES Module 语法而浏览器里无法直接import vue。demo 用一个简单的正则替换把import { ref, computed } from vue改写为对象解构const { ref, computed } Vue其中\sas\s→:的处理兼容了import { ref as r }这类重命名写法。随后在脚本末尾追加return __Component让整个脚本变成一个返回组件的函数体。4. 求值并挂载组件// Note this is not sandboxed, its NOT secure. const component new Function(return (Vue) {${scripts}})()(Vue) const app Vue.createApp(component) const el document.createElement(div) app.mount(el) return { element: el }new Function(...)把改写后的脚本包装成一个立即调用的工厂函数参数传入完整的Vue命名空间拿回编译产物中的组件对象Vue.createApp(component)app.mount(el)将组件挂载到一个新建的div返回{ element: el }命中了CodeRunnerOutputDom这一输出类型CodeRunner 组件会用DomElement.vue把它放进输出区域Vue 响应式系统因此照常工作点击幻灯片上的按钮就能改变计数。demo 源码在new Function上一行明确注释了安全边界Note this is not sandboxed, its NOT secure.——这段代码直接在主线程求值任意代码只适合本地演示不应照搬到不可信输入的场景。5. 让 shiki 认识 vue 语言Runner 只负责运行代码块左侧的语法高亮仍由 shiki 完成。setup/shiki.ts 在 shiki setup 中追加了vue、ts、js、html语言import { defineShikiSetup } from slidev/types export default defineShikiSetup((): ShikiSetupReturn { return { langs: [ts, js, vue, html], } })缺少这一步时{monaco-run}编辑器里vue语言可能拿不到正确的文法定义。Code Runner API类型与组装链路demo 用到的 API 在类型层有完整定义理解它们有助于写更复杂的 runner。输入Runner Contextrunner 的第二个参数ctx类型为 packages/types/src/code-runner.ts 中的CodeRunnerContextexport interface CodeRunnerContext { options: Recordstring, unknown // 代码块上 {runnerOptions:...} 传入的选项 highlight: (code, lang, options?) string // shiki 高亮 run: (code, lang) PromiseCodeRunnerOutputs // 调用其它 runner }vue-runner 只用了第一个参数code而ctx.run允许你在一个 runner 里复用另一个 runner例如先用 JS runner 转译再执行ctx.options则能把代码块上的自定义属性透传到 runner 内部。输出五种形态export type CodeRunnerOutput | CodeRunnerOutputHtml // { html } 直接渲染 HTML注意Slidev 不做 sanitize来源须可信 | CodeRunnerOutputDom // { element } 挂载一个 DOM 元素 ← vue-runner 用的就是它 | CodeRunnerOutputError // { error } 以红色文本显示错误 | CodeRunnerOutputText // { text, class?, highlightLang? } 可带 shiki 高亮 | CodeRunnerOutputText[] // 多行文本element形态是跑一个组件类需求Vue、React、Svelte 等的正确姿势它把渲染结果直接作为 DOM 插入输出区事件、响应式都能生效。组装链路内置 runner 与用户 setup 如何合并客户端侧的组装逻辑在 packages/client/setup/code-runners.ts初始注册表内置js / ts / javascript / typescript四种语言全部指向runTypeScript——它先经typescript.transpileModuleESNext 模块 ES2022 target转译并用 AST 转换器把import声明改写为await import(...)再由runJavaScript包装执行随后遍历#slidev/setups/code-runners即用户项目setup/code-runners.ts导出的 setup 函数Object.assign(runners, result)把 vue-runner 注册的vue合并进注册表run(code, lang, options)时按语言 id 查表查不到会抛出Runner for language xxx not found异常被捕获后统一转为{ error }输出——所以在幻灯片上跑一个未注册语言的代码块你会在输出区看到红字报错而不是页面崩溃模块级createSingletonPromise保证整个客户端只初始化一次 shiki 高亮器与注册表。内置 JS/TS runner 还模拟了一个受控consoleinfo/log/debug/warn/error都汇入结果列表clear清空输出并支持通过__slidev_import从扫描导入的依赖模块中取包——对应 docs/custom/config-code-runners.md 介绍的monacoRunAdditionalDepsheadmatter 选项monacoRunAdditionalDeps: - ./path/to/dependency - lodash-es依赖路径相对snippets目录解析包名须与代码中 import 的名字完全一致。适用边界与生产化注意事项demo 在第 36 行诚实地给出了限制声明there is a lot of edge cases that this demo is not handling. Extra work is needed to make it production ready.。结合源码可以归纳出几条明确的边界无沙箱runner 在主线程用new Function执行用户代码setup/code-runners.ts与 docs/custom/config-code-runners.mdThey run in the browserwithouta sandbox environment都强调了这一点。若要跑不可信代码需要自研 runner 转发到远程服务或 Web Worker不支持style编译链路只处理了 script 与 template样式需借助主题全局样式或模板内联import 重写是简化版scripts.replace(...)的正则只覆盖import {...} from vue这一形态从其它包导入的写法无法处理源码注释亦说明it doesnt work with imports from other packages语言 id 必须与代码块语言一致runner 以 key 注册vue {monaco-run}才路由到vuerunner。小结vue-runner demo 用最少的代码串起了 Slidev Code Runners 的完整链路{monaco-run}标记经 monaco.ts 转换器变成Monaco runnable组件CodeRunner.vue 提供编辑与执行交互而setup/code-runners.ts里的 38 行实现SFC 解析 → 内联模板编译 → import 解构化 →new Function求值 →createApp挂载 → 返回{ element }展示了如何把任意语言接入这套机制。如果你想在自己的演示里运行其它框架或 DSL参照 docs/custom/config-code-runners.md 的约定实现一个同构的 runner 即可更完整的输出类型与上下文定义可查 packages/types/src/code-runner.ts。【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidev创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表