Diseñar formularios para rendimiento en aplicaciones basadas en modelos

Crear experiencias en las que las tareas se puedan completar de forma rápida y eficiente es fundamental para la satisfacción del usuario. Las aplicaciones basadas en modelos pueden personalizarse en gran medida para crear experiencias que satisfagan las necesidades de sus usuarios, pero es importante saber cómo codificar, crear y ejecutar de forma eficaz aplicaciones basadas en modelos que se cargan rápidamente cuando un usuario abre y navega en su aplicación mientras trabaja en tareas diarias. Se ha demostrado que el rendimiento es un factor clave de la insatisfacción con una aplicación cuando no está optimizada para el rendimiento.

Las personalizaciones inteligentes y las formas con rendimiento son aspectos importantes para construir formularios muy eficientes y productivas. También es importante asegurarse de que se están creando formularios muy con las prácticas recomendadas de diseño de interfaces de usuario. Para obtener información sobre el diseño de formularios para mejorar la eficiencia y la productividad, consulte Diseñar formularios principales productivos en aplicaciones basadas en modelos.

También es importante asegurarse de que los usuarios estén en dispositivos recomendados y compatibles, y con las especificaciones mínimas requeridas. Más información: Exploradores web y dispositivos móviles admitidos

Trabajo con datos y pestañas

Esta sección cubre cómo los controles que muestran datos y pestañas impactan en el rendimiento del formulario.

Importancia de la pestaña predeterminada

La pestaña predeterminada es la primera pestaña expandida en un formulario. Desempeña un papel especial en la carga de una página de formulario. Por diseño, los controles de la pestaña predeterminada siempre se representan al abrir un registro. Específicamente, la lógica de inicialización del control, como la recuperación de datos, se invoca para cada control en la pestaña.

Por el contrario, una pestaña secundaria no realiza esta inicialización en sus controles cuando el formulario se carga inicialmente. En cambio, la inicialización del control se produce en el momento en que se abre la pestaña secundaria, ya sea mediante la interacción del usuario o llamando al método de API de cliente setFocus. Esto brinda la oportunidad de proteger la carga del formulario inicial del procesamiento de control excesivo, al colocar ciertos controles en pestañas secundarias en lugar de la pestaña predeterminada. Por lo tanto, la estrategia de ubicación del control puede tener un efecto significativo en la capacidad de respuesta de la carga inicial del formulario. Una pestaña predeterminada más interactiva proporciona una mejor experiencia general para modificar campos importantes, interactuar con la barra de comandos y explorar otras pestañas y secciones.

Coloque siempre los controles que más se utilizan en la parte superior de la pestaña predeterminada. La arquitectura de diseño e información no solo es importante para el rendimiento, sino también para mejorar la productividad cuando los usuarios interactúan con los datos del formulario. Más información: Diseñar formularios principales productivos en aplicaciones basadas en modelos

Controles basados en datos

Los controles que requieren datos adicionales más allá del registro primario producen la mayor tensión en la capacidad de respuesta y la velocidad de carga del formulario. Estos controles obtienen datos a través de la red y, a menudo, implican un período de espera (aparecen indicadores de progreso) porque puede llevar tiempo transmitir los datos.

Algunos de los controles basados en datos incluyen:

Mantenga solo los controles de uso más frecuente en la pestaña predeterminada. Los controles basados en datos restantes deben distribuirse en pestañas secundarias para permitir que la pestaña predeterminada se cargue rápidamente. Además, esta estrategia de diseño reduce la posibilidad de obtener datos que no se utilizan.

Hay otros controles que tienen menos impacto que los controles basados en datos, pero que siguen pudiendo participar en la estrategia de diseño anterior para lograr el mejor rendimiento. Estos controles incluyen:

Explorador web

Esta sección cubre las prácticas recomendadas para usar con navegadores web.

No abrir nuevas ventanas

El método API del cliente openForm permite que una opción de parámetro muestre un formulario en una nueva ventana. No use este parámetro ni lo establezca en false. Establecerlo en false asegurará que el método openForm realice el comportamiento predeterminado de mostrar el formulario utilizando la ventana existente. También es posible llamar directamente a la función window.open de JavaScript desde un script personalizado u otra aplicación; sin embargo, esto también debe evitarse. Abrir una nueva ventana significa que todos los recursos de la página deben recuperarse y cargarse desde cero, ya que la página no puede aprovechar las capacidades de almacenamiento en caché de datos en memoria entre un formulario previamente cargado y el formulario en una nueva ventana. Como alternativa a la apertura de nuevas ventanas, considere usar la experiencia de múltiples sesiones que permite que los registros se abran en múltiples pestañas mientras maximiza los beneficios de rendimiento del almacenamiento en caché del cliente.

