Progettare moduli per le prestazioni nelle app basate su modello

Costruire esperienze in cui le attività possano essere completate in modo rapido ed efficiente è fondamentale per la soddisfazione degli utenti. Le app basate su modello possono essere altamente personalizzate per creare esperienze che soddisfino le esigenze dei tuoi utenti, ma è importante sapere come codificare, creare ed eseguire in modo efficace app basate su modello che si caricano rapidamente quando un utente le apre e vi naviga durante le attività quotidiane. È stato dimostrato che le prestazioni sono un fattore chiave dell'insoddisfazione degli utenti verso un'app quando non è ottimizzata per le prestazioni.

Personalizzazioni intelligenti e moduli performanti sono aspetti importanti per la creazione di moduli altamente efficienti e produttivi. È anche importante assicurarsi di creare moduli altamente produttivi con le procedure consigliate nella progettazione e nel layout dell'interfaccia utente. Per informazioni sulla progettazione di moduli per l'efficienza e la produttività, vedere Progettare moduli principali produttivi in app basate su modello.

È anche importante assicurarsi che gli utenti usino dispositivi consigliati e supportati e le specifiche minime richieste. Per altre informazioni: Web browser e dispositivi mobili supportati.

Uso dei dati e schede

Questa sezione illustra come i controlli che visualizzano i dati e le schede influiscono sulle prestazioni del modulo.

Significato della scheda predefinita

La scheda predefinita è la prima scheda espansa in un modulo. Svolge un ruolo speciale nel caricamento di una pagina del modulo. Per impostazione predefinita, viene sempre eseguito il rendering dei controlli della scheda predefinita all'apertura di un record. In particolare, la logica di inizializzazione del controllo, come il recupero dei dati, viene richiamata per ogni controllo nella scheda.

Al contrario, una scheda secondaria non esegue questa inizializzazione sui propri controlli quando il modulo viene caricato inizialmente. Infatti, l'inizializzazione dei controlli avviene nel momento in cui la scheda secondaria viene aperta tramite l'interazione dell'utente o la chiamata al metodo API client setFocus. Ciò offre l'opportunità di proteggere il carico del modulo iniziale dall'elaborazione eccessiva dei controlli grazie all'inserimento di determinati controlli in schede secondarie anziché nella scheda predefinita. La strategia di posizionamento dei controlli può quindi avere un effetto significativo sulla reattività del caricamento del modulo iniziale. Una scheda predefinita più reattiva offre una migliore esperienza complessiva per la modifica di campi importanti, l'interazione con la barra dei comandi e l'esplorazione di altre schede e sezioni.

Metti sempre i controlli di uso più frequente nella parte superiore della scheda predefinita. L'architettura del layout e delle informazioni non è importante solo per le prestazioni, ma anche per migliorare la produttività quando gli utenti interagiscono con i dati nel modulo. Altre informazioni: Progettare moduli principali produttivi in app basate su modello

Controlli basati sui dati

I controlli che richiedono dati aggiuntivi oltre al record principale incidono maggiormente sulla reattività del modulo e sulla velocità di caricamento. Questi controlli recuperano i dati sulla rete e spesso comportano un periodo di attesa (visto come indicatori di avanzamento) perché la trasmissione dei dati può richiedere tempo.

Alcuni dei controlli basati sui dati includono:

Metti i controlli di uso più frequente nella scheda predefinita. I restanti controlli basati sui dati dovrebbero essere distribuiti in schede secondarie per consentire il caricamento rapido della scheda predefinita. Inoltre, questa strategia di layout riduce la possibilità di recuperare dati che finiscono per non essere usati.

Esistono altri controlli che hanno un impatto minore rispetto ai controlli basati sui dati, ma possono comunque avere una parte nella strategia di layout di cui sopra per ottenere le prestazioni migliori. Questi controlli includono:

Web browser

Questa sezione illustra le procedure consigliate da usare con i Web browser.

Non aprire finestre nuove

