hexoでnative lazy-loadingに対応する

フィギュアレビュー記事の表示速度が遅いので、 ブラウザnativeのlazy-loading(遅延読み込み) に対応する。 自前のscriptでloading, width, height属性を付与する作戦。

長くなっちゃったので、記事は折りたたんである↓。

手順

まず、image-sizeを使うので、npm install image-sizeしておく。

次に、↓のscriptをthemes/***/scriptsに置く。 scriptsフォルダに置いたjsファイルはhexoのgenerate時に自動で実行される。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// This script refers to `hexo-native-lazy-load`.
// <https://github.com/fengkx/hexo-native-lazy-load>

// require
const sizeOf = require('image-size');
const path = require('path');
const fs = require('fs');
const log = hexo.log;

// config
const regex = /<img[^>]+src=\"(.+?)\"[^>]*>/gm;

// function
const mylog = (...args) => {
log.debug(path.basename(__filename), ': ', ...args);
};

const replace = (html, rootPath) => {
return html.replace(regex, (s, relativePath) => {
mylog('s: "' + s + '".');

if (s.indexOf('loading="lazy"') !== -1) {
mylog('Already added `loading="lazy"`. Do nothing.');

return s;
}

const imagePath = path.join(rootPath, relativePath);
mylog('image path: ' + imagePath);

if (rootPath !== '' && fs.existsSync(imagePath)) {
const dimensions = sizeOf(imagePath);
mylog('width: ' + dimensions.width + ', height: ' + dimensions.height);

return (
s.substring(0, 4) +
' loading="lazy"' +
` width="${dimensions.width}"` +
` height="${dimensions.height}"` +
s.substring(4, s.length)
);
} else {
mylog('Not found image.');

return s.substring(0, 4) + ' loading="lazy"' + s.substring(4, s.length);
}
});
};

// main
hexo.extend.filter.register('after_post_render', function (data) {
mylog('replace: "' + data.title + '".');
data.content = replace(data.content, hexo.source_dir);
return data;
});

次に、css(stylus)の修正もいる。↓のファイルをいじる。 (landscape以外を使ってる人は適宜読み替えてね。)

  • themes/landscape/source/css/_partial/article.styl

ツリー構造で示すと、↓をいじる。

  • .article-entry
    • img, video
      • max-height: 100vhの設定を無効化(コメントアウト)する。

最終的には↓のようになってる。 (max-height以外のコメントアウトは無視して。)

1
2
3
4
5
6
7
8
img, video
max-width: 100%
// max-height: 100vh
// width: auto
height: auto
// object-fit: contain
display: block
margin: auto

最後に、clean&generateすればOK。

解説と蛇足

jsの中身は hexo-native-lazy-load pluginのほぼパクり。

hexo-native-lazy-load pluginそのままだと、 post_asset__folderを有効にしないと、うまく動作しない。 <img>widthheightを入力してくれない。

この記事のjsはpost_asset_folderが無効でも動作するように改良した(つもり)。 やってることは、大雑把に↓の2つ。

  1. loading="lazy"を付与
  2. image-sizeで画像サイズ取得して、width, height属性を付与

ちなみに、pluginとして公開する予定はなし。体裁を整えるのがめんどいため。

cssについては、本当はmax-height: 100vhの設定も有効のままにしておきたかった。 だけど、lazy-loadingで有効にすると、aspect比が崩れちゃうのよね…。 なんでaspect比を固定する属性とかがcssにないんだろう。不思議。

なお、lazy-loadingが効いてるかどうかは、chromeのデバッガを使えば分かる。 Networkタブで、スクロールするごとに画像がloadされてれば成功。 ↓の例だと、prisma_klangfest_chloe_3.webpが遅延読み込みした画像。 chromeでlazy-loadingの動作確認

他、 先駆者の方の記事 も参考にしてる。 感謝感謝。

これで、フィギュアレビュー記事の体感表示速度が上がったはず。 Netlifyくん、転送速度が結構遅い。 無料のホスティングサービスなのでしょうがない。工夫でカバー。