Closed6

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

saneatsusaneatsu

【2025-01-04】AVIF形式の特徴あれこれ

https://blog.cloudflare.com/generate-avif-images-with-image-resizing/

これを読んで学んだことメモ

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年も差があったんだ。

https://zenn.dev/xx_suzuki/articles/sharp-verification

AVIFはプログレッシブレンダリングができないがそれ以上に小さいので良い

AVIF における 2 つの最大の問題は、エンコード速度とプログレッシブ レンダリングの欠如です。

AVIF 画像は、完全にダウンロードされるまで画面に何も表示されません。対照的に、プログレッシブ JPEG は、読み込み中に、画像の低品質の近似画像を非常に速く表示できます。プログレッシブJPEG が適切に配信されると、Web サイトの読み込みがはるかに速く見えるようになります。プログレッシブ JPEG は、ファイル サイズの半分で読み込まれたように見えます。AVIF は JPEG の半分のサイズで完全に読み込むことができるため、圧縮の利点によってプログレッシブ レンダリングの不足をある程度克服しています。この場合、プログレッシブ レンダリングも強力な圧縮も備えていない WebP だけが取り残されます。

https://f4.cosmoway.net/jpeg_progressive_baseline/
左の画像がベースライン、右がプログレッシブ。
ちなみにどちらの形式でもファイルサイズに大差はないっぽい。


saneatsusaneatsu

【2025-01-05】next-themes使用時にUncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.というエラーが発生している

https://www.npmjs.com/package/next-themes

背景

https://zenn.dev/hayato94087/articles/a340bfe21879a9

実装内容はほぼこれと一緒。
以下のような書き方をしていると発生する

sample.tsx
<ThemeProvider
  attribute="class" // <html> の class を参照
  defaultTheme="system" // システム設定の prefers-color-scheme に対応
  enableSystem // システム設定の prefers-color-scheme に対応
  disableTransitionOnChange // テーマ変更時のフラッシュを無効化
  storageKey="acme-theme" // テーマの設定をローカルステージに保存
>

修正方法

disableTransitionOnChange を削除するとエラーも消える。
有効にはしておきたいけどなぁ。
頑張ってもダメそうならIssue立てる。

sample.tsx
<ThemeProvider
  attribute="class" // <html> の class を参照
  defaultTheme="system" // システム設定の prefers-color-scheme に対応
  enableSystem // システム設定の prefers-color-scheme に対応
- disableTransitionOnChange // テーマ変更時のフラッシュを無効化
  storageKey="acme-theme" // テーマの設定をローカルステージに保存
>

「たまに起きるなぁ」と感じているエラー、しっかり時間を取ってなおすの大切。。
「動くしな」で"一旦"無視するを続けがち。

saneatsusaneatsu

【2025-01-05】React Router caught the following error during renderというエラーがconsoleに無限に出る

問題

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>
  )
}

解決策

https://zenn.dev/kazuki23/articles/35f9227e5c45b2

https://www.npmjs.com/package/react-ga4

react-ga4 を導入した。
最後に更新されたの2年前で85kBあるのか...。

saneatsusaneatsu

【2025-01-05】wrangler devで立ち上げると静的な画像ファイルが見つからないと言われる

背景

開発環境でwrangler devを実行してデバッグ中、以下のエラーが出ている。
これ、remix vite:dev だとエラーが発生しないまま動いてしまう。

[wrangler:inf] GET /static/404.jpg 404 Not Found (12ms)

原因

このようなディレクトリ構造になっているのが原因だった。

MyApp
├── app/
│   └── ...
├── static/
│   └── 404.jpg
└── ...

https://remix.run/docs/en/main/file-conventions/asset-imports

公式ドキュメントを見るとこのように書いてある。

appフォルダ内のファイルはすべてモジュールにインポートできます。 Remixは次のことを行います:

  1. ブラウザのビルドディレクトリにファイルをコピーする
  2. 長期キャッシュのためにファイルにフィンガープリントを付ける
  3. レンダリング中に使用される公開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に指定していた画像であまり使用される機会がなかったので気づかなかった。

このスクラップは2025/01/06にクローズされました