Il metodo API client openForm consente a un'opzione di parametro di visualizzare un modulo in una finestra nuova. Non usare questo parametro oppure impostarlo su false. Impostandolo su false, il metodo openForm seguirà il comportamento predefinito di visualizzare il modulo nella finestra esistente. È possibile anche chiamare direttamente la funzione window.open di JavaScript da uno script personalizzato o da un'altra applicazione. Anche questa scelta dovrebbe tuttavia essere evitata. L'apertura di una nuova finestra significa che tutte le risorse della pagina devono essere recuperate e caricate da zero poiché la pagina non è in grado di sfruttare le funzionalità di memorizzazione nella cache dei dati in memoria tra un modulo caricato precedentemente e il modulo in una nuova finestra. In alternativa all'apertura di nuove finestre, prendi in considerazione l'uso dell'esperienza multisessione che consente di aprire i record in più schede, massimizzando al contempo i vantaggi in termini di prestazioni della memorizzazione nella cache del client.

Usa browser moderni

L'uso del Web browser più aggiornato è fondamentale per garantire che l'app basata su modello venga eseguita il più velocemente possibile. La ragione di ciò è che molti dei miglioramenti delle prestazioni possono essere usati solo nei browser moderni più recenti.

Ad esempio, se la tua organizzazione ha versioni meno recenti di Firefox, di browser non basati su Chromium e così via, molti dei miglioramenti delle prestazioni incorporati in un'app basata su modello non saranno disponibili perché non supportano le funzionalità da cui l'app dipende per funzionare rapidamente e senza intoppi.

Nella maggior parte dei casi, puoi aspettarti di vedere miglioramenti nel caricamento della pagina semplicemente passando a Microsoft Edge, eseguendo l'aggiornamento all'ultima versione del browser corrente da una versione precedente o passando a un browser moderno basato su Chromium.

Personalizzazione JavaScript

Questa sezione spiega come effettuare personalizzazioni intelligenti quando usi JavaScript che ti aiutano a creare moduli e pagine performanti in un'app basata su modello.

Utilizzo di JavaScript con i moduli

La possibilità di personalizzare i moduli usando JavaScript offre agli sviluppatori professionisti una grande flessibilità nello sviluppo dell'aspetto e del comportamento di un modulo. Un uso inadeguato di questa flessibilità può influire negativamente sulle prestazioni del modulo. Gli sviluppatori dovrebbero usare le seguenti strategie per massimizzare le prestazioni del modulo durante l'implementazione delle personalizzazioni JavaScript.

Utilizzare richieste di rete asincrone nella richiesta di dati

Richiedere i dati in modo asincrono anziché sincrono quando sono necessari dati aggiuntivi per le personalizzazioni. Per gli eventi che supportano l'attesa di codice asincrono come gli eventi OnLoad e OnSave del modulo, i gestori eventi devono restituire un evento Promise in modo che la piattaforma attenda fino al completamento di Promise. La piattaforma mostrerà un'interfaccia utente appropriata mentre l'utente attende il completamento dell'evento.

Per eventi che non supportano l'attesa di codice asincrono, come l'evento OnChange del modulo, è possibile usare una soluzione alternativa per interrompere l'interazione con il modulo mentre il codice esegue una richiesta asincrona usando showProgressIndicator. È meglio che usare le richieste sincrone perché gli utenti saranno ancora in grado di interagire con altre parti dell'applicazione mentre è visualizzato un indicatore di avanzamento.

Ecco un esempio dell'uso di codice asincrono nei punti di estensione sincroni.

//Only do this if an extension point does not yet support asynchronous code
try {
    await Xrm.WebApi.retrieveRecord("settings_entity", "7333e80e-9b0f-49b5-92c8-9b48d621c37c");
    //do other logic with data here
} catch (error) {
    //do other logic with error here
} finally {
    Xrm.Utility.closeProgressIndicator();
}

// Or using .then/.finally
Xrm.Utility.showProgressIndicator("Checking settings...");
Xrm.WebApi.retrieveRecord("settings_entity", "7333e80e-9b0f-49b5-92c8-9b48d621c37c")
    .then(
        (data) => {
            //do other logic with data here
        },
        (error) => {
            //do other logic with error here
        }
    )
    .finally(Xrm.Utility.closeProgressIndicator);

Presta attenzione quando usi codice asincrono in un gestore eventi che non supporta l'attesa di codice asincrono. Ciò vale soprattutto per il codice che necessita che sia intrapresa o gestita un'azione sulla risoluzione del codice asincrono. Il codice asincrono può causare problemi se il gestore della risoluzione si aspetta che il contesto dell'applicazione rimanga lo stesso di quando è stato avviato il codice asincrono. Il codice dovrebbe verificare che l'utente si trovi nello stesso contesto dopo ogni punto di continuazione asincrono.

