序章

Vue.js は、パフォーマンスが高く、プログレッシブ Javascriptフレームワークです。 これはGitHubで人気のあるフレームワークであり、活発で役立つコミュニティがあります。

Vue Webフレームワークの機能を示すために、このチュートリアルでは、eコマースアプリのショッピングカートの作成について説明します。 このアプリは、商品情報を保存し、顧客が後でチェックアウトするために購入したい商品を保持します。 情報を保存するには、Vue.jsで広く使用されている状態管理ライブラリVuexを調べます。 これにより、ショッピングカートアプリケーションがデータをサーバーに永続化できるようになります。 また、Vuexを使用して非同期タスク管理を処理します。

チュートリアルを終了すると、次のような機能するショッピングカートアプリケーションが作成されます。

前提条件

ステップ1—VueCLIを使用したアプリケーションのセットアップ

バージョン4.5.0以降、 Vue CLI には、新しいプロジェクトを作成するときにVue3プリセットを選択するための組み込みオプションが用意されています。 最新バージョンのVueCLIを使用すると、Vue 3をそのまま使用して、既存のVue2プロジェクトをVue3に更新できます。 このステップでは、Vue CLIを使用してプロジェクトを作成し、フロントエンドの依存関係をインストールします。

まず、ターミナルから次のコマンドを実行して、最新バージョンのVueCLIをインストールします。

  1. npm install -g @vue/cli

これにより、VueCLIがシステムにグローバルにインストールされます。

注:一部のシステムでは、npmパッケージをグローバルにインストールすると、アクセス許可エラーが発生し、インストールが中断される可能性があります。 使用を避けることがセキュリティのベストプラクティスであるため sudonpm install、代わりに、npmのデフォルトディレクトリを変更することでこれを解決できます。 あなたが遭遇した場合 EACCES エラーが発生した場合は、npmの公式ドキュメントの指示に従ってください。

このコマンドで正しいバージョンを使用していることを確認してください。

  1. vue --version

次のような出力が得られます。

Output
@vue/cli 4.5.10

注:古いバージョンのVue CLIが既にグローバルにインストールされている場合は、ターミナルから次のコマンドを実行してアップグレードします。

  1. npm update -g @vue/cli

これで、新しいプロジェクトを作成できます。

  1. vue create vuex-shopping-cart

これはVueCLIコマンドを使用します vue create 名前の付いたプロジェクトを作成するには vuex-shopping-cart. Vue CLIの詳細については、VueCLIを使用してVue.jsシングルページアプリを生成する方法をご覧ください。

次に、次のプロンプトが表示されます。

Output
Vue CLI v4.5.10 ? Please pick a preset: (Use arrow keys) ❯ Default ([Vue 2] babel, eslint) Default (Vue 3 Preview) ([Vue 3] babel, eslint) Manually select features

を選択してください Manually select features このリストのオプション。

次に、Vueアプリをカスタマイズするための次のプロンプトが表示されます。

Output
... ◉ Choose Vue version ◯ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◉ Router ◉ Vuex ◯ CSS Pre-processors ◯ Linter / Formatter ❯◯ Unit Testing ◯ E2E Testing

このリストから、 Choose Vue version, Router、 と Vuex. これにより、Vueのバージョンを選択し、VuexとVueルーターを使用できるようになります。

次に、 3.x (Preview) Vueのバージョンについては、「いいえ」と答えてください(N) に history mode、および構成を行うオプションを選択します In dedicated config file. 最後に、答えてください N 将来のプロジェクトのためにセットアップを保存しないようにするため。

この時点で、Vueがアプリケーションを作成します。

プロジェクトの作成後、次のコマンドを使用してフォルダーに移動します。

  1. cd vuex-shopping-cart

まず、Flexboxに基づく無料のオープンソースCSSフレームワークであるBulmaをインストールします。 次のコマンドを実行して、プロジェクトにBulmaを追加します。

  1. npm install bulma

プロジェクトでBulmaCSSを使用するには、アプリのエントリポイントである main.js ファイル:

  1. nano src/main.js

次に、次の強調表示されたインポート行を追加します。

vuex-shopping-cart / src / main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './../node_modules/bulma/css/bulma.css'

createApp(App).use(store).use(router).mount('#app')

ファイルを保存して閉じます。

このアプリでは、Axiosモジュールを使用してサーバーにリクエストを送信します。 次のコマンドを実行して、Axiosモジュールを追加します。

  1. npm install axios

次に、アプリを実行して、機能していることを確認します。

  1. npm run serve

案内する http://localhost:8080 選択したブラウザで。 Vueアプリのウェルカムページがあります。

Vueが機能していることを確認したら、サーバーを停止します。 CTRL+C.

このステップでは、コンピューターにVue CLIをグローバルにインストールし、Vueプロジェクトを作成し、必要なnpmパッケージAxiosとBulmaをインストールし、Bulmaをプロジェクトにインポートしました。 main.js ファイル。 次に、アプリのデータを保存するためのバックエンドAPIを設定します。

ステップ2—バックエンドを設定する

このステップでは、Vueプロジェクトで動作する別のバックエンドを作成します。 これは、フロントエンドのVueアプリケーションとは別のプロジェクトフォルダーにあります。

