Shopify Hydrogen and Oxygen: Tworzenie wydajnej witryny sklepowej React na krawędzi
Hydrogen to oficjalny framework Shopify React oparty na Remix: Storefront API GraphQL, prymitywne komponenty wózków, przesyłanie strumieniowe SSR na Oxygen (edge Cloudflare Workers), optymalizacja ze strategiami buforowania i migracją z tradycyjnego motywu Liquid.
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:
- Zidentyfikuj strony priorytetowe: strony produktów i strony kolekcji otrzymują większy ruch. Zacznij tam.
-
Użyj wodoru jako swojej subdomeny:
shop.tuodominio.comna wodorze,tuodominio.comw temacie Płyn. Testuj i mierz wydajność. - Przeprowadź osobną migrację realizacji transakcji: Shopify obsługuje realizację transakcji za pomocą własnego system natywny (Extensibility Checkout) – nie musisz pisać go od nowa.
- 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.







