
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、このブログではLaravel
の記事と並んでVue.js
に関する記事も多く公開しています。
Vue
は私が使ったJS
フレームワークの中で一番すぐ使える(=学習コストが低い)と考えていて、JavaScript
の知識があればおそらくVueは2、3日もあれば基本的な動作はマスターできるんじゃないかと思っています。
そのため、他の開発者さんと共同で作業をする際でもできるだけVue
を使うように提案をしています。(もちろんReact
も素晴らしいんですが、学習コストはVue
よりも高いのでReact
経験者でないと比較的引き継ぎしにくいと考えています。あとはいちいちビルドするのがどうしても好きじゃないんですー)
そして、今回はそんなVue
で1つやってみようと思っていた機能を思い出しました。
それが、
Lazy Loading(れいじーろーでぃんぐ)
です。
Lazy Loading
というのは、元々インターネット回線が遅くてもストレスを感じないようにページ読み込みするために考えられたテクニックで、例えば以下の画像を見てください。
これは、元画像は50 x 50 px
の小さなサムネイルですが300 x 300 px
に引き伸ばして表示したものです。(ですので、ぼやけています)
このようにページが読み込まれたときは、小さな画像を読み込んで表示しておき、後で少しずつオリジナルの(この場合は300 x 300 pxの)大きな画像を読み込むという機能が「Lazy Loading(遅延読み込み)」と呼ばれるものになります。
つまり、Lazy Loading
を使えば、もしページ内に大きな画像があったとしても画像が必要なときに読み込まれるのでページ表示は早くなるというわけですね。
ということで!
今回はVue
でこのLazy Loading
を実装してみたいと思います。
ぜひ皆さんのお役に立てると嬉しいです
開発環境: Vue 2.6、ES6
目次 [非表示]
やりたいこと
今回はVue
の持つ機能「カスタム・ディレクティブ」をつくって実装していきます。
ディレクティブを使った例はこちらです。
<img
src="(サムネイルのURL)"
v-lazy-loading="'(オリジナルのURL)'">
そして、オリジナルのURLがロードされるのは、「表示中のウィンドウの中に画像が入ったとき」とします。
つまり、このような状態です。
また、せっかく独自のディレクティブをつくるのでLazy Loading
が完了したらlazy-load
という独自イベントも実行できるようにしていきます。
では実際にやっていきましょう!
ちなみに今回は「子供向け雑誌の付録」のように基本のコードに番号がふってあって、それにコードをはめ込んでいけば完成するという初の試みで話を進めていきたいと思います。
ぜひ楽しんでやってみてください
今回使う画像について
今回使う画像はURLを指定するだけで好きなサイズの画像が使えるCDN
のplaceholder.comを使います。例えば、500 x 300
の画像がほしければ、
<img src="https://via.placeholder.com/500x300">
とするだけでOKです。
実際の表示はこのようになります。
基本のHTMLコード
では、まず初めに基本となるHTMLコードです。
この中にふってある番号にコードを追加しながら完成させていきます。
<html>
<head>
<!-- ① 画像のスタイルシートをつくる -->
</head>
<body>
<div id="app">
<div v-for="i in 100">
<!-- ⑤ 画像タグをつくる -->
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
<script>
// ④ Vueのミックスインで「v-lazy-loading」が使う関数をつくる
// ③ Vueのカスタム・ディレクティブ「v-lazy-loading」をつくる
// ② Vueの基本形をつくる
</script>
</body>
</html>
この基本コードをlazy_loading.html
のような名前でファイル保存しておくとブログを読み進めながらコードを追加していくことができますよ!
画像のスタイルシートをつくる・・・①番
表示する画像のサイズをスタイルシートで指定しておきましょう。こうすることで、先に読み込まれる画像は小さいですがサイズを固定させることができます。(つまり、画像はぼやけます)
<style>
img {
width: 300px;
height: 300px;
}
</style>
Vueの基本形をつくる・・・②
続いては、Vue
の基本形です。
この中には、さらに別の作業⑥がありますが、今はこのままコードを追加してください。
new Vue({
el: '#app',
// ⑥ 独自イベントのメソッドをつくる
});
なお、#app
は<div id="app">〜</div>
のことを指しています。
Vueのカスタム・ディレクティブ「v-lazy-loading」をつくる・・・③
今回のテーマはカスタム・ディレクティブですが、実はLazy Loading
の性質上メインのコードはもう一つのミックスインの方に多くはいっているためコード量は少なめです。
Vue.directive('lazy-loading', {
bind(el, binding) {
el.setAttribute('original-src', binding.value)
},
inserted(el) {
el.className += ' waiting-load';
}
});
なお、bind()
ではv-lazyloading
で指定するオリジナルの大きな画像のURLを一旦original-src
というプロパティに保持しています。
<img
src="(先に表示する小さな画像のURL)"
v-lazy-loading="(オリジナル画像のURL)">
また、inserted()
では、あとでLazy Loading
すべき画像が判別できるよう「waiting-load」というクラス名を追加しています。
Vueのミックスインで「v-lazy-loading」が使う関数をつくる・・・④
ここが一番コード量が多いですが、少し複雑なのはたった1つのメソッドだけです。
※ なお、ミックスインはVue
の基本形と合体することになります。(使い回しができるように別コードに分けました)
Vue.mixin({
methods: {
lazyLoading() {
// ウィンドウの表示位置
const windowTop = window.scrollY;
const windowBottom = windowTop + window.innerHeight;
// Lazy Loadingすべき画像を全て取得
const images = document.querySelectorAll('.waiting-load');
[].forEach.call(images, el => {
// 画像の位置
const offsetTop = el.offsetTop;
const offsetBottom = offsetTop + el.offsetHeight;
if(windowTop < offsetTop && windowBottom > offsetBottom) {
// 画像をオリジナルのURLに変更し、不要なデータを削除
const originalSrc = el.getAttribute('original-src');
el.setAttribute('src', originalSrc);
el.removeAttribute('original-src');
el.className = el.className.replace(' waiting-load', '');
// lazy-load イベントを実行
const event = new Event('lazy-load');
el.dispatchEvent(event);
}
});
}
},
mounted() {
this.lazyLoading();
window.onscroll = this.lazyLoading;
}
});
まず、mounted()
はページが読み込まれたらすぐに実行されるメソッドですが、この中ではlazyLoading()
というメソッドをすぐに実行し、さらにスクロールしたときにも同じメソッドを実行するようイベントをつけています。
※ 一度すぐに実行しているのは、ページが表示された時点で画像がウィンドウ内に入っていればオリジナル画像を読み込ませるためです。
では、続いてlazyLoading()
です。
この中ではまず現在のスクロール位置を取得しています。(この数値を見れば、ウィンドウの上と下の位置、つまり現在ブラウザで表示されている範囲がわかようになります)
次に、document.querySelectorAll()
を使って「Lazy Loadingを実行するべき全ての画像」を取得し、それらを[].forEach.call()
でループさせています。
そしてループの中では、1つずつ画像のエレメントが取得できるので、その画像の位置と先ほどのウィンドウ位置を比較しウィンドウ内に画像が入っているかをチェックしています。
もしウィンドウ内に入っているようなら、一時的に保管したオリジナル画像のURLをoriginal-src
から取得し、実際に表示されるsrc
として使います。
なお、original-src
とwaiting-load
はもう不要なのでここで削除しておきます。(というか、クラス名waiting-load
は削除しないと何度も何度も処理が実行されるので削除しておくべきです)
そして、最後にLazy Loading
されたときに実行されるイベントを起動します。イベント名はどんなものでもいいのでお好みで変更することができます。
画像タグをつくる・・・⑤
さぁ、完成が近づいてきました。
実際に画像タグを以下のようにセットしてください。
<img
src="https://via.placeholder.com/50?text=50x50"
v-lazy-loading="'https://via.placeholder.com/300?text=300x300'"
@lazy-load="onLazyLoad">
<br><br>
すでに説明しましたが、プロパティの意味は以下のとおりです。
- src ・・・ 先に表示する小さな画像
- v-lazy-loading ・・・ カスタム・ディレクティブ。セットする値はオリジナル画像のURL ※注意
- @lazy-load ・・・ オリジナル画像が読み込まれたら実行されるカスタム・イベント。次の項目で作る
onLazyLoad()
が実行される。
※注意
v-lazy-loading
内ではURLをシングルクォート'
で囲んでいますが、これはカスタム・ディレクティブにはVue
の変数も指定できるからです。
つまり、Vue
の変数にオリジナル画像のURLを入れておけば・・・
new Vue({
el: '#app',
data: {
originalUrl: 'https://via.placeholder.com/300?text=300x300'
}
});
次のように変数の値を参照することができます。(この場合はシングルクォートはいりません)
<img
src="https://via.placeholder.com/50?text=50x50"
v-lazy-loading="originalUrl">
独自イベントのメソッドをつくる・・・⑥
では、最後に「Vueの基本形をつくる」で追加した⑥番目の作業、独自イベントで指定したメソッドonLazyLoad
を追加します。
methods: {
onLazyLoad(e) {
// 独自イベント「lazy-load」
e.target.style.border = '5px solid red';
}
}
この中では実行結果が分かりやすいように、画像に赤い枠線をつけるようにしています。
テストしてみる
では、実際に今回のコードをブラウザで表示してみましょう。
まずはページを表示した直後です。
うまくウィンドウ内の画像は300 x 300
の画像が読み込まれて赤枠が表示されていますが、まだウィンドウ内に入りきっていない画像は50 x 50
のままです。
では、このまま下方向へスクロールをしていきます。
はい!こちらもうまく画像がロードされ赤枠もつきました。
お疲れ様でした
おわりに
ということで、今回はいつもと少し違って子供雑誌の付録のような形式で記事をお届けしました。
もちろんVue
を学習している人全てにお届けしたつもりですが、もし小中学生の皆さんが自由研究的な感じて使ってくれたら嬉しいですね。
(そのため、画像を用意しなくてもいいようにplaceholder.com
などのCDN
を使ってコードを書きました)
ぜひあまり実際にプログラムをやったことがない方も、まずは遊び感覚でチャレンジしてみてくださいね。
ではでは〜!