Shopify Hydrogen and Oxygen: Budování Performant React Storefront at the Edge
Hydrogen je oficiální rámec Shopify React založený na Remix: Storefront API GraphQL, primitivní komponenty vozíku, streamování SSR na Oxygen (edge Cloudflare Workers), optimalizace se strategiemi ukládání do mezipaměti a jak migrovat z tradičního tématu Liquid.
Vodík: Shopify's Commerce Framework
Shopify vodík je oficiální rámec React pro budování výloh bezhlavý Shopify. V aktuální verzi (Hydrogen 2.x) je na něm postaveno Remixy, meta-rámec React, který využívá nativní webová rozhraní API a akce serveru pro architekturu postupně zlepšoval.
Kyslík je globální hostingový runtime Shopify pro projekty Hydrogen: na základě Pracovníci Cloudflare (V8 Isolates), spouští kód na straně serveru na okraji — v datových centrech nejblíže ke koncovému uživateli. Výsledek je jeden TTFB (Time to First Byte) 30-80 ms globálně, se serverem nelze dosáhnout tradiční centralizované.
Nastavení vodíkového projektu
# 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 je založena na remixu a velmi podobná klasice souborové směrování:
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: The Hydrogen Core
Vodík se připojuje k Shopify přes Storefront API GraphQL. Každá operace commerce je dotaz nebo mutace GraphQL. Vodík poskytuje předkonfigurovaného 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: Spravujte svůj košík pomocí vodíku
Vodík řídí vozík přes Shopify Cart API (na základě GraphQL mutace). Rámec obsahuje optimistický systém uživatelského rozhraní pro okamžité aktualizace:
// 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>
);
}
Ukládání kyslíku do mezipaměti: klíč k výkonu
Oxygen spouští vodíkový kód na hraně Cloudflare Workers. Cache je spravována přes Nativní API Cloudflare a pomocníci s vodíkem:
// 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
});
Téma migrace z kapaliny na vodík
Pokud máte existující téma Shopify, migrace na Hydrogen je významný projekt. Doporučený přístup je přírůstkový:
- Identifikujte prioritní stránky: stránky produktů a stránky kolekce mají větší návštěvnost. Začněte tam.
-
Použijte vodík jako svou subdoménu:
shop.tuodominio.comna vodík,tuodominio.comna téma Liquid. Testujte a měřte výkon. - Migrujte pokladnu samostatně: Shopify zpracovává pokladny po svém nativní systém (Checkout Extensibility) — nemusíte jej přepisovat od začátku.
- Přeneste postupně: stránka po stránce, trasa po trase.
Omezení vodíku, která je třeba znát
-
Pokladna spravovaná službou Shopify: pokladna je vždy v doméně
Shopify (
checkout.shopify.com). Vzhled si můžete přizpůsobit pomocí služby Checkout Rozšiřitelnost, ale pokladnu za svou nenahradíte. - Částečné uzamčení dodavatele: Vodík/kyslík funguje pouze u Shopify backend. Pokud chcete změnit obchodní platformu, musíte přepsat frontend.
- Limity rychlosti API: Storefront API má omezení rychlosti. Pro hustý provoz vysoké zvážit agresivní ukládání do mezipaměti nebo vzor BFF s ukládáním do mezipaměti na straně serveru.
Závěry a další kroky
Vodík + kyslík je nejpragmatičtější volbou pro ty, kteří již v ekosystému pracují Shopify a chce jít bezhlavě. Kombinace Remix, GraphQL, edge computing a Nativní správa pokladen nabízí vynikající základ pro vysoce výkonné výlohy.
V příštím článku prozkoumáme Medusa.js, nejlepší alternativa open source headless: zcela samoobslužná, modulární architektura a bez uzamčení dodavatele.







