## 渲染失败后永久空白
四个渲染点都是同一个写法:
<n-alert v-if="renderError" ... />
<div v-else ref="mermaidContainer"></div>
一旦渲染出错,容器被 `v-else` 卸载,`mermaidContainer.value` 变成 null。而
`renderFlowchart` 一进来就先清 `renderError`、再因为 `!container` 直接 return ——
于是**报错提示消失了,图也再画不出来**,只剩一块空白,只能刷新页面。翻历史提交
最容易踩到。改成容器常驻、用 `v-show` 隐藏,和 MermaidEditor 本来的写法一致。
`FlowchartScoreDetail` 那处 Teleport 上不能挂 v-show,把条件挪到内层容器。
实测:修复前切回合法代码后 0 个 svg(永久空白),修复后正常渲染。
## 每次渲染失败都往 body 漏一个 div
`m.render(id, code)` 没传容器,mermaid 会在 `document.body` 上建一个临时
`div#d{id}`。而 `suppressErrorRendering` 默认关着,看 mermaid 源码,解析出错时
是先 `errorRenderer.draw()` 再 `throw`,**清理临时容器的那行在 throw 之后**,
永远执行不到;每次 render 用的又是新的随机 id,`removeExistingElements` 也清不掉
旧的。于是渲染失败一次就留一个。
出题页是边敲边预览,且没有防抖,每个字符触发一次完整渲染,中间态几乎全是语法
错误 —— 实测逐字符敲 26 个字符,body 里留下 16 个残留 div。打开
`suppressErrorRendering`(该分支是先清理再抛)+ 预览防抖 300ms,实测降到 0,
预览功能不受影响。
## 顺带
`loadMermaid` 缓存的是实例,两个组件同屏挂载时会双双落进 `if (!mermaidInstance)`,
import 和 initialize 各跑两次。改成缓存 Promise,并在失败时清掉缓存,避免一次
网络抖动把后续所有渲染都钉死在这个失败结果上。
`SubmitFlowchart` 的 `updatePage` / `openDetailModal` / `loadToEditor` 一并补了
错误兜底(同文件,见下一个提交的说明):前两个失败时 `rendering` 会卡在 true,
弹框永久转圈;`loadToEditor` 是裸 `JSON.parse`,老提交数据坏掉就点了没反应。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
47 lines
1.0 KiB
Vue
47 lines
1.0 KiB
Vue
<script setup lang="ts">
|
|
import { useMyFlowchartStore } from "shared/store/myFlowchart"
|
|
import { useMermaid } from "shared/composables/useMermaid"
|
|
|
|
const store = useMyFlowchartStore()
|
|
const { renderError, renderFlowchart } = useMermaid()
|
|
const mermaidContainer = useTemplateRef<HTMLElement>("mermaidContainer")
|
|
|
|
watch(
|
|
() => store.mermaidCode,
|
|
async (code) => {
|
|
if (!code) return
|
|
await nextTick()
|
|
await renderFlowchart(mermaidContainer.value, code)
|
|
},
|
|
{ immediate: true },
|
|
)
|
|
</script>
|
|
|
|
<template>
|
|
<div style="padding: 8px 0">
|
|
<n-alert v-if="renderError" type="error" title="渲染失败" size="small">
|
|
{{ renderError }}
|
|
</n-alert>
|
|
<div
|
|
v-show="!renderError"
|
|
ref="mermaidContainer"
|
|
class="flowchart-container"
|
|
></div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.flowchart-container {
|
|
width: 100%;
|
|
min-height: 500px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
:deep(.flowchart-container > svg) {
|
|
width: 100%;
|
|
height: auto;
|
|
}
|
|
</style>
|