Ad esempio, potrebbe essere presente codice in un gestore di eventi per effettuare una richiesta di rete e modificare un controllo da disabilitare in base ai dati di risposta. Prima di ricevere la risposta dalla richiesta, l'utente potrebbe aver interagito con il controllo o essere passato a una pagina diversa. Poiché l'utente si trova su una pagina diversa, il contesto del modulo potrebbe non essere disponibile, il che potrebbe causare errori o altro comportamento indesiderato.

Supporto asincrono negli eventi OnLoad e OnSave del modulo

Gli eventi OnLoad e OnSave del modulo supportano i gestori che restituiscono promesse. Gli eventi attenderanno la risoluzione di eventuali promesse restituite da un gestore, fino a un periodo di timeout. Questo supporto può essere abilitato tramite le impostazioni dell'app.

Ulteriori informazioni:

Limita la quantità di dati richiesti durante il caricamento del modulo

Richiedi solo la quantità minima di dati necessaria per eseguire le regole di business su un modulo. Memorizza nella cache quanti più dati richiesti possibili, in particolare i dati che non cambiano spesso o non necessitano di aggiornamento. Ad esempio, immagina di avere un modulo che richiede dati da una tabella impostazione. In base ai dati in questa tabella, il modulo potrebbe scegliere di nascondere una sezione del modulo. In questo caso, JavaScript può memorizzare nella cache i dati in sessionStorage in modo che i dati vengano richiesti solo una volta per sessione (onLoad1). Si potrebbe usare anche una strategia di riconvalida di asset non aggiornato in cui JavaScript usa i dati da sessionStorage nella richiesta dei dati per la successiva navigazione al modulo (onLoad2). Infine, è possibile usare una strategia di deduplicazione nel caso in cui un gestore venga chiamato più volte di seguito (onLoad3).

const SETTING_ENTITY_NAME = "settings_entity";
const SETTING_FIELD_NAME = "settingField1";
const SETTING_VALUE_SESSION_STORAGE_KEY = `${SETTING_ENTITY_NAME}_${SETTING_FIELD_NAME}`;

// Retrieve setting value once per session
async function onLoad1(executionContext) {
    let settingValue = sessionStorage.getItem(SETTING_VALUE_SESSION_STORAGE_KEY);

    // Ensure there is a stored setting value to use
    if (settingValue === null || settingValue === undefined) {
        settingValue = await requestSettingValue();
    }

    // Do logic with setting value here
}

// Retrieve setting value with stale-while-revalidate strategy
async function onLoad2(executionContext) {
    let settingValue = sessionStorage.getItem(SETTING_VALUE_SESSION_STORAGE_KEY);

    // Revalidate, but only await if session storage value is not present
    const requestPromise = requestSettingValue();

    // Ensure there is a stored setting value to use the first time in a session
    if (settingValue === null || settingValue === undefined) {
        settingValue = await requestPromise;
    }
    
    // Do logic with setting value here
}

// Retrieve setting value with stale-while-revalidate and deduplication strategy
let requestPromise;
async function onLoad3(executionContext) {
    let settingValue = sessionStorage.getItem(SETTING_VALUE_SESSION_STORAGE_KEY);

    // Request setting value again but don't wait on it
    // In case this handler fires twice, don’t make the same request again if it is already in flight
    // Additional logic can be added so that this is done less than once per page
    if (!requestPromise) {
        requestPromise = requestSettingValue().finally(() => {
            requestPromise = undefined;
        });
    }

    // Ensure there is a stored setting value to use the first time in a session
    if (settingValue === null || settingValue === undefined) {
        settingValue = await requestPromise;
    }
    
    // Do logic with setting value here
}

async function requestSettingValue() {
    try {
        const data = await Xrm.WebApi.retrieveRecord(
            SETTING_ENTITY_NAME,
            "7333e80e-9b0f-49b5-92c8-9b48d621c37c",
            `?$select=${SETTING_FIELD_NAME}`);
        try {
            sessionStorage.setItem(SETTING_VALUE_SESSION_STORAGE_KEY, data[SETTING_FIELD_NAME]);
        } catch (error) {
            // Handle sessionStorage error
        } finally {
            return data[SETTING_FIELD_NAME];
        }
    } catch (error) {
        // Handle retrieveRecord error   
    }
}

