いちいちコード書くのは面倒くさいと思った
経緯
個人の趣味のブログサイトでイラスト公開のページを作成した際、Next.jsの<Image>
で画像を表示していました。
ただ<article>
のレイアウトをコンポーネント化したのに、画像が増える都度にPropsでパスを渡しながらコンポーネントを増やしていくのは、まだ運用していないにも関わらずちょっと面倒だなと思った次第です。
というかせっかく記事はNotionをCMSとして更新できるので、画像の更新もコードいじりたくないと。
案1:NotionAPIで取得
記事内の画像は普通にNotionAPIで取得しているので、ここの画像もNotionAPIで取得するということも考えました。
ただ、APIで取得した画像の仕様として1時間でリンク切れになってしまうのを画像置場で使うのは。。。
また<Image>
と違って自動最適化に対応しないので却下。
案2:S3などに置く
そもそも趣味でちょっとブログ書きたかっただけで、メインは画像ではないのでそこまでのコストはかけたくないので却下
案3:publicディレクトリに置く
Next.jsはPublicディレクトリに画像を置いておけば簡単に画像にアクセスでき、かつ<Image>
は自動で最適化や遅延ロードにも対応しているので非常に使い勝手がよいです。
調べたらglobというワイルドカードを利用してパスを取得できるライブラリがあったので、それでいけるのではと。
配列で取得できるのでmapで表示していくのにも都合がよい。
ということで案3を採用
Next.jsでglob使用の注意点
そもそもglobはnode.jsのライブラリなのでフロント側のコードに書いたとしても下記エラーで動かないです。
**Module not found: Error: Cannot resolve 'fs'**
サーバーサイドで動かさないといけないので、getStaticProps
配下でパスを取得して、ページにはpropsで渡す必要があります。
下記・・・Aだけで指定フォルダを検査してくれます。拡張子も指定できるので今回はjpgとpngのファイルだけを指定します。関数に渡すパスは絶対パスでも相対パスでもOKです。
これで指定したファイルのパスが配列で取得できます。
export const getStaticProps = async () => {
const glob = require('glob');
**const files = glob.sync( "./public/*.{jpg,png}"); //・・・A**
**const fileNames = files.map((file)=>{ return file.split("/").pop()}) //・・・B**
const comments = commentsArray
return {
props: {
comments:comments,
posts: fileNames,
},
revalidate: 1,
};
};
欲しいのはファイル名
パスの取得ができましたが、Next.jsはpublicディレクトリを読む際に”/ファイル名”
で読めるので、取得したパスからファイル名だけ切り出します**(上記・・・B)**。
あとは切り出したファイル名の配列をPropsで渡すだけです。
全体
コードはこんな感じです。
import { memo } from "react";
import ArticleImg from "../components/articleImg";
import styles from "./illustration.module.css";
import { commentsArray } from "../lib/comment";
const illustration = memo(({comments, posts}) =>{
return (
<>
<main className={styles.container}>
{posts.map((fileName,index)=>{return <ArticleImg key={index} text={comments[index]} imgUrl={`/${fileName}`} /> })
}
</main>
</>)
})
export default illustration
export const getStaticProps = async () => {
const glob = require('glob');
const files = glob.sync( "./public/*.{jpg,png}");
const fileNames = files.map((file)=>{ return file.split("/").pop()})
const comments = commentsArray
return {
props: {
comments:comments,
posts: fileNames,
},
revalidate: 1,
};
};
非常にシンプル!
ちなみに配列の順番はファイルの読み込み順になるので、コメントとずれないようにファイル名を変更してフォルダに入れておく必要があります。
それくらいは許容範囲(^^)
意外と使い勝手が良かった
そんなこんなで調査の時間含めると4〜5時間くらい掛かりましたが、ssgのお陰でページ遷移早いし、別のページに<grid>
でサムネイル画像並べるのもpublicディレクトリ配下でフォルダ分ければロジックそのまま使い回せるので良かった。
参考にした記事
javascript(nodejs)でフルパスからファイル名のみを取得する方法