Share via


Concevoir des formulaires pour les performances dans les applications pilotées par modèle

Créer des expériences où les tâches peuvent être effectuées rapidement et efficacement est crucial pour la satisfaction des utilisateurs. Les applications pilotées par modèle peuvent être hautement personnalisées pour créer des expériences qui répondent aux besoins de vos utilisateurs, mais il est important de savoir comment coder, créer et exécuter efficacement des applications pilotées par modèle qui se chargent rapidement lorsqu’un utilisateur ouvre et navigue dans votre application tout en travaillant sur des tâches quotidiennes. Il a été démontré que les performances sont un facteur clé d’insatisfaction d’une application lorsqu’elle n’est pas optimisée pour les performances.

Les personnalisations intelligentes et les formulaires performants sont des aspects importants pour créer des formulaires hautement efficaces et productifs. Il est également important de veiller à créer des formulaires très productifs avec de bonnes pratiques en matière de conception et de mise en page de l’interface utilisateur. Pour plus d’informations sur la conception de formulaires pour plus d’efficacité et de productivité, voir Concevoir des formulaires principaux productifs dans des applications pilotées par modèle.

Il est également important de s’assurer que les utilisateurs utilisent les appareils recommandés et pris en charge et les spécifications minimales requises. Plus d’informations : Navigateurs web et appareils mobiles pris en charge

Utilisation des données et des onglets

Cette section aborde la manière dont les contrôles qui affichent les données et les onglets ont un impact sur les performance de formulaire.

Signification de l’onglet par défaut

L’onglet par défaut est le premier onglet développé d’un formulaire. Il joue un rôle particulier dans le chargement d’une page de formulaire. Par défaut, les contrôles de l’onglet par défaut sont toujours rendus lors de l’ouverture d’un enregistrement. Plus précisément, la logique d’initialisation du contrôle, telle que la récupération de données, est invoquée pour chaque contrôle de l’onglet.

En revanche, un onglet secondaire n’effectue pas cette initialisation sur ses champs lors du chargement initial du formulaire. Au lieu de cela, l’initialisation du contrôle se produit au moment où l’onglet secondaire est ouvert, soit par interaction avec l’utilisateur, soit en appelant la méthode API du client setFocus. Cela permet de protéger le chargement du formulaire initial du traitement de contrôle excessif en plaçant certains contrôles dans des onglets secondaires plutôt que l’onglet par défaut. Ainsi, la stratégie de placement de contrôle peut avoir un effet significatif sur la réactivité du chargement du formulaire initial. Un onglet par défaut plus réactif offre une meilleure expérience globale pour modifier les champs importants, interagir avec la barre de commandes et explorer d’autres onglets et sections.

Placez toujours les contrôles les plus utilisés en haut de votre onglet par défaut. La mise en page et l’architecture de l’information sont non seulement importantes pour les performances, mais également pour améliorer la productivité lorsque les utilisateurs interagissent avec les données du formulaire. Plus d’informations : Concevoir des formulaires principaux productifs dans des applications pilotées par modèle

Contrôles pilotés par les données

Les contrôles qui nécessitent des données supplémentaires au-delà de l’enregistrement principal produisent le plus de contraintes sur la réactivité des formulaires et la vitesse de chargement. Ces contrôles récupèrent les données sur le réseau et impliquent souvent une période d’attente (considérée comme des indicateurs de progression), car la transmission des données peut prendre du temps.

Certains des contrôles pilotés par les donnée incluent :

Conservez uniquement les contrôles les plus fréquemment utilisés dans l’onglet par défaut. Les contrôles pilotés par les données restants doivent être répartis dans des onglets secondaires pour permettre à l’onglet par défaut de se charger rapidement. De plus, cette stratégie de mise en page réduit le risque de récupérer des données qui finissent par être inutilisées.

D’autres contrôles ont moins d’impact que les contrôles pilotés par les données, mais peuvent toujours participer à la stratégie de mise en page ci-dessus afin d’obtenir les meilleures performances. Ces contrôles comprennent :

Navigateur Web

Cette section couvre les bonnes pratiques à utiliser avec les navigateurs Web.

Ne pas ouvrir de nouvelles fenêtres

La méthode API du client openForm permet à une option de paramètre d’afficher un formulaire dans une nouvelle fenêtre. N’utilisez pas ce paramètre ou définissez-le sur « false ». Le définir sur « false » garantit que la méthode openForm exécute le comportement par défaut d’affichage du formulaire à l’aide de la fenêtre existante. Il est également possible d’appeler directement la fonction JavaScript window.open à partir d’un script personnalisé ou d’une autre application ; cependant, cela devrait également être évité. L’ouverture d’une nouvelle fenêtre signifie que toutes les ressources de la page doivent être récupérées et chargées à partir de zéro, car la page ne peut pas tirer parti des capacités de mise en cache des données en mémoire entre un formulaire précédemment chargé et le formulaire dans une nouvelle fenêtre. Plutôt que d’ouvrir de nouvelles fenêtres, pensez à utiliser l’expérience multisession qui permet d’ouvrir les enregistrements dans plusieurs onglets tout en maximisant les avantages en termes de performances de la mise en cache client.

