08 aug 2025·6 min leestijd

Aangepaste Vue-componenten in gegenereerde UI, veilig bij regeneratie

Leer hoe je aangepaste Vue-componenten toevoegt aan een gegenereerde UI zonder regeneratie te breken, met isolatiepatronen, duidelijke grenzen en eenvoudige overdrachtsregels.

Aangepaste Vue-componenten in gegenereerde UI, veilig bij regeneratie

Wat gaat er kapot als je een gegenereerde UI bewerkt

Gegenereerde UIs zijn bedoeld om opnieuw opgebouwd te worden. In platforms zoals AppMaster wordt Vue3-webapp-code vanuit visuele builders gegenereerd. Regeneratie is hoe wijzigingen consistent blijven over schermen, logica en datamodellen.

Het probleem is eenvoudig: als je gegenereerde bestanden direct bewerkt, kan de volgende regeneratie je aanpassingen overschrijven.

Daarom grijpen teams naar custom code. De ingebouwde UI-blokken dekken vaak standaardformulieren en tabellen, maar echte apps hebben meestal een paar speciale onderdelen nodig: complexe grafieken, kaart-pickers, rich text editors, handtekeningpads of drag-and-drop planners. Dat zijn goede redenen om aangepaste Vue-componenten toe te voegen, zolang je ze behandelt als add-ons en niet als bewerkingen.

Als custom code mengt met gegenereerde code, kunnen fouten vertraagd en verwarrend optreden. Je merkt het misschien pas wanneer de volgende UI-wijziging een regeneratie forceert, of wanneer een collega een scherm in de visuele editor aanpast. Veelvoorkomende problemen zijn:

  • Je aangepaste markup verdwijnt omdat de template is geregenereerd.
  • Imports of registratie breken omdat bestandsnamen of structuur veranderden.
  • Een “kleine fix” wordt een merge-conflict bij elke deploy.
  • Gegeneerde logica en custom logica drijven uiteen, waardoor randgevallen gaan falen.
  • Upgrades voelen riskant omdat je niet weet wat er vervangen wordt.

Het doel is niet om aanpassingen te vermijden. Het doel is om rebuilds voorspelbaar te maken. Als je een duidelijke grens houdt tussen gegenereerde schermen en custom widgets, blijft regeneratie routine in plaats van stressvol.

Een grensregel die regeneratie veilig houdt

Als je wilt aanpassen zonder werk te verliezen, volg dan één regel: bewerk nooit gegenereerde bestanden. Behandel ze als read-only output, zoals een gecompileerd artefact.

Zie je UI als twee zones:

  • Gegenereerde zone: pagina's, layouts en schermen geproduceerd door de generator.
  • Custom zone: jouw handgeschreven Vue-componenten in een aparte map.

De gegenereerde UI moet jouw custom componenten consumeren. Het is niet de plek waar die componenten gebouwd worden.

Om dat op lange termijn te laten werken, houd de “grens” klein en duidelijk. Een custom widget moet zich gedragen als een klein product met een contract:

  • Props in: alleen wat nodig is om te renderen.
  • Events uit: alleen wat de pagina nodig heeft om op te reageren.

Vermijd het binnendringen in globale state of het aanroepen van niet-gerelateerde APIs vanuit de widget tenzij dat expliciet onderdeel is van het contract.

Bij AppMaster-stijl gegenereerde Vue3-schermen betekent dit meestal dat je minimale bedrading in het gegenereerde scherm doet om props door te geven en events af te handelen. Die bedrading kan tussen regeneraties veranderen, maar blijft klein en makkelijk opnieuw te doen. Het echte werk blijft veilig in de custom zone.

Isolatiepatronen die goed werken met Vue3

Het doel is simpel: regeneratie moet vrij gegenereerde bestanden kunnen vervangen, terwijl jouw widget-code onaangeroerd blijft.

