文章目錄
- 1. 項目介紹
- 2. 開發環境準備
- 3. 設計購物車界面
- 4. 創建Vue實例和數據模型
- 5. 實現購物車功能
- 5.1 從本地存儲加載數據
- 5.2 監聽數據變化保存到本地存儲
- 5.3 實現全選/反選功能
- 5.4 計算選中商品的總價和總數量
- 5.5 實現修改商品數量功能
- 5.6 實現刪除商品功能
- 5.7 實現結算功能
- 6. 添加樣式
- 7. 完整代碼
- 8. Vue知識點解析
- 8.1 數據渲染與綁定
- 8.2 條件渲染與列表渲染
- 8.3 類與樣式綁定
- 8.4 事件處理
- 8.5 計算屬性
- 8.6 偵聽器
- 8.7 生命周期鉤子
- 8.8 屬性綁定
- 9. 功能擴展思路
- 10. 總結
1. 項目介紹
本教程將帶領初學者開發一個基于Vue.js的購物車應用,不需要使用腳手架,僅通過引入Vue.js庫即可完成。通過這個項目,你將學習Vue的基礎知識,包括:
- 數據渲染與綁定
- 事件處理
- 計算屬性
- 偵聽器
- 條件渲染與列表渲染
- 本地存儲
完成后的購物車應用具有以下功能:
- 商品列表渲染
- 刪除商品
- 修改商品數量
- 全選/反選功能
- 統計選中商品的總價和總數量
- 數據持久化到本地存儲
2. 開發環境準備
對于初學者,我們采用最簡單的方式搭建環境:通過CDN引入Vue.js。
創建一個基本的HTML文件結構:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue購物車</title><!-- 引入Vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script><!-- 添加一些基本樣式 --><style>/* 樣式稍后添加 */</style>
</head>
<body><div id="app"><!-- 這里將是我們的應用 --></div><script>// Vue代碼將寫在這里</script>
</body>
</html>
3. 設計購物車界面
我們設計一個簡潔的購物車界面,包含以下部分:
- 標題
- 商品列表(每項包含復選框、圖片、名稱、單價、數量控制和刪除按鈕)
- 底部結算欄(全選按鈕、總計和結算按鈕)
更新HTML結構:
<div id="app"><div class="cart"><div class="cart-header"><h1>{{ title }}</h1></div><!-- 購物車為空時顯示 --><div class="empty-cart" v-if="cartItems.length === 0"><p>購物車空空如也,快去選購商品吧!</p></div><!-- 購物車商品列表 --><div class="cart-items" v-else><div class="cart-item" v-for="(item, index) in cartItems" :key="item.id" :class="{'selected-item': item.checked}"><div class="item-check"><input type="checkbox" v-model="item.checked"></div><div class="item-img"><img :src="item.img" :alt="item.name"></div><div class="item-info"><h3>{{ item.name }}</h3><p class="item-price">¥{{ item.price.toFixed(2) }}</p></div><div class="item-quantity"><button @click="decreaseQuantity(item.id)" :disabled="item.quantity <= 1">-</button><input type="number" v-model.number="item.quantity" min="1"><button @click="increaseQuantity(item.id)">+</button></div><div class="item-subtotal"><p>¥{{ (item.price * item.quantity).toFixed(2) }}</p></div><div class="item-remove"><button @click="removeItem(item.id)">刪除</button></div></div></div><!-- 購物車底部結算欄 --><div class="cart-footer" v-if="cartItems.length > 0"><div class="select-all"><input type="checkbox" v-model="selectAll"><label>全選</label></div><div class="cart-total"><p>已選擇 <span>{{ selectedCount }}</span> 件商品</p><p>總計: <span>¥{{ totalPrice }}</span></p></div><div class="checkout"><button @click="checkout">結算</button></div></div></div>
</div>
4. 創建Vue實例和數據模型
在script標簽中,我們創建Vue實例并定義數據模型:
new Vue({el: '#app',data: {title: 'Vue購物車',// 購物車商品數據cartItems: [{id: 1,name: '商品1',price: 199,quantity: 1,img: 'https://via.placeholder.com/80',checked: false},{id: 2,name: '商品2',price: 299,quantity: 2,img: 'https://via.placeholder.com/80',checked: false},{id: 3,name: '商品3',price: 399,quantity: 1,img: 'https://via.placeholder.com/80',checked: false}]},// 計算屬性將在后面定義computed: {// 計算屬性將在這里實現},// 方法將在后面定義methods: {// 方法將在這里實現},// 監聽器將在后面定義watch: {// 監聽器將在這里實現},// 生命周期鉤子函數將在后面定義created() {// 從本地存儲加載購物車數據}
});
5. 實現購物車功能
5.1 從本地存儲加載數據
首先,我們修改生命周期鉤子函數,從本地存儲加載購物車數據:
created() {// 從本地存儲加載購物車數據const savedCart = localStorage.getItem('vue-cart');if (savedCart) {this.cartItems = JSON.parse(savedCart);}
}
5.2 監聽數據變化保存到本地存儲
添加偵聽器,將購物車數據保存到本地存儲:
watch: {// 深度監聽購物車數據變化cartItems: {handler(newValue) {// 將購物車數據保存到本地存儲localStorage.setItem('vue-cart', JSON.stringify(newValue));},deep: true // 深度監聽對象內部的變化}
}
5.3 實現全選/反選功能
添加計算屬性和相關方法來實現全選/反選功能:
computed: {// 計算全選狀態selectAll: {// 獲取全選狀態get() {// 如果購物車為空,返回falseif (this.cartItems.length === 0) return false;// 檢查是否所有商品都被選中return this.cartItems.every(item => item.checked);},// 設置全選狀態set(value) {// 將所有商品的選中狀態設置為全選的狀態this.cartItems.forEach(item => {item.checked = value;});}}
}
5.4 計算選中商品的總價和總數量
添加計算屬性來統計選中商品的總價和總數量:
computed: {// 已定義的計算屬性selectAll: {// ... 前面的代碼},// 計算選中商品的總數量selectedCount() {return this.cartItems.reduce((total, item) => {return item.checked ? total + item.quantity : total;}, 0);},// 計算選中商品的總價totalPrice() {return this.cartItems.reduce((total, item) => {return item.checked ? total + (item.price * item.quantity) : total;}, 0).toFixed(2);}
}
5.5 實現修改商品數量功能
添加方法來增加和減少商品數量:
methods: {// 增加商品數量increaseQuantity(id) {const item = this.cartItems.find(item => item.id === id);if (item) {item.quantity++;}},// 減少商品數量decreaseQuantity(id) {const item = this.cartItems.find(item => item.id === id);if (item && item.quantity > 1) {item.quantity--;}}
}
5.6 實現刪除商品功能
添加方法來刪除購物車中的商品:
methods: {// 已定義的方法increaseQuantity(id) {// ... 前面的代碼},decreaseQuantity(id) {// ... 前面的代碼},// 刪除商品removeItem(id) {if (confirm('確定要刪除這個商品嗎?')) {// 根據id查找商品索引const index = this.cartItems.findIndex(item => item.id === id);if (index !== -1) {// 從數組中刪除商品this.cartItems.splice(index, 1);}}}
}
5.7 實現結算功能
添加一個簡單的結算方法:
methods: {// 已定義的方法// ... 前面的代碼// 結算方法checkout() {// 檢查是否有選中的商品if (this.selectedCount === 0) {alert('請至少選擇一件商品');return;}// 顯示結算信息alert(`您已選擇${this.selectedCount}件商品,總計:¥${this.totalPrice},感謝購買!`);// 實際應用中這里應該跳轉到結算頁面或發送請求到后端// 為了演示,我們僅移除已選中的商品this.cartItems = this.cartItems.filter(item => !item.checked);}
}
6. 添加樣式
為了讓購物車看起來更美觀,我們添加一些CSS樣式:
/* 將這些樣式添加到<style>標簽中 */
* {margin: 0;padding: 0;box-sizing: border-box;
}body {font-family: 'Microsoft YaHei', sans-serif;background-color: #f5f5f5;padding: 20px;
}.cart {max-width: 1000px;margin: 0 auto;background-color: white;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);overflow: hidden;
}.cart-header {padding: 20px;background-color: #f8f8f8;border-bottom: 1px solid #eee;
}.cart-header h1 {font-size: 24px;color: #333;text-align: center;
}.empty-cart {padding: 50px 20px;text-align: center;color: #999;
}.cart-items {padding: 0 20px;
}.cart-item {display: flex;align-items: center;padding: 20px 0;border-bottom: 1px solid #eee;
}.cart-item:last-child {border-bottom: none;
}.selected-item {background-color: rgba(135, 206, 250, 0.1);
}.item-check {width: 30px;
}.item-check input {width: 18px;height: 18px;
}.item-img {width: 80px;height: 80px;margin: 0 20px;
}.item-img img {width: 100%;height: 100%;object-fit: cover;border-radius: 4px;
}.item-info {flex: 1;padding-right: 20px;
}.item-info h3 {font-size: 16px;margin-bottom: 5px;color: #333;
}.item-price {color: #ff6700;font-weight: bold;
}.item-quantity {display: flex;align-items: center;margin: 0 20px;
}.item-quantity button {width: 30px;height: 30px;border: 1px solid #ddd;background-color: white;font-size: 16px;cursor: pointer;
}.item-quantity button:disabled {color: #ddd;cursor: not-allowed;
}.item-quantity input {width: 50px;height: 30px;border: 1px solid #ddd;border-left: none;border-right: none;text-align: center;font-size: 14px;
}.item-subtotal {width: 100px;text-align: right;font-weight: bold;color: #ff6700;
}.item-remove {width: 60px;text-align: center;
}.item-remove button {padding: 5px 10px;background-color: #ff6700;color: white;border: none;border-radius: 4px;cursor: pointer;
}.cart-footer {display: flex;justify-content: space-between;align-items: center;padding: 20px;background-color: #f8f8f8;border-top: 1px solid #eee;
}.select-all {display: flex;align-items: center;
}.select-all input {width: 18px;height: 18px;margin-right: 5px;
}.cart-total {flex: 1;text-align: right;padding-right: 20px;
}.cart-total p {margin-bottom: 5px;
}.cart-total span {color: #ff6700;font-weight: bold;font-size: 18px;
}.checkout button {padding: 10px 30px;background-color: #ff6700;color: white;border: none;border-radius: 4px;font-size: 16px;cursor: pointer;
}.checkout button:hover {background-color: #f25600;
}button:hover {opacity: 0.9;
}button:active {opacity: 0.8;
}/* 響應式布局 */
@media (max-width: 768px) {.cart-item {flex-wrap: wrap;}.item-info {width: 100%;padding: 10px 0;}.item-quantity, .item-subtotal, .item-remove {margin-top: 10px;}
}
7. 完整代碼
以下是完整的HTML文件代碼:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue購物車</title><script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: 'Microsoft YaHei', sans-serif;background-color: #f5f5f5;padding: 20px;}.cart {max-width: 1000px;margin: 0 auto;background-color: white;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);overflow: hidden;}.cart-header {padding: 20px;background-color: #f8f8f8;border-bottom: 1px solid #eee;}.cart-header h1 {font-size: 24px;color: #333;text-align: center;}.empty-cart {padding: 50px 20px;text-align: center;color: #999;}.cart-items {padding: 0 20px;}.cart-item {display: flex;align-items: center;padding: 20px 0;border-bottom: 1px solid #eee;}.cart-item:last-child {border-bottom: none;}.selected-item {background-color: rgba(135, 206, 250, 0.1);}.item-check {width: 30px;}.item-check input {width: 18px;height: 18px;}.item-img {width: 80px;height: 80px;margin: 0 20px;}.item-img img {width: 100%;height: 100%;object-fit: cover;border-radius: 4px;}.item-info {flex: 1;padding-right: 20px;}.item-info h3 {font-size: 16px;margin-bottom: 5px;color: #333;}.item-price {color: #ff6700;font-weight: bold;}.item-quantity {display: flex;align-items: center;margin: 0 20px;}.item-quantity button {width: 30px;height: 30px;border: 1px solid #ddd;background-color: white;font-size: 16px;cursor: pointer;}.item-quantity button:disabled {color: #ddd;cursor: not-allowed;}.item-quantity input {width: 50px;height: 30px;border: 1px solid #ddd;border-left: none;border-right: none;text-align: center;font-size: 14px;}.item-subtotal {width: 100px;text-align: right;font-weight: bold;color: #ff6700;}.item-remove {width: 60px;text-align: center;}.item-remove button {padding: 5px 10px;background-color: #ff6700;color: white;border: none;border-radius: 4px;cursor: pointer;}.cart-footer {display: flex;justify-content: space-between;align-items: center;padding: 20px;background-color: #f8f8f8;border-top: 1px solid #eee;}.select-all {display: flex;align-items: center;}.select-all input {width: 18px;height: 18px;margin-right: 5px;}.cart-total {flex: 1;text-align: right;padding-right: 20px;}.cart-total p {margin-bottom: 5px;}.cart-total span {color: #ff6700;font-weight: bold;font-size: 18px;}.checkout button {padding: 10px 30px;background-color: #ff6700;color: white;border: none;border-radius: 4px;font-size: 16px;cursor: pointer;}.checkout button:hover {background-color: #f25600;}button:hover {opacity: 0.9;}button:active {opacity: 0.8;}/* 響應式布局 */@media (max-width: 768px) {.cart-item {flex-wrap: wrap;}.item-info {width: 100%;padding: 10px 0;}.item-quantity, .item-subtotal, .item-remove {margin-top: 10px;}}</style>
</head>
<body><div id="app"><div class="cart"><div class="cart-header"><h1>{{ title }}</h1></div><!-- 購物車為空時顯示 --><div class="empty-cart" v-if="cartItems.length === 0"><p>購物車空空如也,快去選購商品吧!</p></div><!-- 購物車商品列表 --><div class="cart-items" v-else><div class="cart-item" v-for="(item, index) in cartItems" :key="item.id" :class="{'selected-item': item.checked}"><div class="item-check"><input type="checkbox" v-model="item.checked"></div><div class="item-img"><img :src="item.img" :alt="item.name"></div><div class="item-info"><h3>{{ item.name }}</h3><p class="item-price">¥{{ item.price.toFixed(2) }}</p></div><div class="item-quantity"><button @click="decreaseQuantity(item.id)" :disabled="item.quantity <= 1">-</button><input type="number" v-model.number="item.quantity" min="1"><button @click="increaseQuantity(item.id)">+</button></div><div class="item-subtotal"><p>¥{{ (item.price * item.quantity).toFixed(2) }}</p></div><div class="item-remove"><button @click="removeItem(item.id)">刪除</button></div></div></div><!-- 購物車底部結算欄 --><div class="cart-footer" v-if="cartItems.length > 0"><div class="select-all"><input type="checkbox" v-model="selectAll"><label>全選</label></div><div class="cart-total"><p>已選擇 <span>{{ selectedCount }}</span> 件商品</p><p>總計: <span>¥{{ totalPrice }}</span></p></div><div class="checkout"><button @click="checkout">結算</button></div></div></div></div><script>new Vue({el: '#app',data: {title: 'Vue購物車',// 購物車商品數據cartItems: [{id: 1,name: '商品1',price: 199,quantity: 1,img: 'https://via.placeholder.com/80',checked: false},{id: 2,name: '商品2',price: 299,quantity: 2,img: 'https://via.placeholder.com/80',checked: false},{id: 3,name: '商品3',price: 399,quantity: 1,img: 'https://via.placeholder.com/80',checked: false}]},computed: {// 計算全選狀態selectAll: {// 獲取全選狀態get() {// 如果購物車為空,返回falseif (this.cartItems.length === 0) return false;// 檢查是否所有商品都被選中return this.cartItems.every(item => item.checked);},// 設置全選狀態set(value) {// 將所有商品的選中狀態設置為全選的狀態this.cartItems.forEach(item => {item.checked = value;});}},// 計算選中商品的總數量selectedCount() {return this.cartItems.reduce((total, item) => {return item.checked ? total + item.quantity : total;}, 0);},// 計算選中商品的總價totalPrice() {return this.cartItems.reduce((total, item) => {return item.checked ? total + (item.price * item.quantity) : total;}, 0).toFixed(2);}},methods: {// 增加商品數量increaseQuantity(id) {const item = this.cartItems.find(item => item.id === id);if (item) {item.quantity++;}},// 減少商品數量decreaseQuantity(id) {const item = this.cartItems.find(item => item.id === id);if (item && item.quantity > 1) {item.quantity--;}},// 刪除商品removeItem(id) {if (confirm('確定要刪除這個商品嗎?')) {// 根據id查找商品索引const index = this.cartItems.findIndex(item => item.id === id);if (index !== -1) {// 從數組中刪除商品this.cartItems.splice(index, 1);}}},// 結算方法checkout() {// 檢查是否有選中的商品if (this.selectedCount === 0) {alert('請至少選擇一件商品');return;}// 顯示結算信息alert(`您已選擇${this.selectedCount}件商品,總計:¥${this.totalPrice},感謝購買!`);// 實際應用中這里應該跳轉到結算頁面或發送請求到后端// 為了演示,我們僅移除已選中的商品this.cartItems = this.cartItems.filter(item => !item.checked);}},watch: {// 深度監聽購物車數據變化cartItems: {handler(newValue) {// 將購物車數據保存到本地存儲localStorage.setItem('vue-cart', JSON.stringify(newValue));},deep: true // 深度監聽對象內部的變化}},created() {// 從本地存儲加載購物車數據const savedCart = localStorage.getItem('vue-cart');if (savedCart) {this.cartItems = JSON.parse(savedCart);}}});</script>
</body>
</html>
8. Vue知識點解析
通過這個項目,我們學習了以下Vue的知識點:
8.1 數據渲染與綁定
- 插值表達式
{{ }}
:顯示變量內容 v-model
:雙向數據綁定,用于表單輸入和應用數據之間的綁定v-model.number
:自動將輸入值轉換為數字類型
8.2 條件渲染與列表渲染
v-if
/v-else
:根據條件決定是否渲染元素v-for
:遍歷數組渲染列表項:key
:為列表項提供唯一標識符,幫助Vue優化渲染
8.3 類與樣式綁定
:class
:動態綁定CSS類,根據條件添加不同的類- 在本項目中,我們根據商品是否選中來添加不同的背景樣式
8.4 事件處理
@click
:監聽點擊事件- 事件傳參:如
@click="increaseQuantity(item.id)"
,可以向事件處理方法傳遞參數
8.5 計算屬性
computed
:基于響應式依賴進行緩存,只有依賴更新時才重新計算- 計算屬性的getter和setter:如
selectAll
,可以通過getter獲取值,通過setter設置值
8.6 偵聽器
watch
:監聽數據變化,執行相應操作deep
選項:深度監聽對象內部的變化- 在本項目中,我們使用偵聽器將購物車數據保存到本地存儲
8.7 生命周期鉤子
created
:實例創建后執行代碼- 在本項目中,我們在
created
鉤子中從本地存儲加載購物車數據
8.8 屬性綁定
:src
、:alt
、:class
、:disabled
等:動態綁定DOM元素的屬性
9. 功能擴展思路
以下是一些可以進一步擴展購物車功能的思路:
-
商品分類:按照商品分類進行分組顯示。
-
商品篩選:添加篩選功能,如按價格篩選、按品牌篩選等。
-
優惠券功能:添加優惠券選擇和應用功能。
-
庫存檢查:添加庫存數量限制,防止超出庫存購買。
-
收藏功能:允許用戶將商品移動到收藏夾。
-
推薦商品:在購物車底部顯示相關推薦商品。
-
批量操作:支持選中多個商品后批量刪除。
-
價格變動提醒:當商品價格變動時提醒用戶。
-
商品詳情鏈接:點擊商品名稱可以跳轉到商品詳情頁。
-
登錄注冊功能:添加用戶登錄注冊功能,將購物車與用戶賬號關聯。
10. 總結
通過本教程,我們使用Vue.js構建了一個功能完整的購物車應用。雖然這是一個小型應用,但它涵蓋了Vue的核心概念和實踐技巧。作為新手,你可以通過這個項目了解Vue的數據驅動和響應式特性,以及如何使用計算屬性、方法和偵聽器處理數據和用戶交互。
重點是,我們詳細介紹了以下關鍵功能的實現:
-
渲染功能:使用
v-for
高效渲染商品列表,通過:class
動態綁定樣式,使選中的商品具有不同的背景色。 -
刪除功能:使用
@click
綁定刪除事件,獲取當前行的id,使用splice
方法刪除數組中的元素。 -
修改數量:使用加減按鈕和輸入框控制商品數量,通過
v-model.number
確保輸入的是數字類型。 -
全選/反選:通過計算屬性的getter和setter實現全選/反選功能,使用
every
方法檢查是否所有商品都被選中。 -
統計功能:使用計算屬性自動計算選中商品的總數量和總價,保持數據的響應式更新。
-
本地存儲:使用偵聽器監聽購物車數據變化,自動保存到本地存儲,確保刷新頁面后數據不丟失。
希望這個教程能幫助你更好地理解Vue的基礎概念和應用開發流程。隨著你對Vue的深入學習,可以考慮使用Vue CLI、Vue Router、Vuex等進階技術來構建更復雜的應用。