Shopify Waterstof en Zuurstof: Bouw een performante React Storefront aan de Edge
Hydrogen is het officiële Shopify React-framework gebaseerd op Remix: Storefront API GraphQL, primitieve winkelwagencomponenten, SSR-streaming op Oxygen (edge Cloudflare Workers), optimalisatie met cachingstrategieën en hoe u kunt migreren van een traditioneel Liquid-thema.
Waterstof: Shopify's Commerce Framework
Shopify Waterstof is het officiële React-framework voor het bouwen van winkelpuien hoofdloze Shopify. In de huidige versie (Waterstof 2.x) is er op voortgebouwd Remixen, het React-metaframework dat gebruikmaakt van native web-API's en serveracties voor een architectuur geleidelijk verbeterd.
Zuurstof is Shopify's wereldwijde hostingruntime voor waterstofprojecten: gebaseerd op Cloudflare-werknemers (V8 isolaten), voert code aan de serverzijde uit aan de rand: in de datacenters die het dichtst bij de eindgebruiker staan. Het resultaat is één TTFB (Time to First Byte) van 30-80 ms wereldwijd, onmogelijk te bereiken met een server traditioneel gecentraliseerd.
Opzetten van een waterstofproject
# 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
De structuur van een waterstofproject is gebaseerd op Remix en lijkt sterk op de klassieker op bestanden gebaseerde routering:
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: de waterstofkern
Waterstof maakt verbinding met Shopify via de Storefront-API GraphQL. Elke operatie commerce is een GraphQL-query of -mutatie. Hydrogen biedt de vooraf geconfigureerde client:
// 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>
);
}
Winkelwagen-API: beheer uw winkelwagen met waterstof
Waterstof beheert de kar via de Shopify's winkelwagen-API (gebaseerd op GraphQL-mutaties). Het raamwerk bevat een optimistisch UI-systeem voor onmiddellijke updates:
// 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>
);
}
Caching op zuurstof: de sleutel tot prestaties
Oxygen voert Hydrogen-code uit op de Cloudflare Workers-rand. De cache wordt beheerd via Native API's en waterstofhelpers van Cloudflare:
// 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
});
Migratie van Vloeibaar naar Waterstof thema
Als je een bestaand Shopify-thema hebt, is de migratie naar Hydrogen een aanzienlijk project. De aanbevolen aanpak is stapsgewijs:
- Prioriteitspagina's identificeren: productpagina's en collectiepagina's ze ontvangen meer verkeer. Begin daar.
-
Gebruik Hydrogen als uw subdomein:
shop.tuodominio.comover waterstof,tuodominio.comover het Liquid-thema. Test en meet prestaties. - Afrekenen apart migreren: Shopify verzorgt het afrekenen met zijn eigen systeem native systeem (Checkout Extensibility) — u hoeft het niet helemaal opnieuw te schrijven.
- Geleidelijk overbrengen: pagina voor pagina, route voor route.
Beperkingen van waterstof om te weten
-
Afrekenen beheerd door Shopify: afrekenen vindt altijd plaats op het domein
Shopify (
checkout.shopify.com). U kunt het uiterlijk aanpassen met Afrekenen Uitbreidbaarheid, maar u kunt de kassa niet vervangen door uw eigen kassa. - Gedeeltelijke leverancierslock-in: Waterstof/Zuurstof werkt alleen met Shopify zoals backend. Als je het handelsplatform wilt veranderen, moet je de frontend herschrijven.
- Limieten voor API-snelheden: De Storefront API heeft een snelheidslimiet. Voor zwaar verkeer hoog, overweeg agressieve caching of een BFF-patroon met caching op de server.
Conclusies en volgende stappen
Waterstof + Zuurstof is de meest pragmatische keuze voor degenen die al in het ecosysteem werken Shopify en wil zonder hoofd gaan. De combinatie van Remix, GraphQL, edge computing en de Native kassabeheer biedt een uitstekende basis voor krachtige winkelpuien.
In het volgende artikel zullen we het verkennen Medusa.js, het beste open source alternatief headless: volledig zelf-hostbaar, modulaire architectuur en geen leverancierlock-in.







