imguolao / monaco-vue

Use monaco-editor loaded from CDN in Vue 2&3, no need to bundling.
https://imguolao.github.io/monaco-vue/
MIT License
211 stars 21 forks source link

path、value、language 同时配置时出现的bug #65

Closed Lzw2016 closed 1 month ago

Lzw2016 commented 2 months ago

源码中的 pathvaluelanguage使用了3个不同的 watch 导致更新时存在回调执行顺序问题,editorRef.value!.getModel()getOrCreateModel 调用的顺序存在问题!

    // path
    watch(
      () => props.path,
      (newPath, oldPath) => {
        const model = getOrCreateModel(
          monacoRef.value!,
          props.value || props.defaultValue || '',
          props.language || props.defaultLanguage || '',
          newPath || props.defaultPath || '',
        )

        if (model !== editorRef.value!.getModel()) {
          props.saveViewState && viewStates.set(oldPath, editorRef.value!.saveViewState())
          editorRef.value!.setModel(model)
          props.saveViewState && editorRef.value!.restoreViewState(viewStates.get(newPath)!)
        }
      },
    )

    // value
    watch(
      () => props.value,
      newValue => {
        if (editorRef.value && editorRef.value.getValue() !== newValue) {
          editorRef.value.setValue(newValue!)
        }
      },
    )

    // language
    watch(
      () => props.language,
      language =>
        isEditorReady.value && monacoRef.value!.editor.setModelLanguage(editorRef.value!.getModel()!, language!),
    )

应该使用

watch([() => props.path, () => props.value, () => props.language], ([newPath, newValue, newLanguage]) => {
    // 统一执行回调逻辑,控制好 editorRef.value!.getModel()与 getOrCreateModel 的调用顺序存
})

其它 props 的 watch 不知道有没有类似问题,需要检查

Lzw2016 commented 2 months ago

我在项目中通过一下代码简单的解决了这个问题:

watch(
    [
        () => props.path,
        () => props.language,
        () => value.value,
        () => props.line,
    ],
    ([newPath, newLanguage, newValue, newLine], [oldPath, oldLanguage, oldValue, oldLine]) => {
        // 读取数据
        const { defaultPath, defaultLanguage, defaultValue, saveViewState } = props;
        const { monaco, monacoEditor, viewStates } = data;
        // 校验
        if (!monaco) {
            console.warn("Monaco 还未加载完成!")
            return;
        }
        if (!monacoEditor) {
            console.warn("MonacoEditor 还未创建!")
            return;
        }
        // 获取当前编辑器Model
        const currentModel = monacoEditor.getModel();
        const currOrNewModel = getOrCreateModel(
            monaco,
            newValue ?? defaultValue,
            newLanguage ?? defaultLanguage,
            newPath ?? defaultPath ?? data.uniquePath,
        );
        // 创建新的编辑器Model
        if (currentModel !== currOrNewModel) {
            data.editorModels.add(currOrNewModel);
            // 保存编辑器状态
            if (saveViewState) viewStates.set(oldPath, monacoEditor.saveViewState());
            // 设置新的编辑器Model
            monacoEditor.setModel(currOrNewModel);
            // 还原编辑器
            if (saveViewState) {
                const viewState = viewStates.get(newPath) ?? null;
                monacoEditor.restoreViewState(viewState);
            }
            return;
        }
        // 更新 value
        if (monacoEditor.getValue() !== newValue) monacoEditor.setValue(newValue ?? '');
        // 更新 language
        if (newLanguage !== oldLanguage) monaco.editor.setModelLanguage(currentModel, newLanguage ?? '');
        // 更新 line
        if (newLine !== oldLine && hasValue(newLine)) monacoEditor.revealLine(newLine);
    },
);