Remix+CloudflareでWebサイトを作る 48(AVIF形式について色々、removeChild' on Nodeを修正、react-ga4、画像ファイルの置き場はapp以下)

【2025-01-04】AVIF形式の特徴あれこれ
これを読んで学んだことメモ
WebPの10年後にAVIFが登場
AVIF の大きな特徴の 1 つは、WebP の最大の欠陥を修正したことです。WebP は 10 年以上前に登場しましたが、AVIF は WebP 形式を大幅にアップグレードしたものです。
WebP は 8 ビットの色深度に制限されており、最適な圧縮モードでは、画像の解像度の半分の色しか保存できません (クロマ サブサンプリングと呼ばれます)。これにより、WebP では飽和色のエッジがぼやけたり、ピクセル化されたりすることになります。AVIF は、フル解像度で 10 ビットと 12 ビットの色、およびハイ ダイナミック レンジ (HDR) をサポートします。
Safari が WebP サポートを追加したのはごく最近で、Chrome より 10 年遅れていました。
こんな一文があったけどこういう歴史知らなかった。
以下の記事を見ていたのでなんとなく聞き馴染みのないWebPやAVIFだったらAVIF選んでおこ、くらいに思ってたけど登場までに10年も差があったんだ。
AVIFはプログレッシブレンダリングができないがそれ以上に小さいので良い
AVIF における 2 つの最大の問題は、エンコード速度とプログレッシブ レンダリングの欠如です。
AVIF 画像は、完全にダウンロードされるまで画面に何も表示されません。対照的に、プログレッシブ JPEG は、読み込み中に、画像の低品質の近似画像を非常に速く表示できます。プログレッシブJPEG が適切に配信されると、Web サイトの読み込みがはるかに速く見えるようになります。プログレッシブ JPEG は、ファイル サイズの半分で読み込まれたように見えます。AVIF は JPEG の半分のサイズで完全に読み込むことができるため、圧縮の利点によってプログレッシブ レンダリングの不足をある程度克服しています。この場合、プログレッシブ レンダリングも強力な圧縮も備えていない WebP だけが取り残されます。
ちなみにどちらの形式でもファイルサイズに大差はないっぽい。

Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
というエラーが発生している
【2025-01-05】next-themes使用時に
背景
実装内容はほぼこれと一緒。
以下のような書き方をしていると発生する
<ThemeProvider
attribute="class" // <html> の class を参照
defaultTheme="system" // システム設定の prefers-color-scheme に対応
enableSystem // システム設定の prefers-color-scheme に対応
disableTransitionOnChange // テーマ変更時のフラッシュを無効化
storageKey="acme-theme" // テーマの設定をローカルステージに保存
>
修正方法
disableTransitionOnChange
を削除するとエラーも消える。
有効にはしておきたいけどなぁ。
頑張ってもダメそうならIssue立てる。
<ThemeProvider
attribute="class" // <html> の class を参照
defaultTheme="system" // システム設定の prefers-color-scheme に対応
enableSystem // システム設定の prefers-color-scheme に対応
- disableTransitionOnChange // テーマ変更時のフラッシュを無効化
storageKey="acme-theme" // テーマの設定をローカルステージに保存
>
「たまに起きるなぁ」と感じているエラー、しっかり時間を取ってなおすの大切。。
「動くしな」で"一旦"無視するを続けがち。

React Router caught the following error during render
というエラーがconsoleに無限に出る
【2025-01-05】問題
wrangler dev
で立ち上げると以下のようなエラーがConsoleに無限に出続けてクラッシュする。
React Router caught the following error during render {componentStack: '\n at fr (http://localhost:8787/assets/root-C3cX…//localhost:8787/assets/root-C3cXiYNK.js:17:1632)'}
componentDidCatch @ index-DyN0ENtR.js:36
i.componentDidCatch.n.callback @ components-gOunnxL1.js:24
ea @ components-gOunnxL1.js:22
原因を探る
script2
の箇所が原因で発生している
function GoogleAnalyticsScript({ gaTrackingId }: { gaTrackingId: string }) {
useEffect(() => {
// Google Analyticsのメインスクリプトを読み込み
const script1 = document.createElement("script");
script1.src = `https://www.googletagmanager.com/gtag/js?id=${gaTrackingId}`;
script1.async = true;
document.head.appendChild(script1);
// 初期化スクリプトの設定
const script2 = document.createElement("script"); // 🚨ここ以下
script2.async = true;
script2.innerHTML = `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gaTrackingId}', {
page_path: window.location.pathname,
});
`;
document.head.appendChild(script2);
return () => {
document.head.removeChild(script1);
document.head.removeChild(script2);
};
}, [gaTrackingId]);
return null;
}
function App({ children }: { children?: ReactNode }) {
return (
<html>
<body>
<GoogleAnalyticsScript gaTrackingId="xxx" />
</body>
</html>
)
}
解決策
react-ga4
を導入した。
最後に更新されたの2年前で85kBあるのか...。

wrangler dev
で立ち上げると静的な画像ファイルが見つからないと言われる
【2025-01-05】背景
開発環境でwrangler dev
を実行してデバッグ中、以下のエラーが出ている。
これ、remix vite:dev
だとエラーが発生しないまま動いてしまう。
[wrangler:inf] GET /static/404.jpg 404 Not Found (12ms)
原因
このようなディレクトリ構造になっているのが原因だった。
MyApp
├── app/
│ └── ...
├── static/
│ └── 404.jpg
└── ...
公式ドキュメントを見るとこのように書いてある。
appフォルダ内のファイルはすべてモジュールにインポートできます。 Remixは次のことを行います:
- ブラウザのビルドディレクトリにファイルをコピーする
- 長期キャッシュのためにファイルにフィンガープリントを付ける
- レンダリング中に使用される公開URLをモジュールに返す
解決方法
ということでapp
ディレクトリ内にファイルを置く。
MyApp
├── app/
│ ├── static/
│ │ └── 404.png
│ └── ...
└── ...
こんな感じにすればOK。
import NotFoundImage from "~/static/404.jpg";
<img
src="xxx"
alt="yyy"
onError={(e) => {
const target = e.target;
if (target instanceof HTMLImageElement) {
target.onerror = null; // 無限ループを防ぐために、onErrorをクリア
target.src = NotFoundImage
}
}}
/>
img
タグのonErrorに指定していた画像であまり使用される機会がなかったので気づかなかった。