水素: Shopify のコマース フレームワーク

Shopify 水素 ストアフロントを構築するための公式 React フレームワークです ヘッドレスShopify。現在のバージョン (Hydrogen 2.x) では、以下に基づいて構築されています。 リミックス、 アーキテクチャのネイティブ Web API とサーバー アクションを活用する React メタフレームワーク 徐々に改善されました。

酸素 Shopify の Hydrogen プロジェクト用のグローバル ホスティング ランタイムです。 に基づいて Cloudflare ワーカー (V8 Isolates)、サーバー側コードを実行します エッジ — エンドユーザーに最も近いデータセンター内。結果は1つです 世界全体で 30 ~ 80 ミリ秒の TTFB (最初のバイトまでの時間)、サーバーでは達成不可能 伝統的な集中型。

水素プロジェクトの立ち上げ

# Crea un nuovo progetto Hydrogen
npm create @shopify/hydrogen@latest my-store

# Rispondi alle domande:
# - Store domain: mystore.myshopify.com
# - Storefront API token: [da Shopify Admin > Apps > Develop apps]
# - Language: TypeScript
# - CSS: Tailwind CSS

cd my-store
npm run dev
# Apre su http://localhost:3000

Hydrogen プロジェクトの構造は Remix に基づいており、古典的なプロジェクトと非常によく似ています。 ファイルベースのルーティング:

app/
├── components/           # Componenti UI riutilizzabili
│   ├── Cart.tsx          # Componente carrello
│   ├── ProductCard.tsx   # Card prodotto
│   └── Header.tsx        # Header con mini-cart
├── routes/              # File-based routing Remix
│   ├── _index.tsx        # Homepage (/)
│   ├── products.$handle.tsx  # Pagina prodotto (/products/[handle])
│   ├── collections.$handle.tsx  # Collection (/collections/[handle])
│   └── cart.tsx          # Pagina carrello (/cart)
├── lib/
│   ├── fragments.ts     # GraphQL fragments riutilizzabili
│   └── utils.ts         # Utility functions
├── root.tsx             # Root layout
└── entry.server.tsx     # Server-side entry point

ストアフロント API: 水素コア

Hydrogen は、以下を介して Shopify に接続します。 ストアフロント API GraphQL。あらゆる操作 commerce は GraphQL クエリまたはミューテーションです。 Hydrogen は、事前構成されたクライアントを提供します。

// app/routes/products.$handle.tsx
// Pagina dettaglio prodotto

import {json} from '@shopify/remix-oxygen';
import {useLoaderData} from '@remix-run/react';
import {Image, Money} from '@shopify/hydrogen';
import type {LoaderFunctionArgs} from '@shopify/remix-oxygen';

const PRODUCT_QUERY = `#graphql
  query Product($handle: String!, $selectedOptions: [SelectedOptionInput!]!) {
    product(handle: $handle) {
      id
      title
      descriptionHtml
      vendor
      selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {
        id
        availableForSale
        selectedOptions { name value }
        price { amount currencyCode }
        compareAtPrice { amount currencyCode }
        image { url altText width height }
      }
      options {
        name
        values
      }
      variants(first: 100) {
        nodes {
          id
          availableForSale
          selectedOptions { name value }
          price { amount currencyCode }
        }
      }
    }
  }
`;

export async function loader({ params, context, request }: LoaderFunctionArgs) {
  const { handle } = params;
  const { storefront } = context;

  // Leggi le opzioni selezionate dalla URL (?color=red&size=M)
  const searchParams = new URL(request.url).searchParams;
  const selectedOptions = [];
  for (const [name, value] of searchParams) {
    selectedOptions.push({ name, value });
  }

  const { product } = await storefront.query(PRODUCT_QUERY, {
    variables: { handle, selectedOptions },
    cache: storefront.CacheShort(),    // cache di 1 minuto su Oxygen
  });

  if (!product) throw new Response('Not Found', { status: 404 });

  return json({ product });
}

export default function Product() {
  const { product } = useLoaderData<typeof loader>();
  const { selectedVariant } = product;

  return (
    <div className="product">
      {selectedVariant?.image && (
        <Image
          data={selectedVariant.image}
          className="product-image"
          sizes="(min-width: 768px) 50vw, 100vw"
        />
      )}
      <h1>{product.title}</h1>
      {selectedVariant && (
        <Money data={selectedVariant.price} />
      )}
      <AddToCartButton variant={selectedVariant} />
    </div>
  );
}

