Dərin dərs: Shopify's Storefront API'sini React və Redux ilə istifadə edin

Hər kəs üçün elektron ticarət! (... veb saytlar, bu )

Avqust 2018-də Chris tərəfindən yazılıb, 2018-ci il noyabr ayında yeniləndi

Pexels.com saytında neqativ məkan

Fon və motivasiya

Beləliklə, burada motivasiya olduqca sadə idi. Shopify veb saytımıza getmədən veb saytımdakı ziyarətçilərin məhsullarımı xüsusi domenimdə axtara, tapa və seçə bilmələrini istəyirdim.

İkincisi motivasiya, Shopify-ın fabrika şablonlarından birini istifadə etməkdənsə, bir veb sayt üçün öz kod bazamın olmasını istəməyimdir. Shopify komandasına hücum yoxdur! Şablonlar müasir və təmizdir, lakin olduqca sadədir. Bu şablonların yüksək dərəcədə özelleştirilebildiğine əminəm, amma hal-hazırda bunlardan xəbərdar deyiləm.

Beləliklə, bu, hər iki dünyanın ən yaxşısıdır - Shopify API'si və ödəmə prosesi əlavə edilmiş xüsusi Reakt saytım (artıq qurulmuş və onlayn)!

Bu təlimatın sonunda Shopify məhsullarınızı veb saytınızdakı istənilən səhifəyə əlavə edə biləcəksiniz. Shopify-da baş verən alış-veriş prosesinin yeganə hissəsi, istifadəçinin kassanı vurmasıdır.

Həm də bu təlim üçün boş bir qazan plitəsi deposu yaratdım.

Burada Medium-da yazma motivasiyası sadəcə bu proseslə bağlı bir dərs tapa bilməməyim idi - buna görə birini etməyə qərar verdim!

4 ildir peşəkar inkişafçıyam və 7 ildir ki, proqram hazırlayıram, köhnə məktəb Fortran və Perl-dən React, Javascript, Python və Node-a qədər texniki yığınlarda işləmişəm.

Siren Apparel, 5 ildir çalışdığım layihə / start-up / istehsalçı şirkətlərimdən biridir və bu günə qədər 5 fərqli polis və yanğınsöndürmə hissəsinə yardım etdik!

Nəhayət bu dərsliklə başlayaq.

Shopify Storefront API

Shopify-dakı ecazkar insanlar Storefront API-ni bir yerə yığdılar. Storefront API'si, Shopify olmayan saytınızda məhsul şəkilləri, məhsul variantları, məhsul ölçüləri, alış-veriş sepeti və "Səbətə əlavə et" və "Ödəmə" düymələrini əlavə etmək üçün Cavab Bileşenləri yaratmağa imkan verir.

* Diqqət yetirin ki, bu təlimat Shopify Mağaza İdarəetməsinin özü üçün React-də komponentlər yaratmaq üçün istifadə olunan Shopify Polaris ilə əlaqəli deyil.

İlk addımlar: reaksiya-js-buy deposu

Shopify komandası tərəfindən yaradılan bu React nümunəsinə baxın. Bu təlimatdakı kodların çoxu bu depodan gəlir.

... bir baxdın? Yaxşı!

İndi birbaşa kodu atlayaq! React saytınızın kök qovluğuna gedin və shopify-buy modulunu terminaldan quraşdırın:

cd my-awesome-react-project / npm install - shopify-buy qeyd edin

(və ya ipək seçsəniz alış-veriş etmək üçün iplik əlavə edin)

Sonra müştərini index.js (App.js DEYİL!) Faylınızdakı JS Buy SDK-dan idxal etməlisiniz:

Müştərini 'shopify-buy' məhsulundan idxal et;

Sonra ReactDOM.render () çağırışının üstünə aşağıdakı konfiqurasiya obyektini əlavə edin:

const client = Client.buildClient ({storefrontAccessToken: 'giriş əlamətiniz', domen: 'your-shopify-url.myshopify.com'});

İndex.js üçün hər şey budur - tezliklə buna çatacağıq.

İndi rahat bir alış-veriş və ödəmə təcrübəsi üçün lazım olan bütün komponentləri əlavə edək. Bütün komponentləri react-js-buy deposundan kopyalayın:

Səbət.js