Utiliser des navigateurs modernes

L’utilisation du navigateur Web le plus récent est essentielle pour garantir que votre application pilotée par un modèle s’exécute aussi rapidement que possible. La raison en est que la plupart des améliorations de performances ne peuvent être utilisées que dans les navigateurs modernes les plus récents.

Par exemple, si votre organisation dispose d’anciennes versions de Firefox, les navigateurs non basés sur Chrome, etc., de nombreux gains de performances intégrés à une application pilotée par modèle ne seront pas disponibles dans les anciennes versions de navigateur, car elles ne prennent pas en charge les fonctionnalités dont l’application dépend pour s’exécuter rapidement et en douceur.

Dans la plupart des cas, vous pouvez vous attendre à voir des améliorations de chargement de page en passant simplement à Microsoft Edge, la mise à jour vers la dernière version actuelle du navigateur à partir d’une version plus ancienne ou le passage à un navigateur moderne basé sur Chrome.

Personnalisation JavaScript

Cette section explique comment effectuer des personnalisations intelligentes lorsque vous utilisez JavaScript pour vous aider à créer des formulaires et des pages performants dans une application pilotée par un modèle.

Utilisation de JavaScript avec les formulaires

La possibilité de personnaliser les formulaires par JavaScript offre aux développeurs professionnels une grande flexibilité quant à l’apparence et au comportement d’un formulaire. L’utilisation inappropriée de cette flexibilité peut avoir un impact négatif sur les performances du formulaire. Les développeurs doivent utiliser les stratégies suivantes pour maximiser les performances des formulaires lors de la mise en œuvre des personnalisations JavaScript.

Utiliser des requêtes réseau asynchrones lors de la demande de données

Demandez des données de manière asynchrone plutôt que synchrone lorsque des données supplémentaires sont nécessaires pour les personnalisations. Pour les événements qui prennent en charge l’attente de code asynchrone comme les événements de formulaire OnLoad et de formulaire OnSave, les gestionnaires d’événements doivent renvoyer une Promise pour que la plate-forme attende jusqu’à ce que la Promise soit réglée. La plate-forme affiche une interface utilisateur appropriée pendant que l’utilisateur attend la fin de l’événement.

Pour les événements qui ne prennent pas en charge l’attente de code asynchrone, comme l’événement de formulaire OnChange, vous pouvez utiliser une solution de contournement pour arrêter l’interaction avec un formulaire pendant que le code effectue une requête asynchrone en utilisant showProgressIndicator. Il est préférable d’utiliser des requêtes synchrones, car les utilisateurs peuvent toujours interagir avec d’autres parties de l’application lorsqu’un indicateur de progression s’affiche.

Voici un exemple utilisant du code asynchrone dans des points d’extension synchrones.

//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);

Soyez prudent lorsque vous utilisez du code asynchrone dans un gestionnaire d’événements qui ne prend pas en charge l’attente de code asynchrone. Cela est particulièrement vrai pour le code qui nécessite qu’une action soit entreprise ou gérée sur la résolution du code asynchrone. Le code asynchrone peut provoquer des problèmes si le gestionnaire de résolution s’attend à ce que le contexte de l’application reste le même qu’au moment du démarrage du code asynchrone. Votre code doit vérifier que l’utilisateur est dans le même contexte après chaque point de continuation asynchrone.

Par exemple, il peut y avoir du code dans un gestionnaire d’événements pour effectuer une requête réseau et désactiver un contrôle en fonction des données de réponse. Avant de recevoir la réponse de la demande, l’utilisateur peut avoir interagi avec le contrôle ou navigué vers une page différente. Étant donné que l’utilisateur se trouve sur une autre page, le contexte du formulaire peut ne pas être disponible, ce qui peut entraîner des erreurs, ou il peut y avoir d’autres comportements indésirables.

Prise en charge asynchrone des événements Form OnLoad et Form OnSave

Les événements OnLoad et OnSave de formulaire prennent en charge les gestionnaires qui renvoient des promesses. Les événements attendent la résolution de toutes les promesses renvoyées par un gestionnaire, jusqu’à un délai d’expiration. Cette prise en charge peut être activée via les paramètres de l’application.

Pour plus d′informations :

Limiter la quantité de données demandées lors du chargement du formulaire

Ne demandez que la quantité minimale de données nécessaire pour exécuter la logique métier sur un formulaire. Mettez en cache les données demandées autant que possible, en particulier pour les données qui ne changent pas souvent ou n’ont pas besoin d’être actualisées. Par exemple, imaginez qu’il existe un formulaire qui demande des données à une table de paramètres. En fonction des données de la table de paramètres, le formulaire peut choisir de masquer une section du formulaire. Dans ce cas, JavaScript peut mettre en cache les données dans sessionStorage afin que les données ne soient demandées qu’une seule fois par session (onLoad1). Une stratégie SWR peut être également utilisée lorsque JavaScript utilise les données de sessionStorage tout en demandant des données pour la prochaine navigation vers le formulaire (onLoad2). Enfin, une stratégie de déduplication peut être utilisée au cas où un gestionnaire est appelé plusieurs fois de suite dans une ligne (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   
    }
}

