tinymce / tinymce-vue

Official TinyMCE Vue component
MIT License
2.08k stars 208 forks source link

The page is closed, but the memory still exists #432

Open runkobe24 opened 3 months ago

runkobe24 commented 3 months ago

What is the current behavior?

In the vue3.x project, tinymce-vue is used. After the page is closed, the memory of tinymce-vue always exists and is not destroyed.

Please provide the steps to reproduce and if possible a minimal demo of the problem via codesandbox.io or similar.

<template>
  <div class="custom-editor">
    <Editor
      v-model="valueHtml"
      api-key="no-api-key"
      tinymce-script-src="/tinymce/tinymce.min.js"
      :init="init"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import Editor from '@tinymce/tinymce-vue'
import { compressImage } from '@/lib/image'
import store from '@/store'

type EditorType = typeof Editor

interface BlobInfoType {
  blob: ()=> string
  base64: ()=> string
  blobUri: ()=> string
  filename: ()=> string
  id: ()=> string
  name: ()=> string
  uri: ()=> string
}

const props = defineProps({
  value: {
    type: String,
    default: '',
  },

  isCustomUpload: {
    type: Boolean,
    default: false
  }
})

const valueHtml = ref(props.value)

const emits = defineEmits(['update:modelValue', 'custom-upload'])

const init = {
  promotion: false,
  license_key: 'gpl',
  language: "zh_CN",
  placeholder: "在这里输入文字",
  height: 500,
  resize: "false",
  // 品牌标识隐藏
  branding: false,
  statusbar: false,
  contextmenu: false,
  // 引用自定义样式
  content_css: '/tinymce/skins/content/default/content.css',
  // 插件
  plugins: "searchreplace autolink directionality visualblocks visualchars image link media table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount emoticons",
  // 工具栏
  toolbar: [
    "H1 H2 H3 fontsize | forecolor backcolor bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | bullist numlist | imageupload table | undo redo customInsertButton",
  ],
  font_size_formats: "12px 默认字号=14px 16px 18px 20px 22px 24px 36px",
  // 顶部的bar隐藏
  menubar: false,
  paste_data_images: true,
  image_dimensions: false,
  content_style: "img {max-width: 100%}",
  nonbreaking_force_tab: true,
  setup: (editor: EditorType) => {
    // console.log('editor', editor);
    // 特殊处理图片上传,新增一个上传按钮,屏蔽掉原来的上传方式
    editor.ui.registry.addButton('imageupload', {
      icon: 'image',
      onAction: () => {
        if (props.isCustomUpload) {
          emits('custom-upload')
          return
        }
        const imageInput = document.createElement('input');
        imageInput.setAttribute('type', 'file');
        imageInput.setAttribute('multiple', 'multiple');
        imageInput.setAttribute('accept', 'image/gif, image/jpg, image/jpeg, image/png');
        document.body.appendChild(imageInput);
        const evt = new MouseEvent('click', {
          bubbles: false,
          cancelable: true,
          view: window,
        });
        imageInput.dispatchEvent(evt);
        imageInput.addEventListener('change', async (e) => {
          const target = evt.target as HTMLInputElement;
          const files = target.files || [];
          for (let i = 0; i < files?.length; i++) {
            console.log(files[i]);
            let file = files[i];
            // console.log(file.size, '大小');
            file = await compressImage(file, { maxWidth: 750 }) as File;
            const d = await store.dispatch('uploadPic', { file: file, type: 20 });
            if (d.success) {
              editor.execCommand("mceInsertContent", false, `<img src='${d.data.url}' style="max-width: 100%;"/>`);
            }
          }
        });
        imageInput.remove();
      },
    });
  },

  images_upload_handler: async (blobInfo: BlobInfoType) => {
    // eslint-disable-next-line no-async-promise-executor
    return new Promise(async (resolve, reject) => {
      if (props.isCustomUpload) {
        emits('custom-upload')
        resolve('')
        return
      }
      const blob = blobInfo.blob()
      const newBlob = new Blob([blob])
      const newFile = await compressImage(newBlob, { maxWidth: 750 })
      const d = await store.dispatch('uploadPic', { file: newFile, type: 20 })
      if (d.success) {
        resolve(d.data.url)
      } else {
        console.error(d.msg)
        resolve('')
      }
    })
  },
}

watch(() => props.value, (val) => {
  valueHtml.value = val
})

const insertHtml = (value: string) => {
  valueHtml.value = value
}
defineExpose({ insertHtml, valueHtml })
</script>

<style lang="stylus">
.custom-editor {
  .tox-sidebar-wrap {
    background: #f0f2f5
  }

  .tox .tox-edit-area__iframe {
    background-color: #fff
    border: 0
    box-sizing: border-box
    flex: 1
    height: 100%
    position: absolute
    width: 375px
    margin: 0 auto
    left: 0
    right: 0
    top: 0
    bottom: 0
    margin: auto
  }
}

</style>

What is the expected behavior? image

In edge, Collect garbage can clear the closed editor memory.

Which versions of TinyMCE, and which browser / OS are affected by this issue? Did this work in previous versions of TinyMCE or tinymce-vue? tinymce-vue: 5.0 vue: 3.4 / vue2.x I have tried several versions of vue and they all have this problem.