Introdução
O desenvolvimento para a Web para dispositivos móveis é um tema em alta hoje em dia. Este ano, pela primeira vez, os smartphones venderam mais do que os PCs. Cada vez mais usuários estão usando um dispositivo móvel para navegar na Web, o que significa que está se tornando fundamental para os desenvolvedores otimizar os sites para navegadores para dispositivos móveis.
O campo de batalha dos dispositivos móveis ainda é um território desconhecido para muitos desenvolvedores. Muitas pessoas têm sites legados que negligenciam totalmente os usuários de dispositivos móveis. Em vez disso, o site foi projetado principalmente para navegação em navegadores para computador e não funciona bem em navegadores para dispositivos móveis. Este site (html5rocks.com) não é exceção. No lançamento, não nos esforçamos muito para criar uma versão para dispositivos móveis do site.
Como criar um html5rocks.com compatível com dispositivos móveis
Como exercício, achei que seria interessante pegar o html5rocks (um site HTML5 existente) e adicionar uma versão compatível com dispositivos móveis. Eu estava preocupado principalmente com o mínimo de trabalho necessário para segmentar smartphones. O objetivo do meu exercício não era criar um site para dispositivos móveis totalmente novo e manter duas bases de código. Isso levaria uma eternidade e seria uma grande perda de tempo. Já tínhamos definido a estrutura do site (marcação). Temos uma aparência (CSS). A funcionalidade principal (JS) estava lá. O fato é que muitos sites estão no mesmo barco.
Este artigo analisa como criamos uma versão para dispositivos móveis do html5rocks otimizada para dispositivos Android e iOS. Basta carregar html5rocks.com em um dispositivo que seja compatível com um desses sistemas operacionais para ver a diferença. Não há redirecionamentos para m.html5rocks.com ou outras bobagens desse tipo. Você recebe o html5rocks como ele é, com o benefício adicional de algo que tem uma ótima aparência e funciona bem em um dispositivo móvel.
Consultas de mídia CSS
O HTML4 e o CSS2 já oferecem suporte a folhas de estilo dependentes de mídia há algum tempo. Exemplo:
<link rel="stylesheet" media="print" href="printer.css">
segmentaria dispositivos de impressão e forneceria um estilo específico para o conteúdo da página quando ela fosse impressa. O CSS3 leva a ideia de tipos de mídia um passo adiante e melhora a funcionalidade deles com consultas de mídia. As consultas de mídia ampliam a utilidade dos tipos de mídia, permitindo uma rotulagem mais precisa das folhas de estilo. Isso permite que a apresentação do conteúdo seja personalizada para uma variedade específica de dispositivos de saída sem precisar mudar o conteúdo em si. Parece perfeito para um layout que precisa ser modificado.
É possível usar consultas de mídia no atributo media das folhas de estilo externas para segmentar largura da tela, largura do dispositivo, orientação etc. Para ver a lista completa, consulte a especificação de consulta de mídia do W3C.
Segmentação de tamanhos de tela
No exemplo a seguir, phone.css se aplica a dispositivos
que o navegador considera "portáteis" ou dispositivos com telas de até 320 px de largura.
<link rel='stylesheet'
media='handheld, only screen and (max-device-width: 320px)' href='phone.css'>
Prefixar uma consulta de mídia com a palavra-chave "only" faz com que
os navegadores não compatíveis com CSS3 ignorem a regra.
O exemplo a seguir segmentaria tamanhos de tela entre 641 px e 800 px:
<link rel='stylesheet'
media='only screen and (min-width: 641px) and (max-width: 800px)' href='ipad.css'>
As consultas de mídia também podem aparecer em tags <style> inline. O exemplo a seguir
segmenta os tipos de mídia all quando estão na orientação retrato:
<style>
@media only all and (orientation: portrait) { ... }
</style>
media="handheld"
Precisamos parar um minuto e conversar sobre media="handheld".
Na verdade, o Android e o iOS ignoram media="handheld". A alegação é que os usuários vão
sentir falta do conteúdo de alta qualidade fornecido por folhas de estilo direcionadas a media="screen",
e os desenvolvedores têm menos probabilidade de manter uma versão media="handheld"
de qualidade inferior. Por isso, como parte do lema "Web completa", a maioria dos navegadores de smartphones modernos
simplesmente ignora as folhas de estilo para dispositivos portáteis.
Seria ideal usar esse recurso para segmentar dispositivos móveis, mas vários navegadores o implementaram de maneiras diferentes:
- Alguns leem apenas a folha de estilo somente leitura.
- Alguns leem apenas a folha de estilo portátil, se houver, mas usam a folha de estilo de tela como padrão.
- Alguns leem as folhas de estilo de dispositivos portáteis e de tela.
- Alguns leem apenas a folha de estilo somente leitura da tela.
O Opera Mini não ignora media="handheld". O truque para fazer com que o Windows Mobile reconheça media="handheld" é colocar em maiúsculas o valor do atributo de mídia para a folha de estilo da tela:
<!-- media="handheld" trick for Windows Mobile -->
<link rel="stylesheet" href="screen.css" media="Screen">
<link rel="stylesheet" href="mobile.css" media="handheld">
Como o html5rocks usa consultas de mídia
As consultas de mídia são muito usadas no html5rocks para dispositivos móveis. Eles nos permitiram ajustar o layout sem precisar fazer mudanças significativas na marcação do modelo do Django… uma verdadeira salvação! Além disso, o suporte em vários navegadores é muito bom.
No <head> de cada página, você vai encontrar as seguintes folhas 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 sempre definiu a aparência principal do
html5rocks.com, mas agora estamos aplicando novos estilos (mobile.css)
para larguras de tela abaixo de 800 px. A consulta de mídia abrange smartphones (~320 px) e o iPad (~768 px).
O efeito: estamos substituindo gradualmente os estilos em base.css (somente quando necessário)
para melhorar a aparência em dispositivos móveis.
Algumas das mudanças de estilo que o mobile.css aplica:
- Reduz o espaço em branco/preenchimento extra no site. Telas pequenas significam que o espaço é valioso.
- Remove
:hoverestados. Eles nunca serão mostrados em dispositivos touchscreen. - Ajusta o layout para uma única coluna. Saiba mais sobre isso mais adiante.
- Remove o
box-shadowao redor da div do contêiner principal do site. Sombras de caixa grandes reduzem o desempenho da página. - Usou o modelo de caixa flexível CSS
box-ordinal-grouppara mudar a ordem de cada seção na página inicial. Você vai notar que "LEARN BY MAJOR HTML5 FEATURE GROUPS" aparece antes da seção "TUTORIALS" na página inicial, mas depois dela na versão para dispositivos móveis. Essa ordem fazia mais sentido para dispositivos móveis e não exigia mudanças na marcação. O CSS flexbox é o máximo! - Remove as mudanças de
opacity. Mudar os valores Alfa afeta o desempenho em dispositivos móveis.
Metatags para dispositivos móveis
O WebKit para dispositivos móveis oferece alguns recursos que melhoram a experiência de navegação dos usuários em determinados dispositivos.
Configurações da janela de visualização
A primeira metaconfiguração (e uma que você vai usar com mais frequência) é a propriedade da janela de visualização. Definir uma janela de visualização informa ao navegador como o conteúdo deve se ajustar à tela do dispositivo e que o site é otimizado para dispositivos móveis. Exemplo:
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
informa ao navegador para definir a janela de visualização como a largura do dispositivo com uma escala inicial de 1. Esse exemplo também permite o zoom, algo que pode ser desejável
para um site, mas não para um app da Web. É possível impedir o zoom com user-scalable=no
ou limitar o dimensionamento a um determinado nível:
<meta name=viewport
content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">
O Android estende a metatag viewport, permitindo que os desenvolvedores especifiquem para qual resolução de tela o site foi desenvolvido:
<meta name="viewport" content="target-densitydpi=device-dpi">
Os valores possíveis para target-densitydpi são device-dpi, high-dpi,
medium-dpi, low-dpi.
Se você quiser modificar sua página da Web para diferentes densidades de tela, use a
consulta de mídia CSS -webkit-device-pixel-ratio e/ou a propriedade window.devicePixelRatio
em JavaScript e defina a propriedade meta target-densitydpi
como device-dpi. Isso impede que o Android faça o escalonamento na sua página da Web
e permite que você faça os ajustes necessários para cada densidade, usando CSS e JavaScript.
Consulte a documentação do WebView do Android para mais informações sobre como segmentar resoluções de dispositivos de destino.
Navegação em tela cheia
Há dois outros valores meta que são específicos do iOS. apple-mobile-web-app-capable
e apple-mobile-web-app-status-bar-style vão renderizar o conteúdo da página
no modo de tela cheia, como em um app, e deixar a barra de status translúcida:
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
Para mais informações sobre todas as opções de metadados disponíveis, consulte a documentação de referência do Safari.
Ícones da tela inicial
Os dispositivos iOS e Android também aceitam um
rel="apple-touch-icon" (iOS) e rel="apple-touch-icon-precomposed" (Android)
para links. Eles criam um ícone chamativo, semelhante ao de um app, na tela inicial do usuário quando ele adiciona seu site aos 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" />
Como o html5rocks usa metatags para dispositivos móveis
Para juntar tudo, veja um snippet da seção <head>
do 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>
Layout vertical
Em telas menores, é muito mais conveniente rolar verticalmente do que horizontalmente. Para dispositivos móveis, é melhor manter o conteúdo em um layout vertical de coluna única. Para o html5rocks, usamos consultas de mídia CSS3 para criar esse layout. Novamente, sem mudar a marcação.
Otimizações para dispositivos móveis
A maioria das otimizações que fizemos são coisas que deveriam ter sido feitas em primeiro lugar. Por exemplo, reduzir o número de solicitações de rede, compactar JS/CSS, usar gzipping (incluído no App Engine) e minimizar as manipulações de DOM. Essas técnicas são práticas recomendadas comuns, mas às vezes são ignoradas quando um site é lançado às pressas.
Ocultar automaticamente a barra de endereço
Os navegadores para dispositivos móveis não têm o espaço de tela que os computadores têm. Para piorar, em plataformas diferentes, às vezes você acaba com uma barra de endereço enorme na parte de cima da tela, mesmo depois que a página é carregada.
Uma maneira fácil de lidar com isso é rolar a página usando JavaScript.
Mesmo que seja por um pixel, isso vai resolver o problema da barra de endereço.
Para forçar a ocultação da barra de endereço no html5rocks, anexei um manipulador de eventos onload ao objeto window e rolei a página verticalmente em um pixel:
// Hides mobile browser's address bar when page is done loading.
window.addEventListener('load', function(e) {
setTimeout(function() { window.scrollTo(0, 1); }, 1);
}, false);
Também incluímos esse listener na variável de modelo is_mobile,
já que ele não é necessário no computador.
Reduzir as solicitações de rede e economizar largura de banda
É um fato conhecido que reduzir o número de solicitações HTTP pode melhorar muito o desempenho. Os dispositivos móveis limitam ainda mais o número de conexões simultâneas que o navegador pode fazer. Portanto, os sites para dispositivos móveis vão se beneficiar ainda mais com a redução dessas solicitações externas. Além disso, reduzir cada byte é fundamental, porque a largura de banda costuma ser limitada em smartphones. Você pode estar fazendo os usuários gastarem dinheiro!
Confira a seguir algumas das abordagens que adotamos para minimizar as solicitações de rede e reduzir a largura de banda no html5rocks:
Remova os iframes, eles são lentos. Uma grande parte da nossa latência veio de widgets de compartilhamento de terceiros (Buzz, Google Friend Connect, Twitter, Facebook) nas páginas de tutoriais. Essas APIs foram incluídas por tags
<script>e criam iframes que diminuem a velocidade da página. Os widgets foram removidos para dispositivos móveis.display:none: em alguns casos, ocultávamos a marcação se ela não se ajustasse ao perfil para dispositivos móveis. Um bom exemplo foram as quatro caixas arredondadas na parte de cima da página inicial:
Eles não estão no site para dispositivos móveis. É importante lembrar que
o navegador ainda faz uma solicitação para cada ícone, mesmo que o contêiner
esteja oculto com display:none. Portanto, não bastava apenas ocultar esses botões. Isso não só desperdiçaria largura de banda, mas o usuário nem
veria os frutos desse desperdício. A solução foi criar um booleano "is_mobile" no modelo do Django para omitir condicionalmente seções de HTML.
Quando o usuário está acessando o site em um dispositivo inteligente, os botões são omitidos.
Cache de aplicativo: além de oferecer suporte off-line, ele também acelera a inicialização.
Compactação de CSS/JS: estamos usando o compressor YUI em vez do compilador Closure, principalmente porque ele lida com CSS e JS. Um problema que encontramos foi que as consultas de mídia inline (consultas de mídia que aparecem dentro de uma folha de estilo) foram rejeitadas no compressor YUI 2.4.2 (consulte este problema). O uso do YUI Compressor 2.4.4+ corrigiu o problema.
Usar sprites de imagem CSS sempre que possível.
Usamos o pngcrush para compactação de imagens.
Usou dataURIs para imagens pequenas. A codificação em base64 adiciona mais de 30%de tamanho à imagem, mas salva a solicitação de rede.
A Pesquisa personalizada do Google foi carregada automaticamente usando uma única tag script em vez de ser carregada dinamicamente com
google.load(). A segunda faz uma solicitação extra.
<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"> </script>
- Nossos estilos de formatação de código e o Modernizr estavam sendo incluídos em todas as páginas, mesmo que nunca fossem usados. O Modernizr é ótimo, mas ele executa vários testes em cada carregamento. Alguns desses testes fazem modificações caras no DOM e deixam o carregamento de página mais lento. Agora, incluímos essas bibliotecas apenas nas páginas em que elas são realmente necessárias. -2 solicitações :)
Outros ajustes de performance:
- Movemos todo o JS para a parte de baixo da página (quando possível).
- Remoção de tags
<style>inline. - Pesquisas de DOM em cache e manipulações de DOM minimizadas: sempre que você toca no DOM, o navegador realiza um refluxo. Os refluxos são ainda mais caros em um dispositivo móvel.
- Transferência de código inútil do lado do cliente para o servidor. Especificamente, a verificação
para definir o estilo de navegação da página atual:
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'; } } - Os elementos com larguras fixas foram substituídos por
width:100%ouwidth:autofluidos.
Cache do aplicativo
A versão para dispositivos móveis do html5rocks usa o cache de aplicativos para acelerar o carregamento inicial e permitir que os usuários leiam o conteúdo off-line.
Ao implementar o AppCache no seu site, é muito importante que você não armazene em cache o arquivo de manifesto (explicitamente no próprio arquivo de manifesto ou implicitamente com cabeçalhos pesados de controle de cache). Se o manifesto for armazenado em cache pelo navegador, será um pesadelo depurar. O iOS e o Android fazem um trabalho particularmente bom de armazenamento em cache desse arquivo, mas não oferecem uma maneira conveniente de limpar o cache como os navegadores para computador.
Para evitar esse armazenamento em cache no nosso site, primeiro configuramos o App Engine para nunca armazenar em cache arquivos de manifesto:
- url: /(.*\.(appcache|manifest))
static_files: \1
mime_type: text/cache-manifest
upload: (.*\.(appcache|manifest))
expiration: "0s"
Em segundo lugar, usamos a API JS para informar ao usuário quando um novo manifesto terminou de ser baixado. Eles recebem uma solicitação para atualizar a 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 economizar tráfego de rede, mantenha o manifesto simples. Ou seja, não
mencione todas as páginas do site. Basta listar as imagens, o CSS e os arquivos JavaScript importantes. A última coisa que você quer fazer é forçar o navegador para dispositivos móveis
a baixar um grande número de recursos em cada atualização do appcache. Em vez disso, lembre-se
de que o navegador vai armazenar em cache implicitamente uma página HTML quando o usuário visitar
(e ela incluir um atributo <html manifest="...">).