まず、Vueディレクトリから移動します。

  1. cd ..

名前の付いた別のディレクトリを作成します cart-backend:

  1. mkdir cart-backend

バックエンドフォルダを作成したら、それを作業ディレクトリにします。

  1. cd cart-backend

必要なファイルを使用してプロジェクトを初期化することから始めます。 次のコマンドを使用して、アプリのファイル構造を作成します。

  1. touch server.js
  2. touch server-cart-data.json
  3. touch server-product-data.json

あなたは touch ここでコマンドを実行して、空のファイルを作成します。 The server.js ファイルにはNode.jsサーバーが保持され、JSONにはショップの商品とユーザーのショッピングカートのデータが保持されます。

次のコマンドを実行して、 package.json ファイル:

  1. npm init

npmとNodeの詳細については、Node.jsシリーズのコーディング方法をご覧ください。

これらのバックエンド依存関係をNodeプロジェクトにインストールします。

  1. npm install concurrently express body-parser

Express は、Webアプリケーション用のノードフレームワークであり、APIリクエストを処理するための便利な抽象化を提供します。 同時には、ExpressバックエンドサーバーとVue.js開発サーバーを同時に実行するために使用されます。 ついに、 body-parser APIへのリクエストを解析するExpressミドルウェアです。

次に、 server.js アプリケーションのルートにあるファイル:

  1. nano server.js

次に、次のコードを追加します。

cart-backend / server.js
const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');
const path = require('path');

const app = express();
const PRODUCT_DATA_FILE = path.join(__dirname, 'server-product-data.json');
const CART_DATA_FILE = path.join(__dirname, 'server-cart-data.json');

app.set('port', (process.env.PORT || 3000));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use((req, res, next) => {
  res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
  res.setHeader('Pragma', 'no-cache');
  res.setHeader('Expires', '0');
  next();
});

app.listen(app.get('port'), () => {
  console.log(`Find the server at: http://localhost:${app.get('port')}/`);
});

このスニペットは、最初にノードモジュールをバックエンドに追加します。 fs ファイルシステムに書き込むモジュールと path ファイルパスの定義を容易にするモジュール。 次に、Expressを初期化します app JSONファイルへの参照を次のように保存します PRODUCT_DATA_FILECART_DATA_FILE. これらはデータリポジトリとして使用されます。 最後に、Expressサーバーを作成し、ポートを設定し、応答ヘッダーを設定するミドルウェアを作成し、サーバーがポートでリッスンするように設定しました。 Expressの詳細については、Expressの公式ドキュメントを参照してください。

The setHeader メソッドは、HTTP応答のヘッダーを設定します。 この場合、使用しています Cache-Control アプリのキャッシュを指示します。 詳細については、Cache-Controlに関するMozillaDeveloperNetworkの記事をご覧ください。

次に、フロントエンドがショッピングカートにアイテムを追加するためにクエリを実行するAPIエンドポイントを作成します。 これを行うには、 app.post HTTPをリッスンする POST リクエスト。

次のコードをに追加します server.js 最後の直後 app.use() ミドルウェア:

cart-backend / server.js
...
app.use((req, res, next) => {
  res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
  res.setHeader('Pragma', 'no-cache');
  res.setHeader('Expires', '0');
  next();
});

app.post('/cart', (req, res) => {
    fs.readFile(CART_DATA_FILE, (err, data) => {
      const cartProducts = JSON.parse(data);
      const newCartProduct = { 
        id: req.body.id,
        title: req.body.title,
        description: req.body.description,
        price: req.body.price,
        image_tag: req.body.image_tag, 
        quantity: 1 
      };
      let cartProductExists = false;
      cartProducts.map((cartProduct) => {
        if (cartProduct.id === newCartProduct.id) {
          cartProduct.quantity++;
          cartProductExists = true;
        }
      });
      if (!cartProductExists) cartProducts.push(newCartProduct);
      fs.writeFile(CART_DATA_FILE, JSON.stringify(cartProducts, null, 4), () => {
        res.setHeader('Cache-Control', 'no-cache');
        res.json(cartProducts);
      });
    });
  });

app.listen(app.get('port'), () => {
  console.log(`Find the server at: http://localhost:${app.get('port')}/`);
});

このコードは、フロントエンドからカートアイテムを含むリクエストオブジェクトを受け取り、それらを server-cart-data.json プロジェクトのルートにあるファイル。 こちらの製品はJavaScriptオブジェクトid, title, description, price, image_tag、 と quantity プロパティ。 コードはまた、カートがすでに存在するかどうかをチェックして、繰り返される製品のリクエストが増加するだけであることを確認します quantity.

次に、ショッピングカートからアイテムを削除するためのAPIエンドポイントを作成するコードを追加します。 今回は使用します app.delete HTTPをリッスンする DELETE リクエスト。

次のコードをに追加します server.js 前のエンドポイントの直後:

cart-backend / server.js
...
      fs.writeFile(CART_DATA_FILE, JSON.stringify(cartProducts, null, 4), () => {
        res.setHeader('Cache-Control', 'no-cache');
        res.json(cartProducts);
      });
    });
  });