Een praktische aanpak is om bespoke widgets als een kleine interne module te houden: componenten, styles en helper-utilities op één plek. In gegenereerde Vue3-apps betekent dat meestal dat custom code buiten gegenereerde pagina's leeft en geïmporteerd wordt als dependency.

Een wrapper-component helpt enorm. Laat de wrapper met de gegenereerde app praten: lees de bestaande datavorm van de pagina, normaliseer die en geef schone props door naar de widget. Als de gegenereerde datavorm later verandert, update je vaak de wrapper in plaats van de widget te herschrijven.

Een paar patronen houden stand:

  • Behandel de widget als een black box: props in, events uit.
  • Gebruik een wrapper om API-responses, datums en ID's naar widget-vriendelijke formaten te mappen.
  • Houd styles gescoped zodat gegenereerde pagina's je widget niet per ongeluk overschrijven.
  • Vertrouw niet op parent DOM-structuur of pagina-specifieke class-namen.

Wat styling betreft: geef de voorkeur aan scoped CSS (of CSS Modules) en namespace gedeelde classes. Als de widget het app-thema moet volgen, geef thematokens als props (kleuren, spacing, font-size) in plaats van page styles te importeren.

Slots kunnen veilig zijn als ze klein en optioneel blijven, zoals een “empty state” boodschap. Als slots echter de kernlayout of het gedrag gaan aansturen, heb je de widget terug in de gegenereerde laag geplaatst — en daar begint de pijn bij regeneratie.

Een stabiel componentcontract ontwerpen (props en events)

De veiligste manier om regeneratie pijnloos te houden is om elke widget te behandelen als een stabiele interface. Gegenereerde schermen kunnen veranderen. Je component zou dat niet moeten doen.

Begin bij inputs (props). Houd ze weinig, voorspelbaar en makkelijk te valideren. Geef de voorkeur aan eenvoudige primitieve waarden en plain objects die jij controleert. Voeg defaults toe zodat de widget goed gedraagt ook als een pagina nog niets doorgeeft. Als iets foutief kan zijn (IDs, datumstrings, enum-achtige waarden), valideer het en faal zacht: toon een lege staat in plaats van te crashen.

Voor outputs standaardiseer je events zodat widgets consequent aanvoelen in de rest van de app. Een betrouwbare set is:

  • update:modelValue voor v-model
  • change voor door de gebruiker bevestigde wijzigingen (niet elke toetsaanslag)
  • error wanneer de component zijn taak niet kan voltooien
  • ready wanneer async werk klaar is en de widget bruikbaar is

Als er async werk bij betrokken is, maak dat dan onderdeel van het contract. Exposeer loading en disabled props, en overweeg errorMessage voor server-side fouten. Als de component zelf data ophaalt, emit nog steeds error en ready zodat de parent kan reageren (toast, logging, fallback-UI).

Verwachtingen rond toegankelijkheid

Bouw toegankelijkheid in het contract. Accepteer een label (of ariaLabel) prop, documenteer keyboard-gedrag en houd focus voorspelbaar na acties.

Bijvoorbeeld: een timeline-widget op een dashboard moet pijltjestoetsen ondersteunen om tussen items te navigeren, Enter om details te openen, en moet de focus teruggeven aan de controle die een dialoog opende wanneer de dialoog sluit. Dat maakt de widget herbruikbaar op gegenereerde schermen zonder extra werk.

Stap voor stap: voeg een bespoke widget toe zonder gegenereerde bestanden aan te raken

Houd gegenereerde code read-only
Laat het platform broncode regenereren terwijl jouw custom-map onaangeroerd blijft.
Genereer code

