Introducción
Desarrollar para la Web móvil es un tema muy popular en la actualidad. Este año, por primera vez, los teléfonos inteligentes llegaron a las PCs vendidas. Cada vez más usuarios usan un dispositivo móvil para navegar por la Web, lo que significa que es fundamental que los desarrolladores optimicen sus sitios para los navegadores móviles.
El campo de batalla de los "dispositivos móviles" sigue siendo un campo desconocido para muchos desarrolladores. Muchas personas tienen sitios heredados existentes que ignoran a los usuarios de dispositivos móviles por completo. En cambio, el sitio se diseñó principalmente para la navegación en computadoras de escritorio y se degrada de forma deficiente en los navegadores para dispositivos móviles. Este sitio (html5rocks.com) no es una excepción. En el lanzamiento, no dedicamos mucho esfuerzo a la versión para dispositivos móviles del sitio.
Cómo crear un archivo html5rocks.com optimizado para dispositivos móviles
Como ejercicio, pensé que sería interesante tomar html5rocks (un sitio HTML5 existente) y mejorarlo con una versión optimizada para dispositivos móviles. Mi principal preocupación era la cantidad mínima de trabajo necesaria para segmentar anuncios para teléfonos inteligentes. El objetivo de mi ejercicio no era crear un sitio móvil completamente nuevo y mantener dos bases de código. Eso habría tardado una eternidad y habría sido una gran pérdida de tiempo. Ya definimos la estructura del sitio (marcado). Teníamos un aspecto y una apariencia (CSS). La funcionalidad principal (JS) estaba allí. El punto es que muchos sitios están en la misma situación.
En este artículo, se explica cómo creamos una versión para dispositivos móviles de html5rocks optimizada para dispositivos iOS y Android. Solo carga html5rocks.com en un dispositivo que admita uno de esos SO para ver la diferencia. No hay redireccionamientos a m.html5rocks.com ni a otros sitios de ese tipo. Obtienes html5rocks tal como está… con el beneficio adicional de que se ve muy bien y funciona bien en un dispositivo móvil.
Búsquedas de medios de CSS
HTML4 y CSS2 admiten hojas de estilo dependientes de los medios desde hace tiempo. Por ejemplo:
<link rel="stylesheet" media="print" href="printer.css">
se segmentaría para dispositivos de impresión y proporcionaría un diseño específico para el contenido de la página cuando se imprima. CSS3 lleva la idea de los tipos de medios un paso más allá y mejora su funcionalidad con las consultas de medios. Las consultas de medios extienden la utilidad de los tipos de medios, ya que permiten etiquetar las hojas de estilo de forma más precisa. Esto permite que la presentación del contenido se personalice para un rango específico de dispositivos de salida sin tener que cambiar el contenido en sí. Suena perfecto para un diseño existente que necesita modificaciones.
Puedes usar consultas de medios en el atributo media
de tus hojas de estilo externas para segmentar el ancho de la pantalla, el ancho del dispositivo, la orientación, etc. Para obtener la lista completa, consulta la especificación de consultas de medios del W3C.
Cómo segmentar por tamaños de pantalla
En el siguiente ejemplo, phone.css
se aplicaría a los dispositivos que el navegador considera "portátiles" o a los dispositivos con pantallas de menos de 320 px de ancho.
<link rel='stylesheet'
media='handheld, only screen and (max-device-width: 320px)' href='phone.css'>
Si agregas el prefijo "only
" a una consulta de medios, los navegadores que no sean compatibles con CSS3 ignorarán la regla.
El siguiente código se orientaría a tamaños de pantalla de entre 641 px y 800 px:
<link rel='stylesheet'
media='only screen and (min-width: 641px) and (max-width: 800px)' href='ipad.css'>
Las consultas de medios también pueden aparecer dentro de las etiquetas <style>
intercaladas. Los siguientes objetivos se orientan a los tipos de medios all
cuando están en orientación vertical:
<style>
@media only all and (orientation: portrait) { ... }
</style>
media="handheld"
Debemos detenernos un momento y hablar sobre media="handheld"
.
La realidad es que Android y iOS ignoran media="handheld"
. El argumento es que los usuarios se perderán el contenido de alta gama que proporcionan los diseños de página segmentados para media="screen"
y es menos probable que los desarrolladores mantengan una versión de media="handheld"
de menor calidad. Por lo tanto, como parte de su lema de “Web completa”, la mayoría de los navegadores de smartphones modernos simplemente ignoran las hojas de estilo para dispositivos portátiles.
Lo ideal sería usar esta función para segmentar anuncios para dispositivos móviles, pero varios navegadores la implementaron de diferentes maneras:
- Algunos solo leen la hoja de estilo para dispositivos portátiles.
- Algunos solo leen la hoja de estilo para dispositivos portátiles si hay una, pero de forma predeterminada usan la hoja de estilo para pantallas.
- Algunos leen la hoja de estilo para dispositivos portátiles y la hoja de estilo para pantallas.
- Algunos solo leen la hoja de estilo de la pantalla.
Opera Mini no ignora media="handheld"
. El truco para hacer que Windows Mobile reconozca media="handheld"
es usar mayúsculas en el valor del atributo multimedia para la hoja de estilo de la pantalla:
<!-- media="handheld" trick for Windows Mobile -->
<link rel="stylesheet" href="screen.css" media="Screen">
<link rel="stylesheet" href="mobile.css" media="handheld">
Cómo html5rocks usa consultas de medios
Las consultas de medios se usan mucho en html5rocks para dispositivos móviles. Nos permitieron ajustar el diseño sin tener que hacer cambios significativos en nuestro marcado de plantillas de Django. ¡Fue una verdadera salvación! Además, su compatibilidad con los distintos navegadores es bastante buena.
En el <head>
de cada página, verás los siguientes archivos de diseño:
<link rel='stylesheet'
media='all' href='/static/css/base.min.css' />
<link rel='stylesheet'
media='only screen and (max-width: 800px)' href='/static/css/mobile.min.css' />
base.css
siempre definió el aspecto principal de html5rocks.com, pero ahora aplicamos nuevos estilos (mobile.css
) para anchos de pantalla inferiores a 800 px. Su consulta de medios abarca teléfonos inteligentes (~320 px) y el iPad (~768 px).
El efecto: anularemos de forma incremental los estilos en base.css
(solo cuando sea necesario) para que las funciones se vean mejor en dispositivos móviles.
Estos son algunos de los cambios de diseño que aplica mobile.css
:
- Reduce el espacio en blanco o el padding adicional en todo el sitio. Las pantallas pequeñas significan que el espacio es un bien preciado.
- Quita los estados
:hover
. Nunca se verán en dispositivos táctiles. - Ajusta el diseño para que sea de una sola columna. Explicaré eso después.
- Quita el
box-shadow
alrededor del div del contenedor principal del sitio. Las sombras de cuadro grandes reducen el rendimiento de la página. - Se usó el modelo de caja flexible de CSS
box-ordinal-group
para cambiar el orden de cada sección en la página principal. Notarás que la sección “APRENDER POR GRUPOS DE FUNCIONES PRINCIPALES DE HTML5” aparece antes de la sección “TUTORIALES” en la página principal, pero después de ella en la versión para dispositivos móviles. Este orden tenía más sentido para los dispositivos móviles y no requería cambios en el lenguaje de marcado. ¡Flexbox de CSS por la victoria! - Quita los cambios de
opacity
. Cambiar los valores de alfa afecta el rendimiento en dispositivos móviles.
Metaetiquetas de dispositivos móviles
Mobile WebKit admite algunos accesorios que brindan a los usuarios una mejor experiencia de navegación en ciertos dispositivos.
Configuración de la vista del puerto
El primer parámetro de configuración de meta (y el que usarás con más frecuencia) es la propiedad viewport. Configurar un viewport le indica al navegador cómo debe ajustarse el contenido en la pantalla del dispositivo y le informa que el sitio está optimizado para dispositivos móviles. Por ejemplo:
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
le indica al navegador que configure la ventana de visualización en el ancho del dispositivo con una escala inicial de 1. Este ejemplo también permite el zoom, algo que puede ser conveniente para un sitio web, pero no para una app web. Podríamos evitar el zoom con user-scalable=no
o limitar el escalamiento a un nivel determinado:
<meta name=viewport
content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">
Android extiende la metaetiqueta de viewport, lo que permite a los desarrolladores especificar para qué resolución de pantalla se desarrolló el sitio:
<meta name="viewport" content="target-densitydpi=device-dpi">
Los valores posibles para target-densitydpi
son device-dpi
, high-dpi
, medium-dpi
y low-dpi
.
Si deseas modificar tu página web para diferentes densidades de pantalla, usa la consulta de medios -webkit-device-pixel-ratio
de CSS o la propiedad window.devicePixelRatio
en JavaScript y, luego, establece la metapropiedad target-densitydpi
en device-dpi
. Esto evita que Android realice el escalamiento en tu página web y te permite realizar los ajustes necesarios para cada densidad a través de CSS y JavaScript.
Consulta la documentación de WebView de Android para obtener más información sobre cómo orientarse a resoluciones de dispositivos.
Navegación en pantalla completa
Hay otros dos metavalores que son iOS-Sfic. apple-mobile-web-app-capable
y apple-mobile-web-app-status-bar-style
renderizarán el contenido de la página en el modo de pantalla completa similar a una app y harán que la barra de estado sea translúcida:
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
Para obtener más información sobre todas las opciones de meta disponibles, consulta la documentación de referencia de Safari.
Íconos de la pantalla principal
Los dispositivos iOS y Android también aceptan rel="apple-touch-icon"
(iOS) y rel="apple-touch-icon-precomposed"
(Android) para los vínculos. Crean un ícono llamativo similar a una aplicación en la pantalla de inicio del usuario cuando agrega tu sitio a favoritos:
<link rel="apple-touch-icon"
href="/static/images/identity/HTML5_Badge_64.png" />
<link rel="apple-touch-icon-precomposed"
href="/static/images/identity/HTML5_Badge_64.png" />
Cómo utiliza HTML5rocks las metaetiquetas de dispositivos móviles
Si juntamos todo, este es un fragmento de la sección <head>
de html5rocks:
<head>
...
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link rel="apple-touch-icon"
href="/static/images/identity/HTML5_Badge_64.png" />
<link rel="apple-touch-icon-precomposed"
href="/static/images/identity/HTML5_Badge_64.png" />
...
</head>
Diseño vertical
En pantallas más pequeñas, es mucho más conveniente desplazarse verticalmente que horizontalmente. Para los dispositivos móviles, se prefiere mantener el contenido en un diseño vertical de una sola columna. En html5rocks, usamos consultas de medios CSS3 para crear ese diseño. De nuevo, sin cambiar el lenguaje de marcado.
Optimizaciones para dispositivos móviles
La mayoría de las optimizaciones que realizamos son acciones que se deberían haber hecho en primer lugar. Por ejemplo, reducir la cantidad de solicitudes de red, la compresión de JS/CSS, el gzip (disponible de forma gratuita en App Engine) y minimizar las manipulaciones del DOM. Estas técnicas son prácticas recomendadas comunes, pero, en ocasiones, se pasan por alto cuando se abre un sitio rápidamente.
Ocultar automáticamente la barra de direcciones
Los navegadores para dispositivos móviles carecen del espacio en pantalla de sus contrapartes para computadoras de escritorio. Para empeorar las cosas, en diferentes plataformas, a veces terminas con una gran barra de direcciones en la parte superior de la pantalla, incluso después de que la página termina de cargarse.
Una forma sencilla de solucionarlo es desplazar la página con JavaScript.
Incluso hacerlo por un píxel se encargará de la molesta barra de direcciones.
Para ocultar de forma forzosa la barra de direcciones en html5rocks, adjunté un controlador de eventos onload
al objeto window
y desplacé la página verticalmente un píxel:
// Hides mobile browser's address bar when page is done loading.
window.addEventListener('load', function(e) {
setTimeout(function() { window.scrollTo(0, 1); }, 1);
}, false);
También unimos este objeto de escucha en nuestra variable de plantilla is_mobile
, ya que no es necesario en la versión para computadoras.
Reduce las solicitudes de red y ahorra ancho de banda
Es un hecho conocido que reducir la cantidad de solicitudes HTTP puede mejorar mucho el rendimiento. Los dispositivos móviles limitan aún más la cantidad de conexiones simultáneas que puede establecer el navegador, por lo que los sitios para dispositivos móviles se beneficiarán aún más de la reducción de estas solicitudes adicionales. Además, reducir cada byte es fundamental porque el ancho de banda a menudo es limitado en los teléfonos. Es posible que estés cobrando dinero a los usuarios.
A continuación, se muestran algunos de los enfoques que adoptamos para minimizar las solicitudes de red y reducir el ancho de banda en html5rocks:
Quita los iframes, ya que son lentos. Una gran cantidad de nuestra latencia provenía de widgets de uso compartido de terceros (Buzz, Google Friend Connect, Twitter y Facebook) en las páginas de instructivos. Estas APIs se incluyeron a través de etiquetas
<script>
y crean iframes que disminuyen la velocidad de la página. Se quitaron los widgets para dispositivos móviles.display:none
: En algunos casos, ocultábamos el marcado si no se ajustaba al perfil para dispositivos móviles. Un buen ejemplo son los cuatro cuadros redondeados en la parte superior de la página principal:
No se muestran en el sitio móvil. Es importante recordar que el navegador aún realiza una solicitud para cada ícono, a pesar de que su contenedor está oculto con display:none
. Por lo tanto, no fue suficiente ocultar estos botones. No solo se desperdiciaría el ancho de banda, sino que el usuario ni siquiera vería los resultados de ese desperdicio. La solución fue crear un valor booleano “is_mobile” en nuestra plantilla de Django para omitir condicionalmente secciones de HTML.
Cuando el usuario ve el sitio en un dispositivo inteligente, los botones no se incluyen.
Caché de la aplicación: No solo nos brinda compatibilidad sin conexión, sino que también crea un inicio más rápido.
Compresión de CSS/JS: Usamos el compresor YUI en lugar del compilador Closure principalmente porque controla CSS y JS. Un problema que encontramos fue que las consultas de medios intercaladas (consultas de medios que aparecen dentro de una hoja de estilo) fallaban en el compresor YUI 2.4.2 (consulta este problema). Se solucionó el problema con el uso de YUI Compressor 2.4.4 o versiones posteriores.
Se usan sprites de imágenes CSS siempre que sea posible.
Se usó pngcrush para la compresión de imágenes.
Se usaron dataURIs para imágenes pequeñas. La codificación en base64 agrega alrededor de un 30%o más de tamaño a la imagen, pero guarda la solicitud de red.
Se cargó automáticamente la Búsqueda personalizada de Google con una sola etiqueta de secuencia de comandos en lugar de cargarla de forma dinámica con
google.load()
. La última realiza una solicitud adicional.
<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"> </script>
- Nuestra impresora de código y Modernizr se incluían en todas las páginas, incluso si nunca se usaban. Modernizr es excelente, pero ejecuta muchas pruebas en cada carga. Algunas de esas pruebas realizan modificaciones costosas en el DOM y ralentizan la carga de la página. Ahora, solo incluimos estas bibliotecas en las páginas donde realmente se necesitan. -2 solicitudes :)
Ajustes adicionales de rendimiento:
- Se movió todo el código JS a la parte inferior de la página (siempre que sea posible).
- Se quitaron las etiquetas
<style>
intercaladas. - Búsquedas de DOM almacenadas en caché y manipulaciones del DOM minimizadas: Cada vez que tocas el DOM, el navegador realiza un reprocesamiento. Los reflujos son aún más costosos en un dispositivo móvil.
- Se transfirió al servidor el código del cliente que no se usaba. Específicamente, la verificación para ver si se establece el diseño de navegación de la página actual:
js var lis = document.querySelectorAll('header nav li'); var i = lis.length; while (i--) { var a = lis[i].querySelector('a'); var section = a.getAttribute("data-section"); if (new RegExp(section).test(document.location.href)) { a.className = 'current'; } }
- Se reemplazaron los elementos con anchos fijos por elementos flexibles
width:100%
owidth:auto
.
Caché de aplicación
La versión para dispositivos móviles de html5rocks usa la caché de aplicaciones para acelerar la carga inicial y permite que los usuarios lean contenido sin conexión.
Cuando implementes AppCache en tu sitio, es muy importante que no almacenes en caché el archivo de manifiesto (ya sea de forma explícita en el archivo de manifiesto o de forma implícita con encabezados de control de caché pesados). Si el navegador almacena en caché tu manifiesto, la depuración será una pesadilla. iOS y Android hacen un trabajo particularmente bueno con el almacenamiento en caché de este archivo, pero no proporcionan una manera conveniente de borrar la caché como lo hacen los navegadores para computadoras de escritorio.
Para evitar dicho almacenamiento en caché en nuestro sitio, primero configuramos App Engine para que nunca almacene en caché los archivos de manifiesto:
- url: /(.*\.(appcache|manifest))
static_files: \1
mime_type: text/cache-manifest
upload: (.*\.(appcache|manifest))
expiration: "0s"
En segundo lugar, usamos la API de JS para informar al usuario cuando se terminaba de descargar un manifiesto nuevo. Se le solicita que actualice la página:
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
if (confirm('A new version of this site is available. Load it?')) {
window.location.reload();
}
}
}, false);
Para guardar el tráfico de red, mantén tu manifiesto simple. Es decir, no llames
todas las páginas de tu sitio. Solo debes enumerar los archivos de imágenes, CSS y JavaScript importantes. Lo último que quieres hacer es forzar al navegador para dispositivos móviles a descargar una gran cantidad de recursos en cada actualización del caché de apps. En su lugar, recuerda que el navegador almacenará en caché de forma implícita una página HTML cuando el usuario la visite (y que incluye un atributo <html manifest="...">
).