Direkte Integration
Importiere Grapesjs, rufe grapesjs.init() in onMounted() auf, ruf editor.destroy() in onUnmounted() auf. Keine zusätzlichen Abhängigkeiten, nichts zwischen dir und der Editor-API.
PageKit — der selbst gehostete GrapesJS-Website-Builder, als Quellcode. Early Access sichern
Integrieren Sie den Open-Source-Visual Editor GrapesJS in Vue 3- und Nuxt-Anwendungen, um visuelle Seiteneditoren, CMS-Editoren, Landingpage-Builder, E-Mail-Editoren und White-Label-Bearbeitungserlebnisse zu erstellen.
Die Aufteilung ist es wert, vor jeglichem Code benannt zu werden. Deine Vue-Anwendung besitzt Routing, Benutzer, Berechtigungen und Daten. GrapesJS besitzt den Canvas und alles, was eine Person darin bearbeitet. Die beiden treffen sich an einem DOM-Knoten und einem Speicheraufruf.
Nichts in dieser Kette erfordert Vue, um zu wissen, wie der Editor funktioniert, und nichts erfordert, dass GrapesJS weiß, dass er in einer Vue-App ist. Deshalb ist die Integration kurz.
Ja. GrapesJS ist framework-agnostisch und kann direkt in Vue-3-Anwendungen integriert werden. Man kann den Editor mit Vue-Lebenszyklus-Hooks initialisieren, die Editor-Instanz in einem Composable oder einer Komponente speichern, GrapesJS-Ereignisse mit der Vue-Anwendung verbinden und Projektdaten über das eigene Backend speichern.
Importiere Grapesjs, rufe grapesjs.init() in onMounted() auf, ruf editor.destroy() in onUnmounted() auf. Keine zusätzlichen Abhängigkeiten, nichts zwischen dir und der Editor-API.
Packen Sie diesen Lebenszyklus in Ihrem eigenen Composable – oder einem Community-Paket – ein, damit Komponenten eine Vue-förmige API statt einer imperativen erhalten. Nützlich, wenn mehr als ein Bildschirm einen Editor montiert.
GrapesJS veröffentlicht einen offiziellen Framework-Wrapper, @grapesjs/react, für React. Es gibt kein äquivalentes Vue-Paket aus dem GrapesJS-Projekt, daher ist jeder Vue-Wrapper, den du auf npm findest, ein Community-Projekt. Überprüfe die Vue- und GrapesJS-Peer-Bereiche, bevor du ihn einsetzt.
Vue 3 ist ein Anwendungsframework. GrapesJS ist eine visuelle Bearbeitungs-Engine. Sie lösen verschiedene Probleme, und die Integration ist sauber, gerade weil sie sich nicht überlappen.
Fünf davon gehören dir. Der Rest existiert bereits, und die beiden übrigen sind Konfiguration und nicht Konstruktion.
Eine Abhängigkeit. GrapesJS liefert seine eigenen TypeScript-Definitionen aus, daher gibt es kein separates @types-Paket hinzuzufügen.
npm install grapesjsDer Editor ist ohne ihn unstilisiert. In einem Vite- oder Nuxt-Projekt kann man ihn aus der Komponente oder dem Composable importieren, der den Editor erstellt; in einer App mit globalem Stylesheet importiert man ihn stattdessen einmal dorthin.
Kopiere das in eine Komponente und es läuft. Alles nach diesem Abschnitt ist eine Ergänzung, keine Neuschreibung.
<template>
<div ref="editorContainer" class="editor-shell"></div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, shallowRef, ref } from 'vue';
import grapesjs, { type Editor } from 'grapesjs';
import 'grapesjs/dist/css/grapes.min.css';
// A template ref: the name matches the ref="" attribute above.
const editorContainer = ref<HTMLDivElement | null>(null);
// shallowRef, not ref — see "Performance" further down the page.
const editor = shallowRef<Editor | null>(null);
onMounted(() => {
if (!editorContainer.value) return;
editor.value = grapesjs.init({
container: editorContainer.value,
height: '100%',
fromElement: false,
storageManager: false,
});
});
onUnmounted(() => {
editor.value?.destroy();
editor.value = null;
});
</script>
<style scoped>
.editor-shell {
height: 100vh;
}
</style>Der Template-Referenzname entspricht dem Attribut ref="" – so bindet Vue sie in <script setup>.
Der Editor lebt in shallowRef, nicht in ref. Vue würde sonst den gesamten Editor-Objektgraphen laufen lassen, was ihn reaktiv macht.
fromElement ist falsch, daher beginnt GrapesJS mit den Komponenten, die du weitergibst, und nicht mit dem Markup, das im Container war.
Die gesamte Integration ist eine Lebenszyklus-Kartierung. Vue zeigt an, wann der DOM-Knoten existiert und wann er kurz davor ist zu verschwinden; GrapesJS benötigt genau diese beiden Momente.
Lies es von oben bis unten, das ist der gesamte Vertrag zwischen den beiden Bibliotheken.
Weder onMounted noch onUnmounted laufen auf dem Server, weshalb ein einfaches Vue-SPA nichts Zusätzliches benötigt. Eine servergerenderte App benötigt einen zusätzlichen Schutz – der Abschnitt Nuxt behandelt das.
Sobald ein zweiter Bildschirm einen Editor braucht, verschiebe den Lebenszyklus in ein Composable. Die Komponente sagt dann, was sie will, nicht wie der Editor verdrahtet ist.
// composables/useGrapesJS.ts
import { onMounted, onUnmounted, shallowRef, type Ref } from 'vue';
import grapesjs, { type Editor, type EditorConfig } from 'grapesjs';
import 'grapesjs/dist/css/grapes.min.css';
type EditorOptions = Omit<EditorConfig, 'container'>;
export function useGrapesJS(
container: Ref<HTMLElement | null>,
options: EditorOptions = {},
) {
const editor = shallowRef<Editor | null>(null);
const init = (): Editor | undefined => {
if (editor.value || !container.value) return;
editor.value = grapesjs.init({
container: container.value,
height: '100%',
fromElement: false,
storageManager: false,
...options,
});
return editor.value;
};
const destroy = (): void => {
editor.value?.destroy();
editor.value = null;
};
onMounted(init);
onUnmounted(destroy);
return { editor, init, destroy };
}<template>
<div ref="editorContainer" class="editor-shell"></div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useGrapesJS } from '~/composables/useGrapesJS';
const editorContainer = ref<HTMLElement | null>(null);
const { editor } = useGrapesJS(editorContainer, {
blockManager: {
blocks: [
{
id: 'hero',
label: 'Hero',
category: 'Sections',
content: '<section>…</section>',
},
],
},
});
</script>Widerstehe es, das weiter auszubauen, als nötig. Ein Composable, das jedes GrapesJS-Modul in eine reaktive Fassade umhüllt, ist eine zweite API zum Lernen und eine zweite Sache, die man synchron halten muss.
GrapesJS verwaltet den Editor-Zustand. Vue verwaltet den Anwendungszustand. Ereignisse sind die Naht: Abonniere die wenigen, die deine Benutzeroberfläche tatsächlich widerspiegelt, und schreibe sie in gewöhnliche Referenzen.
import { onMounted, ref, shallowRef } from 'vue';
import type { Editor, Component } from 'grapesjs';
const isReady = ref(false);
const isDirty = ref(false);
const saveState = ref<'idle' | 'saving' | 'saved' | 'error'>('idle');
const selectedName = ref<string | null>(null);
function bindEditorEvents(editor: Editor): void {
editor.on('load', () => {
isReady.value = true;
});
editor.on('component:selected', (component: Component) => {
selectedName.value = component?.getName?.() ?? null;
});
editor.on('component:update', () => {
isDirty.value = true;
});
editor.on('storage:start:store', () => {
saveState.value = 'saving';
});
editor.on('storage:end:store', () => {
saveState.value = 'saved';
isDirty.value = false;
});
editor.on('storage:error', () => {
saveState.value = 'error';
});
}| Event | Was deine Benutzeroberfläche damit macht |
|---|---|
| load | Blende den Ladezustand aus, sobald das Projekt im Canvas steht |
| component:selected | Zeigen Sie, welches Element in Ihrer eigenen Werkzeugleiste ausgewählt ist |
| component:update | Markiere das Dokument als verschmutzt, aktiviere die Speichern-Schaltfläche |
| storage:start:store | Zeigen Sie eine Speicheranzeige |
| storage:end:store | Zeigt gerettet, räumt die schmutzige Flagge weg |
| storage:error | Decke einen Fehler auf, auf den der Nutzer reagieren kann |
Spiegele das, was du anzeigst, nicht das, was der Editor enthält. Das Kopieren des Komponentenbaums in einen Vue-Store liefert dir zwei Quellen der Wahrheit und einen Synchronisationsfehler.
GrapesJS basiert auf Browser-DOM-APIs, daher sollte die Editor-Initialisierung nur im Browserkontext erfolgen. In Nuxt bedeutet das, den Editor aus dem Server-Rendering fernzuhalten – alles andere ist die gleiche Integration, die du bereits hast.
<!-- pages/editor.vue -->
<template>
<ClientOnly>
<VisualEditor />
<template #fallback>
<p class="editor-placeholder">Loading the editor…</p>
</template>
</ClientOnly>
</template><!-- components/VisualEditor.client.vue -->
<template>
<div ref="editorContainer" class="editor-shell"></div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useGrapesJS } from '~/composables/useGrapesJS';
// Reached only in the browser: the .client suffix keeps this
// component out of the server render, and onMounted never runs
// on the server anyway.
const editorContainer = ref<HTMLElement | null>(null);
const { editor } = useGrapesJS(editorContainer);
</script>Wickle den Editor dort, wo er verwendet wird. Der Rückfall-Slot gibt dem Server etwas zum Rendern, sodass kein leerer Frame vor der Hydration vorhanden ist.
Die eigene Benennungskonvention von Nuxt: Eine Komponente, deren Dateiname auf .client endet, wird nur im Browser dargestellt. Nützlich, wenn der Editor an mehreren Stellen verwendet wird.
Beides ist gültig und kombiniert sich. Keines von beiden ist von GrapesJS vorgeschrieben – was GrapesJS verlangt, ist einfach, dass init() dort läuft, wo das Dokument existiert.
SSR rendert die Anwendungsshell. GrapesJS läuft im Browser. Die Benennung dieser Grenze macht eine Nuxt-Integration vorhersehbar, statt eine Quelle für Hydrationsfehler.
Der Editor-Bildschirm ist keine Seite, die man für SEO serverrendert – es ist ein authentifiziertes Werkzeug. Die erstellten Seiten sind das, was man rendert und indexiert, und das sind reines HTML und CSS.
Baue einen kompletten Vue-Seitenbauer →Nein. Direkte Integration ist eine vollständige, unterstützte Methode zur Nutzung von GrapesJS, und genau das tun die Beispiele auf dieser Seite. Ein Wrapper ist eine Bequemlichkeit, keine Voraussetzung.
| Herangehensweise | Am besten für | Kompromiss |
|---|---|---|
| Direkter GrapesJS | Maximale Kontrolle | Man schreibt den Lebenszyklus selbst – etwa fünfzehn Zeilen. |
| Vue-Wrapper / Integration | Eine stärker auf Vue ausgerichtete API | Eine Abhängigkeit zwischen dir und der Editor-API vom Veröffentlichungsplan einer anderen Person. |
| Eigenes Composable | Integration wiederverwendbarer Anwendungen | Deine Pflege – aber es ist die oben gezeigte Datei, und sie wird nicht veraltet. |
Es lohnt sich, vor der Einführung etwas zu überprüfen, denn die Suchergebnisse für diese Frage sind älter als die beschriebenen Pakete.
Habe es mit dem npm-Register bei 2026-09-02 abgeglichen. Wenn du ein neueres Vue-Paket findest, lies zuerst dessen peerDependencies: Dieses einzelne Feld zeigt an, ob es Vue 3 und ein aktuelles GrapesJS anvisiert.
Für Vue 3 ist heute das Composable oben der Wrapper. Es umfasst gut zwanzig Zeilen, nutzt die GrapesJS-API direkt und kann keinem Release hinterherhinken, von dem du abhängst.
Das Mounten des Editors ist der erste Nachmittag. Ein Seitenbauer, dem deine Nutzer vertrauen, ist der Rest der Arbeit, und es ist hauptsächlich die Konfiguration der Module, die GrapesJS bereits offenlegt.
Canvas
Die editierbare Oberfläche und ihre Gerätebreiten.
Components
Die Typen, die deine Nutzer einordnen können, und deren Eigenschaften.
Blocks
Die Palette, von der sie ziehen.
Style Manager
Welche CSS-Eigenschaften sind offen und wem?
Asset Manager
Bilder und Medien aus deinem Lager.
Storage Manager
Wo Projektdaten geladen und gespeichert werden.
Commands
Benannte Aktionen, die an deine eigene Benutzeroberfläche gebunden werden können.
Device Manager
Reaktionsschnelle Breakpoints für Bearbeitung und Ausgabe.
Plugins
Alles oben ist verpackt und wiederverwendbar.
Derselbe Editor-Core, anders konfiguriert. Jeder dieser Blöcke ist ein anderer Satz von Blöcken, ein anderes Speicherziel und ein anderer Veröffentlichungsschritt.
Erstellen Sie eine visuelle Seitenbearbeitung in Ihrer Vue-Anwendung.
WeiterlesenGib den Content-Teams eine visuelle Bearbeitungsoberfläche über die Seiten, die deine Nuxt-Seite bereits rendert.
WeiterlesenErstellen Sie wiederverwendbare Marketing-Layouts, die Ihr Wachstumsteam ohne Deployment zusammenstellen kann.
WeiterlesenErstelle E-Mail-Vorlagen visuell, wobei die Ausgabe für Mail-Clients statt für Browser geformt ist.
WeiterlesenBinde einen gebrandeten Editor in dein Produkt ein, unter deiner eigenen Benutzeroberfläche und deinen eigenen Berechtigungen.
WeiterlesenGib den Nutzern visuelle Kontrolle über HTML und CSS und gib genau das zurück.
WeiterlesenDrei Dinge, die leicht zu vermischen und teuer zu vermischen sind. Das am ersten Tag richtig zu machen, macht ein Dokument auch ein Jahr später wieder bearbeitbar.
Der editierbare Zustand des GrapesJS-Projekts – Seiten, Komponenten, Stile, Assets. Lies es mit editor.getProjectData(), stelle es mit editor.loadProjectData() wieder her. Das ist das, was man erhalten muss.
Das gerenderte Markup, von editor.getHtml(). Generiere es jedes Mal, wenn du veröffentlichst. Es ist Ausgabe, kein Quellcode.
Die Stile, die der Editor erstellt hat, stammen aus editor.getCss(). Gleiche Regel: generiert, nicht als Wahrheitsquelle gespeichert.
Speichere editierbare Projektdaten. Generiere HTML und CSS, wenn du rendern oder veröffentlichen musst.
Nur das gerenderte HTML zu speichern, ist ein Fehler, der später nicht mehr rückgängig gemacht werden kann. Man kann HTML immer aus Projektdaten regenerieren; man kann Projektdaten aus HTML nicht zuverlässig wiederherstellen.
// Editable state — store this, it is what the editor reloads.
const projectData = editor.getProjectData();
// Rendered output — regenerate this whenever you publish.
const html = editor.getHtml();
const css = editor.getCss();
await fetch(`/api/projects/${projectId}/publish`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ projectData, html, css }),
});
// Sanitize `html` on the server before it is served to anyone.GrapesJS ist egal, was dein Backend ist. Ein Speicheradapter besteht aus zwei asynchronen Funktionen – eine, die Projektdaten zurückgibt, eine, die sie akzeptiert – sodass der Editor mit der bereits vorhandenen API kommuniziert.
import grapesjs, { type ProjectData } from 'grapesjs';
// Wherever your app keeps it — a route param, a prop, a store.
const projectId = props.projectId;
const editor = grapesjs.init({
container: editorContainer.value!,
fromElement: false,
storageManager: {
type: 'app-api',
autosave: true,
stepsBeforeSave: 10,
},
// Registered as a plugin so the adapter exists before
// GrapesJS performs its first load.
plugins: [
(ed) => {
ed.Storage.add('app-api', {
async load(): Promise<ProjectData> {
const res = await fetch(`/api/projects/${projectId}`);
if (!res.ok) {
throw new Error(`Load failed: ${res.status}`);
}
return await res.json();
},
async store(data: ProjectData) {
const res = await fetch(`/api/projects/${projectId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ projectData: data }),
});
if (!res.ok) {
throw new Error(`Save failed: ${res.status}`);
}
},
});
},
],
});Die Registrierung des Adapters über das Plugin-Array ist wichtig: Es garantiert, dass der Speicher existiert, bevor GrapesJS erstmals geladen wird.
Der Asset Manager ist eine Benutzeroberfläche über einem Endpunkt. Zeigt man ihn auf seinen eigenen und jede bestehende Regel – Authentifizierung, Validierung, Quoten, CDN – gilt weiterhin.
// Fetched from your own API before the editor is created.
const assets = await fetch('/api/assets').then((res) => res.json());
const editor = grapesjs.init({
container: editorContainer.value!,
assetManager: {
assets,
// Uploads go to your endpoint, with your auth and
// your validation.
upload: '/api/assets',
uploadName: 'files',
multiUpload: true,
autoAdd: true,
headers: { Authorization: `Bearer ${token.value}` },
credentials: 'same-origin',
// Reject what the backend would reject anyway,
// before the round trip.
beforeUpload: (files: File[]) => {
const limit = 5 * 1024 * 1024;
const tooBig = files.some((f) => f.size > limit);
if (tooBig) {
toast.error('Images must be under 5 MB');
return false;
}
},
},
});Ein benutzerdefinierter Komponententyp ist der Weg, wie der Editor dein Produkt lernt. Er bekommt ein eigenes Einstellungspanel, eigene Drop-Regeln und einen eigenen Block in der Palette.
import type { Editor } from 'grapesjs';
export function pricingCardPlugin(editor: Editor): void {
editor.Components.addType('pricing-card', {
isComponent: (el) => el.dataset?.gjsType === 'pricing-card',
model: {
defaults: {
tagName: 'section',
attributes: { 'data-gjs-type': 'pricing-card' },
// Traits become the settings panel for this component.
traits: [
{ type: 'text', name: 'plan', label: 'Plan name' },
{ type: 'text', name: 'price', label: 'Price' },
{ type: 'checkbox', name: 'featured', label: 'Featured' },
],
components: `
<h3 data-gjs-type="text">Starter</h3>
<p data-gjs-type="text">$0 / month</p>
<a href="#">Choose plan</a>
`,
},
},
});
editor.Blocks.add('pricing-card', {
label: 'Pricing card',
category: 'Marketing',
content: { type: 'pricing-card' },
});
}
// Pass it to the editor like any other plugin:
// grapesjs.init({ container, plugins: [pricingCardPlugin] })Benutzerdefinierte Komponenten sind auch, wie du den Editor einschränkst. Das Setzen von Dropable oder Draggable auf einem Typ verhindert, dass ein Nutzer eine Preiskarte in einer Navigationsleiste ablegt, und Traits geben ihm ein beschriftetes Feld statt einer rohen CSS-Kontrolle.
Das sind GrapesJS-Komponententypen, keine Vue-Komponenten. Der Canvas rendert DOM; deine Vue-Komponenten rendern deine Anwendung darum herum.
Ein Plugin ist eine Funktion, die den Editor empfängt – dieselbe Funktionssignatur, egal ob sie von npm, GJS.Market oder deinem eigenen Repository stammt. Keiner von ihnen interessiert sich dafür, dass die Host-Anwendung Vue ist.
import grapesjs from 'grapesjs';
import basicBlocks from 'grapesjs-blocks-basic';
import { pricingCardPlugin } from '~/editor/pricing-card';
const editor = grapesjs.init({
container: editorContainer.value!,
plugins: [
// Options go through a wrapper function: `pluginsOpts`
// is keyed by string, so it only reaches plugins that
// were passed to `plugins` by name.
(ed) => basicBlocks(ed, { flexGrid: true }),
pricingCardPlugin,
],
});Die Palette, die Textbearbeitung und die Formular-Controls, die den Canvas ab dem ersten Tag nützlich machen.
Durchsuchen-KategorieEine Startpalette, damit der Canvas beim ersten Öffnen nicht leer ist.
Tailwind-förmige Abschnitte für Teams, deren Ausgaben bereits Tailwind verwenden.
Eine reichhaltigere Inline-Textleiste als die eingebaute.
Formfelder als editierbare Komponenten mit dem Merkmalsfeld für ihre Attribute.
Kein Argument, dass Bauen falsch ist – sondern eine Bestandsaufnahme dessen, was Bauen bedeutet. Jede Reihe ist ein Subsystem, das existiert, egal ob man es geplant hat oder nicht.
| Leistungsfähigkeit | Bau dich selbst auf | GrapesJS |
|---|---|---|
| Canvas | Bau | Enthalten |
| Drag & drop | Bau | Enthalten |
| Components | Bau | Enthalten |
| Blocks | Bau | Enthalten |
| Style Manager | Bau | Enthalten |
| Asset Manager | Bau | Erweiterbar |
| Storage Manager | Bau | Erweiterbar |
| Plugins | Baue ein Ökosystem auf | Plugin-Architektur |
| Vue integration | Nativ | Framework-agnostisch |
Vue gibt dir das Anwendungs-Framework. GrapesJS gibt dir die visuelle Bearbeitungs-Engine.
Denn Vue-Komponenten sind Anwendungsbausteine, und ein visueller Editor braucht noch etwas anderes: Canvas-Management, einen Komponentenbaum mit Drop-Regeln, Auswahl- und Hover-Zustand, eine Style-Schicht, die echtes CSS schreibt, Geräteswitching, Rückgängig machen und neu machen, Asset-Management, Serialisierung, benannte Befehle und einen Erweiterungspunkt für das Ganze. Das kannst du in Vue bauen – es ist einfach ein anderes Projekt als das, das du dir vorgenommen hast.
Vue + GrapesJS sind ein anderes Konzept als allein Vue-Komponenten.
Deine Nuxt- oder Vue-Anwendung: die Shell, die Sitzung und wer welches Projekt öffnen darf.
Der Editor, montiert auf einem Bildschirm. Er erhält ein Projekt und gibt ein Projekt aus.
Deine eigenen Routen. GrapesJS ruft sie über die von dir geschriebenen Speicher- und Asset-Adapter auf.
Wo Projekte, Medien, Nutzer und Versionen tatsächlich existieren – unverändert vom darüberliegenden Editor.
Der Editor ist ein Blatt in diesem Baum, nicht die Wurzel. Alles darüber ist Code, den du sowieso geschrieben hättest.
Mach den Editor nicht tief reaktiv
Das ist der, der tatsächlich beißt. shallowRef oder eine einfache modulbezogene Variable – niemals ref() um die Editor-Instanz herum. Vue würde einen sehr großen Objektgraphen laufen lassen und Effekte auf internen Churn erneut ausführen.
Initialisieren nur bei Bedarf
Erstelle den Editor auf dem Bildschirm, der bearbeitet, nicht in einem Layout oder einem Store, den jede Route mountet.
Lazy-Load den Editor
GrapesJS ist ein umfangreiches Bundle. Lade es in einem eigenen asynchronen Abschnitt, damit Routen, die nie bearbeiten, nie dafür bezahlen.
Halte Updates aus dem globalen Zustand heraus
Das Pushen jedes component:update in einen Store verwandelt jeden Tastendruck in ein anwendungsweites Rendering.
Paginate große Asset-Sammlungen
Seeden Sie den Asset-Manager von einem paged-Endpunkt aus, anstatt Tausende von URLs in den Editor zu schicken.
Halte die Komponentenbäume handhabbar
Sehr tiefgründige Dokumente kosten mehr zum Rendern, Serialisieren und Differenzieren. Struktur fördert sich selbst, wenn deine Blöcke das tun.
Ein visueller Editor ist ein System, das vom Nutzer erstelltes HTML akzeptiert. Behandeln Sie es so.
Fast jedes Integrationsproblem, das für diese Kombination gemeldet wurde, gehört zu diesen sieben.
Symptom
Der Container ist null oder der Editor rendert ins Nichts.
Fix
Erstelle den Editor innerhalb von onMounted(). Die Vorlagenreferenz hat vorher kein Element.
Symptom
Das Gedächtnis wächst und abgestandene Zuhörer feuern nach dem Verlassen der Route.
Fix
Ruf editor.destroy() in onUnmounted() an und lösche die Referenz.
Symptom
Der Editor wirkt träge; Vue-Devtools stocken.
Fix
Halte die Instanz in shallowRef oder komplett außerhalb der Vue-Reaktivität.
Symptom
Das Dokument ist nicht definiert oder es gibt eine Flüssigkeitsminderung auf dem Editor-Weg.
Fix
Rendere den Editor nur als Client – <ClientOnly>, eine .client-Komponente oder beides.
Symptom
Zwei Quellen der Wahrheit und Bearbeitungen, die bei der Navigation verschwinden.
Fix
Lass GrapesJS das Projekt übernehmen; spiegele nur die wenigen Werte, die deine Benutzeroberfläche anzeigt.
Symptom
Der Editor lädt, aber die Panels sind ungestaltet und unbrauchbar.
Fix
Importiere Grapesjs/dist/css/grapes.min.css oder nimm sie in dein globales Stylesheet auf.
Symptom
Typfehler oder eine Integration, die auf Vue 2 Lebenszyklus-Hooks basiert.
Fix
Überprüfen Sie peerDependencies, bevor Sie einen Wrapper verwenden. Mehrere populäre Beispiele stammen aus der Zeit vor Vue 3.
Bei Vue 2 verwendet dieselbe Integration mounted() und beforeDestroy() anstelle von onMounted() und onUnmounted(); die GrapesJS-Seite bleibt unverändert. Neue Arbeiten sollten Vue 3 anvisieren – alles auf dieser Seite geht davon aus.
Die Reihenfolge entspricht ungefähr der Reihenfolge, die diese Seite eingeführt hat.
Ja. GrapesJS ist eine framework-agnostische Bibliothek, die in ein DOM-Element rendert, also funktioniert es in Vue 3 ohne Adapter. Initialisiere es in onMounted() und zerstöre es in onUnmounted().
Führe npm Install Grapesjs aus. Das Paket enthält eigene TypeScript-Definitionen. Du musst auch das Stylesheet laden, entweder indem du grapesjs/dist/css/grapes.min.css importierst oder es in deinen globalen CSS einbaust.
Erstellen Sie eine Template-Ref auf dem Container-Element und rufen Sie dann grapesjs.init({ container: containerRef.value }) innerhalb von onMounted() auf. Das Element existiert vor dem Mount nicht, daher gibt eine frühere Initialisierung GrapesJS nichts zum Rendern.
Ja, und es ist der empfohlene Weg. onMounted() und onUnmounted() werden direkt auf grapesjs.init() und editor.destroy() abgebildet, und die gesamte Integration passt in ein einziges Composable.
Ja. Jedes Beispiel auf dieser Seite verwendet <script setup lang="ts">. Eine mit ref() deklarierte Vorlagenreferenz bindet automatisch das passende Attribut ref="".
Ja. Die Integration ist identisch mit dem einfachen Vue 3; die einzige Neuerung ist, dass der Editor mit <ClientOnly> oder einer .Client-Komponente aus dem Server-Rendering herausgehalten wird.
Ja, mit dem Editor, der nur clientbasiert gerendert wird. GrapesJS benötigt Browser-DOM-APIs, daher kann es während eines Serverrenderings nicht laufen – aber die Seiten, die es erzeugt, sind einfaches HTML und CSS, und genau diese sind das, was man serverrendert und indexiert.
Nein. Direkte Integration ist ein vollständiger Ansatz und genau das, was die hier genannten Beispiele verwenden. GrapesJS veröffentlicht einen offiziellen React-Wrapper, aber kein Vue-Äquivalent, sodass jeder Vue-Wrapper auf npm ein Community-Projekt ist – überprüfen Sie vor der Einführung dessen Vue- und GrapesJS-Peer-Bereiche.
Ja. GrapesJS liefert TypeScript-Definitionen im Paket, sodass der Editor-Typ und das Konfigurationsobjekt ohne zusätzliche @types Abhängigkeit getypt werden.
Ja. Registriere einen Speicheradapter mit editor.Storage.add(), der load() und store() implementiert. Beides sind gewöhnliche asynchrone Funktionen, sodass sie jede bereits vorhandene API aufrufen können.
Ja. editor.Components.addType() definiert einen Komponententyp mit eigenen Eigenschaften und Drop-Regeln, und editor.Blocks.add() fügt ihn in die Palette ein. So lernt der Editor den Wortschatz deines Produkts.
Ja. Ein GrapesJS-Plugin ist eine Funktion, die die Editor-Instanz empfängt und daher vom Framework, das es hostet, nicht beeinflusst wird. Gib Plugins im Plugins-Array von grapesjs.init() weiter.
Ja – das ist der häufige Fall. Das Einbinden des Editors ist der kurze Teil; der Rest besteht darin, Blöcke, Speicher, Assets und Berechtigungen darum herum zu konfigurieren.
Integrieren Sie die visuelle Bearbeitungs-Engine in Ihre Vue- oder Nuxt-Anwendung, behalten Sie die Kontrolle über Ihre Daten und Ausgaben und erweitern Sie den Editor um die Plugins, die Ihr Produkt benötigt.
Baue deine Anwendung mit Vue 3. Füge visuelle Bearbeitung mit GrapesJS hinzu.