LineItem.js

Məhsul.js

Məhsullar.js

VariantSelector.js

Bu komponentləri src / qovluğunuzdakı bir Komponent / shopify / qovluğuna qoyacağıq. İstəyirsinizsə, bu komponent sənədlərini başqa bir yerə src / qovluğuna yerləşdirə bilərsiniz. Təlimatın qalan hissəsi bunları komponentlərdə / shopify / içində olduğunuzu düşünür.

App.js dəyişdirin

App.js-in geniş dəyişikliklərə ehtiyacı var. Əvvəlcə öz layihənizə kopyaladığınız səbət komponentini idxal edin:

Avtomobilləri './component/shopify/Cart' hesabından idxal edin;

App.js komponentiniz mənim kimi vətəndaşsız idisə, bütün bu konstruktor () funksiyasını təhlükəsiz surətdə kopyalamalısınız:

Constructor () {super (); this.updateQuantityInCart = this.updateQuantityInCart.bind (bu); this.removeLineItemInCart = this.removeLineItemInCart.bind (bu); this.handleCartClose = this.handleCartClose.bind (bu); }

Artıq bir statusunuz varsa, bu məcburi sətirləri kopyalamağınız kifayətdir. Bu üç xətt, Shopify alış-veriş səbətinin düzgün işləməsi üçün lazım olan hadisə idarəedici funksiyalardır.

"Bəs avtomobil üçün vəziyyət nədir?"

Soruşa bilərsən; və ya:

"Alış-veriş sepeti üçün bu hadisə idarəedicilərini təyin etmək nədir?"

Gələcək, amma hələ yox!

Daha sonra istifadə edə bilərsiniz Div bitmədən əvvəl göstərmə () funksiyanızın alt hissəsinə komponent əlavə edin.

Zənnimcə, alış-veriş səbəti tətbiqinizin hər yerində olmalıdır. Bu səbəbdən komponenti istifadə etməyin məntiqli olduğunu düşünürəm tətbiqinizin kök komponentində - başqa sözlə, App.js:

Qayıt ( ... );

Yenə də səbət hadisəsi işləyicilərinə hələ heç bir kod əlavə etməmişəm. Ayrıca, App.js.-də alış-veriş sepeti üçün status komponentlərinin çatışmazlığını düzəltmədim.

Bunun yaxşı səbəbləri var.

Bu layihənin ortasında məhsul komponentimin əlbəttə ki, App.js sənədimdə olmadığını gördüm.

Bunun əvəzinə, üç uşaq komponenti üzərində basdırıldı.

Beləliklə, məhsulları üç səviyyədə uşaqlara ötürmək və sonra funksiya işləyicilərini zirvəyə qaytarmaq əvəzinə ...

Mən seçdim…

Redux !!!

Pooh! Bilirəm, Redux-u çox çətin olmasa da,% * $ -da bir ağrı olduğunu bilirəm! başlanğıcda bütün tələb olunan qazan plitəsi ilə qoşulmaq. Bununla birlikdə, bir e-ticarət mağazasında çalışan bir geliştiriciyseniz və ya bir e-ticarət ticarəti sahibisinizsə, bunu təsəvvür edin: Redux veb saytımızdakı və ya veb tətbiqimizdəki hər hansı bir komponentdən və ya səhifədən alış-veriş səbətinin vəziyyətinə daxil olmağa imkan verir. .

Siren Giyim genişləndikcə və daha çox məhsul inkişaf etdirdikcə bu qabiliyyət vacib olacaqdır. Daha çox məhsul yaratsaq, bütün məhsullarla ayrıca bir mağaza səhifəsi yaradacağam və yalnız ana səhifədə bir neçə seçmə məhsul buraxacağam.

Bir istifadəçi kiçik bir alış-veriş edərkən, bəzi əşyalar və ya Siren Geyimi haqqında məlumatları oxuduqdan sonra yoxlamağa qərar verdikdə alış-veriş səbətinə giriş imkanı vacibdir. Nə qədər hərəkət etməyinizin əhəmiyyəti yoxdur, səbətinizdən heç bir şey itirilmir!

Bir sözlə, kod bazası veb saytımız üçün çox böyük olmadığı halda Redux-un indi tətbiq olunmasının daha yaxşı olduğuna qərar verdim.

