Componenti Vue personalizzati in UI generate, in sicurezza con rigenerazione
Scopri come aggiungere componenti Vue personalizzati in UI generate senza compromettere la rigenerazione, usando pattern di isolamento, confini chiari e regole di consegna semplici.

Cosa si rompe quando modifichi un'UI generata
Le UI generate sono pensate per essere ricostruite. In piattaforme come AppMaster, il codice di app web Vue3 viene prodotto da builder visuali. La rigenerazione è il modo in cui le modifiche restano coerenti tra schermate, logica e modelli di dati.
Il problema è semplice: se modifichi i file generati direttamente, la prossima rigenerazione può sovrascrivere le tue modifiche.
Per questo i team ricorrono al codice custom. I blocchi UI integrati spesso coprono form e tabelle standard, ma le app reali di solito richiedono qualche elemento speciale: grafici complessi, selettori su mappa, editor rich text, pad per firme o planner drag-and-drop. Sono buone ragioni per aggiungere componenti Vue personalizzati, a patto di trattarli come add-on, non come modifiche ai file generati.
Quando il codice custom si mischia con quello generato, i problemi possono emergere in ritardo e in modo confuso. Potresti non accorgertene fino a quando una modifica dell'UI non forza una rigenerazione, o finché un collega non tocca una schermata nell'editor visuale. Problemi comuni includono:
- Il tuo markup personalizzato scompare perché il template è stato rigenerato.
- Import o registrazioni si rompono perché sono cambiati nomi di file o struttura.
- Un "piccolo fix" diventa un conflitto di merge a ogni deploy.
- La logica generata e la logica custom divergono e i casi limite iniziano a fallire.
- Gli aggiornamenti sembrano rischiosi perché non sai cosa verrà sostituito.
L'obiettivo non è evitare la personalizzazione. È rendere le ricostruzioni prevedibili. Se mantieni un confine pulito tra schermate generate e widget personalizzati, la rigenerazione resta una routine anziché una fonte di stress.
Una regola di confine che mantiene sicura la rigenerazione
Se vuoi personalizzare senza perdere lavoro, segui una regola: non modificare mai i file generati. Trattali come output di sola lettura, come un artefatto compilato.
Pensa alla tua UI come a due zone:
- Zona generata: pagine, layout e schermate prodotti dal generatore.
- Zona custom: i tuoi componenti Vue scritti a mano in una cartella separata.
L'UI generata dovrebbe consumare i tuoi componenti custom. Non dovrebbe essere il posto dove quei componenti vengono costruiti.
Per far funzionare questo a lungo termine, mantieni il "confine" piccolo e chiaro. Un widget custom dovrebbe comportarsi come un piccolo prodotto con un contratto:
- Props in: solo ciò che serve per renderizzare.
- Eventi out: solo ciò a cui la pagina deve reagire.
Evita di accedere allo stato globale o chiamare API non correlate dal widget a meno che questo non sia esplicitamente parte del contratto.
Con schermate Vue3 generate in stile AppMaster, questo di solito significa che fai un wiring minimo nella schermata generata per passare props e gestire eventi. Quel wiring può cambiare tra rigenerazioni, ma resta piccolo e facile da rifare. Il vero lavoro rimane al sicuro nella zona custom.
Pattern di isolamento che funzionano bene con Vue3
L'obiettivo è semplice: la rigenerazione deve poter sostituire liberamente i file generati, mentre il tuo codice del widget resta intatto.
Un approccio pratico è mantenere i widget su misura come un piccolo modulo interno: componenti, stili e utility helper in un unico posto. Nelle app Vue3 generate, questo di solito significa che il codice custom vive fuori dalle pagine generate ed è importato come dipendenza.
Un componente wrapper aiuta molto. Lascia che il wrapper parli con l'app generata: legge la forma dei dati esistente nella pagina, la normalizza e passa props pulite al widget. Se la forma dei dati generati cambia in seguito, spesso aggiorni il wrapper invece di riscrivere il widget.
Alcuni pattern che reggono bene:
- Tratta il widget come una scatola nera: props in, eventi out.
- Usa un wrapper per mappare risposte API, date e ID in formati friendly per il widget.
- Mantieni gli stili scoped così le pagine rigenerate non sovrascrivono accidentalmente il widget.
- Non fare affidamento sulla struttura DOM del parent o su nomi di classi specifici della pagina.
Sullo styling, preferisci CSS scoped (o CSS Modules) e namespace per le classi condivise. Se il widget deve corrispondere al tema dell'app, passa i token del tema come props (colori, spaziatura, dimensione font) invece di importare gli stili della pagina.
Gli slot possono essere sicuri quando rimangono piccoli e opzionali, come un messaggio di "empty state". Se gli slot cominciano a controllare il layout o il comportamento core, hai ricollocato il widget nel layer generato — ed è lì che cominciano i problemi con la rigenerazione.
Progettare un contratto di componente stabile (props e eventi)
Il modo più sicuro per mantenere la rigenerazione indolore è trattare ogni widget come un'interfaccia stabile. Le schermate generate possono cambiare. Il tuo componente no.
Inizia dagli input (props). Mantienili pochi, prevedibili e facili da validare. Preferisci tipi primitivi semplici e oggetti piani che controlli. Aggiungi valori default in modo che il widget si comporti bene anche quando una pagina non passa ancora nulla. Se qualcosa può essere malformato (ID, stringhe di data, valori simili a enum), convalidalo e fallisci con grazia: mostra uno stato vuoto invece di andare in crash.
Per gli output, standardizza gli eventi così i widget risultino coerenti nel resto dell'app. Un set affidabile è:
update:modelValueperv-modelchangeper cambi confermati dall'utente (non ogni battitura)errorquando il componente non può completare il suo lavororeadyquando il lavoro asincrono termina e il widget è utilizzabile
Se è coinvolto lavoro asincrono, rendilo parte del contratto. Esporre loading e disabled come props e considerare errorMessage per errori lato server. Se il componente recupera i dati da solo, emetti comunque error e ready così il parent può reagire (toast, logging, UI di fallback).
Aspettative di accessibilità
Includi l'accessibilità nel contratto. Accetta una prop label (o ariaLabel), documenta il comportamento da tastiera e tieni il focus prevedibile dopo le azioni.
Per esempio, un widget timeline su una dashboard dovrebbe supportare le frecce per spostarsi tra gli elementi, Enter per aprire i dettagli e dovrebbe restituire il focus al controllo che ha aperto un dialogo quando questo si chiude. Questo rende il widget riutilizzabile tra schermate rigenerate senza lavoro aggiuntivo.
Passo dopo passo: aggiungere un widget su misura senza toccare i file generati
Parti in piccolo: una schermata che interessa agli utenti, un widget che la rende distintiva. Mantenere la prima modifica ristretta rende più facile vedere cosa la rigenerazione tocca e cosa no.
-
Crea il componente fuori dall'area generata. Mettilo in una cartella di tua proprietà e tienilo sotto controllo versione (spesso
customoextensions). -
Mantieni la superficie pubblica piccola. Poche props in, pochi eventi out. Non passare tutto lo stato della pagina.
-
Aggiungi un wrapper sottile che controlli tu. Il suo compito è tradurre i "dati pagina generati" nel contratto del widget.
-
Integra tramite un punto di estensione supportato. Referenzia il wrapper in modo che non sia necessario modificare i file generati.
-
Rigenera e verifica. La tua cartella custom, il wrapper e il componente dovrebbero rimanere invariati e continuare a compilare.
Mantieni i confini netti. Il widget si concentra sulla resa e sull'interazione. Il wrapper mappa i dati e inoltra le azioni. Le regole di business restano nel layer di logica dell'app (backend o processi condivisi), non sepolte nel widget.
Un controllo di buon senso: se la rigenerazione avvenisse ora, un collega potrebbe ricostruire l'app e ottenere lo stesso risultato senza rifare modifiche manuali? Se la risposta è sì, il tuo pattern è solido.
Dove mettere la logica per mantenere l'UI manutenibile
Un widget custom dovrebbe preoccuparsi principalmente di come appare e come risponde all'input utente. Più regole di business metti nel widget, più diventa difficile riusarlo, testarlo e modificarlo.
Un buon default è: tieni la logica di business nel layer di pagina o feature, e mantieni il widget "dumb". La pagina decide quali dati passare al widget e cosa fare quando il widget emette un evento. Il widget renderizza e segnala l'intento dell'utente.
Quando hai bisogno di logica vicina al widget (formattazione, stato piccolo, validazione client-side), nascondila dietro un piccolo service layer. In Vue3 questo può essere un modulo, un composable o uno store con un'API chiara. Il widget importa quell'API, non internals sparsi dell'app.
Una divisione pratica:
- Widget (componente): stato UI, gestione input, aspetti visivi, emette eventi come
select,change,retry. - Service/composable: shaping dei dati, caching, mappatura degli errori API in messaggi utente.
- Page/container: regole di business, permessi, cosa caricare e quando salvare.
- Parti generate dell'app: lasciarle intatte; passare i dati e ascoltare gli eventi.
Evita chiamate API dirette dentro il widget a meno che questo non sia esplicitamente il contratto del widget. Se gestisce il fetch, fallo risultare evidente (nominalo tipo CustomerSearchWidget e mantieni il codice di chiamata in un unico service). Altrimenti, passa items, loading ed error come props.
I messaggi di errore dovrebbero essere rivolti agli utenti e coerenti. Invece di mostrare testo grezzo dal server, mappa gli errori in un piccolo set di messaggi usati ovunque, tipo "Impossibile caricare i dati. Riprova." Includi un'azione di retry quando possibile e registra dettagli tecnici al di fuori del widget.
Esempio: un widget ApprovalBadge custom non dovrebbe decidere se una fattura è approvabile. Lascia che la pagina calcoli status e canApprove. Il badge emette approve, la pagina esegue la regola reale e chiama il backend (per esempio l'API modellata in AppMaster), poi passa lo stato di successo o errore pulito nell'UI.
Errori comuni che causano dolore dopo la rigenerazione
La maggior parte dei problemi non dipende da Vue. Derivano dal mescolare lavoro custom in posti di proprietà del generatore, o dal fare affidamento su dettagli propensi a cambiare.
Gli errori che trasformano più spesso un piccolo widget in una manutenzione ricorrente:
- Modificare i file Vue generati direttamente e dimenticare cosa è stato cambiato.
- Usare CSS globali o selettori larghi che influenzano altre schermate quando il markup cambia.
- Leggere o mutare forme di stato generate direttamente, così una banale rinomina rompe il widget.
- Mettere troppe assunzioni specifiche della pagina in un singolo componente.
- Cambiare l'API del componente (props/eventi) senza un piano di migrazione.
Uno scenario comune: aggiungi un widget tabella custom e funziona. Un mese dopo, una modifica al layout generato fa sì che la tua regola globale .btn ora influenzi login e pagine admin. Oppure un oggetto dati cambia da user.name a user.profile.name e il widget fallisce silenziosamente. Il problema non era il widget: dipendeva da dettagli instabili.
Due abitudini prevengono la maggior parte di questo:
Prima, tratta il codice generato come sola lettura e tieni i file custom separati, con un confine d'import chiaro.
Seconda, mantieni il contratto del componente piccolo ed esplicito. Se devi evolverlo, aggiungi una semplice prop di versione (per esempio apiVersion) o supporta entrambe le forme di prop per una release.
Lista di controllo rapida prima del rilascio di un componente custom
Prima di fare merge di un widget su misura in un'app Vue3 generata, fai un rapido reality check. Dovrebbe superare la prossima rigenerazione senza eroismi e qualcun altro dovrebbe poterlo riutilizzare.
- Test di rigenerazione: esegui una rigenerazione completa e ricompila. Se hai dovuto ri-editare un file generato, il confine è sbagliato.
- Input e output chiari: props in, emits out. Evita dipendenze "magiche" come manipolare il DOM esterno o assumere uno specifico store di pagina.
- Contenimento degli stili: scope agli stili e usa un prefisso chiaro per le classi (per esempio
timeline-). - Tutti gli stati sono ok: loading, error e empty state devono essere presenti e sensati.
- Riutilizzo senza clonazione: conferma di poterlo mettere su una seconda pagina cambiando solo props e handler, non copiando internals.
Un modo rapido per validare: immagina di aggiungere il widget in una schermata admin e poi in una per il customer portal. Se entrambi funzionano con soli cambi di props e gestione eventi, sei in zona sicura.
Un esempio realistico: aggiungere una timeline a una dashboard
Un team di supporto spesso vuole una schermata che racconti la storia di un ticket: cambi di stato, note interne, risposte del cliente e eventi di pagamento o consegna. Una timeline funziona bene, ma non vuoi modificare file generati e perdere il lavoro alla prossima rigenerazione.
L'approccio sicuro è isolare il widget fuori dall'UI generata e inserirlo nella pagina tramite un wrapper sottile.
Il contratto del widget
Mantienilo semplice e prevedibile. Per esempio, il wrapper passa:
ticketId(string)range(ultimi 7 giorni, ultimi 30 giorni, custom)mode(compact vs detailed)
Il widget emette:
selectquando l'utente clicca un eventochangeFiltersquando l'utente modifica range o mode
Ora il widget non sa nulla della dashboard, dei modelli di dati o di come vengono fatte le richieste. Renderizza la timeline e segnala le azioni dell'utente.
Come il wrapper lo collega alla pagina
Il wrapper sta vicino alla dashboard e traduce i dati di pagina nel contratto. Legge l'ID del ticket dallo stato della pagina, converte i filtri UI in un range e mappa i record del backend nel formato eventi che il widget si aspetta.
Quando il widget emette select, il wrapper può aprire un pannello dettagli o attivare un'azione di pagina. Quando emette changeFilters, il wrapper aggiorna i filtri di pagina e ricarica i dati.
Quando la UI della dashboard viene rigenerata, il widget resta intatto perché vive fuori dai file generati. Di solito dovrai solo toccare il wrapper se la pagina rinomina un campo o cambia come memorizza i filtri.
Abitudini di test e rilascio che prevengono sorprese
I componenti custom solitamente falliscono in modi banali: una forma di prop cambia, un evento smette di essere emesso o una pagina generata ri-renderizza più spesso di quanto il widget si aspetti. Alcune abitudini catturano questi problemi presto.
Test locale: individuare i break del confine per tempo
Tratta il confine tra UI generata e widget come una API. Testa il widget senza tutta l'app prima, usando props hardcodate che rispettino il contratto.
Renderizzalo con props "happy path" e con valori mancanti. Simula eventi chiave (save, cancel, select) e conferma che il parent li gestisca. Testa dati lenti e schermi piccoli. Verifica che non scriva nello stato globale a meno che non sia parte del contratto.
Se costruisci su un'app AppMaster Vue3, esegui questi controlli prima di rigenerare qualsiasi cosa. È più facile diagnosticare un problema di confine quando non hai cambiato due cose insieme.
Regressione dopo rigenerazione: cosa ricontrollare per primo
Dopo ogni rigenerazione, ricontrolla i punti di contatto: vengono passate le stesse props e vengono gestiti gli stessi eventi? Qui si manifesta la maggior parte dei guasti.
Mantieni l'inclusione prevedibile. Evita import fragili che dipendono da percorsi di file che potrebbero spostarsi. Usa un punto di ingresso stabile per i tuoi componenti custom.
Per la produzione, aggiungi logging leggero e cattura errori nel widget:
- Montato con props chiave (sanitarizzate)
- Violazioni del contratto (prop richiesta mancante, tipo sbagliato)
- Chiamate API fallite con un breve codice errore
- Stati vuoti inattesi
Quando qualcosa si rompe, vuoi rispondere rapidamente: la rigenerazione ha cambiato gli input o è cambiato il widget?
Prossimi passi: rendere il pattern ripetibile nell'app
Una volta che il primo widget funziona, il vero vantaggio è renderlo ripetibile così il prossimo non diventi un palliativo.
Crea un piccolo standard interno per i contratti dei widget e documentalo dove il team conserva le note dell'app. Mantienilo semplice: naming, props obbligatorie vs opzionali, piccolo set di eventi, comportamento di errore e ownership chiara (cosa vive nella UI generata vs nella cartella custom).
Scrivi anche le regole di confine in linguaggio semplice: non modificare i file generati, isola il codice custom e passa i dati solo tramite props ed eventi. Previene la "soluzione rapida" che diventa un costo di manutenzione permanente.
Prima di costruire un secondo widget, fai una piccola prova di rigenerazione. Spedisci il primo widget, poi rigenera almeno due volte durante cambiamenti normali (cambio di etichetta, cambiamento di layout, nuovo campo) e conferma che nulla si rompe.
Se usi AppMaster, spesso conviene tenere la maggior parte di UI e logica negli editor visuali (UI builders, Business Process Editor e Data Designer). Salva i componenti Vue custom per widget veramente su misura che gli editor non esprimono, come timeline specializzate, interazioni di grafici o controlli di input insoliti. Se vuoi un punto di partenza pulito per questo approccio, AppMaster su appmaster.io è pensato attorno alla rigenerazione, quindi isolare i widget custom diventa una parte naturale del workflow.
FAQ
Modificare i file Vue generati è rischioso perché la rigenerazione può sovrascriverli completamente. Anche se una modifica sembra sopravvivere una volta, una piccola modifica visiva nel builder può ricreare i template e cancellare le tue modifiche manuali.
Metti tutto il codice Vue scritto a mano in una cartella separata di tua proprietà (per esempio custom o extensions) e importalo come dipendenza. Considera le pagine generate come output di sola lettura e collega i componenti tramite una piccola interfaccia stabile.
Un wrapper è un componente sottile che controlli tu, che sta tra una pagina generata e il tuo widget. Traduce la forma dei dati della pagina in props pulite e converte gli eventi del widget in azioni di pagina, così se i dati generati cambiano in seguito spesso aggiorni solo il wrapper.
Mantieni il contratto piccolo: poche props per i dati necessari e pochi eventi per riportare l'intento dell'utente. Preferisci valori semplici e oggetti piani che controlli, aggiungi valori di default, valida gli input e fallisci con grazia mostrando uno stato vuoto invece di lanciare errori.
update:modelValue è il migliore quando il widget si comporta come un controllo di form e deve supportare v-model. change è più adatto per azioni "confermate" dall'utente, come quando clicca Salva o termina una selezione, così il parent non elabora ogni battitura.
Isola gli stili del widget e usa un prefisso chiaro per le classi così le pagine rigenerate non sovrascrivono accidentalmente il CSS. Se hai bisogno del tema dell'app, passa i token del tema come props (colori, spaziatura, dimensione font) invece di importare o dipendere dagli stili di pagina.
Di norma tieni le regole di business fuori dal widget. Lascia che la pagina o il backend decidano permessi, regole di validazione e comportamento di salvataggio. Il widget si occupa della resa e dell'interazione ed emette eventi come select, retry o approve.
Evita di dipendere da dettagli instabili come percorsi di file generati, struttura DOM del parent o forme di oggetti di stato interne. Se serve, nascondili dietro un wrapper così una rinomina come user.name -> user.profile.name non ti costringe a riscrivere il widget.
Testa il widget in isolamento con props hardcodate che rispettino il contratto, inclusi valori mancanti o malformati. Poi rigenera e verifica subito due cose: le stesse props vengono ancora passate e gli stessi eventi vengono gestiti dalla pagina.
Non tutte le UI richiedono codice custom; usa componenti personalizzati per ciò che il visual builder non può esprimere bene, come grafici complessi, selettori su mappa, campi firma o planner drag-and-drop. Se un requisito si può ottenere regolando l'UI e i processi generati, sarà generalmente più facile da mantenere.


