Adicionar uma galeria de imagens ao RichTextEditor no Vue

O RichTextEditor no exemplo com Vite + Vue, com uma imagem da galeria do servidor Express inserida no texto

A caixa de diálogo Insert Gallery do RichTextEditor precisa de três coisas em um app Vue: os scripts do editor carregados antes do app, um componente que cria o editor quando é montado e o destrói quando é desmontado, e um servidor de desenvolvimento que encaminha as requisições da caixa de diálogo para a sua API. Esta página mostra uma configuração com Vite + Vue 3 que faz as três coisas, testada com o exemplo em Express.

Carregar os scripts do editor antes do app

O editor é um conjunto de scripts de navegador comuns que definem window.RichTextEditor. Eles não são módulos ES, então vão no index.html, antes do script de módulo que monta o Vue:

HTML · index.html
<!-- Plain browser scripts from public/, loaded before the Vue bundle. -->
<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css">
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<script src="/rte-files.js"></script>

Os arquivos ficam em public/richtexteditor/, que o Vite serve sem alterações. O rte-files.js fornece as funções de upload que o editor e o botão Upload da galeria chamam. Scripts clássicos no <head> são executados antes do bundle de módulos, então a variável global já existe quando o componente é montado.

O componente

Vue · src/components/RichTextEditor.vue
<script setup>
import { onBeforeUnmount, onMounted, useTemplateRef } from "vue";

// window.RichTextEditor comes from the scripts in index.html.
const props = defineProps({
  options: { type: Object, required: true },
  initialHtml: { type: String, default: "" },
});
const container = useTemplateRef("container");
let editor = null; // a plain variable: the editor object does not need to be reactive

onMounted(() => {
  editor = new window.RichTextEditor(container.value, props.options);
  editor.setHTMLCode(props.initialHtml);
});
onBeforeUnmount(() => {
  editor?.destroy();
  editor = null;
});

defineExpose({ getHTML: () => editor.getHTMLCode() });
</script>

<template>
  <div ref="container"></div>
</template>
  • onMounted é executado assim que a div do template está no documento. useTemplateRef("container") (Vue 3.5) retorna esse elemento.
  • onBeforeUnmount chama destroy() enquanto o elemento ainda está lá. Verificamos isso desmontando o app inteiro com app.unmount(): nenhum elemento do editor ficou no documento e não houve erros na página.
  • A instância é uma variável comum. Nada no template depende dela, então ela não precisa ser reativa, e o Vue não tem motivo para envolvê-la em um proxy.
  • defineExpose dá ao componente pai um método getHTML(). O botão Show HTML do exemplo o chama por meio de uma template ref e imprimiu <p>Hello from Vue.</p>.

O componente pai passa as opções uma única vez. O componente as lê quando o editor é criado e não as observa:

Vue · src/App.vue
const options = {
  toolbar: "full",
  galleryEndpoint: "/rte/gallery", // forwarded to Express by vite.config.js
  maxUploadFileSize: 4000000,
};

Acessar o servidor de API no desenvolvimento

O endpoint da galeria é relativo, /rte/gallery, e a página é servida pelo Vite na porta 5112. O proxy de desenvolvimento do Vite encaminha as rotas de galeria e upload e as imagens para o exemplo em Express na porta 5101:

JavaScript · vite.config.js
// /rte/ and /uploads/ go to the Express sample (node-express, port 5101).
// Keep the trailing slashes: "/rte" alone would also catch /rte-files.js.
proxy: {
  "/rte/": "http://localhost:5101",
  "/uploads/": "http://localhost:5101",
},

O navegador vê uma única origem, então não há CORS para configurar. Mantenha as barras finais. O Vite compara uma chave simples com o início da URL, então "/rte" também encaminharia /rte-files.js para o Express. Vimos isso acontecer com o mesmo bloco de proxy no exemplo em React. Com "/rte/", o Vite serve a própria cópia.

Use o proxy apenas no desenvolvimento. Em produção, sirva o dist/ compilado e a API a partir da mesma origem.

O que a caixa de diálogo mostra

A imagem no topo é o editor neste app Vue depois que uma imagem foi inserida pela galeria. As pastas e imagens vêm do servidor Express, descrito no guia da galeria em Node.js. Duas limitações vêm da galeria na versão lançada, seja qual for o framework. A dica de formatos lista JFIF, BMP e AVIF, que o servidor de exemplo recusa. E um upload recusado mostra "Upload failed for <name>." em vez da mensagem do servidor.

Teste

Baixe o projeto de exemplo deste guia e o exemplo em Express. Inicie primeiro o Express (npm install, npm start, com a pasta richtexteditor do editor ao lado de server.mjs). Depois, no projeto Vue:

Shell
npm install
npm run dev
  ➜  Local:   http://localhost:5112/

O npm run dev primeiro copia os arquivos do editor para public/. Defina RTE_DIR com a pasta do seu editor ou coloque a pasta em public/richtexteditor você mesmo. Abra http://localhost:5112 e clique em Insert Gallery. A caixa de diálogo lista as pastas do servidor. New folder e Upload funcionam pelo proxy, e Insert adiciona <img src="/uploads/..."> ao texto. Executamos exatamente essa sequência no Chromium com Vue 3.5.44 e Vite 8.3.5.

O mesmo guia para outras plataformas

React · Angular

Leia a seguir