Introducción
Desarrollar para la Web móvil es un tema muy popular en la actualidad. 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 "móvil" sigue siendo un territorio desconocido para una gran cantidad de desarrolladores. Muchas personas tienen sitios heredados que no tienen en cuenta 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 una excepción. En el lanzamiento, no dedicamos mucho esfuerzo a la versión para dispositivos móviles del sitio.
Cómo crear un 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 (lenguaje de 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 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 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étera. 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 segmentaría para 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 contenido multimedia también pueden aparecer dentro de etiquetas <style>
intercaladas. Los siguientes objetivos se orientan a los tipos de medios all
en orientación vertical:
<style>
@media only all and (orientation: portrait) { ... }
</style>
media="handheld"
Debemos detenernos un momento y hablar sobre media="handheld"
.
La verdad es que Android y iOS ignoran media="handheld"
. El argumento es que los usuarios 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 que Windows Mobile reconozca media="handheld"
es mayúsculas el valor del atributo media 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: anulamos de forma incremental los estilos en base.css
(solo cuando es necesario) para que todo se vea 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 marcado. ¡Flexbox de CSS es lo mejor! - Quita los cambios de
opacity
. Cambiar los valores de alfa afecta el rendimiento en dispositivos móviles.
Metaetiquetas para dispositivos móviles
Mobile WebKit admite algunas funciones que les 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 establezca el viewport 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 del 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 segmentar 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. Estos crean un ícono llamativo similar a 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 metaetiquetas para 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. Una vez más, sin cambiar el 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, a veces, se pasan por alto cuando se apresura el lanzamiento de un sitio.
Ocultar automáticamente la barra de direcciones
Los navegadores para dispositivos móviles carecen del espacio en pantalla de sus contrapartes para computadoras. 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, es fundamental reducir cada byte porque el ancho de banda suele ser limitado en los teléfonos. Es posible que estés cobrando dinero a los usuarios.
Los siguientes son 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 están 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 de DOM minimizadas: Cada vez que tocas el DOM, el navegador realiza un reabastecimiento. 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'; } }
- Los elementos con anchos fijos se reemplazaron por
width:100%
owidth:auto
fluidos.
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 ese almacenamiento en caché para nuestro sitio, primero configuramos App Engine para que nunca almacenen 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 termina 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 ahorrar tráfico de red, mantén el manifiesto simple. Es decir, no hagas referencia a 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 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="...">
).