Table of Contents
- Prerequisites
- Project Setup
- Project Structure Overview
- Core Components Development
- 4.1 Product Listing Page
- 4.2 Product Detail Page
- 4.3 Shopping Cart
- State Management with Vuex
- Routing with Vue Router
- Backend Integration
- UI Enhancement with Vuetify
- Testing the Application
- Deployment
- Conclusion
- References
Prerequisites
Before starting, ensure you have the following tools installed:
- Node.js (v14+ recommended) and npm (v6+) or Yarn (v1+): To run the Vue.js development server and manage dependencies.
- Vue CLI: The official Vue.js scaffolding tool. Install it globally with:
npm install -g @vue/cli # or yarn global add @vue/cli - Basic knowledge of Vue.js: Familiarity with components, props, directives (e.g.,
v-for,v-bind), and lifecycle hooks. - Basic JavaScript/HTML/CSS: For handling logic, structure, and styling.
Project Setup
Let’s start by creating a new Vue.js project with Vue CLI. We’ll include essential features like Vuex (state management) and Vue Router (routing) during setup.
-
Run the following command and follow the prompts:
vue create ecommerce-site -
Select “Manually select features” and check:
- Babel
- Vuex
- Vue Router
- CSS Pre-processors (we’ll use Sass/SCSS)
- Linter / Formatter
-
Choose your preferred CSS pre-processor (e.g., Sass/SCSS with dart-sass), linter (e.g., ESLint + Standard config), and set “Lint on save” for real-time feedback.
-
Navigate into the project folder and start the development server:
cd ecommerce-site npm run serve # or yarn serve -
Open
http://localhost:8080in your browser—you should see the default Vue welcome page.
Project Structure Overview
Familiarize yourself with the generated project structure:
ecommerce-site/
├── public/ # Static assets (index.html, favicon)
├── src/
│ ├── assets/ # Images, fonts, global CSS
│ ├── components/ # Reusable UI components (e.g., ProductCard)
│ ├── views/ # Page-level components (e.g., ProductList, Cart)
│ ├── router/ # Vue Router configuration
│ ├── store/ # Vuex state management
│ ├── services/ # API calls (e.g., product fetching)
│ ├── App.vue # Root component
│ └── main.js # Entry point
├── package.json # Dependencies and scripts
└── ...
Core Components Development
4.1 Product Listing Page
The product listing page displays all available products. We’ll create a ProductList view and a reusable ProductCard component.
Step 1: Create the ProductCard Component
Create src/components/ProductCard.vue:
<template>
<div class="product-card">
<img :src="product.image" :alt="product.name" class="product-image">
<h3 class="product-name">{{ product.name }}</h3>
<p class="product-price">${{ product.price.toFixed(2) }}</p>
<button @click="addToCart" class="add-to-cart-btn">Add to Cart</button>
</div>
</template>
<script>
export default {
props: {
product: {
type: Object,
required: true,
// Define expected product properties
properties: {
id: Number,
name: String,
price: Number,
image: String
}
}
},
methods: {
addToCart() {
// Dispatch Vuex action to add product to cart (we’ll implement this later)
this.$store.dispatch('addToCart', this.product);
}
}
};
</script>
<style scoped lang="scss">
.product-card {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
max-width: 250px;
text-align: center;
.product-image {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 4px;
}
.product-name {
font-size: 1.1rem;
margin: 12px 0;
}
.product-price {
font-weight: bold;
color: #e53935;
margin: 8px 0;
}
.add-to-cart-btn {
background: #4caf50;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s;
&:hover {
background: #388e3c;
}
}
}
</style>
Step 2: Create the ProductList View
Create src/views/ProductList.vue to display multiple ProductCard components:
<template>
<div class="product-list">
<h2>Our Products</h2>
<div class="product-grid">
<ProductCard
v-for="product in products"
:key="product.id"
:product="product"
/>
</div>
</div>
</template>
<script>
import ProductCard from '@/components/ProductCard.vue';
import { fetchProducts } from '@/services/productService'; // We’ll create this service next
export default {
components: { ProductCard },
data() {
return {
products: []
};
},
async mounted() {
// Fetch products from API on component mount
this.products = await fetchProducts();
}
};
</script>
<style scoped lang="scss">
.product-list {
padding: 24px;
h2 {
text-align: center;
margin-bottom: 32px;
font-size: 2rem;
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 24px;
justify-items: center;
}
}
</style>
4.2 Product Detail Page
Create a ProductDetail view to display a single product’s details (e.g., description, larger image).
<template>
<div class="product-detail">
<div class="product-image-container">
<img :src="product.image" :alt="product.name" class="product-image">
</div>
<div class="product-info">
<h1>{{ product.name }}</h1>
<p class="price">${{ product.price.toFixed(2) }}</p>
<p class="description">{{ product.description }}</p>
<div class="quantity-selector">
<label>Quantity:</label>
<input type="number" v-model="quantity" min="1" max="10">
</div>
<button @click="addToCart" class="add-to-cart-btn">Add to Cart</button>
</div>
</div>
</template>
<script>
import { fetchProductById } from '@/services/productService';
export default {
data() {
return {
product: null,
quantity: 1
};
},
async mounted() {
// Get product ID from route params
const productId = this.$route.params.id;
this.product = await fetchProductById(productId);
},
methods: {
addToCart() {
// Add product with selected quantity to cart
this.$store.dispatch('addToCart', { ...this.product, quantity: this.quantity });
this.$router.push('/cart'); // Redirect to cart after adding
}
}
};
</script>
<style scoped lang="scss">
/* Add styling for layout, image, and buttons */
</style>
4.3 Shopping Cart
Create a Cart view to display items in the cart, quantities, and total price.
<template>
<div class="cart">
<h2>Your Cart</h2>
<div v-if="cartItems.length === 0" class="empty-cart">
<p>Your cart is empty!</p>
<router-link to="/">Continue Shopping</router-link>
</div>
<div v-else>
<div class="cart-items">
<div v-for="item in cartItems" :key="item.id" class="cart-item">
<img :src="item.image" :alt="item.name" class="item-image">
<div class="item-details">
<h3>{{ item.name }}</h3>
<p>Price: ${{ item.price.toFixed(2) }}</p>
<div class="quantity-controls">
<button @click="updateQuantity(item.id, item.quantity - 1)" :disabled="item.quantity <= 1">-</button>
<span>{{ item.quantity }}</span>
<button @click="updateQuantity(item.id, item.quantity + 1)" :disabled="item.quantity >= 10">+</button>
</div>
<button @click="removeItem(item.id)" class="remove-btn">Remove</button>
</div>
<p class="item-total">${{ (item.price * item.quantity).toFixed(2) }}</p>
</div>
</div>
<div class="cart-summary">
<p>Total: ${{ cartTotal.toFixed(2) }}</p>
<button class="checkout-btn">Proceed to Checkout</button>
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
computed: {
...mapGetters(['cartItems', 'cartTotal'])
},
methods: {
...mapActions(['updateQuantity', 'removeFromCart']),
removeItem(id) {
this.removeFromCart(id);
}
}
};
</script>
<style scoped lang="scss">
/* Add styling for cart layout, items, and buttons */
</style>
State Management with Vuex
Use Vuex to manage the cart state globally (shared across components).
Step 1: Define the Store
Update src/store/index.js:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
cartItems: [] // Array of cart items: { id, name, price, image, quantity }
},
mutations: {
ADD_TO_CART(state, product) {
// Check if product already exists in cart
const existingItem = state.cartItems.find(item => item.id === product.id);
if (existingItem) {
existingItem.quantity += product.quantity || 1;
} else {
state.cartItems.push({ ...product, quantity: product.quantity || 1 });
}
},
UPDATE_QUANTITY(state, { id, quantity }) {
const item = state.cartItems.find(item => item.id === id);
if (item) item.quantity = quantity;
},
REMOVE_FROM_CART(state, id) {
state.cartItems = state.cartItems.filter(item => item.id !== id);
}
},
actions: {
addToCart({ commit }, product) {
commit('ADD_TO_CART', product);
},
updateQuantity({ commit }, { id, quantity }) {
commit('UPDATE_QUANTITY', { id, quantity });
},
removeFromCart({ commit }, id) {
commit('REMOVE_FROM_CART', id);
}
},
getters: {
cartItems: state => state.cartItems,
cartTotal: state => state.cartItems.reduce((total, item) => total + (item.price * item.quantity), 0),
itemCount: state => state.cartItems.reduce((count, item) => count + item.quantity, 0)
}
});
Routing with Vue Router
Define routes in src/router/index.js to navigate between pages.
import Vue from 'vue';
import VueRouter from 'vue-router';
import ProductList from '../views/ProductList.vue';
import ProductDetail from '../views/ProductDetail.vue';
import Cart from '../views/Cart.vue';
Vue.use(VueRouter);
const routes = [
{
path: '/',
name: 'ProductList',
component: ProductList
},
{
path: '/product/:id',
name: 'ProductDetail',
component: ProductDetail,
props: true // Pass route params as props to ProductDetail
},
{
path: '/cart',
name: 'Cart',
component: Cart
}
];
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
});
export default router;
Update src/App.vue to include navigation links and a cart icon with item count:
<template>
<div id="app">
<nav class="navbar">
<router-link to="/" class="logo">E-Commerce Store</router-link>
<div class="nav-links">
<router-link to="/">Home</router-link>
<router-link to="/cart" class="cart-link">
Cart ({{ itemCount }})
</router-link>
</div>
</nav>
<router-view />
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters(['itemCount'])
}
};
</script>
Backend Integration
For this project, we’ll use a mock API (e.g., JSONPlaceholder or a local Express server) to fetch product data.
Step 1: Create a Product Service
Create src/services/productService.js to handle API calls with Axios:
import axios from 'axios';
// Use a mock API or local server URL
const API_URL = 'https://jsonplaceholder.typicode.com';
// Fetch all products
export const fetchProducts = async () => {
try {
const response = await axios.get(`${API_URL}/products`);
return response.data;
} catch (error) {
console.error('Error fetching products:', error);
return [];
}
};
// Fetch product by ID
export const fetchProductById = async (id) => {
try {
const response = await axios.get(`${API_URL}/products/${id}`);
return response.data;
} catch (error) {
console.error(`Error fetching product ${id}:`, error);
return null;
}
};
Install Axios first:
npm install axios
UI Enhancement with Vuetify
Vuetify is a Material Design component library for Vue.js that simplifies UI development. Install it with:
vue add vuetify
Update components to use Vuetify’s pre-built components (e.g., <v-card>, <v-btn>, <v-img>) for a polished look. For example, refactor ProductCard.vue with Vuetify:
<template>
<v-card class="product-card" max-width="250">
<v-img :src="product.image" :alt="product.name" height="200"></v-img>
<v-card-title>{{ product.name }}</v-card-title>
<v-card-text>
<p class="product-price">${{ product.price.toFixed(2) }}</p>
</v-card-text>
<v-card-actions>
<v-btn color="success" @click="addToCart" block>Add to Cart</v-btn>
</v-card-actions>
</v-card>
</template>
Testing the Application
Test key features manually:
- Product Listing: Verify products load and display correctly.
- Add to Cart: Click “Add to Cart” and check if the cart icon updates.
- Product Detail: Navigate to a product detail page and add with a custom quantity.
- Cart Management: Update quantities, remove items, and verify the total price.
For automated testing, use Vue Test Utils to write unit tests for components like ProductCard.
Deployment
Deploy your e-commerce site to a platform like Netlify or Vercel:
-
Build the project:
npm run buildThis generates a
dist/folder with optimized assets. -
Netlify: Drag-and-drop the
dist/folder into the Netlify dashboard or connect your GitHub repo for CI/CD.
Conclusion
In this project, you built a functional e-commerce site with Vue.js, including product listing, details, cart management, and routing. Key takeaways:
- Vue.js’s component-based architecture simplifies UI development.
- Vuex manages global state (e.g., cart items) across components.
- Vue Router enables seamless navigation between pages.
- Vuetify accelerates UI design with Material Design components.
Extend the project by adding user authentication (Firebase), payment integration (Stripe), or product reviews!