Begin klein: één scherm dat gebruikers belangrijk vinden, één widget die het verschil maakt. Een kleine eerste wijziging maakt het makkelijker te zien wat regeneratie wel en niet beïnvloedt.

  1. Maak de component buiten het gegenereerde gebied. Zet hem in een map die jij beheert en onder versiebeheer (vaak custom of extensions).

  2. Houd de publieke oppervlakte klein. Een paar props in, een paar events uit. Geef niet de volledige paginastatus door.

  3. Voeg een dunne wrapper toe die je ook beheert. Zijn taak is de “gegenereerde paginadata” naar het widget-contract te vertalen.

  4. Integreer via een ondersteund extensiepunt. Verwijs naar de wrapper op een manier die geen bewerken van gegenereerde bestanden vereist.

  5. Regenerate en verifieer. Je custom-map, wrapper en component moeten ongewijzigd blijven en nog steeds compileren.

Houd grenzen scherp. De widget richt zich op weergave en interactie. De wrapper mapt data en forwarded acties. Business rules blijven in de logica-laag van de app (backend of gedeelde processen), niet verborgen in de widget.

Een nuttige sanity-check: als regeneratie nu gebeurt, kan een collega de app herbouwen en hetzelfde resultaat krijgen zonder handmatige aanpassingen opnieuw te doen? Als ja, is je patroon solide.

Waar je logica plaatst zodat de UI onderhoudbaar blijft

Een custom widget zou zich vooral moeten bekommeren om hoe het eruitziet en hoe het reageert op gebruikersinput. Hoe meer business rules je in de widget stopt, hoe lastiger hij is om te hergebruiken, te testen en te wijzigen.

Een goed uitgangspunt: houd business logic in de pagina- of feature-laag en maak de widget "dumb". De pagina beslist welke data de widget krijgt en wat er gebeurt als de widget een event emit. De widget rendert en rapporteert gebruikersintentie.

Als je logica dicht bij de widget nodig hebt (formattering, kleine state, client-side validatie), verberg die dan achter een kleine service-laag. In Vue3 kan dat een module, een composable of een store met een helder API zijn. De widget importeert die API, niet willekeurige app-internals.

Een praktische splitsing:

  • Widget (component): UI-state, inputafhandeling, visuals, emit events zoals select, change, retry.
  • Service/composable: data-shaping, caching, mapping van API-fouten naar gebruikersvriendelijke berichten.
  • Page/container: business rules, permissies, welke data te laden en wanneer opslaan.
  • Gegenereerde app-onderdelen: onaangeraakt laten; geef data door en luister naar events.

Vermijd directe API-calls in de widget tenzij dat expliciet het contract is. Als het eigen fetching onderdeel is van de widget, maak dat duidelijk (noem het bijvoorbeeld CustomerSearchWidget en houd alle call-code in één service). Anders, geef items, loading en error als props door.

Foutmeldingen moeten gebruikersgericht en consistent zijn. In plaats van ruwe servertekst te tonen, map errors naar een kleine set berichten die de app overal gebruikt, zoals “Kon data niet laden. Probeer opnieuw.” Voeg waar mogelijk een retry-actie toe en log gedetailleerde fouten buiten de widget.

Voorbeeld: een custom ApprovalBadge widget zou niet zelf moeten beslissen of een factuur te goedkeuren is. Laat de pagina status en canApprove berekenen. De badge emit approve en de pagina voert de echte regel uit en roept de backend aan, en geeft daarna cleane success- of error-state terug aan de UI.

Veelgemaakte fouten die na de volgende regeneratie pijn veroorzaken

Mix no-code en Vue3
Gebruik de visuele UI-builder en plug daarna bespoke Vue-componenten als add-ons in.
Bouw webapp

De meeste problemen gaan niet over Vue. Ze ontstaan doordat custom werk in plaatsen wordt gemengd die de generator beheert, of doordat je vertrouwt op details die waarschijnlijk veranderen.