Shopify üçün Redux-un tətbiqi, Çılpaq Minimum qazan plitəsi ilə SDK almaq

NPM paketlərini redux və react-redux quraşdırın:

npm yüklə - redux reaksiya-redux qeyd edin

./Store-dan react-redux təminatçını və mağazanızı index.js-ə daxil edin:

{React} redux'dan {provider} idxal edin; Yükü './store' dan idxal et;

Sarın -Komponent mağazanıza köçürülmüşdür tətbiqinizi Redux mağazanıza bağlamaq üçün index.j-də:

ReactDOM.render ( , document.getElementById ('kök'));

(Diqqət yetirin ki, mənim də var Siren Apparel veb saytındakı məzmunu dinamik şəkildə göstərmək üçün beynəlmiləlləşmə və lokalizasiyadan necə istifadə etdiyim barədə başqa bir yazıda. Başqa bir gün üçün başqa bir hekayə.)

Əlbətdə ki, hələ bir ./store.js faylı yaratmamışıq. Mağazanızı store.jsin src / root-də yaradın və bunları yapışdırın:

{createStore} 'redux' -dan idxal; './Reducers/cart' dan reduktorları idxal edin;
default default createStore ixracı (reduktor);

Redüktörlər sənədinizi src / reducers / cart.js-də yaradın və bu kodu yapışdırın:

// İlkin vəziyyət const initState = {isCartOpen: false, checkout: {lineItems: []}, products: [], business: {}}
// const tədbirlər CLIENT_CREATED = 'CLIENT_CREATED "const PRODUCTS_FOUND = CHECKOUT_FOUND const" PRODUCTS_FOUND' = 'CHECKOUT_FOUND "const SHOP_FOUND =' SHOP_FOUND" const ADD_VARIANT_TO_CART = 'ADD_VARIANT_TO_CART "const UPDATE_QUANTITY_IN_CART =' UPDATE_QUANTITY_IN_CART" const REMOVE_LINE_ITEM_IN_CART = 'REMOVE_LINE_ITEM_IN_CART "const OPEN_CART =' OPEN_CART 'const CLOSE_CART =' CLOSE_CART '
// Reduksiyaların ixracı default (state = initState, action) => {switch (action.type) {case CLIENT_CREATED: return {... state, client: action.payload} case PRODUCTS_FOUND: return {... state, products: action.payload} case CHECKOUT_FOUND: return {... state, checkout: action.payload} case SHOP_FOUND: return {... state, shop: action.payload} case ADD_VARIANT_TO_CART: return {... state, isCartOpen: action. payload.isCartOpen, checkout: action.payload.checkout} case UPDATE_QUANTITY_IN_CART: return {... state, checkout: action.payload.checkout} case REMOVE_LINE_ITEM_IN_CART: return {... state, checkout: action.payload.checkout} case OPEN_CART : return {... state, isCartOpen: true} case CLOSE_CART: return {... state, isCartOpen: false} Standard: return status}}

Narahat olma, mən yalnız bu böyük reduktoru göndərməyəcəyəm və nə olduğunu müzakirə etməyəcəyəm. Hər tədbirə gəlirik! Unutmamalı olduğumuz bir neçə şey.

Dövlətin Shopify GitHub nümunəsindəki kimi yazdıqlarından ilkin vəziyyəti götürürük və initState-ə, yəni dövlətin aşağıdakı dörd hissəsinə əlavə edirik:

isCartOpen: false, checkout: {lineItems: []}, məhsullar: [], business: {}

Bununla birlikdə, həyata keçirməyimdə dövlətin bir müştəri hissəsini də yaradıram. CreateClient () funksiyasını bir dəfə çağırıram və dərhal index.js-də Redux vəziyyətinə gətirirəm. Beləliklə index.js saytına keçək:

İndex.js səhifəsinə qayıt

const client = Client.buildClient ({storefrontAccessToken: 'your-shopify-token', domain: 'your-shopify-url.myshopify.com'}); store.dispatch ({növü: 'CLIENT_CREATED', faydalı yük: müştəri});

Shopify Purchase SDK nümunəsində məhsullar haqqında məlumat almaq və React funksional komponentiWillMount () -da məlumat saxlamaq üçün bəzi asenkron zənglər var. Bu nümunə kodu belə görünür:

komponentWillMount () {this.props.client.checkout.create (). sonra ((res) => {this.setState ({Checkout: res,});});
this.props.client.product.fetchAll (). sonra ((res) => {this.setState ({products: res,});});
this.props.client.shop.fetchInfo (). sonra ((res) => {this.setState ({business: res,});}); }

Sayt yüklənmədən əvvəl mümkün qədər index.js-də bunu etməyi seçdim. Sonra cavabın hər hissəsi alındıqda uyğun bir hadisə atdım:

// buildClient () sinxronizasiya olunur, buna görə hamısını sonra araya bilərik! client.product.fetchAll (). sonra ((res) => {store.dispatch ({type: 'PRODUCTS_FOUND', faydalı yük: res});}); client.checkout.create (). sonra ((res) => {store.dispatch ({type: 'CHECKOUT_FOUND', faydalı yük: res});}); client.shop.fetchInfo (). sonra ((res) => {store.dispatch ({tip: 'SHOP_FOUND', faydalı yük: res});});

Reduktor indi yaradıldı və index.js üçün Shopify API istemcisinin başlanğıcı tamamlandı.

App.js səhifəsinə qayıt

İndi Redux mağazasını App.js-dəki tətbiq statusu ilə birləşdirin:

{connect} 'react-redux' -dan idxal;

və mağazanı da idxal etməyi unutmayın:

Yükü './store' dan idxal et;

Varsayılan ixrac tətbiqini aşağı hissədə aşağıdakı kimi dəyişdirin:

default default connect ((state) => state) (App);

Bu, Redux statusunu tətbiqetmə komponenti ilə birləşdirir.

İndi Redux-dan getState () ilə render () funksiyasında Redux statusuna çatırıq (vanilla react's this.state-dən istifadə etmək əvəzinə):

render () {... const state = store.getState (); }

Nəhayət: hadisə işləyicisi (biz hələ də App.js-dəyik)

Yuxarıdan, App.js-də yalnız üç hadisə işləyicisinə ehtiyacımız olduğunu bilirsiniz, çünki alış-veriş sepeti yalnız üç istifadə edir: updateQuantityInCart, removeLineItemInCart və handleCartClose. Yerli komponent vəziyyətini istifadə edən GitHub deposundan orijinal səbət hadisəsi işləyiciləri belə görünürdü:

updateQuantityInCart (lineItemId, Quantity) {const checkoutId = this.state.checkout.id const lineItemsToUpdate = [{id: lineItemId, Quantity: parseInt (Quantity, 10)}]
This.props.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) -dən qayıtdı. Sonra (res => {this.setState ({Checkout: res,});}); }
removeLineItemInCart (lineItemId) {const checkoutId = this.state.checkout.id
This.props.client.checkout.removeLineItems (checkoutId, [lineItemId]) qaytarın. sonra (res => {this.setState ({Checkout: res,});}); }
handleCartClose () {this.setState ({isCartOpen: false,}); }

Tədbirləri Redux mağazasına aşağıdakı şəkildə göndərmək üçün yenidən dizayn edə bilərik:

updateQuantityInCart (lineItemId, Quantity) {const state = store.getState (); // Redux Store-dan vəziyyət checkoutId = state.checkout.id const lineItemsToUpdate = [{id: lineItemId, Quantity: parseInt (Quantity, 10)}] state.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate =then). > {store.dispatch ({type: 'UPDATE_QUANTITY_IN_CART', faydalı yük: {checkout: res}});}); } removeLineItemInCart (lineItemId) {const state = store.getState (); // Redux Store-dan vəziyyət checkoutId = state.checkout.id state.client.checkout.removeLineItems (checkoutId, [lineItemId]). sonra (res => {store.dispatch ({type: 'REMOVE_LINE_ITEM_IN_CART', faydalı yük: {checkout: res}});}); } handleCartClose () {store.dispatch ({type: 'CLOSE_CART'}); } handleCartOpen () {store.dispatch ({type: 'OPEN_CART'}); }

Əgər davam etsəniz, bu funksiyanı özüm üçün bir dayaq kimi istifadə etdiyim üçün əvvəlcədən öz handleCartOpen funksiyamı əlavə etdiyimi dedim. - Komponenti keçin ki, bir istifadəçi nav-dakı bir keçid vasitəsi ilə alış-veriş səbətini aça və bağlasın. Gələcəkdə bu funksiyanı dayaq kimi ötürmək əvəzinə Nav-un özünə keçirə bilərdim, çünki Redux Mağazası da əlbəttə ki, orada da mövcud olacaqdır!

Nəhayət bunu əlavə edin -Komponent əlavə edildi!

Beləliklə, Shopify mağazanızdakı uyğun məhsulu göstərən bir neçə sadə istinadla sadə bir işiniz var? Ha! Bunları aradan qaldırın və yenisi ilə əvəz edin -Komponent!

Əvvəlcə, komponentinizi mağazanızın markalanmasını istədiyiniz yerə idxal edin. (Unutmayın, shopify nümunə komponentlərini kod bazamda shopify / adlı bir qovluğa qoydum.)

Məhsullarınız harada olursa olsun bu. (Yaratdığım qazan plitəsində, bu məhsulun bir bölümü olan hər hansı bir səhifəyə tətbiq oluna biləcəyini bildirmək üçün bunu GenericProductsPage komponentinə qoydum.)

'./Shopify/Products' məhsullarını idxal edin;

İndi Redux qazan plitəsinin işlənməsi son 15-20 dəqiqədə nəticə verir: Vəziyyətimizin məhsul hissəsini - rekvizitlər vasitəsilə dəfələrlə ötürülən vanil reaksiya vəziyyəti ilə deyil, Redux vəziyyəti ilə qeyd edə bilərik, səliqəli bir sətirli konst vəziyyətində = store.getState () ;:

render () {const state = store.getState (); // Redux Mağazasından olan vəziyyət, oProducts = ;

Komponentin özünü render () funksiyasında istədiyiniz yerə qoymağı unutmayın. Mənim üçün bu yer Bootstrap stili dərslərində və HTML-də basdırıldı:

... {/ * /. sıra * /} {/ * /. service-content-one * /} ...

Nəhayət, alışveriş sepetinin bu məhsul komponenti ilə işləməsi üçün addVariantToCart tək bir hadisə funksiyasına ehtiyacımız var. Yenidən istinad üçün, vanil reaksiyası ilə addVariantToCart-ın (həmçinin shopify nümunə anbarından) orijinal versiyası:

addVariantToCart (variantId, Quantity) {this.setState ({isCartOpen: true,});
const lineItemsToAdd = [{variantId, Miqdarı: parseInt (Miqdarı, 10)}] const checkoutId = this.state.checkout.id
This.props.client.checkout.addLineItems (checkoutId, lineItemsToAdd) .then (res => {this.setState ({checkout: res,});}) qayıt; }

və yeni, Redux dostu store.dispatch () versiyası:

addVariantToCart (variantId, Quantity) {const state = store.getState (); // Redux Store-dan vəziyyət const lineItemsToAdd = [{variantId, Quantity: parseInt (Quantity, 10)}] const checkoutId = state.checkout.id state.client.checkout.addLineItems (checkoutId, lineItemsToAdd) .then (res => { store.dispatch ({type: 'ADD_VARIANT_TO_CART', faydalı yük: {isCartOpen: true, checkout: res}});}); }

əlbəttə ki, istifadə edəcəyik.

Bunu konstruktorda bağlamağı unutmayın:

this.addVariantToCart = this.addVariantToCart.bind (bu);

Ayrıca, bu komponenti App.js kimi mağazaya bağlamalı və mağazanı idxal etməlisiniz:

{connect} 'react-redux' idxal yükündən '../store';

Yuxarıda və (Shopify məhsul komponentinin adını göstərdiyiniz komponenti qəbul etsəniz GenericProductPage:

default default connect ((state) => state) (GenericProductsPage);

aşağıda.

Əla! Komponentlər nə qədər dərin basdırılsa da və ya məhsul komponentiniz elan edildiyi yerdə, avtomobilin vəziyyəti ilə əlaqə qura bilər!

Son BONUS nümunəsi: başlıqdakı alış-veriş səbəti və ya naviqasiya

Başlığınızda / naviqasiya çubuğunuzda "alış-veriş sepeti" düyməsini istəsəniz, bu düyməni naviqasiya komponentinizin göstərmə funksiyasına əlavə edin (eyni zamanda açılış stili ilə hazırkı saytımdan bir nümunə - qazan plitəsindəki nümunədə çox sadə bir versiyaya rast gəlmək olar) :

Burada, handleCartOpen, App.js-ə əlavə etməyiniz lazım olan yeni bir işləmə metodudur:

Constructor () {super (); ... this.handleCartOpen = this.handleCartOpen.bind (bu); ...}

konstruktorda. Sonra App.js-də Nav komponentinizə istinad etdiyiniz zaman (və ya Nav'unuzu harada yerləşdirmisinizsə), funksiya işləyicisini keçin:

Bu Reduxda da bir hadisəyə çevrilə bilərdi, ancaq uşaq olduğum üçün bunu vanil reaksiya yolu ilə etdim.

Styling komponenti

Shopify-ın CSS sənədinə, app.css-ə, storefront-api nümunə deposundakı paylaşılan / qovluqda etibar etdim (onu qaçırmaq olmaz, paylaşılan / içindəki yeganə sənəddir).

Bunu üslublarınıza / qovluğunuza və ya lazım olduğu hər yerə kopyaladığınızdan və index.js sənədinizə yapışdırdığınızdan əmin olun. İndex.js-də belə görünür:

idxal './styles/shopify.css';

O vaxtdan bəri Shopify nümunə anbarında olan app.css adını shopify.css olaraq dəyişdirdim və Qovluq Stillərinə köçürdüm. Bu konvensiya qazan plitəsi deposu kodunda da istifadə olunur.

Buradan dükanların tam olaraq shopify.css-də açıq mavi rəngli düymələrin təyin olunduğu yeri görmək olduqca asandır. Ətraflı CSS düzəlişlərini sizin üçün saxlayacağam.

Amma kim bilir, bəlkə də nə vaxtsa bu barədə yazacağam - amma Shopify-ın tərzlərini dəyişdirmək üçün olduqca yaxşı və asandır.

Çörəklər

Zənnimcə bu Redux'un mükəmməl (ediləsi bir siyahıdan istifadə) istifadəsidir. Redux, hadisə funksiyalarını və Shopify alış-veriş səbətinin vəziyyətini səliqəli şəkildə təşkil edir və hər hansı digər komponentdən alış-veriş səbətinin vəziyyətinə çatmağı asanlaşdırır. Bu vəziyyəti uşaqlara ötürməkdən və hadisələri React tətbiqi üzərindən daha yüksək səviyyəli funksiyalara geri göndərmək üçün çoxsaylı hadisə idarəedicilərindən istifadə etməkdən daha asandır.

Dərslikdə nümunə olaraq göstərildiyi kimi, Nav komponentində və başlanğıc səhifəsinin mağaza sahəsindəki alış-veriş səbətinin vəziyyətinə daxil ola bilərsiniz. Siren paltarı hazır olduqdan sonra onu bir növ seçilən məhsul sahəsinə də asanlıqla əlavə edə bilərəm.

Kodu tapın

Bu tətbiqetmə qazanxanasına burada baxa bilərsiniz. Tətbiqləri qurmaq və reaksiya vermək üçün boş bir tətbiqdir, lakin index.js və App.js-də tətbiq olunan bu təlimatdakı bütün dəyişikliklərlə yanaşı super təməl GenericStorePage və Nav komponentidir.

Repo üzərindəki kodu bu təlimin mənalı olduğundan əmin olmaq üçün burada öz təlimatımı yenidən oxuyarkən və yeniləyərkən yaratdım!

Dəli olduğum üçün Siren Apparel-in veb saytı tamamilə açıqdır. Beləliklə, tətbiqetməyimlə dolaşmaq istəyirsinizsə, deposuna baxın!

Ümid edirəm bu dərslikdən zövq aldın! Bir şey aydın deyilsə və ya sadəcə işləmirsə, xahiş edirəm mənə bildirin! Mən sizə kömək etməyə çalışacağam!

Qazan anbarında istifadə etdiyim sadə nav nümunəsi üçün CSS-Snippets-dən Lisa Catalano-ya təşəkkür edirəm!

Aşağıdan yuxarıya, altüst olmaq!

Chris