Utilizar navegadores modernos

El uso del navegador web más actualizado es clave para garantizar que su aplicación basada en modelo se ejecute lo más rápido posible. La razón de esto es que muchas de las mejoras de rendimiento solo se pueden utilizar en los navegadores modernos más nuevos.

Por ejemplo, si su organización tiene versiones anteriores de Firefox, navegadores no basados en Chromium, etc., muchas de las mejoras de rendimiento que están integradas en una aplicación basada en modelos no estarán disponibles en las versiones anteriores del navegador porque no son compatibles con las funciones de las que depende la aplicación para ejecutarse rápidamente y sin problemas.

En la mayoría de los casos, puede esperar ver mejoras en la carga de la página simplemente cambiando a Microsoft Edge, actualizando a la última versión actual del navegador desde una versión anterior, o pasando a un navegador moderno basado en Chromium.

Personalización de JavaScript

Esta sección cubre cómo realizar personalizaciones inteligentes cuando usa JavaScript, que le ayudan a crear formularios y páginas eficaces en una aplicación basada en modelos.

Uso de JavaScript con formularios

La capacidad de personalizar los formularios con JavaScript proporciona a los desarrolladores profesionales una gran flexibilidad sobre cómo se ve y se comporta un formulario. El uso inadecuado de esta flexibilidad puede afectar negativamente al rendimiento del formulario. Los desarrolladores deben utilizar las siguientes estrategias para maximizar el rendimiento del formulario al implementar personalizaciones de JavaScript.

Utilizar solicitudes de red asincrónicas al solicitar datos

Solicite datos de forma asincrónica en lugar de sincrónica cuando se necesiten datos adicionales para las personalizaciones. Para eventos que admiten la espera de código asincrónico como los eventos de formulario OnLoad y formulario OnSave, los controladores de eventos deben devolver un Promise para que la plataforma espere hasta que Promise esté establecido. La plataforma mostrará una interfaz de usuario adecuada mientras el usuario espera a que se complete el evento.

Para eventos que no admiten la espera de código asincrónico, como el evento del formulario OnChange, puede usar una solución alternativa para detener la interacción con un formulario mientras el código realiza una solicitud asincrónica mediante showProgressIndicator. Esto es mejor que usar solicitudes sincrónicas porque los usuarios aún podrán interactuar con otras partes de la aplicación a medida que se muestra un indicador de progreso.

Aquí hay un ejemplo que usa código asincrónico en puntos de extensión sincrónicos.

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

Debe tener cuidado al usar código asincrónico en un controlador de eventos que no admite la espera de código asincrónico. Esto es particularmente cierto para el código que necesita que se tome o maneje una acción en la resolución del código asincrónico. El código asincrónico puede causar problemas si el controlador de resolución espera que el contexto de la aplicación permanezca igual que cuando se inició el código asincrónico. Su código debe comprobar que el usuario esté en el mismo contexto después de cada punto de continuación asincrónico.

Por ejemplo, puede haber código en un controlador de eventos para realizar una solicitud de red y cambiar un control para que se deshabilite en función de los datos de respuesta. Antes de que se reciba la respuesta de la solicitud, el usuario puede haber interactuado con el control o haber navegado a una página diferente. Debido a que el usuario está en una página diferente, es posible que el contexto del formulario no esté disponible, lo que podría generar errores o podría haber otro comportamiento no deseado.

Soporte asincrónico en el formulario OnLoad y el formulario OnSave

Los eventos OnLoad y OnSave del formulario admiten controladores que devuelven promesas. Los eventos esperarán a que se resuelvan las promesas devueltas por un controlador, hasta un período de tiempo de espera. Este soporte se puede habilitar a través de la configuración de la aplicación.

Más información:

Limitar la cantidad de datos solicitados durante la carga del formulario

Solicite solo la cantidad mínima de datos que sea necesaria para realizar la lógica empresarial en un formulario. Almacene en caché los datos que se solicitan tanto como sea posible, especialmente para los datos que no cambian con frecuencia o que no necesitan estar actualizados. Por ejemplo, suponga que hay un formulario que solicita datos de una tabla de configuración. Según los datos de la tabla de configuración, el formulario puede optar por ocultar una sección del formulario. En este caso, JavaScript puede almacenar datos en caché en sessionStorage, para que los datos solo se soliciten una vez por sesión (onLoad1). También se puede usar una estrategia obsoleta mientras se revalida cuando JavaScript usa los datos de sessionStorage al solicitar datos para la próxima navegación al formulario (onLoad2). Finalmente, se podría usar una estrategia de desduplicación en caso de que se llame a un controlador varias veces seguidas en una fila (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   
    }
}