Fouten die er vaak voor zorgen dat een kleine widget verandert in terugkerend onderhoud:

  • Gegenereerde Vue-bestanden direct bewerken en vergeten wat je hebt veranderd.
  • Gebruik van globale CSS of brede selectors die stilletjes andere schermen beïnvloeden wanneer de markup verandert.
  • Direct lezen of muteren van gegenereerde state-vormen, zodat een onschuldige hernoeming de widget breekt.
  • Te veel pagina-specifieke aannames in één component proppen.
  • De component-API (props/events) wijzigen zonder migratieplan.

Een veelvoorkomend scenario: je voegt een custom tabel-widget toe en die werkt. Een maand later zorgt een wijziging in de gegenereerde layout ervoor dat jouw globale .btn-regel nu ook login- en admin-pagina's beïnvloedt. Of een data-object verandert van user.name naar user.profile.name en de widget faalt stilletjes. De widget was niet het probleem — de afhankelijkheid van onstabiele details was het.

Twee gewoonten voorkomen het meeste hiervan:

Ten eerste, behandel gegenereerde code als read-only en houd custom bestanden apart met een duidelijke importgrens.

Ten tweede, houd het componentcontract klein en expliciet. Als je het moet evolueren, voeg een eenvoudige versie-prop toe (bijv. apiVersion) of ondersteun zowel oude als nieuwe prop-vormen voor één release.

Snel checklist voordat je een custom component uitrolt

Gebruik eerst ingebouwde opties
Leun op ingebouwde auth- en betalingsmodules, en customiseer UI waar het echt telt.
Gebruik modules

Voer vóór merge een korte realiteitscheck uit. Het moet de volgende regeneratie zonder heldendaden overleven en iemand anders moet het kunnen hergebruiken.

  • Regeneratie-test: draai een volledige regeneratie en rebuild. Als je een gegenereerd bestand moest opnieuw bewerken, zit de grens fout.
  • Duidelijke inputs en outputs: props in, emits uit. Vermijd magische afhankelijkheden zoals externe DOM of het aannemen van een specifiek paginastore.
  • Style containment: scope styles en gebruik een duidelijke class-prefix (bijv. timeline-).
  • Alle toestanden zien er goed uit: loading, error en empty states moeten aanwezig en logisch zijn.
  • Herbruik zonder klonen: controleer of je hem op een tweede pagina kunt laten werken door props en event handlers aan te passen, niet door intern te kopiëren.

Een snelle manier om dit te valideren: stel je voor dat je de widget aan een admin-scherm toevoegt en daarna aan een klantportaal. Als beide werken met alleen propwijzigingen en event-handling, zit je veilig.

Een realistisch voorbeeld: een timeline-widget toevoegen aan een dashboard

Support-teams willen vaak één scherm dat het verhaal van een ticket vertelt: statuswijzigingen, interne notities, klantreacties en betaal- of leveringsgebeurtenissen. Een timeline-widget past goed, maar je wilt geen gegenereerde bestanden bewerken en werk verliezen bij de volgende regeneratie.

De veilige aanpak is de widget geïsoleerd buiten de gegenereerde UI te houden en hem via een dunne wrapper in de pagina te droppen.

Het widget-contract

Houd het simpel en voorspelbaar. Bijvoorbeeld geeft de wrapper door:

  • ticketId (string)
  • range (laatste 7 dagen, laatste 30 dagen, custom)
  • mode (compact vs gedetailleerd)

De widget emit:

  • select wanneer de gebruiker op een gebeurtenis klikt
  • changeFilters wanneer de gebruiker range of mode aanpast

Nu weet de widget niets over de dashboardpagina, datamodellen of hoe requests gemaakt worden. Hij rendert een timeline en rapporteert gebruikersacties.

Hoe de wrapper hem koppelt aan de pagina

De wrapper staat naast het dashboard en vertaalt paginadata naar het contract. Hij leest het huidige ticketId uit de paginastate, zet UI-filters om naar een range en mapt backend-records naar het event-formaat dat de widget verwacht.

Wanneer de widget select emit, kan de wrapper een details-paneel openen of een paginactie triggeren. Bij changeFilters update de wrapper de paginfilters en vernieuwt data.