app.delete('/cart/delete', (req, res) => {
  fs.readFile(CART_DATA_FILE, (err, data) => {
    let cartProducts = JSON.parse(data);
    cartProducts.map((cartProduct) => {
      if (cartProduct.id === req.body.id && cartProduct.quantity > 1) {
        cartProduct.quantity--;
      } else if (cartProduct.id === req.body.id && cartProduct.quantity === 1) {
        const cartIndexToRemove = cartProducts.findIndex(cartProduct => cartProduct.id === req.body.id);
        cartProducts.splice(cartIndexToRemove, 1);
      }
    });
    fs.writeFile(CART_DATA_FILE, JSON.stringify(cartProducts, null, 4), () => {
      res.setHeader('Cache-Control', 'no-cache');
      res.json(cartProducts);
    });
  });
});

app.listen(app.get('port'), () => {
  console.log(`Find the server at: http://localhost:${app.get('port')}/`); // eslint-disable-line no-console
});

このコードは、カートから削除するアイテムを含むリクエストオブジェクトを受け取り、 server-cart-data.json そのを介してこのアイテムのファイル id. 存在し、数量が1より大きい場合、カート内のアイテムの数量が差し引かれます。 それ以外の場合、アイテムの数量が1未満の場合、カートから削除され、残りのアイテムは server-cart-data.json ファイル。

ユーザーに追加機能を提供するために、APIエンドポイントを作成して、ショッピングカートからすべてのアイテムを削除できるようになりました。 これもリッスンします DELETE リクエスト。

次の強調表示されたコードをに追加します server.js 前のエンドポイントの後:

cart-backend / server.js
...
    fs.writeFile(CART_DATA_FILE, JSON.stringify(cartProducts, null, 4), () => {
      res.setHeader('Cache-Control', 'no-cache');
      res.json(cartProducts);
    });
  });
});

app.delete('/cart/delete/all', (req, res) => {
  fs.readFile(CART_DATA_FILE, () => {
    let emptyCart = [];
    fs.writeFile(CART_DATA_FILE, JSON.stringify(emptyCart, null, 4), () => {
      res.json(emptyCart);
    });
  });
});

app.listen(app.get('port'), () => {
  console.log(`Find the server at: http://localhost:${app.get('port')}/`); // eslint-disable-line no-console
});

このコードは、空の array を返すことにより、カートからすべてのアイテムを削除する役割を果たします。

次に、APIエンドポイントを作成して、製品ストレージからすべての製品を取得します。 これは使用します app.get 聞くために GET リクエスト。

次のコードをに追加します server.js 前のエンドポイントの後:

cart-backend / server.js
...
app.delete('/cart/delete/all', (req, res) => {
  fs.readFile(CART_DATA_FILE, () => {
    let emptyCart = [];
    fs.writeFile(CART_DATA_FILE, JSON.stringify(emptyCart, null, 4), () => {
      res.json(emptyCart);
    });
  });
});

app.get('/products', (req, res) => {
  fs.readFile(PRODUCT_DATA_FILE, (err, data) => {
    res.setHeader('Cache-Control', 'no-cache');
    res.json(JSON.parse(data));
  });
});
...

このコードはファイルシステムのネイティブを使用します readFile 内のすべてのデータをフェッチするメソッド server-product-data.json ファイルを作成し、JSON形式で返します。

最後に、カートストレージからすべてのアイテムを取得するためのAPIエンドポイントを作成します。

cart-backend / server.js
...
app.get('/products', (req, res) => {
  fs.readFile(PRODUCT_DATA_FILE, (err, data) => {
    res.setHeader('Cache-Control', 'no-cache');
    res.json(JSON.parse(data));
  });
});

app.get('/cart', (req, res) => {
  fs.readFile(CART_DATA_FILE, (err, data) => {
    res.setHeader('Cache-Control', 'no-cache');
    res.json(JSON.parse(data));
  });
});
...

同様に、このコードはファイルシステムのネイティブを使用します readFile 内のすべてのデータをフェッチするメソッド server-cart-data.json ファイルを作成し、JSON形式で返します。

保存して閉じます server.js ファイル。

次に、テスト目的でJSONファイルにモックデータを追加します。

開く server-cart-data.json 以前に作成したファイル:

  1. nano server-cart-data.json

次の製品オブジェクトの配列を追加します。

cart-backend / server-cart-data.json
[
    {
        "id": 2,
        "title": "MIKANO Engine",
        "description": "Lorem ipsum dolor sit amet, consectetur  dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis! ",
        "price": 650.9,
        "image_tag": "diesel-engine.png",
        "quantity": 1
    },
    {
        "id": 3,
        "title": "SEFANG Engine",
        "description": "Lorem ipsum dolor sit amet, consectetur  dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis!",
        "price": 619.9,
        "image_tag": "sefang-engine.png",
        "quantity": 1
    }
]

これは、ユーザーのショッピングカートで起動する2つのエンジンを示しています。

ファイルを保存して閉じます。

今すぐ開きます server-product-data.json ファイル:

  1. nano server-product-data.json

次のデータをに追加します server-product-data.json ファイル:

cart-backend / server-product-data.json
[
    {
      "id": 1,
      "title": "CAT Engine",
      "description": "Lorem ipsum dolor sit amet, consectetur  dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis!",
      "product_type": "power set/diesel engine",
      "image_tag": "CAT-engine.png",
      "created_at": 2020,
      "owner": "Colton",
      "owner_photo": "image-colton.jpg",
      "email": "[email protected]",
      "price": 719.9
    },
    {
      "id": 2,
      "title": "MIKANO Engine",
      "description": "Lorem ipsum dolor sit amet, consectetur  dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis! ",
      "product_type": "power set/diesel engine",
      "image_tag": "diesel-engine.png",
      "created_at": 2020,
      "owner": "Colton",
      "owner_photo": "image-colton.jpg",
      "email": "[email protected]",
      "price": 650.9
    },
    {
      "id": 3,
      "title": "SEFANG Engine",
      "description": "Lorem ipsum dolor sit amet, consectetur  dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis!",
      "product_type": "power set/diesel engine",
      "image_tag": "sefang-engine.png",
      "created_at": 2017,
      "owner": "Anne",
      "owner_photo": "image-anne.jpg",
      "email": "[email protected]",
      "price": 619.9
    },
    {
      "id": 4,
      "title": "CAT Engine",
      "description": "Lorem ipsum dolor sit amet, consectetur  dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis!",
      "product_type": "power set/diesel engine",
      "image_tag": "lawn-mower.png",
      "created_at": 2017,
      "owner": "Irene",
      "owner_photo": "image-irene.jpg",
      "email": "[email protected]",
      "price": 319.9
    }
    
  ]

これにより、ユーザーがカートに入れることができるすべての可能な製品が保持されます。

ファイルを保存して閉じます。

最後に、次のコマンドを実行してサーバーを実行します。

  1. node server

あなたはあなたのターミナルでこのようなものを受け取るでしょう:

Output
Find the server at: http://localhost:3000/

このウィンドウでこのサーバーを実行したままにします。

最後に、Vueアプリでプロキシサーバーを設定します。 これにより、フロントエンドとバックエンド間の接続が可能になります。

Vueアプリのルートディレクトリに移動します。

  1. cd ../vuex-shopping-cart

ターミナルで、次のコマンドを実行してVue構成ファイルを作成します。

  1. nano vue.config.js

次に、次のコードを追加します。

vuex-shopping-cart / vue.config.js
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000/',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
}

これにより、フロントエンドからバックエンドサーバーにリクエストが送信されます。 http://localhost:3000/. プロキシ構成の詳細については、VuedevServer.proxyドキュメントを確認してください。

ファイルを保存して閉じます。

このステップでは、ショッピングカートのAPIエンドポイントを処理するサーバー側のコードを記述しました。 ファイル構造を作成することから始めて、必要なコードをに追加することで終わりました server.js JSONファイル内のファイルとデータ。 次に、フロントエンドの状態ストレージを設定します。

ステップ3—Vuexを使用した状態管理の設定

Vuexでは、ストアはアプリケーションの状態が保持される場所です。 アプリケーションの状態を更新するには、コンポーネント内でアクションをディスパッチして、ストアでミューテーションをトリガーする必要があります。 Vuexストアは、状態、ミューテーション、アクション、およびゲッターで構成されています。

このステップでは、これらの各部分を作成し、その後、すべてをVuexストアに結合します。

次に、アプリケーションの状態を保存する場所を作成します。

The store ルートディレクトリのフォルダ src プロジェクトのは、プロジェクトのセットアップ時に自動的に作成されます。 を見つけます store のフォルダ src 次に、プロジェクトのディレクトリに、という名前の新しいフォルダを作成します modules:

  1. mkdir src/store/modules

このフォルダ内に、 productcart フォルダ:

  1. mkdir src/store/modules/product
  2. mkdir src/store/modules/cart

これらは、製品在庫とユーザーのカートのすべての状態ファイルを保持します。 これらの2つのファイルを同時にビルドし、それぞれを別々のターミナルで開きます。 このようにして、ミューテーション、ゲッター、およびアクションを並べて比較することができます。

最後に、 index.js のファイル product フォルダ:

  1. nano src/store/modules/product/index.js

次のコードを追加して、 productItems:

vuex-shopping-cart / src / store / modules / product / index.js
import axios from 'axios';
const state = {
  productItems: [] 
}

ファイルを保存して開いたままにします。

同様に、新しいターミナルで、 index.js ファイルに cart 次のディレクトリ:

  1. nano src/store/modules/cart/index.js

次に、のコードを追加します cartItems:

vuex-shopping-cart / src / store / modules / cart / index.js
import axios from 'axios';
const state = {
  cartItems: []
}

このファイルを保存しますが、開いたままにしておきます。

これらのコードスニペットでは、Axiosモジュールをインポートして状態を設定しました。 The state コンポーネント間で共有する必要のあるアプリケーションレベルのデータを保持するストアオブジェクトです。

状態を設定したので、ミューテーションに進みます。

突然変異

Mutations は、ストアの状態を変更するメソッドです。 これらは通常、文字列タイプと、状態とペイロードをパラメーターとして受け入れるハンドラーで構成されます。

