文章目錄
- 計算屬性配置項 computed
- HTML 結構
- Vue 實例
- 數據
- 方法
- 計算屬性
- 綁定數據和方法
- 完整代碼
- vue3商品加減案例
- 監聽器配置項 watch
- 簡單類型寫法
- 深度監聽寫法
計算屬性配置項 computed
使用 Vue 實現一個商品價格計算器,設置一個初始單價,初始數量為 1,用戶可以通過加減按鈕修改數量,并實時計算出總價。
下面將一步步詳細解釋上述代碼的實現過程
HTML 結構
首先,我們需要在 HTML 文件中添加一個 div
元素作為 Vue 應用的根元素,并給其一個 id 值作為掛載點:
<div id="app"><!-- 在這里添加商品價格計算器的 HTML 結構 -->
</div>
在這個 div
元素中,我們可以添加一個標題標簽、三個段落標簽,前兩個段落標簽顯示單價和數量,第三個段落標簽用于顯示計算出來的總價,其中加減按鈕用于增加或減少數量:
<h1>商品價格計算器</h1>
<p>單價:{{ price }}</p>
<p>數量: <button v-show="num>0" @click="minus">-</button>{{num}}<button @click="plus">+</button></p>
<p>總價:{{ total }}</p>
這里我們使用了 Vue 的模板語法,通過 {{ }}
將數據綁定到 HTML 中。
Vue 實例
接下來,在 JavaScript 文件中創建一個 Vue 實例,將其掛載到 HTML 頁面中的 div#app
元素上:
<script src="../js/vue3.js"></script>
<script>let vm = Vue.createApp({data() {return {price: 998,num: 1}},methods: {minus(){this.num--},plus(){this.num++}},// 計算屬性配置項 實際應用中 某個數據是需要使用data中其他數據計算得到computed:{// 計算屬性的具體配置是函數形式total(){// 必須返回一個數據return this.num*this.price}}})vm.mount('#app')
</script>
在 Vue 實例的選項中,我們需要先定義一些數據和方法。
數據
首先,我們需要設置一個初始單價、初始數量和總價的值,這里我們設置單價為 998 元,數量為 1,總價為 998 元:
data() {return {price: 998,num: 1}
},
這里使用了 Vue 實例的 data
選項,用于定義數據和變量。
方法
接著,我們需要定義兩個方法 plus
和 minus
,用于增加或減少商品數量,并且實時計算出總價:
methods: {minus(){this.num--},plus(){this.num++}
}
這里我們使用了 Vue 實例的 methods
選項,用于定義方法。
計算屬性
使用 computed
選項定義一個計算屬性 total
,用于計算商品的總價,其中計算方法是將 num
值乘以 price
值:
computed:{// 計算屬性的具體配置是函數形式total(){// 必須返回一個數據return this.num*this.price}
}
綁定數據和方法
最后,我們需要將定義好的數據和方法綁定到 HTML 頁面中的模板語法中:
<p>單價:{{ price }}</p>
<p>數量: <button v-show="num>0" @click="minus">-</button>{{num}}<button @click="plus">+</button></p>
<p>總價:{{ total }}</p>
這里使用了 Vue 的模板語法,通過 {{ }}
將數據綁定到 HTML 中,通過 v-show
和 @click
將方法綁定到按鈕的點擊事件上。
完整代碼
綜上,以下是完整的商品價格計算器代碼:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>商品價格計算器</title>
</head>
<body><div id="app"><h1>商品價格計算器</h1><p>單價:{{ price }}</p><p>數量: <button v-show="num>0" @click="minus">-</button>{{num}}<button @click="plus">+</button></p><p>總價:{{ total }}</p><!-- 計算屬性的使用直接通過插值語法使用 --></div><script src="../js/vue3.js"></script><script>let vm = Vue.createApp({data() {return {price: 998,num: 1}},methods: {minus(){this.num--},plus(){this.num++}},// 計算屬性配置項 實際應用中 某個數據是需要使用data中其他數據計算得到computed:{// 計算屬性的具體配置是函數形式total(){// 必須返回一個數據return this.num*this.price}}})vm.mount('#app')</script>
</body>
</html>
輸出效果:
vue3商品加減案例
這段代碼是商家信息頁面的 Vue 組件,主要作用是展示商家的基本信息和食品列表,并實現選擇食品和結算功能。
它通過發送請求獲取商家信息和食品列表數據,并在頁面中展示出來。
用戶可以通過點擊加號和減號來選擇需要購買的食品,同時底部購物車欄會實時更新已選擇的食品的數量和總價格。
當用戶點擊去結算按鈕時,會跳轉到訂單頁面并將已選擇的食品和總價格存儲到 sessionStorage
中。
在頁面加載前,組件會從 sessionStorage
中獲取商家信息。
詳細代碼:
template 頁面布局部分
<!-- 食品列表 --><ul class="choese"><li class="choese-img" v-for="(food, index) in foods" :key="index"><div><img src="../assets/img/sp01.png" alt=""><div><p>{{ food.foodName }}</p><p>{{ food.foodExplain }}</p><p>¥{{ food.foodPrice }}</p></div></div><div><i class="fa fa-minus-circle" @click="minus(index)"></i><span id="num">{{ food.num }}</span><i class="fa fa-plus-circle" @click="add(index)"></i></div></li></ul><!-- 底部購物車欄 --><div class="bottom-tab"><div><div><p>¥{{ allmoney }}</p><p>配送費{{ this.business.deliveryPrice }}元</p></div><div><div><i class="fa fa-shopping-cart fa-lg"></i></div></div><div>{{ allnum }}</div></div><div id="pay" @click="gotoOrders">去結算</div></div></div>
script 部分主要定義了數據和方法,computed 部分定義了計算屬性,watch 部分監聽總價格變化,beforeMount 和 mounted 部分則分別在掛載前和掛載后發送請求獲取數據。
<script>
import router from '@/router';
import axios from 'axios';export default {data () {return {// 商家信息business: {},// 食品列表foods: [],// 已選擇的食品buyfoods: [],// 底部購物車欄樣式paystyle: {pointerEvents: 'none',backgroundColor: 'gray'}}},methods: {// 去結算gotoOrders() {let login = sessionStorage.getItem('login')if (login == null) {router.push('/login')} else {// 跳轉到訂單頁面router.push('/orders')// 篩選已選擇的食品this.buyfoods = this.foods.filter((food) => {return food.num > 0})// 將已選擇的食品和總價格存儲到 sessionStoragesessionStorage.setItem('buyfoods',JSON.stringify(this.buyfoods))sessionStorage.setItem('allmoney',this.allmoney)}},// 增加已選擇的食品數量add(index) {this.foods[index].num++},// 減少已選擇的食品數量minus(index) {if (this.foods[index].num>0) {this.foods[index].num--}}},components: {},computed: {// 所有已選擇食品的數量allnum() {let allnum = 0this.foods.forEach(food => {allnum += food.num});return allnum},// 所有已選擇食品的總價格allmoney() {let allmoney = 0this.foods.forEach(food => {allmoney += food.foodPrice*food.num});allmoney += this.business.deliveryPricereturn allmoney}},watch: {// 監控總價格變化,根據商家的起送價格調整底部購物車欄樣式allmoney(newVal,oldVal) {if (newVal > this.business.startPrice) {this.paystyle.pointerEvents = 'auto'this.paystyle.backgroundColor = 'rgb(77, 212, 77)'} else {this.paystyle.pointerEvents = 'none'this.paystyle.backgroundColor = 'gray'}}},// 生命周期鉤子,在掛載前從 sessionStorage 中獲取商家信息,并發送請求獲取食品列表beforeMount(){this.business = JSON.parse(sessionStorage.getItem('business'))},mounted () {axios.get(`http://localhost:8888/elmjavaweb/food?businessId=${this.business.businessId}&action=query&foodName=&foodExplain=`).then((resp) => {this.foods = resp.data// 定義已選擇的食品數量為 0this.foods.forEach(food => {food['num'] = 0});})},
}
</script>
樣式部分定義了頁面樣式。
<style scoped>
/* 定義頁面元素的樣式 */
.wrapper{margin-bottom: 14vw; /* 下邊距為14vw */width: 100vw; /* 寬度為100vw */
}
.location{width: 100vw; /* 寬度為100vw */height: 12vw; /* 高度為12vw */background-color: #0097ff; /* 背景顏色為#0097ff */font-size: 3.5vw; /* 字體大小為3.5vw */color: white; /* 字體顏色為白色 */display: flex; /* 設置為彈性盒子布局 */justify-content: center; /* 主軸居中對齊 */align-items: center; /* 交叉軸居中對齊 */font-size: 5vw; /* 字體大小為5vw */
}
.business-img{width: 100vw; /* 寬度為100vw */height: 60vw; /* 高度為60vw */display: flex; /* 設置為彈性盒子布局 */flex-direction: column; /* 垂直方向排列 */align-items: center; /* 交叉軸居中對齊 */
}
.business-img img{width: 40vw; /* 寬度為40vw */height: 30vw; /* 高度為30vw */margin-top: 3vw; /* 上邊距為3vw */margin-bottom: 3vw; /* 下邊距為3vw */
}
.business-img p:first-of-type{font-size: 5vw; /* 字體大小為5vw */font-weight: bold; /* 字體加粗 */
}
.business-img p:nth-of-type(2),p:last-of-type{color: #666; /* 字體顏色為#666 */margin-top: 2vw; /* 上邊距為2vw */
}
.choese{width: 100vw; /* 寬度為100vw */display: flex; /* 設置為彈性盒子布局 */flex-direction: column; /* 垂直方向排列 */margin-top: 0; /* 上邊距為0 */
}
.choese-img{height: 25vw; /* 高度為25vw */display: flex; /* 設置為彈性盒子布局 */justify-content: space-between; /* 主軸空間平均分配 */align-content: center; /* 交叉軸居中對齊 */margin-top: 0; /* 上邊距為0 */
}
.choese-img div:first-of-type{display: flex; /* 設置為彈性盒子布局 */
}
.choese-img div:first-of-type img{width: 20vw; /* 寬度為20vw */height: 20vw; /* 高度為20vw */margin: 2.5vw; /* 上下左右邊距均為2.5vw */
}
.choese-img div:first-of-type div{display: flex; /* 設置為彈性盒子布局 */flex-direction: column; /* 垂直方向排列 */align-items: start; /* 交叉軸起始對齊 */justify-content: space-around; /* 主軸空間平均分配 */
}
.choese-img div:first-of-type div p{color: #666; /* 字體顏色為#666 */font-size: 3.5vw; /* 字體大小為3.5vw */
}
.choese-img div:first-of-type div p:first-of-type{font-weight: bold; /* 字體加粗 */font-size: 4.5vw; /* 字體大小為4.5vw */margin: 2vw 0; /* 上邊距為2vw,下邊距為0 */
}
.choese-img div:first-of-type div p:last-of-type{margin-bottom: 2vw; /* 下邊距為2vw */
}
.choese-img div:last-of-type{display: flex; /* 設置為彈性盒子布局 */justify-content: center; /* 主軸居中對齊 */align-items: center; /* 交叉軸居中對齊 */margin-top: 0; /* 上邊距為0 */margin-right: 2.5vw; /* 右邊距為2.5vw */
}
.fa-plus-circle{margin-top: 0; /* 上邊距為0 */color: #0097ff; /* 字體顏色為#0097ff */
}
.fa-minus-circle{color: #888; /* 字體顏色為#888 */
}
.bottom-tab{width: 100vw; /* 寬度為100vw */height: 15vw; /* 高度為15vw */position: fixed; /* 定位方式為fixed */left: 0; /* 左邊距為0 */bottom: 0; /* 下邊距為0 */display: flex; /* 設置為彈性盒子布局 */margin-top: 0; /* 上邊距為0 */
}
.bottom-tab div:first-of-type{background-color: #555; /* 背景顏色為#555 */flex: 7; /* 彈性盒子占據7份空間 */display: flex; /* 設置為彈性盒子布局 */justify-content: center; /* 主軸居中對齊 */
}
.bottom-tab div:first-of-type div:first-of-type{display: flex; /* 設置為彈性盒子布局 */flex-direction: column; /* 垂直方向排列 */align-items: start; /* 交叉軸起始對齊 */color: #fff; /* 字體顏色為白色 */font-size: 4.5vw; /* 字體大小為4.5vw */margin-left: 20vw; /* 左邊距為20vw */
}
.bottom-tab div:first-of-type div:nth-of-type(2){position: fixed; /* 定位方式為fixed */background-color: #555; /* 背景顏色為#555 */width: 15vw; /* 寬度為15vw */height: 15vw; /* 高度為15vw */border-radius: 10vw; /* 邊框半徑為10vw */left: 5vw; /* 左邊距為5vw */bottom: 5vw; /* 下邊距為5vw */
}
.bottom-tab div:first-of-type div:nth-of-type(2) div{width: 12vw; /* 寬度為12vw */height: 12vw; /* 高度為12vw */border-radius: 10vw; /* 邊框半徑為10vw */background-color: #0097ff; /* 背景顏色為#0097ff */display: flex; /* 設置為彈性盒子布局 */justify-content: center; /* 主軸居中對齊 */align-items: center; /* 交叉軸居中對齊 */position: fixed; /* 定位方式為fixed */margin: 0; /* 邊距為0 */left: 6.5vw; /* 左邊距為6.5vw */bottom: 6.5vw; /* 下邊距為6.5vw */
}
.bottom-tab div:first-of-type div:last-of-type{background-color: red; /* 背景顏色為紅色 */position: fixed; /* 定位方式為fixed */width: 5vw; /* 寬度為5vw */height: 5vw; /* 高度為5vw */border-radius: 5vw; /* 邊框半徑為5vw */font-size: 3.5vw; /* 字體大小為3.5vw */left: 15vw; /* 左邊距為15vw */bottom: 15vw; /* 離頁面底部距離15vw */
}
.bottom-tab div:first-of-type div:first-of-type p:last-of-type{margin-top: 0; /* 上邊距0 */font-size: 3vw; /* 字體大小3vw */color: #999; /* 字體顏色#999 */
}
.bottom-tab div:last-of-type{flex: 3; /* flex子項占比3 */background-color: rgb(77, 212, 77); /* 背景色rgb(77, 212, 77) */margin-top: 0; /* 上邊距0 */color: #fff; /* 字體顏色白色 */display: flex; /* 布局方式:flex */justify-content: center; /* 在flex容器里居中顯示 */align-items: center; /* 在flex容器里居中顯示 */font-size: 5vw; /* 字體大小5vw */font-weight: bold; /* 字體加粗 */
}
#num{margin: 0 2vw; /* 左右邊距2vw */
}
</style>
監聽器配置項 watch
提示:這里可以添加本文要記錄的大概內容:
監聽器配置項:監聽某個數據的變化
定義函數:函數名必須是監聽的數據名
Vue 調用此函數時會傳入兩個參:新值、舊值
Vue3 的監聽器可以監聽 Vue 實例中某個數據的變化,當這個數據變化時,就會自動執行相應的函數,這樣可以實現一些自動更新頁面、數據校驗等功能。
具體來說,監聽器主要有以下作用:
-
自動更新頁面:當 Vue 實例的某個數據變化時,監聽器可以自動更新相關的頁面內容,而不需要手動修改 DOM。
-
數據校驗:當 Vue 實例中的某個數據變化時,可通過監聽器對輸入的數據進行校驗,保證數據的有效性。
-
數據同步:當 Vue 實例中的某個數據綁定到多個組件中時,可通過監聽器實現數據的同步,保證數據的一致性。
-
監控數據變化:當 Vue 實例中的某個數據發生變化時,通過監聽器可以獲取到新舊值,從而實現數據的監控和統計。
Vue3 監聽器是 Vue3 中非常重要的一個特性,它可以讓我們的開發變得更加高效和靈活,使得我們能夠更好地處理和管理數據。
簡單類型寫法
【示例代碼】:
讓數據減到零不再往下減
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="app"><button @click="minus">-</button>{{num}}</div>
</body>
<script src="../js/vue3.js"></script>
<script>let vm = Vue.createApp({data() {return {num: 1}},methods: {minus(){this.num--}},})vm.mount('#app')
</script>
</html>
這段代碼使用了 Vue3 的 createApp
方法創建了一個 Vue 實例 vm,實現了一個簡單的計數器功能,點擊按鈕可以將 num 值遞減。具體解釋如下:
-
首先,使用 createApp 方法創建了一個 Vue 實例 vm,這個實例包含了 data、methods 等選項。
-
在 data 中定義了 num 屬性,初始值為 1。
-
在 methods 中定義了一個 minus 方法,當按鈕被點擊時,會將 num 的值減 1。
-
最后,使用 vm.mount 方法將 Vue 實例掛載到頁面上的 #app 元素上,使其生效。
但是現在可以減到負數
接下來加上監聽器,用這種方式控制num的數量,到零就不在讓他往下減
// 監聽器配置項:監聽某個數據的變化watch: {// 定義函數 函數名必須是監聽的數據名// Vue 調用此函數時會傳入兩個參 新值 舊值num(a,b){this.num = a<0?0:a// a<0嗎?如果小于0正常賦值}}
深度監聽寫法
如果監聽的事件在對象中,那么監聽器的寫法必須是深度監聽寫法
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="app"><button @click="minus">-</button>{{goods.num}}</div>
</body>
<script src="../js/vue3.js"></script>
<script>let vm = Vue.createApp({data() {return {goods: {num: 1}}},methods: {minus(){this.goods.num--}},// 監聽器配置項:監聽某個數據的變化watch: {// 如果監聽的事件在對象中,那么監聽器的寫法必須是深度監聽寫法goods:{deep:true,handler(a,b){this.goods.num = a.num<0?0:a.num}}}})vm.mount('#app')
</script>
</html>