Utilizza le informazioni disponibili nell'API client anziché effettuare richieste. Ad esempio, anziché richiedere i ruoli di sicurezza di un utente al caricamento del modulo, è possibile usare getGlobalContext.userSettings.roles.

Caricare il codice solo quando è necessario

Carica tutto il codice necessario per gli eventi per un particolare modulo. Se il codice vale solo per modulo A e modulo B, non dovrebbe essere incluso in una libreria che viene caricata per modulo C. Dovrebbe essere in una libreria propria.

Evita di caricare librerie nell'evento OnLoad se vengono usate solo per gli eventi OnChange o OnSave. Caricale invece in quegli eventi. In questo modo la piattaforma può rinviare il caricamento fino a dopo il caricamento del modulo. Altre informazioni: Ottimizzare le prestazioni del modulo

Rimuovi l'uso delle API della console nel codice di produzione

Non usare i metodi API della console come console.log nel codice di produzione. La registrazione dei dati nella console può aumentare significativamente la richiesta di memoria e potrebbe impedire la pulizia dei dati in memoria. Ciò può portare l'app a rallentare nel tempo e alla fine a bloccarsi.

Evitare perdite di memoria

Le perdite di memoria nel codice possono portare a prestazioni più lente nel tempo e anche causare l'arresto anomalo dell'app. Le perdite di memoria si verificano quando l'applicazione non riesce a rilasciare la memoria quando non è più necessaria. Con tutte le personalizzazioni e i componenti del codice nel modulo, è necessario:

  • Considerare attentamente e testare gli scenari per qualsiasi cosa responsabile della pulizia della memoria, come le classi responsabili della gestione del ciclo di vita degli oggetti.
  • Pulire tutti i listener di eventi e le iscrizioni, soprattutto se è nell'oggetto window.
  • Pulire tutti i timer come setInterval.
  • Evitare, limitare e ripulire i riferimenti a oggetti globali o statici.

Per i componenti di controllo personalizzati, la pulizia può essere eseguita nel metodo eliminare in modo permanente.

Per ulteriori informazioni sulla risoluzione dei problemi di memoria, vai a questa documentazione per sviluppatori Edge.

Strumenti che puoi usare per rendere le app performanti

Questa sezione descrive gli strumenti che possono aiutarti a comprendere i problemi di prestazioni e a offrire consigli su come ottimizzare le personalizzazioni nelle app basate su modello.

Informazioni dettagliate prestazioni

Informazioni dettagliate prestazioni è uno strumento self-service per i produttori di app aziendali che analizza i dati di telemetria di runtime e fornisce un elenco prioritario di consigli per aiutare a migliorare le prestazioni delle app basate su modello. Questa funzione fornisce una serie giornaliera di approfondimenti analitici relativi alle prestazioni di un'app Power Apps basata su modello o di customer engagement, come Dynamics 365 Sales o Dynamics 365 Service, con consigli ed elementi utilizzabili. I produttori di app aziendali possono visualizzare informazioni dettagliate sulle prestazioni a livello di app in Power Apps. Altre informazioni: Che cos'è Informazioni dettagliate prestazioni? (anteprima)

Verifica soluzione

Il controllo della soluzione è un potente strumento in grado di analizzare le personalizzazioni di client e server alla ricerca di problemi di prestazioni o di affidabilità. Può analizzare JavaScript sul lato client, XML del modulo e plug-in sul lato server .NET e fornire informazioni mirate su ciò che potrebbe rallentare gli utenti finali. È consigliabile eseguire il controllo della soluzione ogni volta che pubblichi modifiche in un ambiente di sviluppo, in modo che eventuali problemi di prestazioni emergano prima della distribuzione agli utenti finali. Ulteriori informazioni: Utilizzare la verifica soluzione per convalidare le app basate su modelli in Power Apps

Alcuni esempi di problemi relativi alle prestazioni rilevati con il controllo della soluzione:

Controllo oggetto

Il controllo oggetto esegue diagnostica in tempo reale sugli oggetti componente della soluzione. Se vengono rilevati problemi, viene restituito un suggerimento su come risolvere il problema. Altre informazioni: Utilizzare il controllo oggetto per la diagnostica di un componente di soluzione (anteprima)

Passaggi successivi

Progettare moduli principali produttivi nelle app basate su modello