はじめに
最近はモバイルウェブ向けの開発がホットトピックとなっています。今年は初めて、スマートフォンがパソコンを完売しました。モバイル デバイスを使用してウェブを閲覧するユーザーが増えています。そのため、デベロッパーはモバイル ブラウザ向けにサイトを最適化することが重要になってきています。
多くのデベロッパーにとって、「モバイル」という戦いはまだ未知の領域です。多くの方は、モバイル ユーザーを完全に無視した古いサイトを持っています。このサイトは主にパソコンでのブラウジング用に設計されており、モバイル ブラウザでは機能しません。このサイト(html5rocks.com)も例外ではありません。リリース時点では、モバイル版のサイトについてはほとんど労力を割いていません。
モバイル向けの html5rocks.com の作成
まずは html5rocks(既存の HTML5 サイト)を、モバイル フレンドリーなバージョンで拡張してみるのがよいと考えた演習です。スマートフォンをターゲットとするのに必要な最低限の作業量が 主な関心事でした今回の演習の目標は、まったく新しいモバイルサイトを作成し、2 つのコードベースを管理することでした。それは永遠に続くことでしょうし、大いに時間の無駄になっていたでしょう。サイトの構造(マークアップ)はすでに定義しています。デザイン(CSS)そこにはコア機能(JS)がありました。重要な点は、多くの施設がこの同じボートに載っているということです。
この記事では、Android および iOS デバイス向けに最適化された html5rocks のモバイル版を、Google がどのように作成したかについて説明します。これらの OS のいずれかをサポートするデバイスで html5rocks.com を読み込み、違いを確認してみましょう。m.html5rocks.com など、このような内容にリダイレクトすることは一切ありません。html5rocks はそのまま ... で、モバイル デバイスでの見栄えが良く、動作に問題があるというメリットもあります。
CSS メディアクエリ
HTML4 と CSS2 では、しばらく前からメディア依存のスタイルシートがサポートされています。例:
<link rel="stylesheet" media="print" href="printer.css">
印刷デバイスを対象とし、印刷時にページ コンテンツのスタイルを設定します。CSS3 は、メディアタイプの概念をさらに一歩進めて、メディアクエリによって機能を強化します。メディアクエリを使用すると、スタイルシートのラベル付けの精度を高めて、メディアタイプの有用性を高めることができます。これにより、コンテンツ自体を変更することなく、特定の範囲の出力デバイスに合わせてコンテンツの表示をカスタマイズできます。これは、既存のレイアウトの変更が必要なケースに最適です。
外部スタイルシートの media
属性でメディアクエリを使用して、画面の幅、デバイスの幅、画面の向きなどをターゲットに設定できます。完全なリストについては、W3C メディアクエリの仕様をご覧ください。
ターゲットとする画面サイズ
次の例では、phone.css
は、ブラウザが「ハンドヘルド」と見なすデバイス、または画面幅が 320 ピクセル以下のデバイスに適用されます。
<link rel='stylesheet'
media='handheld, only screen and (max-device-width: 320px)' href='phone.css'>
メディアクエリの先頭にキーワード「only
」を付けると、CSS3 非準拠のブラウザでこのルールが無視されます。
次の例では、641 ~ 800 ピクセルの画面サイズがターゲットになります。
<link rel='stylesheet'
media='only screen and (min-width: 641px) and (max-width: 800px)' href='ipad.css'>
メディアクエリはインライン <style>
タグ内に含めることもできます。以下は、縦向きのときに all
メディアタイプをターゲットにしています。
<style>
@media only all and (orientation: portrait) { ... }
</style>
media="handheld"
少し立ち止まって、media="handheld"
について話し合う必要があります。実際、Android と iOS では media="handheld"
は無視されます。この主張は、media="screen"
をターゲットとするスタイルシートによって提供されるハイエンド コンテンツをユーザーが見逃し、デベロッパーが低品質の media="handheld"
バージョンを維持する可能性が低いという主張です。そのため、「完全なウェブ」というモットーの一環として、最新のスマートフォン ブラウザのほとんどは、ハンドヘルド スタイルシートを無視しています。
この機能を使用してモバイル デバイスをターゲットに設定することが理想的ですが、実装方法はブラウザによって異なります。
- ハンドヘルド スタイル シートのみを読み取るコードもあります。
- ハンドヘルド スタイル シートが存在する場合はそれを読み取り、それ以外の場合はスクリーン スタイル シートをデフォルトとするものもあります。
- ハンドヘルド スタイル シートとスクリーン スタイル シートの両方を読み取るコードもあります。
- スクリーン スタイルシートのみを読み取っているものもあります。
Opera Mini が media="handheld"
を無視しないようになりました。Windows Mobile で media="handheld"
が認識されるようにするには、次のように、画面スタイルシートのメディア属性値を大文字で表記する方法があります。
<!-- media="handheld" trick for Windows Mobile -->
<link rel="stylesheet" href="screen.css" media="Screen">
<link rel="stylesheet" href="mobile.css" media="handheld">
html5rocks でのメディアクエリの使用方法
メディアクエリはモバイルの html5rocks で頻繁に使用されています。これにより、Django テンプレート マークアップに大きな変更を加えることなく、レイアウトを調整できるようになりました。これはまさに救世主です。さらに、さまざまなブラウザでのサポートも非常に優れています。
各ページの <head>
には、次のスタイルシートがあります。
<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
は html5rocks.com のメインのデザインを常に定義してきましたが、今回は 800 px 未満の画面幅に対して新しいスタイル(mobile.css
)を適用します。そのメディアクエリは、スマートフォン(約 320 ピクセル)と iPad(約 768 ピクセル)に対応しています。結果: base.css
のスタイルを(必要な場合にのみ)段階的にオーバーライドして、モバイルで見やすくします。
mobile.css
によって適用されるスタイル変更の一部を以下に示します。
- サイト全体の余分なスペースやパディングを減らします。画面が小さいということは、スペースが限られていることを意味します。
:hover
状態を削除しました。タッチデバイスでは表示されません。- レイアウトを 1 列になるように調整します。詳しくは後で説明します。
- サイトのメイン コンテナ div の周囲の
box-shadow
を削除しました。大きなボックス シャドウがあると、ページのパフォーマンスが低下します。 - CSS Flex ボックス モデル
box-ordinal-group
を使用して、ホームページの各セクションの順序を変更しました。 モバイル版では、ホームページの「チュートリアル」セクションの前に「HTML5 の主な機能グループ別に学習する」と表示されます。この順序はモバイルにとって理にかなっています。マークアップの変更は必要ありませんでした。CSS Flexbox opacity
件の変更を削除します。アルファ値を変更すると、モバイルのパフォーマンスに影響します。
モバイル メタタグ
Mobile WebKit は、特定のデバイスでより快適にブラウジングできるようにする各種機能をサポートしています。
ビューポートの設定
最初のメタ設定(最も頻繁に使用するメタ設定)は、ビューポート プロパティです。ビューポートを設定すると、コンテンツがデバイスの画面にどのように収まるかがブラウザに通知され、サイトがモバイル向けに最適化されていることがブラウザに通知されます。例:
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
初期スケールを 1 として、デバイスの幅にビューポートを設定するようブラウザに指示します。この例ではズームも可能になっていますが、ウェブサイトでは望ましいですが、ウェブアプリでは望ましくありません。user-scalable=no
を使用してズームを防ぐか、スケーリングを特定のレベルに制限することができます。
<meta name=viewport
content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">
Android では、ビューポート メタタグを拡張して、デベロッパーがサイト開発時の画面解像度を指定できるようにしています。
<meta name="viewport" content="target-densitydpi=device-dpi">
target-densitydpi
に有効な値は device-dpi
、high-dpi
、medium-dpi
、low-dpi
です。
さまざまな画面密度向けにウェブページを変更する場合は、-webkit-device-pixel-ratio
CSS メディアクエリか JavaScript の window.devicePixelRatio
プロパティ(あるいはその両方)を使用し、target-densitydpi
メタプロパティを device-dpi
に設定します。これにより、Android はウェブページでスケーリングを行わなくなり、CSS と JavaScript を使用して各密度に必要な調整を行えるようになります。
デバイスの解像度をターゲットに設定する方法について詳しくは、Android の WebView のドキュメントをご覧ください。
全画面表示
他にも iOS 用の 2 つのメタ値があります。apple-mobile-web-app-capable
と apple-mobile-web-app-status-bar-style
は、アプリのような全画面モードでページ コンテンツをレンダリングし、ステータスバーを半透明にします。
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
使用可能なすべてのメタ オプションの詳細については、Safari リファレンス ドキュメントをご覧ください。
ホーム画面のアイコン
iOS デバイスと Android デバイスでは、リンクに rel="apple-touch-icon"
(iOS)と rel="apple-touch-icon-precomposed"
(Android)も使用できます。ユーザーがサイトをブックマークすると、ホーム画面にアプリのような派手なアイコンが作成されます。
<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" />
html5rocks でモバイル メタタグが使用されている仕組み
すべてをまとめると、html5rocks の <head>
セクションのスニペットは次のようになります。
<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>
縦長レイアウト
小さい画面では、水平方向よりも垂直方向にスクロールする方がはるかに便利です。 モバイルではコンテンツを 1 列に格納する縦向きのレイアウトが推奨されます。html5rocks では、CSS3 メディアクエリを使用してそのようなレイアウトを作成しました。ここでもマークアップを変更しません。
モバイル最適化
最適化のほとんどは、そもそも最初に行われるべきものです。たとえば、ネットワーク リクエスト数の削減、JS/CSS 圧縮、gzip 圧縮(App Engine で無料で利用可能)、DOM 操作の最小化などがあります。これらは一般的なベスト プラクティスですが、サイトを急いでいるときに見落とされることがあります。
アドレスバーを自動的に非表示にする
モバイル ブラウザには、パソコン用の画面ほど画面スペースがありません。さらに、プラットフォームによっては、ページの読み込みが完了した後も、画面上部に古いアドレスバーが表示されることがあります。
これに対処する簡単な方法の一つは、JavaScript を使用してページをスクロールすることです。これを 1 ピクセルずつ行うと、アドレスバーへの対処が不要になります。
html5rocks でアドレスバーを強制的に非表示にするために、onload
イベント ハンドラを window
オブジェクトにアタッチし、ページを垂直方向に 1 ピクセルスクロールしました。
// Hides mobile browser's address bar when page is done loading.
window.addEventListener('load', function(e) {
setTimeout(function() { window.scrollTo(0, 1); }, 1);
}, false);
また、このリスナーは is_mobile
テンプレート変数でラップしました。デスクトップでは不要なためです。
ネットワーク リクエストを減らして帯域幅を節約する
HTTP リクエストの数を減らすとパフォーマンスが大幅に向上することがわかっています。モバイル デバイスではブラウザが同時に接続できる数がさらに制限されるため、モバイルサイトではこのような無関係なリクエストを削減することによるメリットがいっそう向上します。さらに、スマートフォンでは帯域幅が制限されることが多いため、1 バイトずつ削減することが重要です。ユーザーの費用が増加する可能性があります。
html5rocks でのネットワーク リクエストを最小限に抑えて帯域幅を減らすために採用したアプローチを、次にいくつか紹介します。
iframe を削除する - iframe は低速です。レイテンシの大部分は、チュートリアル ページのサードパーティ共有ウィジェット(Buzz、Google Friend Connect、Twitter、Facebook)に由来しています。これらの API は
<script>
タグを介して含まれており、iframe を作成してページの速度を低下させます。ウィジェットはモバイル用に削除されました。display:none
- 特定のケースで、モバイル プロファイルに合わないマークアップが非表示になっていました。その好例が、ホームページの上部に表示される 4 つの丸いボックスです。
モバイルサイトにない。コンテナが display:none
で非表示になっている場合でも、ブラウザは各アイコンに対してリクエストを実行することに留意してください。そのため、これらのボタンを非表示にするだけでは不十分です。そうすると帯域幅が無駄になるだけでなく、ユーザーが無駄に目にする機会すらなくなります。これを解決するには、Django テンプレートで「is_mobile」ブール値を作成し、条件付きで HTML のセクションを省略しました。ユーザーがスマート デバイスでサイトを表示している場合、ボタンは表示されません。
アプリケーション キャッシュ - オフライン サポートができるだけでなく、起動時間の短縮にもつながります。
CSS/JS 圧縮 - Closure コンパイラの代わりに YUI 圧縮ツールを使用します。主に、CSS と JS の両方を処理するためです。発生した問題の 1 つは、インライン メディアクエリ(スタイルシート内に表示されるメディアクエリ)が YUI 圧縮ツール 2.4.2 で読み取られることでした(この問題をご覧ください)。YUI Compressor 2.4.4+ を使うと、この問題は解決しました。
可能な場合は CSS 画像スプライトを使用します。
画像圧縮に pngcrush を使用しました。
小さい画像に dataURI を使用しました。Base64 エンコードを行うと、画像が約 30%以上のサイズになりますが、ネットワーク リクエストは削減されます。
Google カスタム検索を
google.load()
で動的に読み込むのではなく、単一のスクリプトタグを使用して自動読み込みするようにしました。後者は追加のリクエストを行います。
<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"> </script>
- 当社のコードプリティ プリンタと Modernizr は、使用されたことがないにもかかわらず、すべてのページに組み込まれていました。Modernizr は優れていますが、読み込みのたびに多数のテストを実行します。一部のテストでは、DOM にコストのかかる変更が加わり、ページの読み込みが遅くなります。これらのライブラリは、実際に必要なページにのみ追加されました。リクエスト 2 件
パフォーマンスのその他の微調整:
- すべての JS をページの最下部に移動しました(可能な場合)。
- インライン
<style>
タグを削除しました。 - キャッシュされた DOM ルックアップと DOM 操作の最小化 - DOM をタップするたびに、ブラウザはリフローを行います。モバイル デバイスでは、リフローはさらにコストがかかります。
- 不要なクライアントサイドのコードをサーバーにオフロードする。具体的には、現在のページのナビゲーション スタイル設定を確認するチェックは次のとおりです。
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'; } }
- 固定幅の要素を流動的な
width:100%
またはwidth:auto
に置き換えました。
アプリケーション キャッシュ
モバイル版の html5rocks では、アプリケーション キャッシュを使用して初期読み込みを高速化し、ユーザーはコンテンツをオフラインで読むことができます。
サイトに AppCache を実装する場合は、マニフェスト ファイルを(マニフェスト ファイル自体に明示的に、または大量のキャッシュ制御ヘッダーを使用して暗黙的に)キャッシュに保存しないことが重要です。マニフェストがブラウザによってキャッシュされた場合、デバッグは大変です。iOS と Android では、このファイルのキャッシュ保存は優れていますが、パソコンのブラウザのようなキャッシュのフラッシュに便利な方法はありません。
サイトでこのようなキャッシュが行われないように、まず、マニフェスト ファイルをキャッシュに保存しないように App Engine を次のように設定します。
- url: /(.*\.(appcache|manifest))
static_files: \1
mime_type: text/cache-manifest
upload: (.*\.(appcache|manifest))
expiration: "0s"
次に、JS API を使用して、新しいマニフェストのダウンロードが完了したことをユーザーに通知します。ページの更新を求めるメッセージが表示されます。
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);
ネットワーク トラフィックを節約するため、マニフェストはシンプルにします。サイトのすべてのページを
呼びかけることはあまりありません重要な画像、CSS、JavaScript ファイルを
列挙するだけです最後に、アプリ キャッシュが更新されるたびに、モバイル ブラウザに多数のアセットをダウンロードするよう強制します。代わりに、ユーザーがアクセスするとブラウザは html ページを暗黙的にキャッシュします(HTML ページには <html manifest="...">
属性が含まれます)。