Vue3とVuexでショッピングカートを作成する方法
序章
Vue.js は、パフォーマンスが高く、プログレッシブ Javascriptフレームワークです。 これはGitHubで人気のあるフレームワークであり、活発で役立つコミュニティがあります。
Vue Webフレームワークの機能を示すために、このチュートリアルでは、eコマースアプリのショッピングカートの作成について説明します。 このアプリは、商品情報を保存し、顧客が後でチェックアウトするために購入したい商品を保持します。 情報を保存するには、Vue.jsで広く使用されている状態管理ライブラリVuexを調べます。 これにより、ショッピングカートアプリケーションがデータをサーバーに永続化できるようになります。 また、Vuexを使用して非同期タスク管理を処理します。
チュートリアルを終了すると、次のような機能するショッピングカートアプリケーションが作成されます。
前提条件
-
Node.jsを実行する開発環境が必要になります。 このチュートリアルは、Node.jsバージョン10.22.0およびnpmバージョン6.14.6でテストされました。 これをmacOSまたはUbuntu18.04にインストールするには、Node.jsをインストールしてmacOSにローカル開発環境を作成する方法またはの
PPAを使用したインストール ]セクションの手順に従います。 Ubuntu18.04にNode.jsをインストールする方法。 -
また、JavaScript、HTML、およびCSSの基本的な知識も必要です。これは、HTMLシリーズを使用してWebサイトを構築する方法、CSSシリーズを使用してWebサイトを構築する方法にあります。 、およびJavaScriptでコーディングする方法。
ステップ1—VueCLIを使用したアプリケーションのセットアップ
バージョン4.5.0以降、 Vue CLI には、新しいプロジェクトを作成するときにVue3プリセットを選択するための組み込みオプションが用意されています。 最新バージョンのVueCLIを使用すると、Vue 3をそのまま使用して、既存のVue2プロジェクトをVue3に更新できます。 このステップでは、Vue CLIを使用してプロジェクトを作成し、フロントエンドの依存関係をインストールします。
まず、ターミナルから次のコマンドを実行して、最新バージョンのVueCLIをインストールします。
- npm install -g @vue/cli
これにより、VueCLIがシステムにグローバルにインストールされます。
注:一部のシステムでは、npmパッケージをグローバルにインストールすると、アクセス許可エラーが発生し、インストールが中断される可能性があります。 使用を避けることがセキュリティのベストプラクティスであるため sudo
と npm install
、代わりに、npmのデフォルトディレクトリを変更することでこれを解決できます。 あなたが遭遇した場合 EACCES
エラーが発生した場合は、npmの公式ドキュメントのの指示に従ってください。
このコマンドで正しいバージョンを使用していることを確認してください。
- vue --version
次のような出力が得られます。
Output@vue/cli 4.5.10
注:古いバージョンのVue CLIが既にグローバルにインストールされている場合は、ターミナルから次のコマンドを実行してアップグレードします。
- npm update -g @vue/cli
これで、新しいプロジェクトを作成できます。
- vue create vuex-shopping-cart
これはVueCLIコマンドを使用します vue create
名前の付いたプロジェクトを作成するには vuex-shopping-cart
. Vue CLIの詳細については、VueCLIを使用してVue.jsシングルページアプリを生成する方法をご覧ください。
次に、次のプロンプトが表示されます。
OutputVue 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がアプリケーションを作成します。
プロジェクトの作成後、次のコマンドを使用してフォルダーに移動します。
- cd vuex-shopping-cart
まず、Flexboxに基づく無料のオープンソースCSSフレームワークであるBulmaをインストールします。 次のコマンドを実行して、プロジェクトにBulmaを追加します。
- npm install bulma
プロジェクトでBulmaCSSを使用するには、アプリのエントリポイントである main.js
ファイル:
- nano 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モジュールを追加します。
- npm install axios
次に、アプリを実行して、機能していることを確認します。
- npm run serve
案内する http://localhost:8080
選択したブラウザで。 Vueアプリのウェルカムページがあります。
Vueが機能していることを確認したら、サーバーを停止します。 CTRL+C
.
このステップでは、コンピューターにVue CLIをグローバルにインストールし、Vueプロジェクトを作成し、必要なnpmパッケージAxiosとBulmaをインストールし、Bulmaをプロジェクトにインポートしました。 main.js
ファイル。 次に、アプリのデータを保存するためのバックエンドAPIを設定します。
ステップ2—バックエンドを設定する
このステップでは、Vueプロジェクトで動作する別のバックエンドを作成します。 これは、フロントエンドのVueアプリケーションとは別のプロジェクトフォルダーにあります。
まず、Vueディレクトリから移動します。
- cd ..
名前の付いた別のディレクトリを作成します cart-backend
:
- mkdir cart-backend
バックエンドフォルダを作成したら、それを作業ディレクトリにします。
- cd cart-backend
必要なファイルを使用してプロジェクトを初期化することから始めます。 次のコマンドを使用して、アプリのファイル構造を作成します。
- touch server.js
- touch server-cart-data.json
- touch server-product-data.json
あなたは touch
ここでコマンドを実行して、空のファイルを作成します。 The server.js
ファイルにはNode.jsサーバーが保持され、JSONにはショップの商品とユーザーのショッピングカートのデータが保持されます。
次のコマンドを実行して、 package.json
ファイル:
- npm init
npmとNodeの詳細については、Node.jsシリーズのコーディング方法をご覧ください。
これらのバックエンド依存関係をNodeプロジェクトにインストールします。
- npm install concurrently express body-parser
Express は、Webアプリケーション用のノードフレームワークであり、APIリクエストを処理するための便利な抽象化を提供します。 同時には、ExpressバックエンドサーバーとVue.js開発サーバーを同時に実行するために使用されます。 ついに、 body-parser
APIへのリクエストを解析するExpressミドルウェアです。
次に、 server.js
アプリケーションのルートにあるファイル:
- nano 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_FILE
と CART_DATA_FILE
. これらはデータリポジトリとして使用されます。 最後に、Expressサーバーを作成し、ポートを設定し、応答ヘッダーを設定するミドルウェアを作成し、サーバーがポートでリッスンするように設定しました。 Expressの詳細については、Expressの公式ドキュメントを参照してください。
The setHeader
メソッドは、HTTP応答のヘッダーを設定します。 この場合、使用しています Cache-Control
アプリのキャッシュを指示します。 詳細については、Cache-Controlに関するMozillaDeveloperNetworkの記事をご覧ください。
次に、フロントエンドがショッピングカートにアイテムを追加するためにクエリを実行するAPIエンドポイントを作成します。 これを行うには、 app.post
HTTPをリッスンする POST
リクエスト。
次のコードをに追加します server.js
最後の直後 app.use()
ミドルウェア:
...
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
前のエンドポイントの直後:
...
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
前のエンドポイントの後:
...
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
前のエンドポイントの後:
...
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エンドポイントを作成します。
...
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
以前に作成したファイル:
- nano 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
ファイル:
- nano server-product-data.json
次のデータをに追加します 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
}
]
これにより、ユーザーがカートに入れることができるすべての可能な製品が保持されます。
ファイルを保存して閉じます。
最後に、次のコマンドを実行してサーバーを実行します。
- node server
あなたはあなたのターミナルでこのようなものを受け取るでしょう:
OutputFind the server at: http://localhost:3000/
このウィンドウでこのサーバーを実行したままにします。
最後に、Vueアプリでプロキシサーバーを設定します。 これにより、フロントエンドとバックエンド間の接続が可能になります。
Vueアプリのルートディレクトリに移動します。
- cd ../vuex-shopping-cart
ターミナルで、次のコマンドを実行してVue構成ファイルを作成します。
- nano 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
:
- mkdir src/store/modules
このフォルダ内に、 product
と cart
フォルダ:
- mkdir src/store/modules/product
- mkdir src/store/modules/cart
これらは、製品在庫とユーザーのカートのすべての状態ファイルを保持します。 これらの2つのファイルを同時にビルドし、それぞれを別々のターミナルで開きます。 このようにして、ミューテーション、ゲッター、およびアクションを並べて比較することができます。
最後に、 index.js
のファイル product
フォルダ:
- nano src/store/modules/product/index.js
次のコードを追加して、 productItems
:
import axios from 'axios';
const state = {
productItems: []
}
ファイルを保存して開いたままにします。
同様に、新しいターミナルで、 index.js
ファイルに cart
次のディレクトリ:
- nano src/store/modules/cart/index.js
次に、のコードを追加します cartItems
:
import axios from 'axios';
const state = {
cartItems: []
}
このファイルを保存しますが、開いたままにしておきます。
これらのコードスニペットでは、Axiosモジュールをインポートして状態を設定しました。 The state
コンポーネント間で共有する必要のあるアプリケーションレベルのデータを保持するストアオブジェクトです。
状態を設定したので、ミューテーションに進みます。
突然変異
Mutations は、ストアの状態を変更するメソッドです。 これらは通常、文字列タイプと、状態とペイロードをパラメーターとして受け入れるハンドラーで構成されます。
これで、アプリケーションのすべてのミューテーションを作成します。
次のコードをに追加します product/index.js
直後のファイル state
セクション:
...
const mutations = {
UPDATE_PRODUCT_ITEMS (state, payload) {
state.productItems = payload;
}
}
これにより、 mutations
を保持するオブジェクト UPDATE_PRODUCT_ITEMS
を設定するメソッド productItems
への配列 payload
価値。
同様に、次のコードをに追加します cart/index.js
状態セクションの直後のファイル:
...
const mutations = {
UPDATE_CART_ITEMS (state, payload) {
state.cartItems = payload;
}
}
これは同様のものを作成します UPDATE_CART_ITEMS
ユーザーのショッピングカート用。 これは、大文字の変更を参照するFluxアーキテクチャスタイルに従っていることに注意してください。
行動
アクションはミューテーションを処理するメソッドであるため、ミューテーションは他のアプリケーションコードから隔離されます。
の product/index.js
、作成する actions
アプリケーションのすべてのアクションを含むオブジェクト:
...
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
:
...
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 removeCartItem
と addCartItem
メソッドは cartItem
を作成するためのパラメータとしてのオブジェクト DELETE
また POST
リクエスト。 リクエストが成功した後、 UPDATE_CART_ITEMS
応答データをペイロードとしてミューテーションが呼び出されます。
ES6 destructuring を使用して、 commit
Vuexからの方法 context
物体。 これは使用するのと似ています context.commit
.
ゲッター
Getters は、アプリケーションストアに対して、コンポーネントに対して計算されたプロパティが何であるかを示します。 それらは、計算された状態データの受信を含むストア状態メソッドから計算された情報を返します。
次に、 getters
のすべての情報を取得するオブジェクト product
モジュール:
...
const getters = {
productItems: state => state.productItems,
productItemById: (state) => (id) => {
return state.productItems.find(productItem => productItem.id === id)
}
}
ここで、あなたはメソッドを作りました productItems
状態の商品アイテムのリストを返し、その後に productItemById
、によって単一の製品を返す高階関数 id
.
次に、 getters
のオブジェクト 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
カート内のアイテムの数量を再調整するメソッド。
モジュールのエクスポート
の最後の部分 product
と cart
モジュールはエクスポートします state
, mutations
, actions
、 と getters
アプリケーションの他の部分がそれらにアクセスできるようにオブジェクト。
の product/index.js
、ファイルの最後に次のコードを追加します。
...
const productModule = {
state,
mutations,
actions,
getters
}
export default productModule;
これにより、すべての状態オブジェクトが productModule
オブジェクトを作成し、モジュールとしてエクスポートします。
保存 product/index.js
ファイルを閉じます。
次に、同様のコードをに追加します cart/index.js
:
...
const cartModule = {
state,
mutations,
actions,
getters
}
export default cartModule;
これにより、モジュールが次のようにエクスポートされます。 cartModule
.
ストアの設定
状態、ミューテーション、アクション、およびゲッターがすべて設定された状態で、Vuexをアプリケーションに統合する最後の部分はストアを作成することです。 ここでは、Vuexモジュールを利用して、アプリケーションストアを2つの管理可能なフラグメントに分割します。
ストアを作成するには、 index.js
あなたのファイル store
フォルダ:
- nano 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.vue
と Product_List.vue
、後で作成するVueコンポーネントです。
次のコマンドでルーターファイルを開きます。
- nano 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コンポーネントディレクトリを設定できます。 端末で次のコマンドを実行して、コンポーネントのディレクトリに移動します。
- cd src/components
次のコマンドを実行して、コンポーネントのディレクトリの下に3つの新しいサブフォルダを作成します。
- mkdir core cart product
core
ナビゲーションバーなど、アプリケーションの重要な部分を保持します。 cart
と product
ショッピングカートと総在庫のアイテムとリストビューを保持します。
下 core
ディレクトリ、を作成します Navbar.vue
次のコマンドを実行してファイルを作成します。
- touch core/Navbar.vue
下 cart
ディレクトリ、ファイルを作成します Cart_List_Item.vue
と Cart_List.vue
:
- touch cart/Cart_List_Item.vue cart/Cart_List.vue
最後に、 product
ディレクトリ、次の2つのファイルを作成します。
- touch product/Product_List_Item.vue product/Product_List.vue
ファイル構造の概要がわかったので、フロントエンドアプリの個々のコンポーネントの作成に進むことができます。
Navbar
成分
ナビゲーションバーのカートナビゲーションリンクには、カート内のアイテムの数量が表示されます。 Vuexを使用します mapGetters
コンポーネントで計算されたプロパティを使用してストアゲッターを直接マッピングするヘルパーメソッド。これにより、アプリはストアのゲッターからストアのゲッターにこのデータを取得できます。 Navbar
成分。
navbarファイルを開きます。
- nano 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
(子)コンポーネント。
まず、ファイルを開きます。
- nano product/Product_List.vue
アップデート 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
:
- nano 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
成分
このコンポーネントは、カートに追加されたすべての製品アイテムを表示し、カートからすべてのアイテムを削除する役割を果たします。
このコンポーネントを作成するには、最初にファイルを開きます。
- nano cart/Cart_List.vue
次に、更新します 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
親からの小道具としてのデータをテンプレートにレンダリングします。 また、カート内のアイテムの数量を増減する役割も果たします。
ファイルを開きます。
- nano cart/Cart_List_Item.vue
アップデート 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
コンポーネントメソッドをにマップするヘルパー関数 addCartItem
と removeCartItem
ストアでのアクション。
ファイルを保存して閉じます。
最後に、更新します App.vue
これらのコンポーネントをアプリに取り込むためのファイル。 まず、プロジェクトのルートフォルダに戻ります。
- cd ../..
次に、ファイルを開きます。
- nano 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—アプリケーションの実行
アプリの準備ができたので、開発サーバーを起動して最終製品を試すことができます。
フロントエンドプロジェクトのルートで次のコマンドを実行します。
- npm run serve
これにより、アプリを表示できる開発サーバーが起動します http://localhost:8080
. また、バックエンドが別の端末で実行されていることを確認してください。 これを行うには、で次のコマンドを実行します cart-backend
事業:
- node server
バックエンドとフロントエンドが実行されたら、次の場所に移動します http://localhost:8080
ブラウザで。 機能しているショッピングカートアプリケーションが見つかります。
結論
このチュートリアルでは、データ管理にVue.jsとVuexを使用してオンラインショッピングカートアプリを作成しました。 これらの手法は、eコマースショッピングアプリケーションの基盤を形成するために再利用できます。 Vue.jsの詳細については、Vue.jsトピックページをご覧ください。