Una aplicación de página única ( SPA ) es una aplicación web o un sitio web que interactúa con el usuario reescribiendo dinámicamente la página web actual con nuevos datos del servidor web , en lugar del método predeterminado de cargar páginas nuevas completas. El objetivo es lograr transiciones más rápidas que hagan que el sitio web se parezca más a una aplicación nativa .
En una SPA, nunca se produce una actualización de página; en su lugar, todo el código HTML , JavaScript y CSS necesario es recuperado por el navegador con una sola carga de página, [1] o los recursos apropiados se cargan dinámicamente y se agregan a la página según sea necesario, generalmente en respuesta a las acciones del usuario.
Los orígenes del término aplicación de una sola página no están claros, aunque el concepto fue discutido al menos ya en 2003 por los evangelistas de la tecnología de Netscape. [2] Stuart Morris, un estudiante de programación de la Universidad de Cardiff, Gales, escribió el sitio web autónomo en slashdotslash.com con los mismos objetivos y funciones en abril de 2002, [3] y más tarde el mismo año Lucas Birdeau, Kevin Hakman, Michael Peachey y Clifford Yeh describieron una implementación de aplicación de una sola página en la patente estadounidense 8.136.109. [4] Las formas anteriores se llamaban aplicaciones web enriquecidas .
JavaScript se puede utilizar en un navegador web para mostrar la interfaz de usuario (UI), ejecutar la lógica de la aplicación y comunicarse con un servidor web. Existen bibliotecas gratuitas maduras que respaldan la creación de una SPA, lo que reduce la cantidad de código JavaScript que los desarrolladores deben escribir.
Hay varias técnicas disponibles que permiten al navegador conservar una sola página incluso cuando la aplicación requiere comunicación con el servidor.
Los autores de HTML pueden aprovechar los identificadores de elementos para mostrar u ocultar distintas secciones del documento HTML. Luego, mediante CSS, los autores pueden usar el :target
selector de pseudoclase para mostrar solo la sección de la página a la que navegó el navegador.
Los marcos y bibliotecas de JavaScript para navegadores web, como AngularJS , Ember.js , ExtJS , Knockout.js , Meteor.js , React , Vue.js y Svelte , han adoptado los principios de SPA. Aparte de ExtJS, todos estos son gratuitos.
A partir de 2006, la técnica más destacada utilizada fue Ajax . [1] Ajax implica el uso de solicitudes asincrónicas a un servidor para datos XML o JSON , como con XMLHttpRequest de JavaScript o más moderno fetch()
(desde 2017), o el obsoleto ActiveX Object . A diferencia del enfoque declarativo de la mayoría de los marcos SPA, con Ajax el sitio web usa directamente JavaScript o una biblioteca de JavaScript como jQuery para manipular el DOM y editar elementos HTML. Ajax se ha popularizado aún más con bibliotecas como jQuery , que proporciona una sintaxis más simple y normaliza el comportamiento de Ajax en diferentes navegadores que históricamente tenían un comportamiento variable.
Los WebSockets son una tecnología de comunicación bidireccional cliente-servidor en tiempo real que forma parte de la especificación HTML. Para la comunicación en tiempo real, su uso es superior al de Ajax en términos de rendimiento [9] y simplicidad.
Los eventos enviados por el servidor (SSE) son una técnica mediante la cual los servidores pueden iniciar la transmisión de datos a los clientes del navegador. Una vez que se ha establecido una conexión inicial, un flujo de eventos permanece abierto hasta que el cliente lo cierra. Los SSE se envían a través del protocolo HTTP tradicional y tienen una variedad de características de las que carecen los WebSockets por diseño, como la reconexión automática, los identificadores de eventos y la capacidad de enviar eventos arbitrarios. [10]
Aunque este método está obsoleto, también se pueden lograr llamadas asincrónicas al servidor utilizando tecnologías de complementos del navegador como Silverlight , Flash o applets de Java .
Las solicitudes al servidor normalmente dan como resultado datos sin procesar (p. ej., XML o JSON ) o HTML nuevo . En el caso de que el servidor devuelva HTML, JavaScript en el cliente actualiza un área parcial del DOM ( Document Object Model ). Cuando se devuelven datos sin procesar, a menudo se utiliza un proceso XML / XSL de JavaScript del lado del cliente (y en el caso de JSON, una plantilla ) para traducir los datos sin procesar a HTML, que luego se utiliza para actualizar un área parcial del DOM.
Una SPA traslada la lógica del servidor al cliente, y el servidor web evoluciona hacia una API de datos pura o un servicio web. Este cambio arquitectónico se ha denominado en algunos círculos "arquitectura de servidor ligero" para destacar que la complejidad se ha trasladado del servidor al cliente, con el argumento de que esto, en última instancia, reduce la complejidad general del sistema.
El servidor mantiene en memoria el estado necesario del estado de la página del cliente. De esta manera, cuando llega una petición al servidor (normalmente acciones del usuario), el servidor envía el HTML o JavaScript adecuado con los cambios concretos para llevar al cliente al nuevo estado deseado (normalmente añadir/eliminar/actualizar una parte del DOM del cliente). Al mismo tiempo, se actualiza el estado en el servidor. La mayor parte de la lógica se ejecuta en el servidor y normalmente también se renderiza el HTML en el servidor. De alguna manera, el servidor simula un navegador web, recibe eventos y realiza cambios delta en el estado del servidor que se propagan automáticamente al cliente.
Este enfoque requiere más memoria y procesamiento del servidor, pero la ventaja es un modelo de desarrollo simplificado porque a) la aplicación generalmente está completamente codificada en el servidor y b) los datos y el estado de la interfaz de usuario en el servidor se comparten en el mismo espacio de memoria sin necesidad de puentes de comunicación cliente/servidor personalizados.
Esta es una variante del enfoque de servidor con estado. La página del cliente envía datos que representan su estado actual al servidor, generalmente a través de solicitudes Ajax. Con estos datos, el servidor puede reconstruir el estado del cliente de la parte de la página que necesita ser modificada y puede generar los datos o el código necesarios (por ejemplo, como JSON o JavaScript), que se devuelven al cliente para llevarla a un nuevo estado, generalmente modificando el árbol DOM de la página de acuerdo con la acción del cliente que motivó la solicitud.
Este enfoque requiere que se envíen más datos al servidor y puede requerir más recursos computacionales por solicitud para reconstruir parcial o totalmente el estado de la página del cliente en el servidor. Al mismo tiempo, este enfoque es más fácil de escalar porque no hay datos de página por cliente guardados en el servidor y, por lo tanto, las solicitudes Ajax se pueden enviar a diferentes nodos del servidor sin necesidad de compartir datos de sesión o afinidad de servidores.
Algunas SPA pueden ejecutarse desde un archivo local utilizando el esquema de URI de archivo . Esto brinda a los usuarios la capacidad de descargar la SPA desde un servidor y ejecutar el archivo desde un dispositivo de almacenamiento local, sin depender de la conectividad del servidor. Si una SPA de este tipo desea almacenar y actualizar datos, debe utilizar el almacenamiento web basado en el navegador . Estas aplicaciones se benefician de los avances disponibles con HTML . [11]
Dado que el SPA es una evolución del modelo de redibujado de páginas sin estado para el que se diseñaron originalmente los navegadores, han surgido algunos nuevos desafíos. Las posibles soluciones (de diversa complejidad, exhaustividad y control del autor) incluyen: [12]
Debido a la falta de ejecución de JavaScript en los rastreadores de algunos motores de búsqueda web populares , [17] la SEO ( optimización de motores de búsqueda ) históricamente ha presentado un problema para los sitios web públicos que desean adoptar el modelo SPA. [18]
Entre 2009 y 2015, Google Webmaster Central propuso y luego recomendó un "esquema de rastreo AJAX" [19] [20] que utiliza un signo de exclamación inicial en los identificadores de fragmentos para páginas AJAX con estado ( #!
). El sitio SPA debe implementar un comportamiento especial para permitir la extracción de metadatos relevantes por parte del rastreador del motor de búsqueda. Para los motores de búsqueda que no admiten este esquema de hash de URL , las URL con hash de la SPA permanecen invisibles. Estos URI "hash-bang" han sido considerados problemáticos por varios escritores, incluida Jeni Tennison en el W3C, porque hacen que las páginas sean inaccesibles para aquellos que no tienen JavaScript activado en su navegador. También rompen los encabezados de referencia HTTP ya que los navegadores no pueden enviar el identificador de fragmento en el encabezado Referer. [21] En 2015, Google desaprobó su propuesta de rastreo AJAX hash-bang. [22]
Como alternativa, las aplicaciones pueden procesar la primera carga de página en el servidor y las actualizaciones de página posteriores en el cliente. Esto es tradicionalmente difícil, porque el código de procesamiento podría tener que escribirse en un lenguaje o marco diferente en el servidor y en el cliente. El uso de plantillas sin lógica, la compilación cruzada de un lenguaje a otro o el uso del mismo lenguaje en el servidor y el cliente pueden ayudar a aumentar la cantidad de código que se puede compartir.
En 2018, Google introdujo la representación dinámica como otra opción para los sitios que desean ofrecer a los rastreadores una versión de una página que no sea pesada en JavaScript para fines de indexación. [23] La representación dinámica cambia entre una versión de una página que se representa del lado del cliente y una versión pre-representada para agentes de usuario específicos. Este enfoque implica que su servidor web detecte a los rastreadores (a través del agente de usuario) y los dirija a un renderizador, desde el cual luego se les ofrece una versión más simple del contenido HTML. A partir de 2024, Google ya no recomienda la representación dinámica, [24] sugiriendo en su lugar " representación del lado del servidor , representación estática o hidratación ".
Dado que la compatibilidad con SEO no es algo trivial en las SPA, cabe señalar que las SPA no suelen utilizarse en un contexto en el que la indexación de motores de búsqueda sea un requisito o una opción deseable. Los casos de uso incluyen aplicaciones que muestran datos privados ocultos detrás de un sistema de autenticación . En los casos en los que estas aplicaciones son productos de consumo, a menudo se utiliza un modelo clásico de "redibujo de página" para la página de destino de las aplicaciones y el sitio de marketing, que proporciona suficientes metadatos para que la aplicación aparezca como un resultado en una consulta de motor de búsqueda. Los blogs, foros de soporte y otros artefactos tradicionales de rediseño de página suelen estar alrededor de la SPA y pueden proporcionar a los motores de búsqueda términos relevantes.
A partir de 2021 y específicamente para Google, la compatibilidad SEO para una SPA simple es sencilla y solo requiere que se cumplan unas pocas condiciones simples. [25]
Una forma de aumentar la cantidad de código que se puede compartir entre servidores y clientes es utilizar un lenguaje de plantillas sin lógica como Mustache o Handlebars . Estas plantillas se pueden renderizar desde diferentes lenguajes host, como Ruby en el servidor y JavaScript en el cliente. Sin embargo, el mero hecho de compartir plantillas normalmente requiere la duplicación de la lógica empresarial utilizada para elegir las plantillas correctas y rellenarlas con datos. La renderización a partir de plantillas puede tener efectos negativos en el rendimiento cuando solo se actualiza una pequeña parte de la página, como el valor de una entrada de texto dentro de una plantilla grande. Reemplazar una plantilla completa también puede alterar la selección o la posición del cursor de un usuario, mientras que actualizar solo el valor modificado podría no hacerlo. Para evitar estos problemas, las aplicaciones pueden utilizar enlaces de datos de UI o manipulación granular del DOM para actualizar solo las partes apropiadas de la página en lugar de volver a renderizar plantillas completas. [26]
Como una SPA es, por definición, "una sola página", el modelo rompe el diseño del navegador para la navegación por el historial de páginas utilizando los botones "adelante" o "atrás". Esto presenta un impedimento de usabilidad cuando un usuario presiona el botón atrás, esperando el estado de pantalla anterior dentro de la SPA, pero en su lugar, se descarga la página única de la aplicación y se presenta la página anterior en el historial del navegador.
La solución tradicional para las SPA ha sido cambiar el identificador del fragmento hash de la URL del navegador de acuerdo con el estado actual de la pantalla. Esto se puede lograr con JavaScript y hace que se creen eventos del historial de URL dentro del navegador. Mientras la SPA sea capaz de resucitar el mismo estado de la pantalla a partir de la información contenida en el hash de la URL, se conserva el comportamiento esperado del botón Atrás.
Para abordar aún más este problema, la especificación HTML introdujo pushState y replaceState, que proporcionan acceso programático a la URL real y al historial del navegador.
Las herramientas de análisis como Google Analytics dependen en gran medida de la carga de páginas nuevas en el navegador, iniciada por la carga de una nueva página. Las SPA no funcionan de esta manera.
Después de la primera carga de la página, todos los cambios de contenido y de página posteriores son gestionados internamente por la aplicación, que simplemente debe llamar a una función para actualizar el paquete de análisis. Si no se llama a dicha función, el navegador nunca activa la carga de una nueva página, no se agrega nada al historial del navegador y el paquete de análisis no tiene idea de quién está haciendo qué en el sitio.
De manera similar a los problemas encontrados con los rastreadores de motores de búsqueda, las herramientas DAST pueden tener dificultades con estas aplicaciones ricas en JavaScript. Los problemas pueden incluir la falta de enlaces de hipertexto, preocupaciones sobre el uso de la memoria y los recursos cargados por la SPA que normalmente se ponen a disposición mediante una interfaz de programación de aplicaciones o API. Las aplicaciones de una sola página todavía están sujetas a los mismos riesgos de seguridad que las páginas web tradicionales, como Cross-Site Scripting (XSS) , pero también a una serie de otras vulnerabilidades únicas, como la exposición de datos a través de API y la lógica del lado del cliente y la aplicación del lado del cliente de la seguridad del lado del servidor. [27] Para escanear eficazmente una aplicación de una sola página, un escáner DAST debe poder navegar por la aplicación del lado del cliente de una manera confiable y repetible para permitir el descubrimiento de todas las áreas de la aplicación y la interceptación de todas las solicitudes que la aplicación envía a servidores remotos (por ejemplo, solicitudes API).
Es posible agregar eventos de carga de página a una SPA usando la API de historial HTML; esto ayudará a integrar las analíticas. La dificultad radica en gestionar esto y garantizar que todo se esté rastreando con precisión; esto implica verificar si faltan informes y si hay entradas duplicadas. Algunos marcos proporcionan integraciones de analíticas gratuitas que abordan la mayoría de los principales proveedores de analíticas. Los desarrolladores pueden integrarlas en la aplicación y asegurarse de que todo esté funcionando correctamente, pero no es necesario hacer todo desde cero. [26]
Existen algunas formas de acelerar la carga inicial de una SPA, como la representación previa selectiva de la página de inicio/índice de la SPA, el almacenamiento en caché y varias técnicas de división de código, incluidos los módulos de carga diferida cuando sea necesario. Pero no es posible obviar el hecho de que necesita descargar el marco, al menos parte del código de la aplicación; y accederá a una API para obtener datos si la página es dinámica. [26] Este es un escenario de compromiso de "págueme ahora o págueme después". La cuestión del rendimiento y los tiempos de espera sigue siendo una decisión que debe tomar el desarrollador.
Una SPA se carga completamente en la carga de página inicial y luego las regiones de la página se reemplazan o actualizan con nuevos fragmentos de página cargados desde el servidor a pedido. Para evitar la descarga excesiva de funciones no utilizadas, una SPA a menudo descargará progresivamente más funciones a medida que se necesiten, ya sean pequeños fragmentos de la página o módulos de pantalla completos.
De esta manera, existe una analogía entre los "estados" de una SPA y las "páginas" de un sitio web tradicional. Como la "navegación por estados" en la misma página es análoga a la navegación por páginas, en teoría, cualquier sitio web basado en páginas podría convertirse en un sitio web de una sola página, reemplazando en la misma página solo las partes modificadas.
El enfoque SPA en la web es similar a la técnica de presentación de interfaz de documento único (SDI) popular en las aplicaciones de escritorio nativas .
Blaze es una potente biblioteca para crear interfaces de usuario mediante la escritura de plantillas HTML reactivas.
El navegador puede ejecutar JavaScript y producir contenido sobre la marcha, pero el rastreador no puede.
Históricamente, las aplicaciones Ajax han sido difíciles de procesar para los motores de búsqueda porque el contenido Ajax se produce