級SVG的全鏈路實踐)
1. 什么是 diagram-design不是畫圖工具而是信息結(jié)構(gòu)的翻譯工程“diagram-design”這個詞最近在前端、產(chǎn)品、技術(shù)文檔和教育領(lǐng)域高頻出現(xiàn)但它絕不是簡單地“用 draw.io 拉幾個框、連幾條線”。我做了六年技術(shù)可視化工作帶過二十多個跨職能團(tuán)隊的流程建模項目越來越清楚diagram-design 的本質(zhì)是把模糊的業(yè)務(wù)邏輯、抽象的系統(tǒng)關(guān)系、隱性的決策路徑翻譯成人類視覺系統(tǒng)能瞬間解析的結(jié)構(gòu)化圖形語言。它介于需求分析與前端實現(xiàn)之間是工程師、產(chǎn)品經(jīng)理、架構(gòu)師、培訓(xùn)師共同使用的“第二語言”。你搜到的那些熱詞——HTML、SVG、Mermaid、draw.io——其實代表了 diagram-design 的四個不同“落點層級”Mermaid是“語義層”用純文本描述關(guān)系如graph TD; A--B; B--C適合快速建模、版本可控、嵌入文檔SVG是“渲染層”是真正被瀏覽器繪制的矢量圖形支持交互、動畫、縮放不失真是最終交付的“像素級結(jié)果”draw.io現(xiàn)為 diagrams.net是“協(xié)作層”提供拖拽式界面導(dǎo)出 SVG/HTML/JSON解決多人實時編輯、模板復(fù)用、企業(yè)資產(chǎn)沉淀問題HTML 基礎(chǔ)結(jié)構(gòu)比如!doctype htmlhtml langzh-cn...這類標(biāo)準(zhǔn)頭則是“承載層”決定 diagram 如何嵌入真實網(wǎng)頁、如何響應(yīng)設(shè)備、如何與頁面其他模塊共存。很多人卡在第一步以為學(xué)會 Mermaid 語法就等于掌握了 diagram-design。錯。我見過太多團(tuán)隊用 Mermaid 寫出完美的流程圖但一嵌入內(nèi)部系統(tǒng)就錯位、字體發(fā)虛、中文亂碼、移動端點擊失效——問題不在 Mermaid而在沒理解“從文本到像素”的完整鏈路。真正的 diagram-design 要同時懂三件事邏輯建模能力What to show、圖形表達(dá)規(guī)范How to show it right、前端集成細(xì)節(jié)Where and how it lives in real UI。這篇文章不教你怎么畫圓角矩形而是帶你走通這條從需求到可交付 SVG 的全鏈路每一步都踩過坑、測過邊界、寫過生產(chǎn)代碼。2. diagram-design 的核心設(shè)計思路為什么必須放棄“先畫再嵌”的老套路2.1 傳統(tǒng)做法的三大硬傷失真、失聯(lián)、失控過去三年我?guī)?7 家中大型企業(yè)重構(gòu)技術(shù)文檔體系發(fā)現(xiàn) 90% 的 diagram-design 都卡在同一個死循環(huán)里產(chǎn)品經(jīng)理用 draw.io 畫完圖 → 導(dǎo)出 PNG 插入 Confluence → 工程師復(fù)制粘貼到 HTML 頁面 → 發(fā)現(xiàn)縮放模糊、無法搜索、不能高亮節(jié)點、改一個字就得重畫整張圖。這種“先畫再嵌”的模式本質(zhì)上是把 diagram 當(dāng)作靜態(tài)圖片處理完全違背了現(xiàn)代 Web 的核心原則內(nèi)容即數(shù)據(jù)圖形即接口。具體來說這種模式有三個致命缺陷提示這不是理論問題而是每天都在發(fā)生的線上事故。我們曾因一張 PNG 流程圖里的箭頭顏色被 CDN 自動壓縮變淺導(dǎo)致新員工誤讀審批路徑引發(fā)跨部門流程阻塞。第一失真Loss of FidelityPNG/JPEG 是位圖放大后邊緣鋸齒、文字發(fā)虛。而真實業(yè)務(wù)場景中一張微服務(wù)調(diào)用鏈圖可能包含 40 服務(wù)節(jié)點需要在 4K 屏上展開查看字段名一張 ER 圖要打印 A3 紙供評審字體必須清晰可辨。SVG 天然矢量、無限縮放、CSS 可控樣式這才是唯一解。第二失聯(lián)Loss of ContextPNG 圖片和頁面 DOM 完全隔離。你想點擊“訂單服務(wù)”跳轉(zhuǎn)到該服務(wù)的 Swagger 文檔做不到。想 hover 顯示該節(jié)點的 SLA 指標(biāo)得額外寫 JS 綁定坐標(biāo)——而坐標(biāo)在 PNG 里根本不存在。SVG 元素是真實 DOM 節(jié)點g idorder-service可以直接加onclickjumpToSwagger()可以綁定 Vue 響應(yīng)式數(shù)據(jù)可以被屏幕閱讀器識別。第三失控Loss of Version Controldraw.io 導(dǎo)出的 PNG 是二進(jìn)制文件Git 無法 diff。改了一個連接線團(tuán)隊不知道誰改的、為什么改、是否影響下游。而 Mermaid 代碼是純文本git diff清晰顯示B --|HTTP| C變成了B --|gRPC| C配合 PR 注釋變更可追溯、可回滾、可自動化測試。2.2 新范式三層驅(qū)動設(shè)計法Text → SVG → HTML我們團(tuán)隊現(xiàn)在強(qiáng)制采用“三層驅(qū)動”工作流已穩(wěn)定運行 18 個月零次因 diagram 引發(fā)的線上問題。它的核心不是工具切換而是思維重構(gòu)層級輸入輸出關(guān)鍵動作責(zé)任人語義層TextMermaid / PlantUML 代碼標(biāo)準(zhǔn)化文本文件.mmd用 VS Code Mermaid Preview 實時校驗語法所有圖必須通過mermaid-cliCLI 生成 SVG 并校驗尺寸產(chǎn)品經(jīng)理 / 架構(gòu)師渲染層SVG.mmd文件優(yōu)化后的.svg文件移除冗余defs、壓縮 path 數(shù)據(jù)、添加aria-label、內(nèi)聯(lián)關(guān)鍵 CSS、設(shè)置viewBox和width/height響應(yīng)式屬性前端工程師承載層HTML.svg文件 頁面上下文可交互、可訪問、可 SEO 的嵌入代碼使用object或iframe安全加載禁用外部腳本監(jiān)聽load事件注入交互邏輯為關(guān)鍵節(jié)點添加>graph TD U1[用戶登錄] P1[權(quán)限校驗] U1 -- P1這里U1和P1是純英文 ID雙引號包裹的字符串才是真實顯示文本。這樣既規(guī)避了解析錯誤又保證了 Mermaid CLI 渲染一致性。我們團(tuán)隊的規(guī)范是所有節(jié)點 ID 必須小寫字母數(shù)字禁止下劃線和中文顯示文本必須用雙引號包裹且禁止在引號內(nèi)使用|、、等 Mermaid 特殊符號。另一個致命陷阱是空格敏感性。A -- B和A--B渲染結(jié)果相同但A --B箭頭后無空格會導(dǎo)致 Mermaid 解析器報錯。更隱蔽的是換行Mermaid 允許長文本換行但A[第一行\(zhòng)n第二行]在某些版本中會把\n渲染成實際換行符破壞布局。我們的實操方案是所有多行文本用br替代\n并用 CSS 控制 line-heightgraph LR A[div數(shù)據(jù)庫br連接池/div] -- B[divSQLbr執(zhí)行器/div]然后在 SVG 后續(xù)處理中為div添加內(nèi)聯(lián)樣式styleline-height:1.4;。這個細(xì)節(jié)看似瑣碎但避免了 83% 的“圖能跑但排版炸了”的現(xiàn)場救火。3.2 關(guān)卡二SVG 生成與壓縮——別讓 1KB 的 Mermaid 變成 500KB 的 SVGMermaid CLI 默認(rèn)生成的 SVG 包含大量冗余未使用的defs、重復(fù)的style塊、未壓縮的 path 數(shù)據(jù)、調(diào)試用的注釋。一張 20 節(jié)點的流程圖原始 Mermaid 代碼約 1.2KBCLI 生成的 SVG 卻達(dá) 480KB——全是g transformmatrix(1 0 0 1 0 0)這類嵌套 group。我們用svgoSVG Optimizer做標(biāo)準(zhǔn)化壓縮但不是簡單svgo input.svg -o output.svg。以下是生產(chǎn)環(huán)境必啟的 7 項配置寫在.svgorc中{ plugins: [ {name: removeDoctype}, {name: removeXMLProcInst}, {name: removeComments}, {name: removeTitle}, {name: removeDesc}, {name: removeUselessDefs}, { name: convertPathData, params: { straightCurves: true, transformPrecision: 4, removeEmpty: true } } ] }關(guān)鍵參數(shù)解釋transformPrecision: 4將transformmatrix(0.999999999 0 0 0.999999999 0 0)壓縮為transformmatrix(1 0 0 1 0 0)消除浮點誤差累積straightCurves: true把微小的貝塞爾曲線轉(zhuǎn)為直線對流程圖這類直角圖形無損且大幅減小 path 字符串removeUselessDefs刪除所有未被use引用的defs這是 draw.io 導(dǎo)出 SVG 的最大垃圾來源。實測效果一張 40 節(jié)點的序列圖SVG 從 620KB 壓至 38KB加載速度提升 12 倍且渲染無任何差異。更重要的是壓縮后的 SVG 可讀性反而提高——打開文件你能直接看到text x120 y85API 網(wǎng)關(guān)/text而不是淹沒在 50 層嵌套 group 里。3.3 關(guān)卡三字體與中文渲染——Windows/macOS/Linux 的三重地獄Mermaid 默認(rèn)用trebuchet ms,verdana,sans-serif但在中文環(huán)境極不可靠Windows 上trebuchet ms不存在fallback 到sans-serif而 Windows 的sans-serif是微軟雅黑macOS 是 HelveticaLinux 是 DejaVu Sans——同一份 SVG在三臺機(jī)器上字體高度差 2px導(dǎo)致文字溢出、換行錯位。我們的終極方案是放棄系統(tǒng)字體用 WOFF2 字體子集 CSSfont-face內(nèi)聯(lián)。步驟如下用 Font Squirrel Webfont Generator 上傳msyh.ttc微軟雅黑只勾選“Chinese Simplified”字符集生成 WOFF2Base64 編碼 WOFF2 文件在線工具即可得到約 80KB 的字符串在 SVG 的style塊中內(nèi)聯(lián)style typetext/css font-face { font-family: MSYahei; src: url(data:font/woff2;base64,d09GMgABAAAAA...) format(woff2); } text { font-family: MSYahei, sans-serif; } /style為什么不用 Google Fonts因為國內(nèi)訪問不穩(wěn)定且字體加載異步SVG 渲染時文字可能 fallback 到系統(tǒng)字體。內(nèi)聯(lián) WOFF2 確保“所見即所得”。我們測試過 12 種中文字體微軟雅黑在小字號12–14px下可讀性最優(yōu)且字重均勻不會像思源黑體那樣筆畫粗細(xì)跳躍。注意Mermaid v11 支持fontFamily配置項但僅限于 CSS 字體名無法指定 WOFF2。所以字體內(nèi)聯(lián)必須在 SVG 生成后手動注入我們用 Node.js 腳本自動化完成。3.4 關(guān)卡四響應(yīng)式 viewBox 與寬高比鎖定——讓 diagram 在手機(jī)上不“擠成一團(tuán)”很多團(tuán)隊把 SVG 當(dāng)作圖片設(shè)置width100% heightauto結(jié)果在 iPhone 上文字小到看不見。SVG 的響應(yīng)式核心是viewBox不是width/height。正確做法Mermaid 生成 SVG 時用--width 800 --height 600指定初始畫布生成后用腳本提取svg的width和height屬性計算寬高比ratio width / height將width和height屬性刪除只保留viewBox0 0 [width] [height]在 HTML 中用 CSS 控制容器尺寸div classdiagram-container svg viewBox0 0 800 600 preserveAspectRatioxMidYMid meet !-- content -- /svg /div.diagram-container { width: 100%; max-width: 1200px; aspect-ratio: 4/3; /* 與 viewBox 寬高比一致 */ } .diagram-container svg { width: 100%; height: auto; }preserveAspectRatioxMidYMid meet是關(guān)鍵它確保 SVG 在容器內(nèi)居中顯示且完整可見不裁剪同時保持原始比例。我們曾用slice模式導(dǎo)致流程圖右側(cè)節(jié)點被切掉客戶投訴后連夜改成meet。3.5 關(guān)卡五無障礙a11y支持——不只是合規(guī)更是體驗升級WCAG 2.1 要求所有圖形必須有文本替代。但img srcflow.svg alt用戶登錄流程圖是無效的因為 SVG 內(nèi)部有結(jié)構(gòu)化信息。正確方案是為svg添加roleimg和aria-labelledby在 SVG 內(nèi)部添加title和desc元素ID 與aria-labelledby對應(yīng)為每個關(guān)鍵節(jié)點添加aria-label。例如svg roleimg aria-labelledbyflow-title flow-desc title idflow-title用戶登錄與鑒權(quán)流程/title desc idflow-desc流程包含1. 用戶輸入賬號密碼2. 系統(tǒng)驗證憑證3. 返回 JWT Token4. 客戶端存儲 Token。/desc g idlogin-node aria-label用戶輸入賬號密碼 rect x50 y30 width120 height40/ text x110 y55賬號密碼輸入/text /g /svg實測效果視障用戶用 VoiceOver 朗讀時能聽到完整流程描述而非“圖片”。更意外的收獲是SEO 友好度提升Google 搜索“用戶登錄流程圖”時我們頁面的 snippet 顯示了desc內(nèi)容點擊率提高 22%。3.6 關(guān)卡六交互增強(qiáng)——讓 diagram 成為“活”的操作入口SVG 的 DOM 特性讓我們能把 diagram 變成操作面板。我們給某物流系統(tǒng)的調(diào)度圖加了三項交互節(jié)點點擊跳轉(zhuǎn)g idwarehouse-001 onclickopenWarehouseDetail(001)區(qū)域懸停高亮用 CSS:hover改變g的filter: drop-shadow()動態(tài)數(shù)據(jù)綁定用>svgContent svgContent.replace( /g id([^])/g, g id$1 onclickhandleNodeClick(\$1\)>viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(lon, lat), billboard: { image: path/to/diagram.svg, eyeOffset: new Cesium.Cartesian3(0, 0, 0), pixelOffset: new Cesium.Cartesian2(0, 0), sizeInMeters: true, width: 200, height: 100 } });我們實測過不加transform的 SVG 在 Cesium 中偏移達(dá) 15 米地球曲率影響加了之后定位精度達(dá)厘米級。4. 實操全流程從零開始搭建一個可維護(hù)的 diagram-design 工作流4.1 環(huán)境準(zhǔn)備5 分鐘搭好本地開發(fā)鏈不需要安裝 draw.io 或在線編輯器。我們用純命令行VS Code所有工具開源免費安裝 Node.js v18確保npm可用全局安裝 Mermaid CLInpm install -g mermaid-js/mermaid-cli安裝 SVGOnpm install -g svgoVS Code 插件Mermaid Preview實時預(yù)覽SVG Viewer直接查看 SVG 渲染效果Prettier格式化 Mermaid 代碼。注意不要用mermaid-live-editor這類在線工具生成生產(chǎn)代碼。它版本更新快但 API 不穩(wěn)定昨天能用的%%{init: {theme:base}}%%今天可能失效。CLI 版本鎖死package.json中固定mermaid-js/mermaid-cli: 10.9.2杜絕環(huán)境差異。4.2 第一個 diagram用 Mermaid 寫 ER 圖并生成 SVG以學(xué)校教學(xué)管理 ER 圖為例熱搜詞中高頻出現(xiàn)創(chuàng)建er.mmd文件%%{init: {theme: base, themeVariables: { fontSize: 14px, fontFamily: MSYahei, sans-serif}}}%% erDiagram STUDENT ||--o{ ENROLLMENT : 注冊 ENROLLMENT ||--|| COURSE : 選修 COURSE ||--o{ TEACHER : 授課 STUDENT }|--|| DEPARTMENT : 所屬 TEACHER }|--|| DEPARTMENT : 隸屬生成 SVGmmdc -i er.mmd -o er.svg -w 1200 -H 800 --puppeteerConfigFile puppeteer-config.jsonpuppeteer-config.json內(nèi)容解決中文渲染{ args: [--no-sandbox, --disable-setuid-sandbox], defaultViewport: {width: 1200, height: 800} }壓縮 SVGsvgo er.svg -o er.min.svg --config .svgorc手動注入字體和 a11y 標(biāo)簽用腳本或編輯器在svg開頭插入style塊含 WOFF2 內(nèi)聯(lián)添加title和desc為每個實體g添加aria-label如g idSTUDENT aria-label學(xué)生實體包含學(xué)號、姓名、專業(yè)字段。4.3 嵌入 HTML安全、可訪問、可交互的三重保障生成er.min.svg后不直接img而是用object!doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title教學(xué)管理系統(tǒng) ER 圖/title style .diagram-container { width: 100%; max-width: 1400px; margin: 0 auto; padding: 20px; } .diagram-container object { display: block; width: 100%; height: auto; border: 1px solid #e0e0e0; border-radius: 4px; } /* 懸停高亮效果 */ .diagram-container object:hover g[id] { filter: drop-shadow(0 0 8px rgba(0,120,215,0.5)); transition: filter 0.3s ease; } /style /head body div classdiagram-container object typeimage/svgxml dataer.min.svg aria-label教學(xué)管理系統(tǒng)實體關(guān)系圖 p您的瀏覽器不支持 SVG請a hrefer.min.svg下載查看/a。/p /object /div script // 交互邏輯點擊節(jié)點跳轉(zhuǎn)詳情頁 document.addEventListener(DOMContentLoaded, () { const obj document.querySelector(.diagram-container object); obj.addEventListener(load, () { const svgDoc obj.contentDocument; if (svgDoc) { const nodes svgDoc.querySelectorAll(g[id]); nodes.forEach(node { node.addEventListener(click, (e) { const id e.target.closest(g).id; if (id) { window.open(/entity-detail?id${id}, _blank); } }); }); } }); }); /script /body /html關(guān)鍵點object比iframe更安全不執(zhí)行 SVG 內(nèi)部腳本aria-label為不支持 SVG 的瀏覽器提供降級文案DOMContentLoadedload事件確保 SVG 加載完成后再綁定事件e.target.closest(g)兼容點擊文字或圖形區(qū)域。4.4 CI/CD 集成讓 diagram 變成可測試的代碼在package.json中加入腳本{ scripts: { build:diagrams: mmdc -i src/diagrams/*.mmd -o dist/diagrams/ --puppeteerConfigFile puppeteer-config.json svgo dist/diagrams/*.svg --config .svgorc, test:diagrams: node scripts/validate-diagrams.js, precommit: npm run test:diagrams } }validate-diagrams.js核心邏輯const fs require(fs); const path require(path); const svgFiles fs.readdirSync(dist/diagrams).filter(f f.endsWith(.svg)); svgFiles.forEach(file { const content fs.readFileSync(path.join(dist/diagrams, file), utf8); // 檢查是否含 title 和 desc if (!content.includes(title) || !content.includes(desc)) { throw new Error(SVG ${file} missing a11y tags); } // 檢查字體是否內(nèi)聯(lián) if (!content.includes(font-face)) { throw new Error(SVG ${file} missing font embedding); } // 檢查文件大小 const size fs.statSync(path.join(dist/diagrams, file)).size; if (size 100 * 1024) { // 100KB throw new Error(SVG ${file} too large: ${Math.round(size/1024)}KB); } }); console.log(? All diagrams validated);Git Hook 觸發(fā)precommit確保每張圖入庫前都達(dá)標(biāo)。我們曾因此攔截了 3 次因 Mermaid 版本升級導(dǎo)致的 a11y 缺失。5. 常見問題與排查技巧實錄那些讓你凌晨三點還在 debug 的坑5.1 Mermaid 渲染空白90% 是編碼或語法問題現(xiàn)象VS Code Mermaid Preview 顯示正常但 CLI 生成 SVG 是空白。排查順序檢查文件編碼必須是 UTF-8 無 BOM。用 VS Code 右下角查看若顯示 “UTF-8 with BOM”點擊轉(zhuǎn)換檢查特殊字符Mermaid 不支持、、在文本中必須寫成amp;、lt;、gt;檢查換行符Windows 的\r\n有時被解析為非法字符用dos2unix er.mmd轉(zhuǎn)換檢查主題配置%%{init: {...}}%%若 JSON 格式錯誤如末尾多逗號整個圖失效CLI 不報錯但輸出空白 SVG。實操心得新建.mmd文件時第一行寫%%{init: {theme: base}}%%第二行空行第三行開始寫圖。避免在 init 塊里寫復(fù)雜配置先跑通再迭代。5.2 SVG 在 Chrome 正常Firefox 顯示異常典型表現(xiàn)Firefox 中文字模糊、線條虛化、hover 效果失效。根因Firefox 對 SVG 的paint-order和filter渲染引擎不同。解決方案禁用filter: drop-shadow()改用box-shadow包裹 SVG 容器文字用text而非div并顯式設(shè)置dominant-baselinemiddle所有顏色用十六進(jìn)制#333不用命名色black或 RGBrgb(51,51,51)。我們用caniuse.com查 Firefox 對 SVG 特性的支持發(fā)現(xiàn)paint-order在 v102 才完全支持故生產(chǎn)環(huán)境一律不用。5.3 draw.io 導(dǎo)出的 SVG 無法用 SVGO 壓縮現(xiàn)象svgo input.svg -o output.svg報錯Error: Parse error at ...。原因draw.io 導(dǎo)出的 SVG 包含 XML 命名空間聲明xmlns:xlinkhttp://www.w3.org/1999/xlink和大量xlink:hrefSVGO 默認(rèn)不處理。解決在.svgorc中啟用removeUnknownsAndDefaults插件并添加cleanupIDs{ plugins: [ {name: removeUnknownsAndDefaults}, {name: cleanupIDs}, {name: removeUselessDefs} ] }更徹底的方案用 Python 腳本預(yù)處理移除所有xlink:前綴將xlink:href#id替換為href#id。5.4 Cesium 中 SVG billboard 閃爍或消失現(xiàn)象相機(jī)移動時SVG 圖標(biāo)忽隱忽現(xiàn)。原因Cesium 的 billboard 渲染有 Z-fighting深度沖突尤其當(dāng)多個 SVG 在同一地理坐標(biāo)時。解法為每個 billboard 設(shè)置唯一zIndex在billboard配置中添加disableDepthTestDistance: 0若仍閃爍改用EntityPolygonGraphics繪制 SVG 輪廓而非 billboard。我們最終選擇后者用 D3.js 解析 SVG path轉(zhuǎn)為 Cesium 的Cartesian3數(shù)組雖開發(fā)量增 3 倍但徹底解決閃爍。5.5 Typora 中 Mermaid 不更新升級指南Typora 內(nèi)置 Mermaid 版本老舊v8.x不支持erDiagram等新語法。正確升級路徑下載最新 Typorav1.7在偏好設(shè)置 → Markdown → 渲染中關(guān)閉“使用內(nèi)置 Mermaid”啟用“使用自定義 Mermaid”指向本地node_modules/.bin/mmdc重啟 Typora。注意Typora 的自定義 Mermaid 僅支持 CLI不支持瀏覽器版故init塊中的themeVariables可能失效建議在 Mermaid 代碼中用classDef替代。6. 進(jìn)階實戰(zhàn)用 diagram-design 解決三個真實業(yè)務(wù)難題6.1 難題一API 文檔中的調(diào)用鏈圖如何讓開發(fā)者一鍵跳轉(zhuǎn)到代碼某 SDK 團(tuán)隊的痛點文檔里的 HTTP 調(diào)用流程圖開發(fā)者看完還得手動搜索UserService.java。我們用 diagram-design 實現(xiàn)“圖即代碼”Mermaid 中每個節(jié)點 ID 與 Git 倉庫路徑映射U1[User Service]→ IDuser-serviceSVG 生成后為g iduser-service添加>fetch(${repo}/blob/main/src/main/java/com/example/UserService.java) .then(r r.text()) .then(code showCodeModal(code));效果開發(fā)者點擊圖中“User Service”彈窗直接顯示該服務(wù)的核心方法無需離開文檔。6.2 難題二培訓(xùn)課件中的動態(tài)流程圖如何根據(jù)學(xué)員選擇實時變化某在線教育平臺需要“分支流程圖”學(xué)員選“Java”或“Python”圖自動切換技術(shù)棧節(jié)點。方案用 Mermaid 的classDef定義兩套樣式classDef java fill:#f8f8f2,stroke:#e0e0e0; classDef python fill:#333,stroke:#666;所有節(jié)點按技術(shù)棧加 classA[Spring Boot]:::javaHTML 中用 JS 切換svg的>