これで、アプリケーションのすべてのミューテーションを作成します。

次のコードをに追加します product/index.js 直後のファイル state セクション:

vuex-shopping-cart / src / store / modules / product / index.js
...
const mutations = {
  UPDATE_PRODUCT_ITEMS (state, payload) {
    state.productItems = payload;
  }
}

これにより、 mutations を保持するオブジェクト UPDATE_PRODUCT_ITEMS を設定するメソッド productItems への配列 payload 価値。

同様に、次のコードをに追加します cart/index.js 状態セクションの直後のファイル:

vuex-shopping-cart / src / store / modules / cart / index.js
...
const mutations = {
  UPDATE_CART_ITEMS (state, payload) {
    state.cartItems = payload;
  }
}

これは同様のものを作成します UPDATE_CART_ITEMS ユーザーのショッピングカート用。 これは、大文字の変更を参照するFluxアーキテクチャスタイルに従っていることに注意してください。

行動

アクションはミューテーションを処理するメソッドであるため、ミューテーションは他のアプリケーションコードから隔離されます。

product/index.js、作成する actions アプリケーションのすべてのアクションを含むオブジェクト:

vuex-shopping-cart / src / store / modules / product / index.js
...
const actions = {
  getProductItems ({ commit }) {
    axios.get(`/api/products`).then((response) => {
      commit('UPDATE_PRODUCT_ITEMS', response.data)
    });
  }
}

ここに getProductItems メソッドは非同期を送信します GET 以前にインストールしたAxiosパッケージを使用してサーバーにリクエストします。 リクエストが成功すると、 UPDATE_PRODUCT_ITEMS 応答データをペイロードとしてミューテーションが呼び出されます。

次に、以下を追加します actions に反対する cart/index.js:

vuex-shopping-cart / src / store / modules / cart / index.js
...
const actions = {
  getCartItems ({ commit }) {
    axios.get('/api/cart').then((response) => {
      commit('UPDATE_CART_ITEMS', response.data)
    });
  },
  addCartItem ({ commit }, cartItem) {
    axios.post('/api/cart', cartItem).then((response) => {
      commit('UPDATE_CART_ITEMS', response.data)
    });
  },
  removeCartItem ({ commit }, cartItem) {
    axios.delete('/api/cart/delete', cartItem).then((response) => {
      commit('UPDATE_CART_ITEMS', response.data)
    });
  },
  removeAllCartItems ({ commit }) {
    axios.delete('/api/cart/delete/all').then((response) => {
      commit('UPDATE_CART_ITEMS', response.data)
    });
  }
}

このファイルでは、 getCartItems 非同期を送信するメソッド GET サーバーへのリクエスト。 リクエストが成功すると、 UPDATE_CART_ITEMS 応答データをペイロードとしてミューテーションが呼び出されます。 同じことが removeAllCartItems メソッド、それは作るが DELETE サーバーへのリクエスト。 The removeCartItemaddCartItem メソッドは cartItem を作成するためのパラメータとしてのオブジェクト DELETE また POST リクエスト。 リクエストが成功した後、 UPDATE_CART_ITEMS 応答データをペイロードとしてミューテーションが呼び出されます。

ES6 destructuring を使用して、 commit Vuexからの方法 context 物体。 これは使用するのと似ています context.commit.

ゲッター

Getters は、アプリケーションストアに対して、コンポーネントに対して計算されたプロパティが何であるかを示します。 それらは、計算された状態データの受信を含むストア状態メソッドから計算された情報を返します。

次に、 getters のすべての情報を取得するオブジェクト product モジュール:

vuex-shopping-cart / src / store / modules / product / index.js
...
const getters = {
  productItems: state => state.productItems,
  productItemById: (state) => (id) => {
    return state.productItems.find(productItem => productItem.id === id)
  }
}

ここで、あなたはメソッドを作りました productItems 状態の商品アイテムのリストを返し、その後に productItemById、によって単一の製品を返す高階関数 id.

次に、 getters のオブジェクト cart/index.js:

vuex-shopping-cart / src / store / modules / cart / index.js
...
const getters = {
  cartItems: state => state.cartItems,
  cartTotal: state => {
    return state.cartItems.reduce((acc, cartItem) => {
      return (cartItem.quantity * cartItem.price) + acc;
    }, 0).toFixed(2);
  },
  cartQuantity: state => {
    return state.cartItems.reduce((acc, cartItem) => {
      return cartItem.quantity + acc;
    }, 0);
  }
}

このスニペットでは、 cartItems メソッド。状態のカートアイテムのリストを返し、その後に cartTotal、チェックアウトに使用できるカートアイテムの合計量の計算値を返します。 最後に、あなたは cartQuantity カート内のアイテムの数量を再調整するメソッド。

モジュールのエクスポート

の最後の部分 productcart モジュールはエクスポートします state, mutations, actions、 と getters アプリケーションの他の部分がそれらにアクセスできるようにオブジェクト。

product/index.js、ファイルの最後に次のコードを追加します。

vuex-shopping-cart / src / store / modules / product / index.js
...
const productModule = {
  state,
  mutations,
  actions,
  getters
}

