🚀

初めてのAstroで画像最適化を試してみた @astrojs/imageとastro-imagetools

2022/10/31に公開

はじめに

先日参加したマークアップな夜で画像最適化のセクションがあり、レスポンシブをはじめsizes属性などを使って色々分岐させるのはブラウザに任せるのがいいよねという話がありました。

EJSやPug,素のhtmlなどの場合は手動でloading="lazy"などを追加するかと思います。(もちろん自作で自動化もできると思いますが)
ただスニペットなどを準備していたとしても、やはりそれなりの手間が発生することは間違いないと思います。
これを受けて個人的にWeb制作でもジェネレータやフレームワークを使用するのがベターだと思い始めてきました。

その中でも最近よく見かけるAstroをまだ使ったことがなかったので触ってみながら画像最適化について探ってみました。

デモサイト

デモサイト

下記ページを参考にastro×microCMSの簡易サイトを作ってみました。
https://blog.microcms.io/astro-microcms-introduction/

Astroをはじめてみる

まずは公式ページ通りにAstroをはじめてみます。
https://docs.astro.build/en/getting-started/
初期設定はとりあえず全部recommendedで進めました。

事前準備・設定

VSCodeでAstroを始めると拡張機能をおすすめされたので、とりあえずAstroという拡張機能を入れておきます。

私の場合はファイル保存時にPrettierでフォーマットしているのですが、デフォルトだと.astroがサポートされていないらしいのでプロジェクトフォルダでprettier-plugin-astroというプラグインをインストール、設定します。

npm install -D prettier-plugin-astro
setting.json
{
  "[astro]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  }
}

scssも設定しないと使えませんが今回は割愛します。

@astrojs/image

Astroのドキュメントを読み進めていくと、@astrojs/imageを使えば画像を最適化できるよ!と書いてあったので、こちらを導入してみます。

インストール・設定

ドキュメント通りに進めます。
Quick Installのやり方だと設定ファイルにも自動的に記述してくれるので便利です。

# Using NPM
npx astro add image
# Using Yarn
yarn astro add image
# Using PNPM
pnpm astro add image
astro.config.js
import { defineConfig } from 'astro/config';
import image from '@astrojs/image';//追加

// https://astro.build/config
export default defineConfig({
  integrations: [image()],//追加
});

実際に使用してみる

比較のため普通にsrcで読み込むのと@astrojs/imageで読み込むのと2つの方法でHTMLを追加してみました。

入力ソース

import { Image, Picture } from '@astrojs/image/components';
import sampleImage from '../assets/images/paint.jpg'
<!--(中略)-->
<figure class="figure">
  <img class="figure__img" src="../assets/images/paint.jpg" alt="" width="1920" height="2560">
  <figcaption class="figure__text">normal image</figcaption>
</figure>
<figure class="figure">
  <Image class="figure__img" src={sampleImage} width={1920} alt=""/>
  <figcaption class="figure__text">image with @astrojs/image</figcaption>
</figure>

出力ソース

<figure class="figure astro-YLUFV3SQ">
  <img class="figure__img astro-YLUFV3SQ" src="../assets/images/paint.jpg" alt="" width="1920" height="2560">
  <figcaption class="figure__text astro-YLUFV3SQ">normal image</figcaption>
</figure>
<figure class="figure astro-YLUFV3SQ">
  <img class="figure__img astro-YLUFV3SQ" alt="" width="1920" height="2560" src="/@astroimage/assets/images/paint.jpg?f=jpg&amp;w=1920&amp;h=2560&amp;href=%2F%40astroimage%2Fassets%2Fimages%2Fpaint.jpg" loading="lazy" decoding="async">
  <figcaption class="figure__text astro-YLUFV3SQ">image with @astrojs/image</figcaption>
</figure>

paint.jpgというファイルだけ入れて動作させてみたところ、Imageコンポーネントを利用した方にはloading="lazy"decoding="async"が付与されていました!

多様なプロパティが用意されている

その他、formats/sizes/aspectRatioなどWeb制作でよく使うプロパティは既に用意されています。
https://docs.astro.build/ja/guides/images/

sizesなども自動で画像を最適化させるためにはimgixcloudinaryなどと連携してゴニョゴニョする必要があるのかもしれないです。

Using Images from a CMS or CDN
Section titled Using Images from a CMS or CDN
Image CDNs work with Astro. Use an image’s full URL as the src attribute in an <img> tag or Markdown notation.
Alternatively, if the CDN provides a Node.js SDK, you can use that in your project. For example, Cloudinary’s SDK can generate the <img> tag with the appropriate src for you.
To use external images with the <Image /> and <Picture /> components from Astro’s image integration, you must specify the appropriate dimension and format values for working with remote images.

https://docs.astro.build/ja/guides/images/#using-in-mdx

astro-imagetools

こちらも公式ドキュメント通りに進めました。

インストール・設定

npm install astro-imagetools

# yarn
yarn add astro-imagetools

# pnpm
pnpm add astro-imagetools

astro-imagetoolsの方はQuick Installはないので、インストール後にastro.config.mjsファイルに手動で設定を追加します。

astro.config.mjs
import { astroImageTools } from "astro-imagetools";//追加

export default {
//integrations: [image(), astroImageTools], // @astrojs/imageも入れる場合
  integrations: [astroImageTools],
};

実際に使用してみる

ドキュメントでは最初にReactでの使用方法が書かれていましたがとりあえずフレームワークなしで試してみました。

入力ソース

