loading=lazyのリソース読み込みタイミングについて

# はじめに
この記事は[ZOZOテクノロジーズ #2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech2) 9日目の記事になります。

Chrome76より実装されたloading属性と
loading=lazyが指定された場合のリソース読み込みタイミングについて紹介させていただきます。

# loading属性について

```html
<img src="image.png" loading="lazy" alt="" width="200" height="200">
```
サポートされている値は現在（2019/12/9時点）下記の通りです。

|値 | 動作 |
|-----|-----|
|auto|ブラウザのデフォルトの遅延読み込み動作 (default)|
|lazy|viewportから計算された距離に達するまで、リソースの読み込みを延期|
|eager|ページ上の場所に関係なくリソースをすぐにロード|

因みに、10x10より小さいインラインサイズを指定した<img>要素は遅延されないらしい。

# しきい値について

気になるのは　loading=lazyを指定した場合のリソース読み込みタイミングですよね。

[demoページ](https://mathiasbynens.be/demo/img-loading-lazy)をDevToolsで確認してみても
実際viewportHeightよりかなり下のimageまで読み込まれているのが確認できます。
![スクリーンショット 2019-12-09 3.02.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531481/2b0983de-514a-4d1e-6409-ab904c4d38e6.png)


Chrome Engineering Managerの[Addy Osmani氏の記事](https://addyosmani.com/blog/lazy-loading/)によると

>Chrome’s lazy-loading implementation is based not just on how near the current scroll position is, but also the connection speed. The lazy frame and image loading distance-from-viewport thresholds for different connection speeds are hardcoded

つまり遅延読み込みのしきい値は、接続速度に基づいてハードコードされているとのことなので
[chromiumの該当コード](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/frame/settings.json5?l=937-1003&rcl=e8f3cf0bbe085fee0d1b468e84395aad3ebb2cad)を確認してみます。

![スクリーンショット 2019-12-09 9.43.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531481/a6965df4-509e-25fe-aa44-a1cdbdb38439.png)

どうやら現在（2019/12/9時点）では下記のようにしきい値が定義されているようです。

| connection speed| image | iframe |
|-----|-----|-----|
|4G|3000|4000|
|3G|4000|5000|
|2G|6000|6000|
|slow 2g|8000|8000|
|offline|8000|8000|
|不明|5000|6000|

## つまり
つまり、4Gの場合だと
`image`が`viewportHeight`から`3000px`以内に
入ってきたタイミングで読み込みが開始される仕様になっています。

式で表すと下記のような感じでしょうか。

```js
targetImageElement.getBoundingClientRect().top - window.innerHeight <= 3000
```



## 因みに

lazyImageLoadingDistanceはコマンドラインからオーバーライドできるようです。

```
canary --user-data-dir="$(mktemp -d)" --enable-features=LazyImageLoading --blink-settings=lazyImageLoadingDistanceThresholdPxUnknown=1,lazyImageLoadingDistanceThresholdPxOffline=1,lazyImageLoadingDistanceThresholdPxSlow2G=1,lazyImageLoadingDistanceThresholdPx2G=1,lazyImageLoadingDistanceThresholdPx3G=1,lazyImageLoadingDistanceThresholdPx4G=1 'https://mathiasbynens.be/demo/img-loading-lazy'
```

# 任意のしきい値を設定したい場合

結局、現状で任意のしきい値を設定したい場合は
`IntersectionObserver`等を使用して独自に実装する必要がありそうです。

```html
<img class="lazy" src="placeholderImage.gif" data-src="originalImage.jpg" alt="">
```

```js

const lazyImages = [].slice.call(document.querySelectorAll('img.lazy'))

const lazyImageObserver = new IntersectionObserver(
	(entries, observer) => {
		entries.forEach( entry => {
			if (entry.isIntersecting) {
				const lazyImage = entry.target
				lazyImage.src = lazyImage.dataset.src
				lazyImage.classList.remove('lazy')
				lazyImageObserver.unobserve(lazyImage)
			}
		})
	}, { rootMargin: '100px 0px 100px 0px' }
)

lazyImages.forEach((lazyImage) => {
	lazyImageObserver.observe(lazyImage)
})
```

# 参考

- https://addyosmani.com/blog/lazy-loading/
- https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/frame/settings.json5?l=937-1003&rcl=e8f3cf0bbe085fee0d1b468e84395aad3ebb2cad
- https://web.dev/native-lazy-loading/