Wodór: Ramy handlowe Shopify

Shopify Wodór to oficjalny framework React do tworzenia witryn sklepowych bezgłowy Shopify. W aktualnej wersji (Hydrogen 2.x) jest ona zbudowana Remiksy, metaframework React, który wykorzystuje natywne interfejsy API sieci Web i akcje serwera w architekturze stopniowo poprawiane.

Tlen to globalne środowisko uruchomieniowe hostingu Shopify dla projektów wodorowych: oparty na Pracownicy Cloudflare (V8 Isolates), uruchamia kod po stronie serwera na brzegu — w centrach danych znajdujących się najbliżej użytkownika końcowego. Wynik jest jeden TTFB (czas do pierwszego bajtu) wynoszący 30–80 ms na całym świecie, niemożliwy do osiągnięcia na serwerze tradycyjny scentralizowany.

Konfiguracja projektu wodorowego

# 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

Struktura projektu Hydrogen oparta jest na Remixie i bardzo przypomina klasykę routing oparty na plikach:

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

Storefront API: rdzeń wodorowy

Hydrogen łączy się z Shopify za pośrednictwem Interfejs API sklepu GraphQL. Każda operacja commerce to zapytanie lub mutacja GraphQL. Hydrogen zapewnia wstępnie skonfigurowanego klienta:

// 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: zarządzaj swoim koszykiem za pomocą wodoru

Wodór zarządza wózkiem poprzez Interfejs API koszyka Shopify (na podstawie Mutacje GraphQL). Framework zawiera optymistyczny system interfejsu użytkownika umożliwiający natychmiastowe aktualizacje:

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

Buforowanie w tlenie: klucz do wydajności

Tlen uruchamia kod wodorowy na krawędzi pracowników Cloudflare. Pamięć podręczna jest zarządzana poprzez Natywne interfejsy API Cloudflare i pomocnicy 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
});

Migracja z motywu cieczy do wodoru

Jeśli masz istniejący motyw Shopify, migracja do Hydrogen to znaczący projekt. Zalecane podejście jest przyrostowe:

  1. Zidentyfikuj strony priorytetowe: strony produktów i strony kolekcji otrzymują większy ruch. Zacznij tam.
  2. Użyj wodoru jako swojej subdomeny: shop.tuodominio.com na wodorze, tuodominio.com w temacie Płyn. Testuj i mierz wydajność.
  3. Przeprowadź osobną migrację realizacji transakcji: Shopify obsługuje realizację transakcji za pomocą własnego system natywny (Extensibility Checkout) – nie musisz pisać go od nowa.
  4. Przenoś stopniowo: strona po stronie, trasa po trasie.

Ograniczenia wodoru, które należy poznać

  • Realizacja transakcji zarządzana przez Shopify: kasa jest zawsze w domenie Shopify (checkout.shopify.com). Możesz dostosować wygląd za pomocą usługi Checkout Rozszerzalność, ale nie można zastąpić kasy własną.
  • Częściowe zablokowanie dostawcy: Wodór/tlen działa tylko z Shopify zaplecze. Jeśli chcesz zmienić platformę handlową, musisz przepisać frontend.
  • Limity szybkości API: Interfejs API Storefront ma ograniczenie szybkości. Dla dużego ruchu wysoko rozważ agresywne buforowanie lub wzorzec BFF z buforowaniem po stronie serwera.

Wnioski i dalsze kroki

Wodór + tlen to najbardziej pragmatyczny wybór dla osób już pracujących w ekosystemie Shopify i chce iść bez głowy. Połączenie Remix, GraphQL, obliczeń brzegowych i Natywne zarządzanie kasami stanowi doskonałą podstawę dla wydajnych witryn sklepowych.

W następnym artykule będziemy badać Medusa.js, najlepsza alternatywa typu open source headless: całkowicie samonośny, modułowa architektura i brak uzależnienia od dostawcy.