Introducción
El desarrollo para la Web móvil es un tema candente en estos días. Este año, por primera vez, los smartphones superaron en ventas a las PCs. 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 para dispositivos móviles.
El campo de batalla de los "dispositivos móviles" sigue siendo un territorio inexplorado para una gran cantidad de desarrolladores. Muchas personas tienen sitios heredados existentes que descuidan por completo a los usuarios de dispositivos móviles. 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 la excepción. En el lanzamiento, no nos esforzamos mucho en crear una versión para dispositivos móviles del sitio.
Cómo crear una versión de html5rocks.com optimizada 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. Me preocupaba principalmente la cantidad mínima de trabajo necesaria para orientar los anuncios a smartphones. El objetivo de mi ejercicio no era crear un sitio para dispositivos móviles completamente nuevo y mantener dos bases de código. Eso habría llevado una eternidad y habría sido una gran pérdida de tiempo. Ya habíamos definido la estructura del sitio (lenguaje de marcado). Teníamos un aspecto y un estilo (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 analiza 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 otras tonterías de ese tipo. Obtienes html5rocks tal como está, con el beneficio adicional de algo que se ve bien y funciona correctamente en un dispositivo móvil.
Consultas de medios de CSS
HTML4 y CSS2 admiten hojas de estilo dependientes de los medios desde hace algún tiempo. Por ejemplo:
<link rel="stylesheet" media="print" href="printer.css">
se orientaría a los 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 un etiquetado más preciso de las hojas de estilo. 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 por ancho de pantalla, ancho de dispositivo, orientación, etc. Para obtener la lista completa, consulta la especificación de consultas de medios de 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 dispositivos con pantallas de un ancho de <= 320 px.
<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 cumplen con CSS3 ignorarán la regla.
La siguiente línea segmentaría los 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. El siguiente código se orienta a los tipos de medios all cuando se encuentran en orientación vertical:
<style>
@media only all and (orientation: portrait) { ... }
</style>
media="handheld"
Debemos detenernos un minuto y analizar media="handheld".
De hecho, Android y iOS ignoran media="handheld". Se afirma que los usuarios se perderán el contenido de alta calidad que proporcionan las hojas de estilo orientadas a media="screen" y que es menos probable que los desarrolladores mantengan una versión de menor calidad para media="handheld". 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 de mano.
Lo ideal sería usar esta función para segmentar 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 existe, pero, de lo contrario, usan la hoja de estilo de pantalla de forma predeterminada.
- Algunos leen tanto la hoja de estilo para dispositivos de mano como 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 que Windows Mobile reconozca media="handheld" es escribir en mayúscula el valor del atributo multimedia de 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 las 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 realizar cambios significativos en nuestro lenguaje de marcado de plantillas de Django, lo que nos salvó la vida. Además, su compatibilidad con los distintos navegadores es bastante buena.
En el elemento <head> de cada página, verás las siguientes hojas de estilo:
<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 smartphones (~320 px) y el iPad (~768 px).
El efecto: estamos anulando de forma incremental los estilos en base.css (solo según sea necesario) para que todo se vea mejor en dispositivos móviles.
Estos son algunos de los cambios de estilo que aplica mobile.css:
- Reduce el espacio en blanco o el relleno adicional en todo el sitio. Las pantallas pequeñas implican que el espacio es limitado.
- Quita los estados
:hover. Nunca se verán en dispositivos táctiles. - Ajusta el diseño para que sea de una sola columna. Veremos eso más adelante.
- Quita el
box-shadowalrededor del div del contenedor principal del sitio. Las sombras de cuadros grandes reducen el rendimiento de la página. - Se usó el modelo de caja flexible de CSS
box-ordinal-grouppara cambiar el orden de cada sección de la página principal. Notarás que la sección "LEARN BY MAJOR HTML5 FEATURE GROUPS" aparece antes de la sección "TUTORIALS" 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! - Se quitarán
opacitycambios. Cambiar los valores alfa afecta el rendimiento en dispositivos móviles.
Metaetiquetas para dispositivos móviles
Mobile WebKit admite algunas funciones que brindan a los usuarios una mejor experiencia de navegación en ciertos dispositivos.
Configuración del viewport
El primer parámetro de configuración de meta (y el que usarás con más frecuencia) es la propiedad viewport. Establecer un viewport le indica al navegador cómo debe ajustarse el contenido a 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 el viewport según el ancho del dispositivo con una escala inicial de 1. Este ejemplo también permite el zoom, algo que puede ser deseable 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 la vista del puerto, ya que permite que los desarrolladores especifiquen la resolución de pantalla para la que 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 quieres modificar tu página web para diferentes densidades de pantalla, usa la consulta de medios CSS -webkit-device-pixel-ratio 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 segmentar resoluciones de dispositivos de destino.
Navegación en pantalla completa
Existen otros dos valores de metadatos que son específicos de iOS. 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 al de 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 un rel="apple-touch-icon" (iOS) y un rel="apple-touch-icon-precomposed" (Android) para los vínculos. Estos crean un ícono llamativo similar al de una app en la pantalla principal 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 html5rocks usa las metaetiquetas para dispositivos móviles
Para resumir, aquí tienes 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 de forma vertical que horizontal. 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 de 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 hicimos son cosas que deberían haberse hecho en primer lugar. Por ejemplo, reducir la cantidad de solicitudes de red, comprimir JS/CSS, usar gzip (se incluye de forma gratuita en App Engine) y minimizar las manipulaciones del DOM. Estas técnicas son prácticas recomendadas comunes, pero, a veces, se pasan por alto cuando se publica un sitio con prisa.
Ocultar automáticamente la barra de direcciones
Los navegadores para dispositivos móviles carecen del espacio de pantalla de su contraparte 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 se carga la página.
Una forma sencilla de solucionarlo es desplazar la página con JavaScript.
Incluso si lo haces por un píxel, se solucionará el problema de la molesta barra de direcciones.
Para ocultar de forma forzada 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 computadoras de escritorio.
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 realizar el navegador, por lo que los sitios para dispositivos móviles se beneficiarán aún más si se reducen estas solicitudes externas. Además, reducir cada byte es fundamental, ya que el ancho de banda suele ser limitado en los teléfonos. Es posible que les estés haciendo gastar dinero a los usuarios.
A continuación, se indican 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 parte de nuestra latencia provino 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 lenguaje de marcado si no se ajustaba al perfil para dispositivos móviles. Un buen ejemplo fueron los cuatro cuadros redondeados en la parte superior de la página principal:
No aparecen en el sitio para dispositivos móviles. Es importante recordar que
el navegador sigue realizando una solicitud para cada ícono, a pesar de que su contenedor
esté oculto con display:none. Por lo tanto, no bastaba con ocultar
estos botones. No solo se desperdiciaría ancho de banda, sino que el usuario ni siquiera vería los frutos de ese ancho de banda desperdiciado. La solución fue crear un booleano "is_mobile" en nuestra plantilla de Django para omitir de forma condicional secciones de HTML.
Cuando el usuario ve el sitio en un dispositivo inteligente, los botones se omiten.
Caché de la aplicación: No solo nos brinda asistencia sin conexión, sino que también crea un inicio más rápido.
Compresión de CSS/JS: Usamos el compresor de YUI en lugar del compilador de Closure, principalmente porque controla CSS y JS. Un problema que tuvimos fue que las consultas de medios intercaladas (las que aparecen dentro de una hoja de estilo) se bloquearon en YUI Compressor 2.4.2 (consulta este problema). El uso de YUI Compressor 2.4.4 o una versión posterior solucionó el problema.
Se usaron objetos CSS de imagen siempre que fue 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 un ~30%más de tamaño a la imagen, pero ahorra 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(). Esta última realiza una solicitud adicional.
<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"> </script>
- Nuestra impresión con formato estilístico de código y Modernizr se incluían en cada página, 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 de rendimiento adicionales:
- Se movió todo el JS a la parte inferior de la página (cuando fue posible).
- Se quitaron las etiquetas
<style>intercaladas. - Búsquedas de DOM almacenadas en caché y manipulaciones de DOM minimizadas: Cada vez que tocas el DOM, el navegador realiza un reajuste. Los cambios de posición son aún más costosos en un dispositivo móvil.
- Se descargó el código del cliente que generaba desperdicio en el servidor. Específicamente, la verificación para ver el estilo 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'; } } - Los elementos con anchos fijos se reemplazaron por
width:100%owidth:autofluidos.
Caché de aplicación
La versión para dispositivos móviles de html5rocks usa la caché de la aplicación para acelerar la carga inicial y permitir 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, será una pesadilla depurarlo. iOS y Android hacen un trabajo particularmente bueno para almacenar en caché este archivo, pero no proporcionan una forma conveniente de vaciar la caché como lo hacen los navegadores para computadoras.
Para evitar que se almacenen en caché los archivos de manifiesto de nuestro sitio, primero configuramos App Engine para que nunca los almacene en caché:
- 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 terminó de descargar un manifiesto nuevo. Se les solicita que actualicen 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 ahorrar tráfico de red, mantén tu manifiesto simple. Es decir, no incluyas todas las páginas de tu sitio. Solo enumera las imágenes, los archivos 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 de la caché de la app. En cambio, 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="...">).