Utilice la información disponible en la API del cliente en lugar de realizar solicitudes. Por ejemplo, en lugar de solicitar los roles de seguridad de un usuario al cargar el formulario, puede usar getGlobalContext.userSettings.roles.

Cargar el código solo cuando sea necesario

Cargue tanto código como sea necesario para los eventos de un formulario en particular. Si tiene un código que es solo para el formulario A y el formulario B, no debe incluirse en una biblioteca cargada para el formulario C. Debería estar en su propia biblioteca.

Evite cargar bibliotecas en el evento OnLoad si solo se utilizan para los eventos OnChange u OnSave. En su lugar, cárguelos en esos eventos. De esta forma, la plataforma puede aplazar la carga hasta después de que se cargue el formulario. Más información: Optimizar el rendimiento del formulario

Eliminar el uso de API de consola en el código de producción

No use los métodos de API de consola como console.log en código de producción. El registro de datos en la consola puede aumentar significativamente la demanda de memoria y puede evitar que los datos se limpien en la memoria. Esto puede hacer que la aplicación se vuelva más lenta con el tiempo y eventualmente se bloquee.

Evitar fugas de memoria

Las fugas de memoria en su código pueden conducir a un rendimiento más lento con el tiempo y, finalmente, hacer que su aplicación se bloquee. Las fugas de memoria ocurren cuando la aplicación no puede liberar memoria cuando ya no se necesita. Con todas las personalizaciones y componentes de código en su formulario, debe:

  • Considerar minuciosamente y probar los escenarios para cualquier cosa responsable de limpiar la memoria, como las clases responsables de administrar el ciclo de vida de los objetos.
  • Limpie todos los oyentes de eventos y suscripciones, especialmente si están en el objeto window.
  • Limpie todos los temporizadores como setInterval.
  • Evite, limite y limpie las referencias de limpieza a objetos globales o estáticos.

Para los componentes de control personalizados, la limpieza se puede realizar en el método destroy.

Para obtener más información sobre cómo solucionar problemas de memoria, vaya a esta documentación para desarrolladores de Edge.

Herramientas que se pueden utilizar para ayudar a que las aplicaciones rindan

Esta sección describe las herramientas que pueden ayudarle a comprender los problemas de rendimiento y ofrecer recomendaciones sobre cómo optimizar sus personalizaciones en aplicaciones basadas en modelos.

Información sobre el rendimiento

Informaciones de rendimiento es una herramienta de autoservicio para los creadores de aplicaciones empresariales que analiza los datos de telemetría en tiempo de ejecución y proporciona una lista priorizada de recomendaciones para ayudar a mejorar el rendimiento de las aplicaciones basadas en modelos. Esta característica proporciona un conjunto diario de información analítica relacionada con el rendimiento de una aplicación de participación del cliente o basada en modelos de Power Apps, como Dynamics 365 Sales o Dynamics 365 Service, con recomendaciones y elementos accionables. Los creadores de aplicaciones empresariales pueden ver información detallada sobre el rendimiento a nivel de la aplicación en Power Apps. Más información: ¿Qué son las estadísticas de rendimiento? (versión preliminar)

Comprobador de soluciones

El comprobador de soluciones es una herramienta poderosa que puede analizar las personalizaciones del cliente y del servidor en busca de problemas de rendimiento o confiabilidad. Puede analizar JavaScript del lado del cliente, formularios XML y complementos del lado del servidor .NET y brindar información específica sobre lo que puede ralentizar a los usuarios finales. Recomendamos que ejecute el comprobador de soluciones cada vez que publique cambios en un entorno de desarrollo, de modo que cualquier problema de rendimiento surja antes de llegar a los usuarios finales. Más información: Use el comprobador de soluciones para validar sus aplicaciones basadas en modelos en Power Apps

Algunos ejemplos de problemas relacionados con el rendimiento encontrados con el comprobador de soluciones:

Comprobador de objetos

El verificador de objetos ejecuta diagnósticos en tiempo real sobre los objetos componentes dentro de su solución. Si se detectan problemas, se devuelve una recomendación que describe cómo solucionar el problema. Más información: Usar el comprobador de objetos para diagnosticar un componente de la solución (versión preliminar)

Pasos siguientes

Diseño productivo de formularios principales en aplicaciones basadas en modelos