考え方#
画像の遅延読み込みの方法は、Intersection Observer APIを使用して非常に簡単に実装できます。
しかし、Astro での実装は異なります。なぜなら、ここで実現するのはウェブサイト上の画像の遅延読み込みではなく、ブログ内の画像の遅延読み込みです。
Astro のブログは Markdown で書かれており、Frontmatter にlayout
を追加してレンダリングするコンポーネントを指定します。ブログを開くときには、コンポーネントがすでに読み込まれています。ブログの中の image タグにはすでに src 属性があり、データを取得するためにリクエストが送信されています。
最初の考えは、ページを開いた後、画像がリクエストを送信する前に、image の src 属性を削除して data-src に保存するフック関数を見つけることでした。window.onload
よりも早いフック関数があれば良いのですが、そのような方法はありませんでした。
そのため、markdown を html にレンダリングする際に、image タグを処理することにしました。このアイデアは実現可能だと思われるため、Astro のドキュメントを見てみると、markdown.remarkPluginsという設定方法が見つかりました。
公式ウェブサイトの手がかりに従って、GitHub で検索していると、このパッケージを見つけました:remarkjs/remark。このパッケージには、h タグのレベルを 1 つ下げるコード例があります。つまり、h2 タグが h3 タグに変わります。
import { visit } from 'unist-util-visit'
function myRemarkPluginToIncreaseHeadings() {
return (tree) => {
visit(tree, (node) => {
if (node.type === 'heading') {
node.depth++
}
})
}
}
元の md ドキュメント:
# Hi, Saturn!
ページの html は:
<h1>Hi, Saturn</h1>
フォーマット後、ページの html は:
<h2>Hi, Saturn</h2>
上記の例から、このプラグインが要件を満たすことがわかりましたので、実装を始めます。
実装#
astro.config.mjs
に以下のコードを追加します:
import { visit } from 'unist-util-visit'
function myRemarkPluginToLazyLoadImage() {
return (tree) => {
visit(tree, (node) => {
if (node.type === 'image') {
// url属性をaltに設定し、さらにurlを空にすることで、ページの読み込み時にurl属性がないため画像が読み込まれなくなります
node.alt = node.url
node.url = ''
}
})
}
}
export default defineConfig({
..., // 他の設定
markdown: {
remarkPlugins: [myRemarkPluginToLazyLoadImage],
// これを追加しないと、mdをhtmlに変換せず、プラグイン内のコードのみを処理します
extendDefaultPlugins: true,
}
})
md をインポートする Astro コンポーネントに以下のコードを追加します:
<script>
/* ブログ内のすべてのimgタグを検索します */
var markdownBody = document.querySelector(".markdown-body");
let images = markdownBody.querySelectorAll("img");
const callback = (entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const image = entry.target;
const data_src = image.getAttribute("alt");
image.setAttribute("src", data_src);
observer.unobserve(image);
}
});
};
/* 各imgタグに監視メソッドを追加します */
const observer = new IntersectionObserver(callback);
images.forEach((image) => {
observer.observe(image);
});
}
</script>
これで、画像の遅延読み込みが完了しました!