Vueでショッピングカートアプリを構築する方法.シリーズポートフォリオアプリ


これは“ポートフォリオアプリ”シリーズの2番目のエピソードです.今日はシンプルなショッピングカートを作ります.私はスマートフォンやタブレットについての小さな店をシミュレートします.
この新しいチュートリアルでダイブしましょう.

1.0 /セットアップ

  • 1.1 | Install Vue
  • 1.2 | Creating a new project
  • 1.3 | Images
  • 1.4 | Vuex
  • コンポーネント/ルータ

  • 2.1 | Views & Components
  • 2.2 | Router
  • [ 1.1 ] Vue 3をインストールする

    # Install latest stable of Vue
    
    yarn global add @vue/cli
    

    新しいプロジェクトの作成

    This time, let's manually select features for this new Vue application.

    # run this command
    
    vue create shopping-cart-app
    

    あなたのスペースバーを押して“ルータ”と“VUEX”を選択します

    このオプションを選択します
  • Vueのバージョン3.JS "3 . x (プレビュー)"

  • “ルータのための歴史モード”

  • 「エラー防止のみでESINT」

  • "リントオンセーブ"
  • "パッケージ
  • プリセット"はい"
  • カスタムとこのテンプレートを希望する名前を記録します.
  • [ 1.3 ]画像

    Create four folders "iPadPro", "iPhone12Pro", "iPhoneSE" and "S21".

    assets
    |-- iPadPro
    |-- iPhone12Pro
    |-- iPHoneSE
    |-- S21
    

    Search and import all pictures in each folder as following :


    [ 1.4 ] Vuex

    Let's prepare all Vuex files. In "index.js", we need to initialize four states :

    • cart,
    • total,
    • qty,
    • products,
    # ../store/index.js
    
    import { createStore } from "vuex";
    
    import rootMutations from "./mutations.js";
    import rootActions from "./actions.js";
    import rootGetters from "./getters.js";
    
    const store = createStore({
      state() {
        return {
          cart: [],
          total: 0,
          qty: 0,
          products: [
            {
              id: 1,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Graphite",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGraphite.jpg"),
              price: 999,
            },
            {
              id: 2,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Silver",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProSilver.jpg"),
              price: 999,
            },
            {
              id: 3,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Gold",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGold.jpg"),
              price: 999,
            },
            {
              id: 4,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Pacific Blue",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProPacificBlue.jpg"),
              price: 999,
            },
            {
              id: 5,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Graphite",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGraphite.jpg"),
              price: 1199.0,
            },
            {
              id: 6,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Silver",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProSilver.jpg"),
              price: 1199,
            },
            {
              id: 7,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Gold",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGold.jpg"),
              price: 1199,
            },
            {
              id: 8,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Pacific Blue",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProPacificBlue.jpg"),
              price: 1199,
            },
            {
              id: 9,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Graphite",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGraphite.jpg"),
              price: 1399,
            },
            {
              id: 10,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Silver",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProSilver.jpg"),
              price: 1399,
            },
            {
              id: 11,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Gold",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGold.jpg"),
              price: 1399,
            },
            {
              id: 12,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone 12 Pro",
              color: "Pacific Blue",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPhone12Pro/iPhone12ProPacificBlue.jpg"),
              price: 1399,
            },
            {
              id: 13,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone SE",
              color: "White",
              capacity: "64 GB",
              imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
              price: 399,
            },
            {
              id: 14,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone SE",
              color: "White",
              capacity: "64 GB",
              imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
              price: 399,
            },
            {
              id: 15,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone SE",
              color: "Red",
              capacity: "64 GB",
              imgSrc: require("@/assets/iPhoneSE/iPhoneSERed.jpg"),
              price: 399,
            },
            {
              id: 14,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone SE",
              color: "White",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
              price: 499,
            },
            {
              id: 15,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone SE",
              color: "White",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
              price: 499,
            },
            {
              id: 16,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone SE",
              color: "Red",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPhoneSE/iPhoneSERed.jpg"),
              price: 499,
            },
            {
              id: 17,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone SE",
              color: "White",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
              price: 599,
            },
            {
              id: 18,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone SE",
              color: "White",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
              price: 599,
            },
            {
              id: 19,
              type: "Smartphone",
              brand: "Apple",
              model: "iPhone SE",
              color: "Red",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPhoneSE/iPhoneSERed.jpg"),
              price: 599,
            },
            {
              id: 20,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Space Gray",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1299,
            },
            {
              id: 21,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Silver",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1299,
            },
            {
              id: 22,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Space Gray",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1499,
            },
            {
              id: 23,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Silver",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1499,
            },
            {
              id: 24,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Space Gray",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1599,
            },
            {
              id: 25,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Silver",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1599,
            },
            {
              id: 26,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Space Gray",
              capacity: "1 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1699,
            },
            {
              id: 27,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Silver",
              capacity: "1 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1699,
            },
            {
              id: 28,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Space Gray",
              capacity: "2 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1799,
            },
            {
              id: 29,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi",
              color: "Silver",
              capacity: "2 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1799,
            },
            {
              id: 30,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Space Gray",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1546,
            },
            {
              id: 31,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Silver",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1546,
            },
            {
              id: 32,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Space Gray",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1599,
            },
            {
              id: 33,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Silver",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1599,
            },
            {
              id: 34,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Space Gray",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1699,
            },
            {
              id: 35,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Silver",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1699,
            },
            {
              id: 36,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Space Gray",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1799,
            },
            {
              id: 37,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Silver",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1799,
            },
            {
              id: 38,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Space Gray",
              capacity: "1 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1899,
            },
            {
              id: 39,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Silver",
              capacity: "1 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1899,
            },
            {
              id: 40,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Space Gray",
              capacity: "2 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1999,
            },
            {
              id: 41,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi",
              color: "Silver",
              capacity: "2 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1999,
            },
            {
              id: 42,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1799,
            },
            {
              id: 43,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1799,
            },
            {
              id: 44,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1899,
            },
            {
              id: 45,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1899,
            },
            {
              id: 46,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1799,
            },
            {
              id: 47,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1799,
            },
            {
              id: 48,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1699,
            },
            {
              id: 49,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1699,
            },
            {
              id: 50,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "1 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1799,
            },
            {
              id: 51,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "1 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1799,
            },
            {
              id: 52,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "2 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1899,
            },
            {
              id: 53,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 11-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "2 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1899,
            },
            {
              id: 54,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 1999,
            },
            {
              id: 55,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "128 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 1999,
            },
            {
              id: 56,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 2099,
            },
            {
              id: 55,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "256 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 2099,
            },
            {
              id: 56,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 2199,
            },
            {
              id: 57,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "512 GB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 2199,
            },
            {
              id: 58,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "1 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 2299,
            },
            {
              id: 59,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "1 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 2299,
            },
            {
              id: 60,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Space Gray",
              capacity: "2 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
              price: 2399,
            },
            {
              id: 61,
              type: "Tablet",
              brand: "Apple",
              model: "iPad Pro 12.9-inch display Wifi + Cellular",
              color: "Silver",
              capacity: "2 TB",
              imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
              price: 2399,
            },
            {
              id: 62,
              type: "Smartphone",
              brand: "Samsung",
              model: "S21 5G",
              color: "Phantom Grey",
              capacity: "128 GB",
              imgSrc: require("@/assets/S21/S21PhantomGrey.jpg"),
              price: 799,
            },
            {
              id: 63,
              type: "Smartphone",
              brand: "Samsung",
              model: "S21 5G",
              color: "Phantom Pink",
              capacity: "128 GB",
              imgSrc: require("@/assets/S21/S21PhantomPink.jpg"),
              price: 799,
            },
            {
              id: 64,
              type: "Smartphone",
              brand: "Samsung",
              model: "S21 5G",
              color: "Phantom Violet",
              capacity: "128 GB",
              imgSrc: require("@/assets/S21/S21PhantomViolet.jpg"),
              price: 799,
            },
            {
              id: 65,
              type: "Smartphone",
              brand: "Samsung",
              model: "S21 5G",
              color: "Phantom White",
              capacity: "128 GB",
              imgSrc: require("@/assets/S21/S21PhantomWhite.jpg"),
              price: 799,
            },
          ],
        };
      },
      mutations: rootMutations,
      actions: rootActions,
      getters: rootGetters,
    });
    
    export default store;
    
    

    Now, we are going to create three files "actions.js", "mutations.js", "getters.js"

    store
    |-- actions.js
    |-- getters.js
    |-- index.js
    |-- mutations.js
    
    

    This application required two actions :

    • "addToCart"
    • "removeFromCart"

    And two mutations :

    • "addProductToCart"
    • "removeProductFromCart"
    # ./store/actions.js
    
    export default {
      addToCart(context, payload) {
        const prodId = payload.id;
        const products = context.rootGetters.products;
        const product = products.find((prod) => prod.id === prodId);
        context.commit("addProductToCart", product);
      },
      removeFromCart(context, payload) {
        context.commit("removeProductFromCart", payload);
      },
    };
    
    

    And two mutations :

    • "addProductToCart"
    • "removeProductFromCart"
    # ./store/mutations.js
    
    export default {
      addProductToCart(state, payload) {
        const productData = payload;
        const productInCartIndex = state.cart.findIndex(
          (ci) => ci.id === productData.id
        );
        if (productInCartIndex >= 0) {
          state.cart[productInCartIndex].qty++;
        } else {
          const newItem = {
            id: productData.id,
            type: productData.type,
            brand: productData.brand,
            model: productData.model,
            color: productData.color,
            capacity: productData.capacity,
            imgSrc: productData.imgSrc,
            price: productData.price,
            qty: 1,
          };
          state.cart.push(newItem);
        }
        state.qty++;
        state.total += productData.price;
      },
    
      removeProductFromCart(state, payload) {
        const prodId = payload.id;
        const productInCartIndex = state.cart.findIndex(
          (cartItem) => cartItem.id === prodId
        );
        const prodData = state.cart[productInCartIndex];
        state.cart.splice(productInCartIndex, 1);
        state.qty -= prodData.qty;
        state.total -= prodData.price * prodData.qty;
      },
    };
    
    

    And four getters which return each state :

    # ./store/getters
    
    export default {
      products(state) {
        return state.products;
      },
      totalSum(state) {
        return state.total;
      },
      quantity(state) {
        return state.qty;
      },
      cart(state) {
        return state.cart;
      }
    };
    
    

    ビュー&コンポーネント

    We are going to create four views :

    views
    |-- AllProduts.vue
    |   # Display all products
    |-- Smartphones.vue
    |   # Filter only Smartphones Products
    |-- Tablets.vue
    |   # Filter only Tablets Products
    |-- Cart.vue
        # Display all products added to Cart
    
    

    About the first view "All Products", we need to create three components :

    AllProducts.vue
    |-- Wrapper.vue
    |-- ProductCard.vue
    
    
    # ../views/AllProducts.vue
    
    <template>
      <Wrapper>
        <ProductCard
          v-for="product in products"
          :key="product.id"
          :id="product.id"
          :type="product.type"
          :brand="product.brand"
          :model="product.model"
          :color="product.color"
          :capacity="product.capacity"
          :imgSrc="product.imgSrc"
          :price="product.price"
        />
      </Wrapper>
    </template>
    
    <script>
    import ProductCard from "@/components/ProductCard";
    
    export default {
      components: {
        ProductCard,
      },
      computed: {
        products() {
          const productsSort = this.$store.getters["products"];
          return productsSort.sort((a, b) => {
            if (a.model < b.model) {
              return -1;
            }
            if (a.model > b.model) {
              return 1;
            }
            return 0;
          });
        },
      },
    };
    </script>
    
    
    # ../components/Wrapper.vue
    
    <template>
      <div class="wrapper">
        <div class="product-container">
          <slot />
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "Wrapper",
    }
    </script>
    
    <style>
    .wrapper {
      display: flex;
      flex-direction: column;
      justify-content: center;
    }
    
    .product-container {
      padding-top: 170px;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      flex-direction: row;
    }
    </style>
    
    
    
    <template>
      <div class="product-card">
        <div class="image-container">
          <img class="img-style" :src="imgSrc" :alt="brand + model" />
        </div>
        <p class="price-label">₿ {{ price.toFixed(2) }}</p>
        <button @click="addToCart">Add to Cart</button>
        <span class="product-title">
          {{ brand }} {{ model }} {{ color }} {{ capacity }}
        </span>
      </div>
    </template>
    
    <script>
    export default {
      name: "ProductCard",
      props: [
        "id",
        "type",
        "brand",
        "model",
        "color",
        "capacity",
        "imgSrc",
        "price",
      ],
      methods: {
        addToCart() {
          this.$store.dispatch("addToCart", {
            id: this.id,
            type: this.type,
            brand: this.brand,
            model: this.model,
            color: this.color,
            capacity: this.capacity,
            imgSrc: this.imgSrc,
            price: this.price,
          });
        },
      },
    };
    </script>
    
    <style scoped>
    .product-card {
      display: flex;
      flex-direction: column;
      height: 450px;
      max-height: 450px;
      max-width: 250px;
      background-color: white;
      border-radius: 15px;
      flex: 1 1 240px;
      margin: 10px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
      border: 1px solid rgba(60, 60, 60, 0.2);
      padding: 30px;
    }
    
    .img-style {
      display: flex;
      height: 250px;
      object-fit: cover;
    }
    
    .image-container {
      display: flex;
      justify-content: center;
    }
    
    .product-title {
      display: flex;
      color: rgba(66, 185, 131, 1);
      font-weight: bold;
      height: 100%;
      align-items: flex-start;
      justify-content: center;
    }
    
    .price-label {
      font-weight: bold;
      font-size: 20px;
    }
    </style>
    
    

    Why should we create components in two different folders ? Because "ProductCard.vue" and "Wrapper.vue" are reusable components ! We are going to use it again 💪 in "Smartphones.vue" and "Tablets.vue" :

    # ../views/Smartphones.vue
    
    <template>
      <Wrapper>
        <ProductCard
          v-for="product in products"
          :key="product.id"
          :id="product.id"
          :type="product.type"
          :brand="product.brand"
          :model="product.model"
          :color="product.color"
          :capacity="product.capacity"
          :imgSrc="product.imgSrc"
          :price="product.price"
        />
      </Wrapper>
    </template>
    
    <script>
    import ProductCard from "@/components/ProductCard";
    
    export default {
      components: {
        ProductCard,
      },
      computed: {
        products() {
          const productsFilter = this.$store.getters["products"];
          return productsFilter.filter((product) => {
            if (product.type.includes("Smartphone")) {
              return true;
            } else {
              return false;
            }
          });
        },
      },
    };
    </script>
    
    
    # ../views/Tablets.vue 
    
    <template>
      <Wrapper>
        <ProductCard
          v-for="product in products"
          :key="product.id"
          :id="product.id"
          :type="product.type"
          :brand="product.brand"
          :model="product.model"
          :color="product.color"
          :capacity="product.capacity"
          :imgSrc="product.imgSrc"
          :price="product.price"
        />
      </Wrapper>
    </template>
    
    <script>
    import ProductCard from "@/components/ProductCard";
    
    export default {
      components: {
        ProductCard,
      },
      computed: {
        products() {
          const productsFilter = this.$store.getters["products"];
          return productsFilter.filter((product) => {
            if (product.type.includes("Tablet")) {
              return true;
            } else {
              return false;
            }
          });
        },
      },
    };
    </script>
    
    

    This is how reusable components works ! Awesome right ? 👍

    # ../views/Cart.vue
    
    <template>
      <Wrapper>
        <div class="container-cart">
          <h2>Your Cart</h2>
          <h3>Total Amount: ₿ {{ cartTotal }}</h3>
          <ul>
            <CartCard
              v-for="product in cartProducts"
              :key="product.id"
              :id="product.id"
              :type="product.type"
              :brand="product.brand"
              :model="product.model"
              :color="product.color"
              :capacity="product.capacity"
              :imgSrc="product.imgSrc"
              :price="product.price"
              :qty="product.qty"
            ></CartCard>
          </ul>
        </div>
      </Wrapper>
    </template>
    
    <script>
    import CartCard from "@/components/CartCard.vue";
    
    export default {
      components: {
        CartCard,
      },
      computed: {
        cartTotal() {
          return this.$store.getters["totalSum"].toFixed(2);
        },
        cartProducts() {
          return this.$store.getters["cart"];
        },
      },
    };
    </script>
    
    <style scoped>
    .container-cart {
      display: flex;
      flex-direction: column;
    }
    
    h2 {
      color: #292929;
      text-align: center;
      border-bottom: 2px solid #ccc;
      padding-bottom: 1rem;
    }
    
    h3 {
      text-align: center;
    }
    
    ul {
      list-style: none;
      margin: 0;
      padding: 0;
    }
    </style>
    
    # ../components/CartCard.vue
    
    <template>
      <li>
        <div class="image-container">
          <img :src="imgSrc" :alt="model" />
        </div>
        <div>
          <h3>{{ brand }} {{ model }} {{ color }} {{ capacity }}</h3>
          <div class="item-data">
            <div>
              Price per Item:
              <strong>₿ {{ price.toFixed(2) }}</strong>
            </div>
            <div>
              Quantity:
              <strong>{{ qty }}</strong>
            </div>
          </div>
          <div class="item-total">Total: ₿ {{ itemTotal }}</div>
          <button @click="remove">Remove</button>
        </div>
      </li>
    </template>
    
    <script>
    export default {
      props: [
        "id",
        "type",
        "brand",
        "model",
        "color",
        "capacity",
        "imgSrc",
        "price",
        "qty",
      ],
      computed: {
        itemTotal() {
          return (this.price * this.qty).toFixed(2);
        },
      },
      methods: {
        remove() {
          this.$store.dispatch("removeFromCart", { id: this.id });
        },
      },
    };
    </script>
    
    <style scoped>
    .image-container {
      display: flex;
      align-items: center;
      justify-content: center;
    }
    
    li {
      margin: 1rem auto;
      padding: 1rem;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
      text-align: center;
      max-width: 25rem;
      border-radius: 15px;
      border: 1px solid rgba(60, 60, 60, 0.2);
    }
    
    img {
      display: flex;
      height: 250px;
      object-fit: cover;
    }
    
    .item-data {
      display: flex;
      justify-content: space-between;
    }
    
    .item-total {
      font-weight: bold;
      margin: 1rem 0;
      border-top: 1px solid #474747;
      border-bottom: 2px solid #474747;
      padding: 0.25rem 0;
      width: auto;
    }
    </style>
    
    
    # ../components/TheHeader.vue
    
    <template>
      <div id="nav">
        <router-link to="/">All Products</router-link>
        <router-link to="/smartphones">Smartphones</router-link>
        <router-link to="/tablets">Tablets</router-link>
        <router-link to="/cart">Cart</router-link>
        <span class="counter-cart" v-if="socketCart > 0"> {{ socketCart }} </span>
      </div>
    </template>
    
    <script>
    
    export default {
      computed: {
        socketCart() {
          return this.$store.getters["quantity"];
        },
      },
    };
    </script>
    
    <style>
    #nav {
      position: fixed;
      background: rgb(255, 255, 255);
      background: linear-gradient(
        180deg,
        rgba(255, 255, 255, 1) 0%,
        rgba(255, 255, 255, 1) 94%,
        rgba(255, 255, 255, 0) 100%
      );
      width: 100%;
      padding: 30px;
    }
    
    #nav a {
      font-weight: bold;
      color: #2c3e50;
      text-decoration: none;
      line-height: 30px;
      padding: 0px 30px 0px 30px;
    }
    
    #nav a.router-link-exact-active {
      color: #42b983;
      text-decoration: none;
      padding: 0px 30px 0px 30px;
      line-height: 30px;
    }
    
    .counter-cart {
      display: inline-block;
      padding: 0.3rem 0.7rem 0.3rem 0.7rem;
      background-color: rgba(162, 205, 2, 1);
      color: white;
      border-radius: 50px;
    }
    </style>
    
    
    # ../App.vue
    
    <template>
      <TheHeader />
      <router-view />
    </template>
    
    <script>
    import TheHeader from "./components/TheHeader.vue";
    
    export default {
      components: {
        TheHeader,
      },
    };
    </script>
    
    <style>
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
    }
    
    body {
      margin: 0;
    }
    
    button {
      font: inherit;
      cursor: pointer;
      background-color: rgba(66, 185, 131, 1);
      color: white;
      border: 1px solid rgba(66, 185, 131, 1);
      padding: 0.5rem 1.5rem;
      border-radius: 30px;
      margin-bottom: 20px;
    }
    
    button:hover,
    button:active {
      background-color: #82dcb1;
      border-color: #82dcb1;
    }
    </style>
    
    

    [ 2.2 ]ルータ

    Every components are ready now. We can configuring the router.

    # ../router/index.js
    
    import { createRouter, createWebHistory } from "vue-router";
    import AllProducts from "../views/AllProducts.vue";
    
    const routes = [
      {
        path: "/",
        name: "AllProducts",
        component: AllProducts,
      },
      {
        path: "/smartphones",
        name: "Smartphones",
        component: () => import("../views/Smartphones.vue"),
      },
      {
        path: "/tablets",
        name: "Tablets",
        component: () => import("../views/Tablets.vue"),
      },
      {
        path: "/cart",
        name: "cart",
        component: () => import("../views/Cart.vue"),
      },
    ];
    
    const router = createRouter({
      history: createWebHistory(process.env.BASE_URL),
      routes,
    });
    
    export default router;
    
    
    # ../main.js
    
    import { createApp } from "vue";
    import App from "./App.vue";
    import router from "./router";
    import store from "./store/index.js";
    
    import Wrapper from "@/components/Wrapper";
    
    const app = createApp(App);
    
    app.use(router);
    app.use(store);
    
    app.component("Wrapper", Wrapper);
    
    app.mount("#app");
    
    

    完了!ここでこのショッピングカートアプリケーションを試すことができます.
    https://shopping-cart-demo-sith.netlify.app
    すぐに“ポートフォリオアプリ”シリーズの次のエピソードでお会いしましょう.