Vuetify 对话框 - 在 ACE 编辑器中格式化 2.5MB 的 XML 非常慢

Vuetify dialog - extremely slow formatting 2.5MB of XML in ACE editor

我有一个 Vue/Vuefify 项目,我正在使用 vkbeutify 库格式化 XML,然后在 Ace 编辑器中显示它。编辑器在模态对话框(全屏模式)中打开,当 XML 的数量很少时,一切都运行良好。

我必须解析其中的大约 2.5MB+(在 Notepad++ 中格式化时大约 2,500,000 个字符),然后 Ace 变得无法使用(它最终显示 XML,但需要很长时间)。

我创建了一个带有文本区域和编辑器的简单测试页面,格式化和显示速度非常快。该页面与此完全相同:https://www.webtoolkitonline.com/xml-formatter.html(这使用 Ace 和 vkbeautify 来格式化 xml)

我尝试在将 XML 传递给子对话框之前在文本区域中对其进行预格式化,尝试颠覆 Vue 并通过获取 DOM 在 mounted() 函数中填充编辑器容器内容直接。

独立测试页:

    <!DOCTYPE html>
<html lang="en">
<head>
    <title>ACE in Action</title>
    <style type="text/css" media="screen">#editor {position: absolute;top: 0;right: 0;bottom: 0;left: 0;}</style>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="https://pagecdn.io/lib/ace/1.4.5/ace.js" integrity="sha256-5Xkhn3k/1rbXB+Q/DX/2RuAtaB4dRRyQvMs83prFjpM=" crossorigin="anonymous"></script>
    <script src="https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/vkbeautify/vkbeautify.0.99.00.beta.js"></script>
</head>
<body>
<div style="position: relative; width: 100%; height: 500px;">
<div id="editor">
    <tag><inner_tag>sce to aux</inner_tag></tag>
</div>
</div>
<div>
<form>
    <textarea id="xml"><?xml version="1.0" encoding="UTF-8"?><tag><inner_tag>hi</inner_tag></tag></textarea>
    <fieldset><input type="button" id="format" value="Format"></fieldset>
</form>
</div>
<script>
    var editor = ace.edit("editor");
    editor.setTheme("ace/theme/cobalt");
    editor.session.setMode("ace/mode/xml");
    $( function() {
        $( "#format").click(
            async function() {
                var content = document.getElementById("xml").value;
                try {
                    editor.setValue(  content  );
                    editor.setValue( vkbeautify.xml( editor.getValue()  ) );
                } catch( err ) {
                    alert( "Your document is invalid" );
                }
            }
        );
    });
</script>
</body>
</html>

Vue 组件:

    <template>
  <div>
    <b>Open In Editor </b>
    <v-btn icon color="transparent" depressed small @click="openInEditor()"><v-icon color="#666666">mdi-pencil</v-icon>
    </v-btn>
    <v-dialog v-model="showAceEditorModal" fullscreen hide-overlay eager>
      <v-card>
        <v-toolbar dark color="primary">
          <v-btn icon dark @click="closeAceEditor">
            <v-icon>mdi-close</v-icon>
          </v-btn>
          <v-toolbar-title>{{title}}</v-toolbar-title>
          <v-card-text v-if="editable===false"> Read Only Mode - you won't be able to edit or save changes</v-card-text>
          <v-spacer></v-spacer>
          <v-toolbar-items>
            <v-btn dark text @click="sendEditsToParent">
              {{ closeLabelTxt }}
            </v-btn>
          </v-toolbar-items>
        </v-toolbar>
        <div :id=editorIdX >
          {{ v }}
        </div>
        <div id="test"></div>
      </v-card>
    </v-dialog>
  </div>
</template>
<script>
import * as ace from '@/assets/js/ace/src-min-noconflict/ace.js';
import * as v from '@/assets/js/ace/vkbeautify.js';
export default {
  name: "AceEditor",
  props:['title', 'editable', 'value', 'originalItem', 'editorIdX'],

  data(){
    return {
      v : "",
      oldItem : this.originalItem,
      showAceEditorModal: false
    }
  },

  computed : {
    closeLabelTxt : function (){
      return this.editable?"Save":"Close Me";
    }
  },
  mounted(){
    this.v = document.getElementById("textArea_0").value
    document.getElementById("test").innerText = document.getElementById("textArea_0").value
  },
  methods:{
    openInEditor(){
      this.editorId = this.editorIdX;
      this.editor = ace.edit(  this.editorIdX  );
      if( this.editable === false ){
        this.editor.setReadOnly( true );
      }

      //  deprecation fix
      this.editor.$blockScrolling = Infinity;
      //  ignore doctype warnings
      const session = this.editor.getSession();
      session.on("changeAnnotation", () => {
        const a = session.getAnnotations();
        const b = a.slice(0).filter( (item) => item.text.indexOf('DOC') == -1 );
        if(a.length > b.length) session.setAnnotations(b);
      });

      //  editor options
      this.options = this.options || {};

      //  opinionated option defaults
      this.options.maxLines = this.options.maxLines || Infinity;
      this.options.printMargin = this.options.printMargin || false;
      this.options.highlightActiveLine = this.options.highlightActiveLine || false;

      //  hide cursor
      if(this.options.cursor === 'none' || this.options.cursor === false){
        this.editor.renderer.$cursorLayer.element.style.display = 'none';
        delete this.options.cursor;
      }

      //  add missing mode and theme paths
      if(this.options.mode && this.options.mode.indexOf('ace/mode/')===-1) {
        this.options.mode = `ace/mode/${this.options.mode}`;
      }
      if(this.options.theme && this.options.theme.indexOf('ace/theme/')===-1) {
        this.options.theme = 'ace/theme/${this.options.theme}';
      }
      this.editor.setOptions(this.options);
      this.showAceEditorModal = true;
    },

    closeAceEditor(){
      this.showAceEditorModal = false;
    },
    sendEditsToParent(){
      if( this.editor.getReadOnly() === false ){
        this.$emit('editedItem', [ {"editedItem": this.editor.getValue() }, {"originalItem":this.oldItem}]);
      }
      this.showAceEditorModal = false
    },
    reformatContent(){
      let x = this.editor.session.getValue();
      this.editor.session.setValue("");
      x = vkbeautify.xml( x );
      this.editor.session.setValue( x );
    }
  }
}
</script>

发生这种情况是因为您通过禁用编辑器的虚拟屏幕优化将 maxLines 设置为 InfinitymaxLines 适用于小于 window 高度的小片段。