Utilisez les informations disponibles dans l’API client plutôt que de faire des demandes. Par exemple, plutôt que de demander les rôles de sécurité d’un utilisateur lors du chargement du formulaire, vous pouvez utiliser getGlobalContext.userSettings.roles.

Charger le code uniquement lorsque c’est nécessaire

Chargez autant de code que nécessaire pour les événements d’un formulaire particulier. Si vous avez un code qui est uniquement pour formulaire A et formulaire B, il ne doit pas être inclus dans une bibliothèque chargée pour formulaire C. Il devrait être dans sa propre bibliothèque.

Évitez de charger des bibliothèques dans l’événement OnLoad si elles sont utilisées uniquement pour les événements OnChange ou OnSave. Au lieu de cela, chargez-les dans ces événements. De cette façon, la plate-forme peut différer leur chargement jusqu’à ce que le formulaire se charge. Plus d’informations : Optimiser les performances des formulaires

Supprimer l’utilisation des API de console dans le code de production

N’utilisez pas les méthodes de l’API de la console telles que console.log dans le code de production. La journalisation des données sur la console peut augmenter considérablement la demande de mémoire et peut empêcher le nettoyage des données en mémoire. Cela peut entraîner un ralentissement de l’application au fil du temps et éventuellement un blocage.

Éviter les fuites de mémoire

Les fuites de mémoire dans votre code peuvent ralentir les performances au fil du temps et éventuellement provoquer le blocage de votre application. Les fuites de mémoire se produisent lorsque l’application ne parvient pas à libérer de la mémoire lorsqu’elle n’est plus nécessaire. Avec toutes les personnalisations et tous les composants de code de votre formulaire, vous devez :

  • Examinez et testez soigneusement les scénarios pour tout ce qui est responsable du nettoyage de la mémoire, comme les classes responsables de la gestion du cycle de vie des objets.
  • Nettoyez tous les écouteurs d’événements et les abonnements, surtout s’ils sont sur l’objet window.
  • Nettoyer tous les minuteurs comme setInterval.
  • Évitez, limitez et nettoyez les références aux objets globaux ou statiques.

Pour les composants de contrôle personnalisés, le nettoyage peut être effectué avec la méthode destroy.

Pour plus d’informations sur la résolution des problèmes de mémoire, consultez cette documentation pour les développeurs Edge.

Des outils que vous pouvez utiliser pour rendre les applications performantes

Cette section décrit les outils qui peuvent vous aider à comprendre les problèmes de performances et propose des recommandations sur la façon d’optimiser vos personnalisations dans les applications pilotées par modèle.

Informations sur les performances

Les informations sur les performances désignent un outil en libre-service pour les créateurs d’applications d’entreprise qui analyse les données de télémétrie d’exécution et fournit une liste hiérarchisée de recommandations pour aider à améliorer les performances des applications pilotées par modèle. Cette fonctionnalité fournit un ensemble quotidien d’informations analytiques liées aux performances d’une application pilotée par modèle ou d’engagement client Power Apps, telle que Dynamics 365 Sales ou Dynamics 365 Service, avec des recommandations et des éléments exploitables. Les créateurs d’applications d’entreprise peuvent afficher des informations détaillées sur les performances au niveau de l’application dans Power Apps. Plus d’informations : Que sont les informations sur les performances ? (version préliminaire)

Vérificateur de solution

Le vérificateur de solution est un outil puissant qui peut analyser les personnalisations du client et du serveur pour les problèmes de performances ou de fiabilité. Il peut analyser JavaScript côté client, former des plug-ins XML et .NET côté serveur et donner des informations ciblées sur ce qui peut ralentir les utilisateurs finaux. Nous vous recommandons d’exécuter le vérificateur de solution chaque fois que vous publiez des modifications dans un environnement de développement, afin que tout problème de performances soit signalé avant d’atteindre les utilisateurs finaux. Pour plus d’informations, voir : : Utiliser le vérificateur de solution pour valider vos applications pilotées par modèle dans Power Apps

Quelques exemples de problèmes liés aux performances rencontrés avec le vérificateur de solution :

  • il-specify-column. Évitez de sélectionner toutes les colonnes via les API de requête Dataverse.
  • web-use-async. Interagissez avec les ressources HTTP et HTTPS de manière asynchrone.
  • web-avoid-ui-refreshribbon. Évitez d’utiliser refreshRibbon dans le formulaire OnLoad et EnableRule.

Contrôleur d’objet

Le vérificateur d’objets exécute des diagnostics en temps réel sur les objets composants de votre solution. Si des problèmes sont détectés, une recommandation est renvoyée qui décrit comment résoudre le problème. Plus d’informations : Utiliser le vérificateur d’objets pour diagnostiquer un composant de solution (version préliminaire)

Étapes suivantes

Conception de formulaires principaux productifs dans des applications pilotées par modèle