Añadir una galería de imágenes a RichTextEditor en Vue

RichTextEditor en el ejemplo de Vite + Vue con una imagen de la galería del servidor Express insertada en el texto

El cuadro de diálogo Insert Gallery de RichTextEditor necesita tres cosas en una aplicación Vue: los scripts del editor cargados antes que la aplicación, un componente que cree el editor al montarse y lo destruya al desmontarse, y un servidor de desarrollo que reenvíe las solicitudes del cuadro de diálogo a tu API. Esta página muestra una configuración de Vite + Vue 3 que resuelve las tres, probada con el ejemplo de Express.

Carga los scripts del editor antes que la aplicación

El editor es un conjunto de scripts simples para el navegador que definen window.RichTextEditor. No son módulos ES, así que van en index.html, antes del script de módulo que monta 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>

Los archivos están en public/richtexteditor/, que Vite sirve tal cual. rte-files.js proporciona las funciones de carga a las que llaman el editor y el botón Upload de la galería. Los scripts clásicos de <head> se ejecutan antes que el bundle de módulos, así que la variable global ya existe cuando se monta el componente.

El 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 se ejecuta cuando el div de la plantilla ya está en el documento. useTemplateRef("container") (Vue 3.5) devuelve ese elemento.
  • onBeforeUnmount llama a destroy() mientras el elemento sigue ahí. Lo comprobamos desmontando toda la aplicación con app.unmount(): no quedó ningún elemento del editor en el documento y no hubo errores en la página.
  • La instancia es una variable normal. Nada de la plantilla depende de ella, así que no necesita ser reactiva, y Vue no tiene motivo para envolverla en un proxy.
  • defineExpose da al componente padre un método getHTML(). El botón Show HTML del ejemplo lo llama a través de una ref de plantilla y mostró <p>Hello from Vue.</p>.

El componente padre pasa las opciones una sola vez. El componente las lee al crear el editor y no las observa:

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

Acceder al servidor de API durante el desarrollo

El endpoint de la galería es relativo, /rte/gallery, y la página la sirve Vite en el puerto 5112. El proxy de desarrollo de Vite reenvía las rutas de la galería y de carga, y las imágenes, al ejemplo de Express en el puerto 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",
},

El navegador ve un solo origen, así que no hay que configurar CORS. Mantén las barras finales. Vite compara una clave simple con el principio de la URL, así que "/rte" también reenviaría /rte-files.js a Express. Vimos que eso ocurría con el mismo bloque de proxy en el ejemplo de React. Con "/rte/", Vite sirve su propia copia.

Usa el proxy solo para desarrollo. En producción, sirve el dist/ compilado y la API desde el mismo origen.

Qué muestra el cuadro de diálogo

La imagen del principio es el editor en esta aplicación Vue después de insertar una imagen desde la galería. Las carpetas y las imágenes vienen del servidor Express, descrito en la guía de la galería para Node.js. Hay dos limitaciones que vienen de la galería publicada, sea cual sea el framework. Su indicación de formatos enumera JFIF, BMP y AVIF, que el servidor de ejemplo rechaza. Y una carga rechazada muestra "Upload failed for <name>." en lugar del mensaje del servidor.

Pruébalo

Descarga el proyecto de ejemplo de esta guía y el ejemplo de Express. Inicia primero Express (npm install, npm start, con la carpeta richtexteditor del editor junto a server.mjs). Después, en el proyecto de Vue:

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

npm run dev copia primero los archivos del editor en public/. Define RTE_DIR con la carpeta de tu editor, o coloca tú mismo la carpeta en public/richtexteditor. Abre http://localhost:5112 y haz clic en Insert Gallery. El cuadro de diálogo enumera las carpetas del servidor. New folder y Upload funcionan a través del proxy, e Insert añade <img src="/uploads/..."> al texto. Ejecutamos exactamente esa secuencia en Chromium con Vue 3.5.44 y Vite 8.3.5.

La misma guía para otras plataformas

React · Angular

Para seguir leyendo