Als de dashboard-UI geregenereerd wordt, blijft de widget onaangeraakt omdat hij buiten de gegenereerde bestanden leeft. Meestal bezoek je alleen de wrapper opnieuw als de pagina een veld hernoemt of verandert hoe filters opgeslagen worden.

Test- en releasegewoonten die verrassingen voorkomen

Ship één bespoke widget
Voeg een timeline- of chart-widget toe aan een echte pagina zonder risicovolle handmatige aanpassingen.
Maak een dashboard

Custom components falen meestal op saaie manieren: een prop-vorm verandert, een event stopt met vuren, of een gegenereerd pagina her-rendered vaker dan je widget verwacht. Een paar gewoonten vangen deze problemen vroeg.

Lokale tests: spot grensbreuken vroeg

Behandel de grens tussen gegenereerde UI en je widget als een API. Test de widget zonder de volledige app eerst, met hardgecodeerde props die bij het contract passen.

Render hem met "happy path" props en met ontbrekende waarden. Simuleer belangrijke events (opslaan, annuleren, selecteren) en bevestig dat de parent ze afhandelt. Test trage data en kleine schermen. Controleer dat hij geen globale state schrijft tenzij dat onderdeel van het contract is.

Als je op een AppMaster Vue3-webapp bouwt, voer deze checks uit voordat je iets regenereert. Het is makkelijker een grensprobleem te diagnosticeren als je niet twee dingen tegelijk hebt veranderd.

Regressie na regeneratie: wat eerst te controleren

Na elke regeneratie, controleer de raakpunten: worden dezelfde props doorgegeven en worden dezelfde events afgehandeld? Daar treedt breakage meestal eerst op.

Houd inclusie voorspelbaar. Vermijd fragiele imports die afhankelijk zijn van padstructuren die kunnen verschuiven. Gebruik één stabiel entrypoint voor je custom componenten.

Voor productie, voeg lichte logging en foutcaptatie in de widget toe:

  • Mounted met sleutelprops (geschoond)
  • Contractschendingen (ontbrekende vereiste prop, fout type)
  • Mislukte API-calls met korte foutcode
  • Onverwachte lege staten

Als er iets breekt wil je snel kunnen beantwoorden: heeft regeneratie de inputs veranderd, of is de widget veranderd?

Volgende stappen: maak het patroon herhaalbaar in je app

Als de eerste widget werkt, is de echte winst het herhaalbaar maken zodat de volgende geen one-off hack wordt.

Maak een kleine interne standaard voor widget-contracten en leg die vast waar je team notities bewaart. Houd het simpel: naamgeving, verplicht vs optionele props, een kleine event-set, foutgedrag en duidelijke ownership (wat in gegenereerde UI leeft vs jouw custom-map).

Schrijf ook de grensregels in gewone taal: bewerk geen gegenereerde bestanden, isoleer custom code en geef data alleen door via props en events. Dat voorkomt de “quick fix” die een permanente onderhoudslast wordt.

Voordat je een tweede widget bouwt, doe een kleine regeneratieproef. Ship de eerste widget en regenereer minstens twee keer tijdens normale veranderingen (een labelwijziging, een layoutwijziging, een nieuw veld) en verifieer dat niets breekt.

Als je AppMaster gebruikt, werkt het vaak het beste om de meeste UI en logica in de visuele editors te houden (UI builders, Business Process Editor en Data Designer). Bewaar custom Vue-componenten voor echt bespoke widgets die de editors niet kunnen uitdrukken, zoals gespecialiseerde timelines, interactieve grafieken of ongebruikelijke invoercontrols. Als je een schoon startpunt wilt voor deze aanpak, is AppMaster op appmaster.io ontworpen rond regeneratie, waardoor het isoleren van custom widgets een natuurlijk deel van de workflow wordt.

FAQ