export default productModule;

これにより、すべての状態オブジェクトが productModule オブジェクトを作成し、モジュールとしてエクスポートします。

保存 product/index.js ファイルを閉じます。

次に、同様のコードをに追加します cart/index.js:

vuex-shopping-cart / src / store / modules / product / index.js
...
	const cartModule = {
  state,
  mutations,
  actions,
  getters
}
export default cartModule;

これにより、モジュールが次のようにエクスポートされます。 cartModule.

ストアの設定

状態、ミューテーション、アクション、およびゲッターがすべて設定された状態で、Vuexをアプリケーションに統合する最後の部分はストアを作成することです。 ここでは、Vuexモジュールを利用して、アプリケーションストアを2つの管理可能なフラグメントに分割します。

ストアを作成するには、 index.js あなたのファイル store フォルダ:

  1. nano src/store/index.js

次の強調表示された行を追加します。

vuex-shopping-cart / src / store / index.js
import { createStore } from 'vuex'
import product from'./modules/product';
import cart from './modules/cart';

export default createStore({
  modules: {
    product,
    cart
  }
})

ファイルを保存して、テキストエディタを終了します。

これで、状態管理に必要なメソッドが作成され、ショッピングカート用のストアが作成されました。 次に、データを使用するためのユーザーインターフェイス(UI)コンポーネントを作成します。

ステップ4—インターフェイスコンポーネントの作成

ショッピングカートのストアを設定したので、ユーザーインターフェイス(UI)のコンポーネントの作成に進むことができます。 これには、ルーターにいくつかの変更を加え、ナビゲーションバーのフロントエンドコンポーネントを作成し、製品とカートのリストビューとアイテムビューを作成することが含まれます。

まず、更新します vue-router 設定。 Vue CLIツールを使用してアプリケーションをスキャフォールディングするときに、ルーターオプションを選択したことを思い出してください。これにより、Vueがルーターを自動的にセットアップできるようになります。 これで、ルーターを再構成して、次のパスを提供できます。 Cart_List.vueProduct_List.vue、後で作成するVueコンポーネントです。

次のコマンドでルーターファイルを開きます。

  1. nano vuex-shopping-cart/src/router/index.js

次の強調表示された行を追加します。

vuex-shopping-cart / src / router / index.js
import { createRouter, createWebHashHistory } from 'vue-router'
import CartList from '../components/cart/Cart_List.vue';
import ProductList from '../components/product/Product_List.vue';

const routes = [
  {
    path: '/inventory',
    component: ProductList
  },
  {
    path: '/cart',
    component: CartList
  },
  {
    path: '/',
    redirect: '/inventory'
  },
]
const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

これにより、 /inventory あなたの製品と /cart カート内のアイテムのルート。 また、ルートパスをリダイレクトします / 製品ビューに。

このコードを追加したら、ファイルを保存して閉じます。

これで、UIコンポーネントディレクトリを設定できます。 端末で次のコマンドを実行して、コンポーネントのディレクトリに移動します。

  1. cd src/components

次のコマンドを実行して、コンポーネントのディレクトリの下に3つの新しいサブフォルダを作成します。

  1. mkdir core cart product

core ナビゲーションバーなど、アプリケーションの重要な部分を保持します。 cartproduct ショッピングカートと総在庫のアイテムとリストビューを保持します。

core ディレクトリ、を作成します Navbar.vue 次のコマンドを実行してファイルを作成します。

  1. touch core/Navbar.vue

cart ディレクトリ、ファイルを作成します Cart_List_Item.vueCart_List.vue:

  1. touch cart/Cart_List_Item.vue cart/Cart_List.vue

最後に、 product ディレクトリ、次の2つのファイルを作成します。

  1. touch product/Product_List_Item.vue product/Product_List.vue

ファイル構造の概要がわかったので、フロントエンドアプリの個々のコンポーネントの作成に進むことができます。

ナビゲーションバーのカートナビゲーションリンクには、カート内のアイテムの数量が表示されます。 Vuexを使用します mapGetters コンポーネントで計算されたプロパティを使用してストアゲッターを直接マッピングするヘルパーメソッド。これにより、アプリはストアのゲッターからストアのゲッターにこのデータを取得できます。 Navbar 成分。

navbarファイルを開きます。

  1. nano core/Navbar.vue

コードを次のように置き換えます。

vuex-shopping-cart / src / components / core / Navbar.vue
<template>
    <nav class="navbar" role="navigation" aria-label="main navigation">
      <div class="navbar-brand">
        <a
          role="button"
          class="navbar-burger burger"
          aria-label="menu"
          aria-expanded="false"
          data-target="navbarBasicExample"
        >
          <span aria-hidden="true"></span>
          <span aria-hidden="true"></span>
          <span aria-hidden="true"></span>
        </a>
      </div>
      <div id="navbarBasicExample" class="navbar-menu">
        <div class="navbar-end">
          <div class="navbar-item">
            <div class="buttons">
              <router-link to="/inventory" class="button is-primary">
               <strong> Inventory</strong>
              </router-link>
              <router-link to="/cart"  class="button is-warning">   <p>
    Total cart items:
    <span> {{cartQuantity}}</span> </p>
              </router-link>
            </div>
          </div>
        </div>
      </div>
    </nav>