---
import { Img } from 'astro-imagetools/components';
---
<!--(中略)-->
<figure class='figure'>
  <!--ローカルファイルの場合はプロジェクトフォルダからの相対パス-->
  <figcaption>Local Image with astro-imagetools</figcaption>
  <Img src='/src/assets/images/paint.jpg' alt='' />
</figure>
<figure class='figure'>
  <figcaption>Remote Image with astro-imagetools</figcaption>
  <Img src='https://picsum.photos/1024/768' alt='A random image' />
</figure>

出力ソース

<figure class="figure astro-SX2YV4KN">
  <figcaption class="astro-SX2YV4KN">Local Image with astro-imagetools</figcaption>
  <style>
    .astro-imagetools-img-7FA2C15C {
      object-fit: cover;
      object-position: 50% 50%;
    }

    .astro-imagetools-img-7FA2C15C {
      background-size: cover;
      background-image: url("data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAbABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQFBwb/xAAnEAABAwMDBAEFAAAAAAAAAAABAgMEAAYRBRIhBxMxUUJBQ2GBov/EABcBAQEBAQAAAAAAAAAAAAAAAAcGBAX/xAAlEQABBAEDAgcAAAAAAAAAAAABAgMEEQAFEiFxsRUyQUJh4fD/2gAMAwEAAhEDEQA/AOv6hr32jJJ+L7PP7NSbRyLUinGMvvc++U0/f7jbdmyi6820O+yApasDyanWaN9oRltqS6hUh7CmzuHw9UeyLVCKv3my80NSfCUgnnee2TLteUjWlgH7TR/hNFTuoepwYFxlqTMjsOGOyrY4vB5bH0opp0zaITNn2p7DA3U2XTNeISa3K9Pk52dswpl7M6jpd0acIsdvapHYC0FbiScZKs8c+B7p3WumAatyFF0bUJEVLC1OrC1qJUpeAcbccDFagw0hp53tpCdzhWfyT5NMKGVKB5FRbUFlpsNVYB+8QIc6TESA2uqvpz1zLn+k+hai3Gc1pL8ma0whlbqHikK2jAOCDRWk0V025LzaQhKjQzC40h1ZcUOSST1Of//Z");
      background-position: 50% 50%;
    }
  </style><img src="/assets/paint@1920w.e9618a7a.jpeg" alt="" srcset="/assets/paint@320w.5a2ea190.jpeg 320w, /assets/paint@777w.7993876b.jpeg 777w, /assets/paint@1158w.ae6c204e.jpeg 1158w, /assets/paint@1463w.31d02193.jpeg 1463w, /assets/paint@1691w.6266178a.jpeg 1691w, /assets/paint@1844w.b80c5067.jpeg 1844w, /assets/paint@1920w.e9618a7a.jpeg 1920w" sizes="(min-width: 1920px) 1920px, 100vw" width="1920" height="2560" loading="lazy" decoding="async" class="astro-imagetools-img astro-imagetools-img-7FA2C15C" style="display: inline-block; overflow: hidden; vertical-align: middle; ; max-width: 100%; height: auto;" onload="">

</figure>
<figure class="figure astro-SX2YV4KN">
  <figcaption class="astro-SX2YV4KN">Remote Image with astro-imagetools</figcaption>
  <style>
    .astro-imagetools-img-D71BD208 {
      object-fit: cover;
      object-position: 50% 50%;
    }

    .astro-imagetools-img-D71BD208 {
      background-size: cover;
      background-image: url("data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAYHCP/EACgQAAIBAwMDAgcAAAAAAAAAAAECAwAEBQYREhMhUQcVIiMxMkFxgf/EABYBAQEBAAAAAAAAAAAAAAAAAAQDBf/EAB8RAAIBAgcAAAAAAAAAAAAAAAACAQMREhMUMkFRUv/aAAwDAQACEQMRAD8AbcB6v469hjf2PMxyOSOKW5cbeSw7fyqLjdSW15biWMTxg/iWJkYfsEVm/D65igVY3WUmmtNbRSY24ZeooET/AG7gj4T9Kyqiuu2LC1wzyWC31lhrpDJbXvXQMV5xIzLuOxG4HmisApnL2HksMny+RI5jke58mikaNvRLOjo//9k=");
      background-position: 50% 50%;
    }
  </style><img src="/assets/768.03eee4832798f596f669cd04a87295b2@1024w.12cb083e.jpeg" alt="A random image" srcset="/assets/768.03eee4832798f596f669cd04a87295b2@320w.8dc6d366.jpeg 320w, /assets/768.03eee4832798f596f669cd04a87295b2@672w.66a99108.jpeg 672w, /assets/768.03eee4832798f596f669cd04a87295b2@907w.918d7dbd.jpeg 907w, /assets/768.03eee4832798f596f669cd04a87295b2@1024w.12cb083e.jpeg 1024w" sizes="(min-width: 1024px) 1024px, 100vw" width="1024" height="768" loading="lazy" decoding="async" class="astro-imagetools-img astro-imagetools-img-D71BD208" style="display: inline-block; overflow: hidden; vertical-align: middle; ; max-width: 100%; height: auto;" onload="">

</figure>

Next.jsみたいに最適化されてますね!

まとめ・参考サイト

Astroの全体的な使い勝手は、実際にサイトなどを作ってみないと分からないと思うので、どこかの案件で使ってみたいですね。
https://astro.build/
https://astro-imagetools-docs.vercel.app/en/introduction
https://astro-imagetools-demo.vercel.app/

Discussion