ボーイフレンドを直す方法 あるいは賢いjQuery.Deferredの使い方
問題
モバイルは回線が不安定なので、ロードの失敗が頻繁に起こります。 開発時は高速なwifi環境で開発しているので、リリース間近になって帯域を圧迫していることに気づいたりします。
解決方法
画像を先読みします
var preload = function(src){ var d = $.Deferred(); var img = new Image; img.src= src; img.onload = d.resolve img.onerror = d.reject return d.promise(); };
何をやっているかというと、空のimgタグをつくってそこに画像を読み込みます。その過程でブラウザキャッシュに画像が保存されます。正確に言うとこの時点ではどこにも紐付いていないのでGC対象ですが、その後すぐDOMに画像をはるなら問題ありません。
並列で先読みする(速い・不安定)
var preload_images_paralell = function(srcs){ var dfds = srcs.map(function(src){return preload(src);}); return $.when.apply($, dfds); } preload_images_paralell(["boy.png", "boy2.png"]).done(function(){ console.log('loading done'); });
一応先読みしておきたいけど、そこまで安定性が高いわけじゃないです。画面に貼った時点でDOM要素のwidth, heightが確定しているので、画面がガコガコと再配置されて負荷がかかるのを防ぐのが目的です。
直列で先読みする(遅い・安定)
var preload_images_serial = function(srcs){ var d = $.Deferred(); var src = srcs.shift(); if(src) preload(src).done(function(){preload_images_serial(srcs)};) else d.resolve() return d.promise(); }; preload_images_serial(["boy.png", "boy2.png"]).done(function(){ console.log('loading done'); });
一個一個読み込むので遅いです。その分安定します。UIパーツを構成する必須要素などで使います。 まあ、それ以前に、そもそも裏でいくつのHTTPリクエスト飛んでるか意識したほうがいいです。
まだ残ってる問題
- 平気でImage#onloadもImage#onerrorも通らずに諦めるリクエストがある
- 壊れた画像(しかしブラウザは正常だと認識している)を破棄する方法がない
- IEだとかは何も言わずに諦めるケースがある
- サーバー側で諦めてる場合はどうしようもない
ボーイフレンド(仮)のあのバグ、思い当たるフシあるので直せと言われたら直せるけど、CAの優秀なフロントの方々ならすぐ直せるでしょうに
— はちま禁止公認ブロックアカウント (@mizchi) 2013, 12月 17
(煽っていく)
— はちま禁止公認ブロックアカウント (@mizchi) 2013, 12月 17
:追記
@aklaswad 先読みを安全に行えば壊れるケースが可能な限り減らせるので、そこまでする必要がないという認識です。死ぬ気でやるんだったらphantomJSでとったスクショをOpenCVで編集距離測るとかありますが、やりたくない…
— はちま禁止公認ブロックアカウント (@mizchi) 2013, 12月 17
画像が壊れてるかどうかは、とりあえずimg[src]でレンダリングしてみて、縦横の幅取れば結構分かるんじゃないかな // ボーイフレンドを直す方法 あるいは賢いjQuery.Deferredの使い方 - mizchi's blog http://t.co/IO7trAesLF
— kyo ago (@kyo_ago) 2013, 12月 17