</template>
<script>
import {mapGetters} from "vuex"
export default {
    name: "Navbar",
    computed: {
    ...mapGetters([
      'cartQuantity'
    ])
  },
  created() {
    this.$store.dispatch("getCartItems");
  }
}
</script>

Vueコンポーネントとして、このファイルは template コンポーネントのHTMLを保持する要素。 このスニペットには複数が含まれています navbar BulmaCSSフレームワークから事前に作成されたスタイルを使用するクラス。 詳細については、Bulmaのドキュメントをご覧ください。

これも使用します router-link アプリを商品やカートに接続するための要素、および使用 cartQuantity 計算されたプロパティとして、カート内のアイテムの数を動的に追跡します。

JavaScriptはで保持されます script 要素。状態管理も処理し、コンポーネントをエクスポートします。 The getCartItems ナビゲーションバーコンポーネントが作成されるとアクションがディスパッチされ、サーバーから受信した応答データからのすべてのカートアイテムでストアの状態が更新されます。 この後、ストアゲッターは戻り値と cartQuantity テンプレートにレンダリングされます。 派遣せずに getCartItems 作成されたライフサイクルフックに対するアクション、の値 cartQuantity ストアの状態が変更されるまでは0になります。

ファイルを保存して閉じます。

Product_List 成分

このコンポーネントは、 Product_List_Item 成分。 それは小道具として製品アイテムを渡す責任があります Product_List_Item (子)コンポーネント。

まず、ファイルを開きます。

  1. nano product/Product_List.vue

アップデート Product_List.vue 次のように:

vuex-shopping-cart / src / components / product / Product_List.vue
<template>
  <div class="container is-fluid">
    <div class="tile is-ancestor">
      <div class="tile is-parent" v-for="productItem in productItems" :key="productItem.id">
      <ProductListItem :productItem="productItem"/>
      </div>
    </div>
  </div>
</template>
<script>
import { mapGetters } from 'vuex';
import Product_List_Item from './Product_List_Item'
export default {
  name: "ProductList",
  components: {
    ProductListItem:Product_List_Item
  },
  computed: {
    ...mapGetters([
      'productItems'
    ])
  },
  created() {
    this.$store.dispatch('getProductItems');
  }
};
</script>

に似ています Navbar 前に説明したコンポーネントロジック、ここではVuex mapGetters ヘルパーメソッドは、ストアゲッターをコンポーネントで計算されたプロパティに直接マップして、 productItems ストアからのデータ。 The getProductItems アクションは、 ProductList コンポーネントが作成され、サーバーから受信した応答データのすべての商品アイテムでストアの状態が更新されます。 この後、ストアゲッターは戻り値と productItems テンプレートにレンダリングされます。 派遣せずに getProductItems 作成されたライフサイクルフックに対するアクション。ストアの状態が変更されるまで、テンプレートに商品アイテムは表示されません。

Product_List_Item 成分

このコンポーネントは、 Product_List 成分。 それは受け取ります productItem 親からの小道具としてのデータをテンプレートにレンダリングします。

開ける Product_List_Item.vue:

  1. nano product/Product_List_Item.vue

次に、次のコードを追加します。

vuex-shopping-cart / src / components / product / Product_List_Item.vue
<template>
    <div class="card">
      <div class="card-content">
        <div class="content">
          <h4>{{ productItem.title }}</h4>
          <a
            class="button is-rounded is-pulled-left"
            @click="addCartItem(productItem)"
          >
            <strong>Add to Cart</strong>
          </a>
          <br />
          <p class="mt-4">
            {{ productItem.description }}
          </p>
        </div>
        <div class="media">
          <div class="media-content">
            <p class="title is-6">{{ productItem.owner }}</p>
            <p class="subtitle is-7">{{ productItem.email }}</p>
          </div>
          <div class="media-right">
            <a class="button is-primary is-light">
              <strong>$ {{ productItem.price }}</strong>
            </a>
          </div>
        </div>
      </div>
    </div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
  name: "ProductListItem",
  props: ["productItem"],
  methods: {
    ...mapActions(["addCartItem"]),
  },
};
</script>

に加えて mapGetters 以前のコンポーネントで使用されていたヘルパー関数であるVuexは、 mapActions コンポーネントメソッドをストアのアクションに直接マップするヘルパー関数。 この場合、 mapAction コンポーネントメソッドをにマップするヘルパー関数 addCartItem ストアでのアクション。 これで、カートにアイテムを追加できます。

ファイルを保存して閉じます。

Cart_List 成分

このコンポーネントは、カートに追加されたすべての製品アイテムを表示し、カートからすべてのアイテムを削除する役割を果たします。

このコンポーネントを作成するには、最初にファイルを開きます。

  1. nano cart/Cart_List.vue

次に、更新します Cart_List.vue 次のように:

vuex-shopping-cart / src / components / cart / Cart_List.vue
<template>
  <div id="cart">
    <div class="cart--header has-text-centered">
      <i class="fa fa-2x fa-shopping-cart"></i>
    </div>
    <p v-if="!cartItems.length" class="cart-empty-text has-text-centered">
      Add some items to the cart!
    </p>
    <ul>
      <li class="cart-item" v-for="cartItem in cartItems" :key="cartItem.id">
          <CartListItem :cartItem="cartItem"/>
      </li>
      <div class="notification is-success">
        <button class="delete"></button>
        <p>
          Total Quantity:
          <span class="has-text-weight-bold">{{ cartQuantity }}</span>
        </p>
      </div>
      <br>
    </ul>
    <div class="buttons">
    <button :disabled="!cartItems.length" class="button is-info">
      Checkout (<span class="has-text-weight-bold">${{ cartTotal }}</span>)
    </button>
     
 <button class="button is-danger is-outlined" @click="removeAllCartItems">
    <span>Delete All items</span>
    <span class="icon is-small">
      <i class="fas fa-times"></i>
    </span>
  </button>
       </div>
  </div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import CartListItem from "./Cart_List_Item";
export default {
  name: "CartList",
  components: {
    CartListItem
  },
  computed: {
    ...mapGetters(["cartItems", "cartTotal", "cartQuantity"]),
  },
  created() {
    this.$store.dispatch("getCartItems");
  },
  methods: {
    ...mapActions(["removeAllCartItems"]),
  }
};
</script>

このコードは、テンプレートで v-ifステートメントを使用して、カートが空の場合に条件付きでメッセージをレンダリングします。 それ以外の場合は、カートアイテムのストアを繰り返し処理し、ページにレンダリングします。 また、 cartItems, cartTotal、 と cartQuantity データプロパティを計算するゲッター、および removeAllCartItems アクション。

ファイルを保存して閉じます。

Cart_List_Item 成分

このコンポーネントは、の直接の子コンポーネントです。 Cart_List 成分。 それは受け取ります cartItem 親からの小道具としてのデータをテンプレートにレンダリングします。 また、カート内のアイテムの数量を増減する役割も果たします。

ファイルを開きます。

  1. nano cart/Cart_List_Item.vue

アップデート Cart_List_Item.vue 次のように:

vuex-shopping-cart / src / components / cart / Cart_List_Item.vue
<template>
  <div class="box">
    <div class="cart-item__details">
      <p class="is-inline">{{cartItem.title}}</p>
      <div>
        <span class="cart-item--price has-text-info has-text-weight-bold">
          ${{cartItem.price}} X {{cartItem.quantity}}
        </span>
        
        <span>
          <i class="fa fa-arrow-circle-up cart-item__modify" @click="addCartItem(cartItem)"></i>
          <i class="fa fa-arrow-circle-down cart-item__modify" @click="removeCartItem(cartItem)"></i>
        </span>
      </div>
      
    </div>
  </div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
  name: 'CartListItem',
  props: ['cartItem'],
  methods: {
    ...mapActions([
      'addCartItem',
      'removeCartItem'
    ])
  }
}
</script>

ここでは、 mapAction コンポーネントメソッドをにマップするヘルパー関数 addCartItemremoveCartItem ストアでのアクション。

ファイルを保存して閉じます。

最後に、更新します App.vue これらのコンポーネントをアプリに取り込むためのファイル。 まず、プロジェクトのルートフォルダに戻ります。

  1. cd ../..

次に、ファイルを開きます。

  1. nano src/App.vue

内容を次のコードに置き換えます。

vuex-shopping-cart / src / App.vue
<template>
  <div>
    <Navbar/>
    <div class="container mt-6">
      <div class="columns">
        <div class="column is-12 column--align-center">
          <router-view></router-view>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import Navbar from './components/core/Navbar'
export default {
  name: 'App',
  components: {
    Navbar
  }
}
</script>
<style>
html,
body {
  height: 100%;
  background: #f2f6fa;
}
</style>

App.vue Vueコンポーネントファイル形式で定義されたアプリケーションのルートです。 変更を加えたら、ファイルを保存して閉じます。

このステップでは、ナビゲーションバー、商品在庫、およびショッピングカートのコンポーネントを作成して、ショッピングカートアプリのフロントエンドを設定します。 また、前の手順で作成したストアアクションとゲッターも使用しました。 次に、アプリケーションを起動して実行します。

ステップ5—アプリケーションの実行

アプリの準備ができたので、開発サーバーを起動して最終製品を試すことができます。

フロントエンドプロジェクトのルートで次のコマンドを実行します。

  1. npm run serve

これにより、アプリを表示できる開発サーバーが起動します http://localhost:8080. また、バックエンドが別の端末で実行されていることを確認してください。 これを行うには、で次のコマンドを実行します cart-backend 事業:

  1. node server

バックエンドとフロントエンドが実行されたら、次の場所に移動します http://localhost:8080 ブラウザで。 機能しているショッピングカートアプリケーションが見つかります。

結論

このチュートリアルでは、データ管理にVue.jsとVuexを使用してオンラインショッピングカートアプリを作成しました。 これらの手法は、eコマースショッピングアプリケーションの基盤を形成するために再利用できます。 Vue.jsの詳細については、Vue.jsトピックページをご覧ください。