ARTICLE DETAIL

资讯详情

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

三步跑起 Mermaid 本地编辑器:零依赖离线画图工作台的构建与源码解读

三步跑起 Mermaid 本地编辑器:零依赖离线画图工作台的构建与源码解读 三步跑起 Mermaid 本地编辑器零依赖离线画图工作台的构建与源码解读【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid这是 Mermaid 官方仓库内置的 Mermaid 本地编辑器零依赖、一条命令构建业务代码不足十个文件离线即可在浏览器里画图、存图、导出 SVG。读完你能独立复现整条构建链并照这套模式搭出自己的离线画图台。幕一三步跑起来 一条命令背后的五步构建链在仓库根目录敲下pnpm build:mermaid:full操作就这么多。真正的工序由根 package.json 里这条脚本串起来pnpm clean pnpm build:mermaid pnpm copy:editor pnpm copy:mermaid pnpm copy:dompurify pnpm serve:dist五段各自分工顺序脚本实际命令作用1cleanrimraf packages/mermaid/dist清掉上次产物保证从零开始2build:mermaidpnpm build:esbuild --mermaid走仓库统一的 esbuild 管线编译出mermaid.min.js3copy:editorcpy packages/mermaid-local-editor/static/**/* …把编辑器页面源码原样搬进输出目录4copy:mermaid/copy:dompurify两条cpy将mermaid.min.js、purify.min.js收进vendor/5serve:distsirv packages/mermaid/dist/mermaid-local-editor --port 8081 --dev --no-clear静态托管产物人话版先编译、再搬页面、最后把两个运行时依赖塞进同一个文件夹起个静态服务器收工。其中copy:dompurify的路径写成node_modules/.pnpm/dompurify*/…/purify.min.js是因为 pnpm 把依赖摊在.pnpm存储目录里只能用通配符去捞这份预构建文件。打开 localhost:8081 看到什么浏览器打开http://localhost:8081顶部一条工具栏——折叠按钮、图表下拉框、名称输入框外加 Save / New / Delete / Reset View / Export SVG 五个操作按钮下面左边是源码文本区右边是渲染预览。往左边输入 Mermaid 语法右边立刻出现图全程没有任何网络请求。产物是纯静态文件拷到别的机器、内网服务器照样能完成 Mermaid 离线使用。构建产物目录树跑完之后可独立运行的目录长这样packages/mermaid/dist/mermaid-local-editor/ ├── index.html ├── styles.css ├── app.js ├── js/ │ ├── config.js │ ├── renderer.js │ ├── storage.js │ ├── ui.js │ └── navigation.js └── vendor/ ├── mermaid.min.js └── purify.min.jsvendor/里两个文件正好对上 index.html 中的两条script页面只走相对路径。README 的定位就是直接从dist/里跑理论上连服务器这一步都能省掉。幕二打开代码之前先看地图 ️static/ 目录树与五个 js 模块职责源码集中在 packages/mermaid-local-editor/static/static/ ├── index.html # 页面骨架与工具栏 ├── styles.css # 界面样式 ├── app.js # 唯一入口装配各模块 └── js/ ├── config.js ├── renderer.js ├── storage.js ├── ui.js └── navigation.js五个模块各管一摊调用关系一目了然模块一句话职责被谁调用js/config.js导出initMermaid初始化参数、共享视图状态state、IS_E2E开关app.jsjs/renderer.jsrenderDiagram渲染 SVG、净化、写入 iframe、绑定缩放平移app.js的render()js/storage.jscreateStorage管理两个 localStorage key 的读写app.js、ui.jsjs/ui.jssetupUI/refreshList工具栏按钮、下拉切换、300ms 自动保存、SVG 导出app.jsjs/navigation.jscreateNavigation键盘在节点间逐跳导航app.js渲染完成后由renderer.js回调rebuildNavNodesapp.js 的五步装配顺序app.js 顶层流程是一条直线initMermaid()—— 按编辑器偏好初始化运行时参数详解留到下一幕createStorage()—— 建立存储层首次访问时自动生成main图幕四展开createNavigation({ state, preview, srcPanel, applyTransform })—— 把共享状态和视图变换函数注入导航模块setupUI({...})—— 绑完全部工具栏事件navigation.setupKeyboardNav()接load(storage.current)—— 载入当前图并首渲。三个本地函数撑起骨架render()调渲染器load(name)负责从存储读源 → 写进文本区 → 恢复 scale/panX/panY → 触发渲染applyTransform()把视图状态落到 SVG 的 style 上并顺手同步回存储。先配运行时、再建存储、最后才首渲——这个顺序保证load执行时所有依赖已就位。幕三一行源码如何变成可交互的 SVG四个初始化参数改一行会发生什么js/config.js 只有 24 行initMermaid()里四个参数就是四道闸门startOnLoad: false去掉这行Mermaid 会在页面加载时自动扫描classmermaid的元素并渲染。而本编辑器的渲染入口只有左侧文本区靠 JS 手动触发自动启动开着只会打架。securityLevel: strict改成loose会放开图内点击跳转等交互能力。输入源是用户随手敲的文本解析这一关就把门打开后面几道过滤压力全变大幕五细说。deterministicIds: true元素 id 跨渲染保持稳定。键盘导航模块靠稳定的g.node收集节点列表一旦 id 每次渲染都变已收集的列表立刻作废。flowchart.useMaxWidth: false若为true流程图 SVG 会按容器宽度自适应缩放你放大到三倍布局又把它压回容器尺寸缩放手感抓不住。其余配置theme: dark、fontFamily: Arial、htmlLabels: false是外观偏好不碰主流程。同文件还导出了IS_E2Enavigator.webdriver或 URL 带graph参数时为真和全局共享的state { scale, panX, panY, iframeRef }后者是渲染器、导航、UI 三方共用的视图状态单例。渲染管线mermaid.render 之后经过哪五站js/renderer.js 的renderDiagram()按时间线走完五站渲染await mermaid.render(id, srcValue)把源码编译成 SVG 字符串id 常规模式取Date.now()防连续渲染冲突E2E 模式固定m1。净化DOMPurify.sanitize(svg, { ADD_TAGS: [foreignObject], ADD_ATTR: [xmlns] })。为什么单独放行这两个Mermaid 的文本排版会用到foreignObjectSVG 根节点离开xmlns无法被当作独立文档解析——两者都在 DOMPurify 默认拦截名单里不显式加白就出不来图其余危险节点照旧被剥掉。二次加固遍历所有节点删除以on开头的属性onclick、onload等。净化器放行的是标签与命名空间事件类残留属性由这一遍兜底。沙箱写入创建iframesandbox只给allow-same-origin不给allow-scripts再importNode把 SVG 搬进 iframe 文档。这个框里最多展示不能执行。样式注入向 iframe 写一段style定义 hover 蓝色光晕、.selected-node蓝色描边加发光、浅色文字、body { overflow: hidden }。键盘导航的高亮、鼠标悬停的反馈全靠它。一句话源码进、SVG 字符串出中途过一道白名单过滤再关进无脚本的框里最后贴上交互皮肤。支线一缩放、平移与 E2E 直出滚轮缩放state.scale e.deltaY * -0.0015随后钳制在0.24倍防止放大到糊掉或缩到看不见拖拽平移mousedown/mousemove/mouseup三件套拖拽中光标切grabbingapplyTransform()在 app.js把translate(panX, panY) scale(scale)写到 SVG 上平移量额外钳在±20000px并把view同步写回存储——视图位置随图保存E2E 分支IS_E2E为真时跳过 iframe净化后的 SVG 经DOMParser直接塞进#preview就返回。自动化截图不必等 iframe 布局断言与截图都能直接对着 SVG 做。支线二错误不白屏整段流程套在 try/catch 里抛异常就清空预览区用红色pre显示e.message。输入是自由文本语法错误是常态右侧一条清晰的报错比白屏加一屏控制台警告有用得多。幕四关掉页面数据还在 localStorage 双 key 持久化结构js/storage.js 只依赖两个 key用 localStorage 保存全部图表Key值形态含义mermaid-diagramsJSON 对象{ [图名]: { src, view } }所有图表src是 Mermaid 源码view是{ scale, panX, panY }mermaid-current字符串默认main当前选中的图名首次访问发现main不存在自动补一条默认图diagrams[current] { src: flowchart LR\n UI -- RuntimeBus -- Orchestrator -- Agents, view: { scale: 1, panX: 0, panY: 0 }, };这就是开箱即用的来源打开就有图。注意view是按图存的缩放和平移位置跟着图走切走再切回来现场原样。四个按钮各自的存储行为按钮存储侧动作Save取#name输入框的值当当前图名写入src与当前视图刷新下拉框New弹窗要名字写入默认内容flowchart LR\n A -- B当前图切过去Deleteconfirm确认后删当前图回落到剩余第一张或main下拉切换setCurrent记新当前图重新走load300ms 防抖自动保存是怎么实现的文本区每次input事件先无条件调render()刷新预览渲染要实时再clearTimeout setTimeout(300)把最新源码落盘。连续打字时定时器反复重置只算最后一次写。所以忘了点 Save也不会丢多少东西——防抖把高频输入收敛成一次持久化。SVG 导出与视图重置Reset Viewscale/panX/panY归位1/0/0后调applyTransform()后者会把重置后的视图存回去——重置这个动作本身也被持久化。Export SVG从 iframe 取出当前 SVGnew Blob([svg.outerHTML], { type: image/svgxml })→URL.createObjectURL生成临时地址 → 挂到隐藏a download图名.svg上点击 →revokeObjectURL释放。下载全程在客户端完成不经过任何上传。另外还有个toolbar-collapsedkey1/0记住工具栏折叠状态属于界面偏好记忆与图表数据无关。幕五没有后端凭什么敢直接渲染用户输入 ️前面几幕摆完事实这幕回答一个问题为什么把用户输入直接喂给浏览器渲染这件事成立。把数据流拆成输入、解析、输出、运行四段每段设防解析端——Mermaid 配置层。securityLevel: strict让点击导航、脚本类能力在解析阶段就不可用deterministicIds顺带稳住元素 id。若在这层松手SVG 字符串天然带上可执行语义下游过滤成本成倍上涨。输出端——净化层。所有 SVG 字符串统一过DOMPurify.sanitize白名单只加回foreignObject与xmlns再叠加on*属性剥离。净化器管标签与属性遍历管事件类残留两头不重不漏。运行端——沙箱层。最终 SVG 落进只授allow-same-origin的 iframe。即便前两层漏下残余载荷这个框里没有脚本执行权限也摸不到主页面 JS 上下文。至于完全离线、不做服务端校验为何依然成立服务端校验解决的是可信服务器 ↔ 不可信传输链路的问题这里输入和执行环境本来就在同一个浏览器里等价的服务端就是你自己搭的 sandbox iframe。三道防线把不可信内容拦在可执行领地之外中间不需要任何网络环节——离线不是省事的妥协而是这条链路本来就够短。幕六拿它当模板 键盘节点导航的三个可借鉴细节js/navigation.js 体量不大细节值得抄进自己的项目只用两个键ArrowDown下一节点、ArrowUp上一节点。节点列表来自 iframe 内全部g.node每次渲染结束由rebuildNavNodes回调重新收集并重置索引避免操作失效的旧节点。居中偏移取 1/6 而非 1/2centerCurrentNode里中心按top height / 6计算。严格居中会让焦点节点恰好顶到画面上缘LR/TD 布局下后续节点直接掉出视野取 1/6 把焦点压向画布左上下一步仍在可视区里连续下键体验才顺。编辑时不劫持按键事件处理先判断焦点是否在源码文本区是则直接 return。代码输入优先用键盘导航只在非输入态接管。高亮本身由 renderer 注入的 CSS.selected-node蓝框加发光呈现导航模块只负责切换 class——样式与逻辑分家两边可独立改。二次开发可裁剪点与可迁移模式子包业务代码合计就八个文件入口只有一个app.js裁剪点很明显删导航js/navigation.js及 app.js 里两处调用主流程无感换存储层createStorage的返回值只被用到setCurrent / updateCurrent / deleteCurrent / create四个方法换成 IndexedDB 或文件方案只需对齐这几个签名换主题theme: dark与注入 iframe 的那段 CSS 是仅有的两处外观相关代码。可迁移的模式更短构建期把mermaid.min.js和purify.min.js打进vendor/运行期走渲染 → 净化 → 沙箱 → 持久化一整圈。凡是需要离线画图、内网工具嵌入图表渲染的场景照这个装配顺序就能复刻。它在 monorepo 构建与测试体系中的位置根 package.json 把整条构建链固化成build:mermaid:full一条命令CI 或本地都可一键复现e2e 范围脚本 e2e-diagram-scope.mjs 显式列出了packages/mermaid-local-editor/路径涉及该包的改动会同步触发对应端到端测试。根lint命令对整个仓库跑 ESLint这份源码也在同一套静态检查范围内。【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表