Cart API: 水素でカートを管理

水素は、 ShopifyのカートAPI (に基づく GraphQL の変異)。このフレームワークには、即時更新のための楽観的な UI システムが含まれています。

// Aggiungere un prodotto al carrello
const ADD_TO_CART_MUTATION = `#graphql
  mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
    cartLinesAdd(cartId: $cartId, lines: $lines) {
      cart {
        id
        totalQuantity
        cost {
          totalAmount { amount currencyCode }
        }
        lines(first: 100) {
          nodes {
            id
            quantity
            merchandise {
              ... on ProductVariant {
                id
                title
                product { title }
                price { amount currencyCode }
                image { url altText }
              }
            }
          }
        }
      }
    }
  }
`;

// Componente AddToCartButton con Fetcher di Remix
import { useFetcher } from '@remix-run/react';

function AddToCartButton({ variant }: { variant: ProductVariant }) {
  const fetcher = useFetcher();
  const isAdding = fetcher.state !== 'idle';

  return (
    <fetcher.Form method="POST" action="/cart">
      <input type="hidden" name="intent" value="add" />
      <input type="hidden" name="variantId" value={variant.id} />
      <button
        type="submit"
        disabled={!variant.availableForSale || isAdding}
        className="add-to-cart-btn"
      >
        {isAdding ? 'Aggiunta...' : 'Aggiungi al Carrello'}
      </button>
    </fetcher.Form>
  );
}

Oxygen でのキャッシュ: パフォーマンスの鍵

Oxygen は、Cloudflare Workers エッジで Hydrogen コードを実行します。キャッシュは次の方法で管理されます。 CloudflareのネイティブAPIとHydrogenヘルパー:

// Strategie di cache disponibili in Hydrogen
// Definiamo cache personalizzate per tipo di contenuto

// Cache lunga per pagine prodotto (aggiornamento raro)
export async function loader({ context }: LoaderFunctionArgs) {
  const { storefront } = context;

  const data = await storefront.query(PRODUCTS_QUERY, {
    cache: storefront.CacheLong(),        // cache di 1 ora
  });

  return json(data, {
    headers: {
      'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400',
    },
  });
}

// Cache breve per dati variabili (inventory, prezzi)
const inventoryData = await storefront.query(INVENTORY_QUERY, {
  cache: storefront.CacheShort(),         // cache di 60 secondi
});

// Nessuna cache per dati personalizzati (cart, customer)
const customerData = await storefront.query(CUSTOMER_QUERY, {
  cache: storefront.NoStore(),            // no-cache
});

液体から水素への移行テーマ

既存の Shopify テーマをお持ちの場合、Hydrogen への移行は重要なプロジェクトです。 推奨されるアプローチは段階的です。

  1. 優先ページを特定する:商品ページとコレクションページ 彼らはより多くのトラフィックを受け取ります。そこから始めてください。
  2. Hydrogen をサブドメインとして使用する: shop.tuodominio.com 水素について、 tuodominio.com 液体のテーマについて。パフォーマンスをテストして測定します。
  3. チェックアウトを個別に移行する: Shopify 独自のメソッドでチェックアウトを処理します ネイティブ システム (チェックアウト拡張性) — 最初から書き直す必要はありません。
  4. 徐々に転送: ページごと、ルートごと。

知っておくべき水素の限界

  • チェックアウトはShopifyによって管理されます: チェックアウトは常にドメイン上で行われます Shopify (checkout.shopify.com)。 Checkout で外観をカスタマイズできます 拡張性がありますが、チェックアウトを独自のものに置き換えることはできません。
  • 部分的なベンダーロックイン: 水素/酸素は Shopify でのみ機能します。 バックエンド。コマース プラットフォームを変更したい場合は、フロントエンドを書き直す必要があります。
  • API レート制限: Storefront API にはレート制限があります。交通量が多い場合 高積極的なキャッシュ、またはサーバー側キャッシュを使用した BFF パターンを検討します。

結論と次のステップ

すでにエコシステムで働いている人にとって、水素 + 酸素は最も実用的な選択です Shopify をヘッドレス化したいと考えています。 Remix、GraphQL、エッジ コンピューティング、 ネイティブのチェックアウト管理は、高性能の店頭のための優れた基盤を提供します。

次の記事では、詳しく見ていきます Medusa.js、オープンソースの最良の代替品 ヘッドレス: 完全に自己ホスト可能なモジュラー アーキテクチャであり、ベンダー ロックインはありません。