Waarom verdwijnen mijn UI-wijzigingen na het regenereren van de app?

Het bewerken van gegenereerde Vue-bestanden is riskant omdat regeneratie ze volledig kan overschrijven. Zelfs als je wijziging één keer lijkt te blijven, kan een kleine visuele aanpassing in de builder templates opnieuw genereren en je handmatige aanpassingen wissen.

Hoe kan ik een gegenereerde UI aanpassen zonder mijn werk bij de volgende regeneratie te verliezen?

Zet alle handgeschreven Vue-code in een aparte, door jou beheerde map (bijv. custom of extensions) en importeer die als dependency. Behandel gegenereerde pagina's als read-only output en verbind alleen via een klein, stabiel interface.

Wat is een wrapper-component en waarom helpt het bij gegenereerde schermen?

Een wrapper is een dunne component die jij beheert en die tussen een gegenereerde pagina en je widget zit. Hij vertaalt de datavorm van de pagina naar schone props en zet widget-events om naar paginacties. Als de gegenereerde data later verandert, update je meestal alleen de wrapper.

Wat is de veiligste manier om props en events te ontwerpen voor een herbruikbare widget?

Houd het contract klein: een paar props voor de data die de widget nodig heeft en een paar events om gebruikersintentie te melden. Geef de voorkeur aan eenvoudige waarden en plain objects die je beheert, voeg defaults toe, valideer inputs en faal soepel (bijv. lege toestand) in plaats van te crashen.

Wanneer moet ik `update:modelValue` vs `change` emitten vanuit een custom component?

update:modelValue gebruik je wanneer de widget zich gedraagt als een form control en v-model moet ondersteunen. change gebruik je voor bevestigde acties, zoals wanneer de gebruiker op Opslaan klikt of een selectie voltooit, zodat de parent niet op elke toetsaanslag hoeft te reageren.

Hoe voorkom ik dat de CSS van mijn widget andere gegenereerde pagina's kapotmaakt?

Scope je widget-styles en gebruik een duidelijke class-prefix zodat gegenereerde pagina's niet per ongeluk je CSS overrulen. Als je themawaarden nodig hebt, geef die dan als props (kleuren, spacing, font-size) in plaats van page-level styles te importeren of erop te vertrouwen.

Waar moet business logic leven als ik custom UI-componenten toevoeg?

Standaard: houd business rules buiten de widget. Laat de pagina of backend beslissen over permissies, validatie en opslaan; de widget richt zich op renderen en interactie en emit select, retry of approve.

Wat zijn de meest voorkomende redenen dat custom components breken na regeneratie?

Vermijd afhankelijkheden van onstabiele details zoals gegenereerde bestandsroutes, parent DOM-structuur of interne objectvormen. Als je dat toch nodig hebt, verberg het achter een wrapper zodat bijvoorbeeld een naamswijziging van user.name naar user.profile.name niet je widget breekt.

Wat moet ik testen vóór en na regeneratie om problemen vroeg te vinden?

Test de widget in isolatie met hardgecodeerde props die bij het contract passen, inclusief ontbrekende en foutieve waarden. Regenerate daarna en verifieer eerst: worden dezelfde props nog steeds doorgegeven en worden dezelfde events nog steeds afgehandeld?

Wanneer is het de moeite waard om een bespoke Vue-widget te bouwen in plaats van ingebouwde UI-blokken te gebruiken?

Niet elke UI heeft custom code nodig; voeg custom components toe voor dingen die de visuele builders niet goed kunnen uitdrukken, zoals complexe charts, map pickers, signature pads of drag-and-drop planners. Als een eis met de gegenereerde UI en processen kan, blijft dat meestal makkelijker te onderhouden.

Gemakkelijk te starten
Maak iets geweldigs

Experimenteer met AppMaster met gratis abonnement.
Als je er klaar voor bent, kun je het juiste abonnement kiezen.

Aan de slag