Vue2電商前臺項目——完成Detail詳情頁模塊業(yè)務(wù)
Vue基礎(chǔ)知識點擊此處——Vue.js
一、項目開發(fā)步驟
這里已經(jīng)滾瓜爛熟了哈哈哈哈。。。。
1、準備靜態(tài)頁面
2、拆分組件,配置路由信息
3、寫接口,使用Vuex存儲數(shù)據(jù)
4、把服務(wù)器數(shù)據(jù)渲染到頁面上
這里把詳情頁Detail組件放到路由組件pages里src/pages/Detail/index.vue
二、配置路由規(guī)則+滾動行為
點擊商品圖片 => 路由跳轉(zhuǎn)并傳參 => 跳轉(zhuǎn)到Detail掛載完畢派發(fā)actions => 向后臺發(fā)送請求拿到數(shù)據(jù) => 把數(shù)據(jù)給組件 => 數(shù)據(jù)渲染到頁面
1、配置路由規(guī)則
經(jīng)分析發(fā)現(xiàn),在進行路由跳轉(zhuǎn)時,我們要帶上商品的id,這樣服務(wù)器就能根據(jù)商品id去找到對應(yīng)的商品信息在詳情頁進行展示,所以我們在配置路由規(guī)則時應(yīng)該使用params
參數(shù)占位符,路由規(guī)則配置如下:
{
path: '/detail/:skuId?', //商品id的占位符,跳轉(zhuǎn)時我們要帶上商品參數(shù)
component: Detail,
meta: {
showFooter: true
}
},
然后使用聲明式路由導(dǎo)航在Search頁的商品圖片處跳轉(zhuǎn)并傳參
一般來說,如果路由規(guī)則比較多,我們會把路由規(guī)則另外寫一個js文件引入,這樣的話路由器這邊會比較清晰,方便寫其他東西
// src/router/routes.js
// 本文件用于存儲路由的配置規(guī)則信息
// 引入路由組件
import Home from "@/pages/Home";
import Search from "@/pages/Search";
import Login from "@/pages/Login";
import Register from "@/pages/Register";
import Detail from "@/pages/Detail";
//配置路由
export default [
{
path: "/home",
component: Home,
meta: { showFooter: true },
},
{
name: "sousuo",
//使用params傳參時,需要占位
path: "/search/:keyword?",
component: Search,
meta: { showFooter: true },
},
{
path: "/login",
component: Login,
meta: { showFooter: false },
},
{
path: "/register",
component: Register,
meta: { showFooter: false },
},
{
path: "/detail/:skuid",
component: Detail,
meta: { showFooter: true },
},
//重定向,在項目跑起來的時候,訪問"/",立馬讓他定向到首頁
{
path: "*",
redirect: "/home",
},
];
再在index.js里引入
2、滾動行為
在進行路由跳轉(zhuǎn)時可能滾動條不在最上面。切換路由時,想要頁面滾動到頂部,我們可以在路由器中添加一個scrollBehavior
函數(shù)解決這個問題,返回值是期望滾動到的位置,y:0表示跳轉(zhuǎn)后滾動條距離頂部0px。
export default new VueRouter({
//配置路由規(guī)則
routes,
//配置滾動行為
scrollBehavior(to, from, savedPosition) {
// 始終滾動到頂部
return { y: 0 } //期望滾動到的位置,0意思是跳轉(zhuǎn)后滾動條始終在最上方
},
})
三、請求詳情頁數(shù)據(jù)并展示數(shù)據(jù)
看以下接口文檔:
1、寫接口
先到api里寫接口
2、寫Vuex倉庫
寫倉庫用來actions
接收數(shù)據(jù)并state
存儲數(shù)據(jù),還有getters
簡化數(shù)據(jù)。這里是新建了一個小倉庫detail
,和home
、search
平級,別忘了暴露給大倉庫。
這里面要注意getters中數(shù)據(jù)的寫法,因為請求數(shù)據(jù)是一個異步操作,所以一開始數(shù)據(jù)是空對象,讀空對象身上的屬性肯定報錯,服務(wù)器數(shù)據(jù)回來后,把state中的原數(shù)據(jù)替換,getters就能讀到值,然后重新響應(yīng),所以頁面不會有問題,但是控制臺肯定會先報錯的,如果不想看到這個錯誤,就讓getters中的數(shù)據(jù)在讀不到東西的時候先置空
src/store/detail/index.js
//本文件用于存儲Detail詳情頁的數(shù)據(jù)
import { reqDetailData } from "@/api";
const state = {
detailData: {},
};
const actions = {
async getDetailData(context, skuId) {
let result = await reqDetailData(skuId);
console.log(result);
if (result.code == 200) {
context.commit("GETDETAILDATA", result.data);
}
},
};
const mutations = {
GETDETAILDATA(state, detailData) {
state.detailData = detailData;
},
};
//簡化數(shù)據(jù)
const getters = {};
export default {
state,
mutations,
actions,
getters,
};
3、派發(fā)actions
去Detail組件中派發(fā)請求并傳參過去
注意:這里的skuid是大寫小寫,最好去開發(fā)者工具看看。
這里由于我們在進行從Search到Detail路由跳轉(zhuǎn)的時候把當(dāng)前商品的id傳參傳過去了,傳給了$route
,所以我們可以通過this.$route.params.skuId
拿到商品id并把它派發(fā)給actions發(fā)送ajax請求
。(之所以是params.skuId是因為占位符寫的skuId)
4、組件拿到后臺返回的數(shù)據(jù)并渲染到頁面
我們需要的數(shù)據(jù)都在state.detailData.XXX
里,如果每個都直接這樣寫的話很麻煩,我們可以用getters
簡化一下。
src/store/detail/index.js
//簡化數(shù)據(jù)
const getters = {
categoryView(state) {
//因為返回的時候可能異步操作還沒把數(shù)據(jù)請求過來,所以要加個或空
//服務(wù)器數(shù)據(jù)回來后,把state中的原數(shù)據(jù)替換,getters就能讀到值,然后重新響應(yīng)
return state.detailData.categoryView || {};
},
skuInfo(state) {
return state.detailData.skuInfo || {};
},
spuSaleAttrList(state) {
return state.detailData.spuSaleAttrList || [];
},
};
倉庫根據(jù)參數(shù)拿到后臺數(shù)據(jù)后,Detail
通過mapGetters
來獲取數(shù)據(jù),然后根據(jù)數(shù)據(jù)的結(jié)構(gòu)把它們展示到頁面的對應(yīng)位置,展示數(shù)據(jù)比較簡單。
先用計算屬性撈一下getters:
computed: {
...mapGetters('detail', ['categoryView', 'skuInfo', 'spuSaleAttrList'])
}
找到對應(yīng)位置的對應(yīng)數(shù)據(jù):
在對應(yīng)位置展示:
四、放大鏡展示數(shù)據(jù)
1、Detail父組件把圖片數(shù)據(jù)傳給放大鏡子組件
觀察數(shù)據(jù),發(fā)現(xiàn)放大鏡這部分的圖片數(shù)據(jù)存儲在skuInfo
里的一個數(shù)組中
使用props讓父組件
Detail
給子組件傳值,下圖左邊是父組件右邊是子組件
然后把數(shù)組中第一張圖片拿過來展示作為默認顯示,但是這樣會有bug,如下
2、解決數(shù)據(jù)未請求到時獲取其屬性報錯的問題
問題的關(guān)鍵:不能出現(xiàn)undefined.xxx
請求數(shù)據(jù)是異步操作,所以
getters
中的數(shù)據(jù)skuInfo
沒有讀到的話,會先返回空對象,然后去讀取空對象身上的屬性,肯定會報錯,但是后邊數(shù)據(jù)請求回來了,又會重新渲染重新響應(yīng),所以頁面顯示正常,但是控制臺會先報一個錯,解決辦法還是加個邏輯或
把它置空一下子|| []
其實這個bug的關(guān)鍵就是,不能出現(xiàn)undefined.xxx。前三步都好理解,關(guān)鍵是第四步這個地方,其實空數(shù)組[0]肯定是undefined,然后undefined.imgUrl肯定是會報錯的,這種情況的話可以弄一個計算屬性,如果傳過來的是空數(shù)組,那么skuImageList[0]就先或一個空對象(因為數(shù)組內(nèi)的元素都是對象),空對象.imgUrl是undefined,這樣頁面沒請求到數(shù)據(jù)的時候就不會報錯了
五、放大鏡下輪播圖展示數(shù)據(jù)
這個輪播圖和我們之前封裝的那個全局輪播圖組件樣式是不一樣的,所以全局組件不能拿過來用,我們這里需要自己再重新定義一下
1、輪播圖三大步
還記得做輪播圖的三大步嗎?還是那幾個:引包=>搭建頁面=>添加js
其中第三步還是采用watch+$nextTick
,等數(shù)據(jù)請求過來且v-for
遍歷生成完畢后生成swiper
實例。
有點忘記的可以參考一下我前面的筆記,原生js寫的和引包的都有寫——引包寫法、原生JS寫法。
watch: {
//監(jiān)聽skuImageList數(shù)據(jù)的變化
// 因為它會有一個從空數(shù)組變成有數(shù)據(jù)的過程(一切都因為請求數(shù)據(jù)是一個異步操作)
skuImageList(newVal, oldVal) {
//nextTick能保證頁面結(jié)構(gòu)先渲染出來,然后再執(zhí)行回調(diào)函數(shù)
this.$nextTick(function () {
new Swiper(".swiper-container", {
//前進后退按鈕
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
slidesPerView: 4, // 顯示幾個圖片設(shè)置
slidesPerGroup: 1, // 每一次切換圖片的個數(shù)
});
})
}
},
2、動態(tài)添加active樣式
鼠標點擊某小圖時動態(tài)添加active樣式,這里簡單的話可以直接搞個hover樣式,但是為了練習(xí)js,還是搞點復(fù)雜的吧。
src/pages/detail/ImageList
<div class="swiper-slide" v-for="(slide,index) in skuImageList" :key="slide.id">
<img :src="slide.imgUrl" :class="{active:currentIndex==index}" @click="changeCurrentIndex(index)"/>
</div>
....
<script>
...
data(){
return {
// 響應(yīng)式數(shù)據(jù)
currentIndex: 0, //0表示默認是第一張圖
}
},
methods:{
// 修改響應(yīng)式數(shù)據(jù)
changeCurrentIndex(index){
this.currentIndex = index;
}
},
</script>
3、放大鏡和輪播圖實現(xiàn)聯(lián)動
點擊輪播圖(ImageList組件
)的圖片,就把當(dāng)前圖片的索引傳給放大鏡(Zoom組件
),那么這里就涉及到了兄弟組件通信,可以直接使用全局總線通信。
1、首先給放大鏡這邊綁定全局事件
2、去輪播圖這邊觸發(fā)事件并把當(dāng)前圖片對象的索引值傳過去
3、觸發(fā)事件后執(zhí)行回調(diào),把當(dāng)前索引值通過data給計算屬性中的imgObj
4、vue檢測到data的改變,重新解析模板,更新頁面
5、注:默認放大鏡data中的index是0,這樣默認就會顯示第一張圖片。默認輪播圖data中currentIndex是0,這樣默認輪播圖第一張圖片有active樣式。
六、產(chǎn)品屬性展示數(shù)據(jù)
我們前面在getters已經(jīng)獲取并簡化了數(shù)據(jù):
用mapgetters
引入一下:
在對應(yīng)位置進行展示:
這里我們要實現(xiàn)點擊某個屬性時該屬性高亮,其他屬性變灰的效果,用到排他思想
首先應(yīng)該給每個屬性綁定點擊事件,并且傳入兩個參數(shù)
第一個參數(shù)是當(dāng)前售賣屬性值所在的那個對象spuVal
,第二個參數(shù)是所有售賣屬性對象所在的數(shù)組spu.spuSaleAttrValueList
然后這里由于active樣式我們是動態(tài)添加的,它是否展示取決于spuVal對象的isChecked屬性是1還是0,這里都是我們從服務(wù)器拿過來的屬性且配置到了計算屬性里,所以一旦它們里面的數(shù)據(jù)改變,vue就會重新解析模板,這樣的話我們就可以通過修改isChecked屬性來控制active樣式是否顯示,所以回調(diào)可以這么寫:
methods: {
//產(chǎn)品售賣屬性切換,排他思想
changeActive(spuVal, spuValArr) {
//第一個參數(shù)是當(dāng)前售賣屬性值所在的對象,第二個參數(shù)是所有售賣屬性對象所在的數(shù)組
spuValArr.forEach(el => {
el.isChecked = '0'; //先把數(shù)組中每個對象的active樣式去掉
spuVal.isChecked = '1'; //再給當(dāng)前點擊的對象添加active樣式
});
}
}
七、放大鏡效果實現(xiàn)
pageX: 頁面X坐標位置
pageY: 頁面Y坐標位置offsetX:鼠標坐標到元素的左側(cè)的距離
offsetY:鼠標坐標到元素的頂部的距離offsetLeft: 該元素外邊框距離包含元素內(nèi)邊框左側(cè)的距離
offsetTop:該元素外邊框距離包含元素內(nèi)邊框頂部的距離
offsetWidth: width + padding-left + padding-right + border-left + border-right
offsetHeight: height + padding-top + padding-bottom + border-top + border-bottom
這些元素偏移量如果不太熟悉的話可以點此復(fù)習(xí)一下——PC端網(wǎng)頁特效
在放鼠標移動事件的這個div里綁定鼠標移動事件
看注釋,反正就是計算一下,記得修改style要加px
這里還要注意右邊的放大圖這里,圖片的top和left要多一倍(因為放大圖是放大一倍),而且要和這塊兒綠東西的top和left反的著來(移動圖片,多余的部分是overflow:hidden
的)。綠框往右下角動,圖片往左上角動才對
handlerMask(e) {
//1.獲取這個遮罩層的dom元素
let mask = this.$refs.mask;
//2.計算定位的left和top
let x = e.offsetX - mask.offsetWidth / 2;
let y = e.offsetY - mask.offsetWidth / 2;
//3.約束條件防止盒子跑出去
if (x < 0) x = 0; //防止從左邊跑出去
if (x > mask.offsetWidth) x = mask.offsetWidth; //防止從右邊跑出去
if (y < 0) y = 0; //防止從上邊跑出去
if (y > mask.offsetWidth) y = mask.offsetWidth; //防止從下邊跑出去
//4.修改dom樣式
mask.style.left = x + 'px';
mask.style.top = y + 'px';
//右邊的放大圖跟著變化
let big = this.$refs.big;
big.style.top = - 2 * y + 'px';
big.style.left = - 2 * x + 'px';
}
八、購買產(chǎn)品個數(shù)的加減操作
這個數(shù)量的框既可以點擊加減按鈕操作,也可以用戶自己輸入
1、收集數(shù)量并控制個數(shù)>0
這塊的話我們肯定是要收集這個商品的數(shù)量然后后邊要顯示計算價格啥的,所以這里的話可以使用v-model
收集一下子,然后點擊加號就+1,點擊減號就-1,但是這個數(shù)量不能<1,所以減號這里要加個判斷。
2、校驗用戶自己輸入的值
用戶可能輸入任何花里胡哨的值,所以我們要對其進行校驗,首先給這個輸入框綁定一個onchange
事件,當(dāng)加入購物車數(shù)量輸入框的內(nèi)容改變時離開焦點觸發(fā)此函數(shù)
這里對三種情況進行校驗
第一種:用戶輸入
非數(shù)值字符串
,讓它乘以1,因為任何非數(shù)值字符串乘以1都會是NaN
第二種:用戶輸入負數(shù)
,這種情況和第一種情況如果有一個出現(xiàn),那么就置為默認值1
第三種:用戶輸入小數(shù)
,直接改為取整文章來源:http://www.zghlxwxcb.cn/news/detail-708803.html
changeSkuNum(event) {
// 拿到用戶輸入的值做校驗
let value = event.target.value * 1; //1.任何非數(shù)值字符串乘以1都會是NaN
if (isNaN(value) || value < 1) {
this.skuNum = 1; //2.如果用戶輸入非數(shù)值字符串或負數(shù),那么就改成1
}
//3.如果用戶輸入正常,就向下取整(避免用戶輸入小數(shù)點)
else {
this.shopCarNum = parseInt(value);
}
},
詳情頁的筆記差不多整理到這啦,下一篇整理購物車模塊相關(guān)的的筆記。文章來源地址http://www.zghlxwxcb.cn/news/detail-708803.html
到了這里,關(guān)于Vue2電商前臺項目——完成Detail詳情頁模塊業(yè)務(wù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!