Shopify Liquid ve Vue 3'ü birleştirmenin en temiz yolu değil #app'de monte edilmiş tam bir SPA. Bunun yerine, her biri defineCustomElement tarafından desteklenen, HTML öznitelikleri olarak sunucu tarafından oluşturulan Liquid verileri alan ve buradan kendi reaktif durumlarını yöneten bir dizi dikkatli şekilde yerleştirilmiş Özel Öğe bulunmaktadır. Liquid veri serileştirmesi ve başlangıç işaretlemesini işler; Vue 3 etkileşimi yönetir. İki katman DOM'un üzerinde hiçbir zaman çatışmaz.
Temel noktalar
- Vue 3'ün
defineCustomElementişlevi herhangi bir.ce.vueTek Dosya Bileşenini yerel bir Özel Öğeye dönüştürerek bunu herhangi bir Liquid şablonuna doğrudan yerleştirebilirsiniz. - Liquid,
jsonfiltresi aracılığıyla Shopify verilerini JSON dizelerine serileştirir; Vue bunları monte olduğundapropsveya veri kümesi öznitelikleri olarak okur. - Section Rendering API (ve bundled cart varyantı) tam sayfa yeniden yüklemesi olmadan sunucu tarafı yeniden işlemeyi gerçekleştirir, böylece Vue reaktif katman kalırken Liquid veri kaynağı kalır.
- Tema bileşenleri için Shadow DOM'dan kaçının. Shopify'nin kendi Dawn ve Horizon temaları, tema CSS'sinin ve Tema Editörü'nün içine ulaşabilmesi için özel olarak açık DOM'lu Özel Öğeler kullanır.
- Vue 3 Temmuz 2026'de kararlı sürüm 3.5.40 yayınlanmıştır, bu nedenle bu yazının hedeflediği API yüzeyi güncel ve kararlıdır.
Neden Vue'yu kökünde monte etmeyelim?
Gördüğünüz her "Vue'yu Shopify'ye entegre et" öğreticisi, tüm sayfanın üzerine tek bir kök örneği monte eder. Bu Tema Editörü'nü kırar: editör, postMessage aracılığıyla bölümlerle iletişim kurar ve bölüm DOM düğümlerini ekleme, kaldırma ve yeniden sıralama yeteneğine sahip olmayı bekler. Tüm belgeyi sahiplenen kök Vue örneği buna karşı koymaya çalışacaktır. Canlı önizlemeyi, bölüm vurgulamasını ve blok sürüklemeyi kaybedersiniz.
Doğru mental model şudur: Liquid sayfayı işler. Vue bireysel etkileşimli adaları sahiplenir. Bu tam olarak Shopify'nin kendi temalarının nasıl çalıştığıyla eşleşir. Dawn ve Horizon, sepet, varyant ve medya etkileşimi için düz Özel Öğeleri (class ProductForm extends HTMLElement) kaydederken, diğer her şey Liquid'ten sunucu tarafından oluşturulan HTML'dir. Vue 3 bunu defineCustomElement tam bir Composition API bileşenini tarayıcı yerel özel öğeye birkaç satırda dönüştürdüğü için çok daha az deşme ile yapmanızı sağlar.
Üç katmanlı yığın
| Katman | Teknoloji | Sorumluluk |
|---|---|---|
| Veri ve işaretleme | Shopify Liquid | Ürünleri, koleksiyonları, sepeti, meta alanları sunucu tarafından işle |
| Reaktif adalar | Vue 3 Özel Öğeler | Varyant seçiciler, mini sepet, miktar adım atıcıları, görüntü yakınlaştırma |
| Bölüm güncellemeleri | Section Rendering API | Sepet mutasyonlarından sonra sunucu tarafından oluşturulan HTML'yi yeniden getir |
Bu bölünme, Liquid mantığını JavaScript'te çoğaltmadığınız anlamına gelir. Sunucu, fiyatlar, kullanılabilirlik ve metaalan alanları için gerçeğin kaynağı kalır. Vue yalnızca tam navigasyonlar arasında tarayıcıda ne değiştiğini yönetir.
Liquid verilerini Vue Özel Öğesine bağlama
Köprü, json Liquid filtresiyedir. Shopify değeri sunucu tarafında işler; Vue bunu connectedCallback'te bir prop olarak okur.
Liquid bölüm dosyanızda (sections/product-form.liquid):
<variant-picker
data-product='{{ product | json | escape }}'
data-selected='{{ product.selected_variant.id }}'
></variant-picker>
Vue bileşeninizde (src/components/VariantPicker.ce.vue):
<script setup>
import { computed } from 'vue'
const props = defineProps({
product: { type: String, required: true },
selected: { type: String, default: '' }
})
const parsed = computed(() => JSON.parse(props.product))
const variants = computed(() => parsed.value.variants)
</script>
<template>
<div class="variant-picker">
<button
v-for="v in variants"
:key="v.id"
:aria-pressed="String(v.id) === selected"
@click="$emit('variant-changed', v.id)"
>
{{ v.title }}
</button>
</div>
</template>
Tema giriş JS'nizde (assets/theme.js):
import { defineCustomElement } from 'vue'
import VariantPickerSFC from '../src/components/VariantPicker.ce.vue'
const VariantPicker = defineCustomElement(VariantPickerSFC)
if (!customElements.get('variant-picker')) {
customElements.define('variant-picker', VariantPicker)
}
if (!customElements.get(...)) koruması kritik önem taşır. Shopify bölümleri aynı sayfada birden çok kez işlenebilir (öne çıkan ürünler, ilgili ürünler), bu nedenle aynı komut dosyası etiketi birden fazla kez yürütülebilir. Zaten kayıtlı bir adla customElements.define() çağırmak, bileşeni sessizce kılan bir NotSupportedError atar.
.ce.vue dosya uzantısı kuralına dikkat edin: Vite'nin Vue eklentisi .ce.vue ile biten dosyaları farklı şekilde işler, stilleri bileşenin gölge kökü yerine belge başına enjekte eder. Shopify temaları için açık DOM (Shadow DOM yok) istediğiniz için, bunu defineCustomElement seçeneği olarak shadowRoot: false iletilerek geçersiz kılabilirsiniz veya basitçe <style> bloğu dahil etmeyerek CSS'yi tema stil sayfanızdan yönetebilirsiniz.
Shadow DOM'dan kaçınma
Bu kendine ait bir bölüm hak ediyor çünkü bunu ilk kez hemen hemen her geliştiriciyi saptırıyor.
defineCustomElement varsayılan olarak Shadow DOM kullanır ve bu bir stil sınırı oluşturur. Temanızın base.css dosyası içine ulaşmaz. Tema Editörü'nün bölüm hover taslağı düzgün işlenmez. Shopify'nin kendi temaları tam bu nedenle normal (açık) DOM'lu Özel Öğeler kullanır.
Vue özel öğesinde Shadow DOM'u devre dışı bırakmak için:
const VariantPicker = defineCustomElement(VariantPickerSFC, {
shadowRoot: false
})
shadowRoot: false seçeneği Vue 3.3'te tanıtılmıştır. Daha eski bir minör sürümü kullanıyorsanız, yaratmadan sonra özel öğe sınıfında renderRoot() tanımlayarak aynı etkiyi elde edebilirsiniz. Ancak Vue 3.5.x'te (Temmuz 2026 itibariyle güncel kararlı sürüm), seçenek temiz yoldur.
Sepet döngüsü: Vue + Section Rendering API
Sepetdeki durum yönetimi, çoğu Liquid-artı-Vue uygulamalarının başarısız olduğu yerdir. Üretimde tutulan desen şu şekildedir:
- Vue'un mini-sepet bileşeni,
sectionsparametresiylePOST /cart/add.jsçağırır. - Cart AJAX API'si sepeti mutasyona uğratır ve istenen Liquid bölümlerini aynı yanıtta sunucu tarafında yeniden işler, bunları
response.sectionsaltında döndürür. - Vue, sunucu yanıtından bölüm sarıcısının iç HTML'sini değiştirir, ardından herhangi bir tamamen istemci tarafı sayıyı görüntüle için kendi reaktif durumunu günceller.
Bundled bölüm oluşturma yaklaşımı, iki yerine bir ağ isteği yapmanız anlamına gelir. Resmi Shopify Cart API'si, /cart/add, /cart/change, /cart/clear ve /cart/update üzerinde bundled oluşturmayı destekler.
// Vue mini-sepet composable'ınızın içinde
async function addItem(variantId, quantity = 1) {
const res = await fetch(window.Shopify.routes.root + 'cart/add.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: [{ id: variantId, quantity }],
sections: 'cart-drawer,cart-icon-bubble'
})
})
const data = await res.json()
// Sunucu HTML'sinden Liquid bölümlerini yeniden işle
for (const [id, html] of Object.entries(data.sections ?? {})) {
const el = document.getElementById(`shopify-section-${id}`)
if (el) el.innerHTML = html
}
// Sayı rozet için Vue reaktif durumunu güncelle
cartCount.value = data.items_count
}
Bu desen, Shopify'nin Horizon temasının dahili olarak kullandığı şeydir: bölüm kimlikleri işlenmiş sarıcı üzerindeki data- özniteliklerinden okunur ve kod içine yazılmaz, bu nedenle sepet çekmecesinin id özniteliği (Shopify shopify-section- ön ekini alır) çalışma zamanında varsayılmak yerine çözülür.
İnşa kurulumu: Vite + Shopify CLI
Araçlama kararı önemlidir. 2026 Vue 3 ile Shopify tema geliştirmesi için önerilen kurulum:
- Shopify CLI 3.x geliştirme sunucusu ve tema push katmanı olarak.
- Vite (
@vitejs/plugin-vue) varlık bundler olarak,assets/klasörüne tek birtheme.jsvetheme.cssçıktısı verir. isCustomElementderleyici seçeneğivite.config.jsiçinde Vue'nun derleyicisine özel öğe etiketlerinizin amaçlı olduğunu söylemek için "bilinmeyen bileşen" uyarılarını bastırır.
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) =>
['variant-picker', 'mini-cart', 'quantity-stepper'].includes(tag)
}
}
})
],
build: {
outDir: 'assets',
rollupOptions: {
input: 'src/theme.js',
output: { entryFileNames: 'theme.js', assetFileNames: 'theme.css' }
}
}
})
Bir terminalde vite build --watch ve ikinci terminalde shopify theme dev çalıştırın. Vite, her kaydetmede assets/theme.js yeniden derler; CLI değiştirilen varlığı geliştirme önizlemesine gönderir. Webpack yok, tema-kit yok.
Durum yönetimi: Pinia'yı atlayın, composable'lara yönelin
Çoğu Shopify tema kullanım durumu için Pinia aşırıdır. Özel Öğeler tanım gereği yalıtılmıştır. Bunun yerine neye ihtiyacınız var:
- Composable'lar (
useCart,useVariant)refdurumunu tutan ve mutasyon yöntemlerini ortaya çıkaran. provide/injectebevey Özel Öğesinin iç içe geçmiş biriyle durumu paylaşması gerekirse (örneğin, kaydırma sırasında yapışkan sepeti ekleme çubuğu ile koordine eden bir ürün formu).- Bir paylaşılan singleton modülü (
src/state/cart.jstek birreactivesepet nesnesi ihraç eden) iki tamamen ayrı Özel Öğenin her ikisinin de sepet sayısını okuması gerekirse.
Singleton modülü deseni sepet sayısı rozeti için doğru cevaptır. Hem mini-cart hem de cart-icon-bubble Özel Öğesi tanımlarında aynı modülü içeri aktarın. Vite her şeyi tek bir theme.js'e paketlediği için, modül bir kez değerlendirilir ve reaktif nesne gerçekten paylaşılır.
Bileşen sınırları: Vue'ya ne, Liquid'te ne bırakmalı
Vue Özel Öğesine şununla sarın:
- UI, navigasyon olmadan değişen yerel durumu gerektirir (varyant seçimi, miktar güncellemeleri, seçenekleri değiştirme).
- İyimser UI'a ihtiyaç duyarsınız (sunucu doğrulamadan önce sepet sayılarının artımını gösterin).
- Bileşenin karmaşık olay orkestrasyonu vardır (resim yakınlaştırması varyant değişikliğiyle senkronize edilir).
Düz Liquid'te tutun:
- İçerik sayfa yükü başına statiktir (ürün açıklaması, koleksiyon başlığı, altbilgi).
- Tema Editörü aracılığıyla tüccar özelleştirmesi ana gereksinimdir (Vue değil, bölümleri ve blokları kullanın).
- Bileşen doğrudan Shopify bölüm şema ayarına eşlenir.
10.000 etkin SKU'dan fazlasını içeren bir kataloğu koruyorum, sadece beş bileşen türü Vue özel öğeleridir: varyant seçici, miktar adım atıcı, mini-sepet çekmecesi, isteme ekle geçiş ve karşılaştırma tepsi. Diğer her şey Liquid'tir. Temanın Lighthouse performans puanları yeşil kaldı çünkü Vue DOMContentLoaded kadar içe aktarılmaz ve beş bileşenin tümü gzip min+gzip Vue 3 çalışma zamanının (33.9 KB) toplam etkileşim kazancına değer olmasını sağlayan kadar küçüktür.
Yaklaşım karşılaştırması
| Yaklaşım | Tema Editörü desteği | Bundle boyutu etkisi | Karmaşıklık | Ne zaman kullanacak |
|---|---|---|---|---|
CDN <script> + ada başına createApp | Kısmi (bölüm yeniden sıralamasını bozar) | Orta (ada başına Vue örneği) | Düşük | Sadece prototip yapma |
Tam sayfa üzerinde kök createApp | Editörü bozar | Bir Vue çalışma zamanı | Düşük | Canlı temalarda asla |
defineCustomElement + açık DOM | Tam | Paylaşılan bir Vue çalışma zamanı | Orta | Üretim: etkileşimli adalar |
| Hydrogen (headless) | Yok | Tam React/Remix bundle | Yüksek | Tam özel depo vitrin |
Entegrasyonun işe yaradığını doğrulama
Sevk etmeden önce üç kontrol:
- Tema Editörü kontrolü. Editörü açın, özel öğesi içeren bir bölümü yeni bir konuma sürükleyin. Öğenin yeniden başlatıldığını ve konsolda
NotSupportedErroratmadığını doğrulayın. Öyleyse,customElements.get()koruma eksik. - Hidrasyona zamanlamadan kontrol. DevTools'ta, Slow 3G'ye denetleyin. Liquid tarafından oluşturulan HTML'nin JavaScript yürütülmeden önce görünür olduğunu doğrulayın. Özel öğenizin
<template>connectedCallbackyanıt verirken bir yükleme durumu işlerse, yavaş bağlantılı ziyaretçiler boş bir yuva değil, bir şey görürler. - Bölüm oluşturma gidiş dönüş kontrolü. Ağ sekmesinde, sepete bir öğe ekleyin ve yalnızca bir isteğin
/cart/add.jsadresine gitmesini doğrulayın./cart.jsadresine ikinci bir istek görürseniz, bundled bölüm oluşturmasectionsparametresini doğru şekilde iletmiyor demektir.
İnşa sırasında ortaya çıkan devam eden Liquid söz dizimi soruları için bölüm şeması ve veri serileştirmesi hakkında, benim Shopify Liquid başvurusu sözlüğü filtreleri, nesneleri ve etiket davranışını bir yerde kapsar.
Bu işi dışarıya vermek isteyen mağazalar için Lintel gibi stüdyolar Shopify tema geliştirme hizmeti veriyor.
Sıkça sorulan sorular
defineCustomElement bileşeninin içinde tam Vue 3 Composition API'sini kullanabilir miyim?
Evet. defineCustomElement normal bir Vue bileşenini sarmaladığı için, ref, reactive, computed, watch, provide, inject ve yaşam döngüsü kancalarına tam erişime sahipsiniz. Tek fark, stillerin varsayılan olarak gölge kökün içine enjekte edilmesidir. Shopify temaları için shadowRoot: false iletilerek bunu devre dışı bırakmak isteyeceksiniz.
Vue 3 kullanan Shopify temasında durum yönetimi için Pinia'ya ihtiyacım var mı?
Çoğu tema kullanım durumu için hayır. Tek bir dosyadan dışa aktarılan ve tüm özel öğe tanımlarında içe aktarılan paylaşılan singleton reaktif modülü, Pinia'nın ek yükü olmadan sepet sayısı paylaşımını başarır. Pinia'yı, birçok özel öğeye yayılan gerçekten karmaşık çapraz bileşen durumunuz olduğu durumlar için saklayın.
Vue 3 özel öğeleri Shopify'nin Tema Editörü canlı önizlemesini kıracak mı?
Yalnızca Shadow DOM kullanırsan veya Vue'yu tüm belge üzerinde monte edersen. Açık DOM'lu Özel Öğeler (shadowRoot: false), Shopify yine de DOM düğümlerine normal şekilde erişebildiği ve yeniden sıralayabildiği için Tema Editörü ile tam uyumludur.