codemirror-editor-vue3在a-modal展示导致lineNumber样式错乱问题排查

83 阅读1分钟

实现代码

 <a-modal ref="formModal" :formData="formData" :rules="rules" width="1000" :label-col="{ span: 4 }"
               @handle-ok="handleOk()">
     <div class="container">
         <div class="left-column">
             <div class="params">
                 <div class="section-title">case输入</div>
                 <Codemirror v-model:value="formData.content" :options="cmOptions" border
                             :height="300" @ready="onEditorReady" />
             </div>
             <div class="request">
                 <div class="section-title">case期待输出</div>
                 <Codemirror v-model:value="formData.compare" :options="cmOptions" border
                             :height="280" @ready="(cm) => onEditorReady(cm, 'compare')" />
             </div>
         </div>
         <div class="right-column">
             <div class="response">
                 <div class="section-title">case接口返回</div>
                 <Codemirror v-model:value="formData.detail" :options="cmOptions" border
                             :height="630" @ready="(cm) => onEditorReady(cm, 'detail')" />
             </div>
         </div>
     </div>
</a-modal>
// 编辑器尺寸变化处理
const setupEditorResize = (cmInstance, type) => {
    let lastHeight = cmInstance.getWrapperElement().offsetHeight;
    
    const resizeObserver = new ResizeObserver(([entry]) => {
        const { height } = entry.contentRect;
        
        if (height !== lastHeight) {
            cmInstance.refresh();
            
            if (type === 'compare') {
                highlightErrors(cmInstance, mismatchedLeft.value);
            } else if (type === 'detail') {
                highlightErrors(cmInstance, mismatchedRight.value);
                const lineNumber = mismatchedRight.value[0]?.line - 2 || 0;
                cmInstance.scrollTo(0, cmInstance.charCoords({ line: lineNumber, ch: 0 }, 'local').top);
            }
        }
        
        lastHeight = height;
    });
​
    resizeObserver.observe(cmInstance.getWrapperElement());
    return resizeObserver;
};
​
​
// 编辑器准备就绪回调
const onEditorReady = (cmInstance, type = 'content') => {
    observer.value = setupEditorResize(cmInstance, type);
};
​
// 组件卸载时清理observer
onUnmounted(() => observer.value.disconnect());

排查问题步骤

1.渲染流程分析:Modal开始渲染 -> Modal执行打开动画 -> CodeMirror 组件初始化 -> CodeMirror 计算尺寸和布局 -> CodeMirror 渲染编辑器内容和行号

const onCompareReady = (cmInstance) => {
    console.log('Initial state:', {
        // modal是否显示
        modalVisible: formModal.value?.visible,
        // 容器的宽度
        containerWidth: cmInstance.getWrapperElement().offsetWidth,
        // 容器的高度
        containerHeight: cmInstance.getWrapperElement().offsetHeight,
        // 滚动信息
        scrollInfo: cmInstance.getScrollInfo(),
        // 总行数
        lineCount: cmInstance.lineCount(),
        // 行号区域的宽度
        gutterWidth: cmInstance.getGutterElement().offsetWidth
    });
    
     // 监听尺寸变化
    const resizeObserver = new ResizeObserver((entries) => {
        console.log('Size changed:', {
            width: entries[0].contentRect.width,
            height: entries[0].contentRect.height,
            time: Date.now()
        });
    });
    resizeObserver.observe(cmInstance.getWrapperElement());
};

2.发现初始的高度过高,编辑器的高度会重新渲染。

  • 可能得原因是Modal 动画过程中容器尺寸不稳定
  • CodeMirror 的初始化时机过早
  • 行号计算依赖于准确的容器尺寸
  • 浏览器的重排和重绘时序问题