項(xiàng)目demo地址
github源碼地址
覺(jué)得不錯(cuò),給我的github源碼點(diǎn)個(gè)贊吧QAQ
首頁(yè)##

前言##
這篇文章是總結(jié)自己寫(xiě)項(xiàng)目時(shí)的思路,遇到的問(wèn)題,和學(xué)到的東西,本文只截取一部分來(lái)講,源碼已奉上,覺(jué)得項(xiàng)目還行的點(diǎn)個(gè)贊吧,謝謝
一、搭建環(huán)境##
- 安裝vue-cli
npm install -g vue-cli
- 創(chuàng)建webpack項(xiàng)目
vue init webpack vogue
cd vogue
- 安裝依賴
npm install
- 安裝vue-router
npm install vue-router --save-dev
- 安裝vuex
npm install vuex --save-dev
- 運(yùn)行
npm run dev
二、目錄結(jié)構(gòu)##

- components中是所有頁(yè)面組件
- store中的index.js存放了vuex狀態(tài)管理的東西,此處本應(yīng)分成actions.js,mutations.js,getters.js的,可是我試了很多次沒(méi)成功,還是將他們放在一個(gè)文件中,顯得有點(diǎn)冗余了,這點(diǎn)失誤了,會(huì)找原因的
- static中存放了圖片,圖片是壓縮了的,網(wǎng)站是https://tinypng.com/,還存放了字體,和一點(diǎn)css,css放在這里有一個(gè)原因就是,我想給某個(gè)元素設(shè)置background時(shí),將style寫(xiě)在static里才行。
- dist文件是后來(lái)npm run build后生成的,生成的dist中的index.html中的link都是沒(méi)有加引號(hào)的,我自己加上才可以直接運(yùn)行
三、項(xiàng)目開(kāi)發(fā)##
開(kāi)發(fā)過(guò)程中,頁(yè)面是一個(gè)一個(gè)寫(xiě)的,不過(guò)還是要先確定路由,路由嵌套
main.js###
先說(shuō)說(shuō)路由吧,寫(xiě)在了main.js中,直接上圖

文章開(kāi)頭有首頁(yè),home的路徑就是‘/home’,這里路由嵌套,用‘:id’來(lái)識(shí)別,Brands.vue組件在后文中會(huì)解釋如何得到id,home頁(yè)的八個(gè)導(dǎo)航,分別導(dǎo)向‘/home’,‘/news’,'/collections','/shop','/home/clot','/home/madness','/home/bape','/home/assc',購(gòu)物車導(dǎo)向'/cart','login|register'導(dǎo)向‘/login’,'/newsarticle'是在news組件中導(dǎo)向的,‘/shoppingitem’是shop組件中導(dǎo)向的
App.vue###


v-for列表渲染的數(shù)據(jù)如left_navs和contents均來(lái)自state
對(duì)象迭代
<div v-for="(value, key, index) in object">
{{ index }}. {{ key }} : {{ value }}
</div>
如何得到state中的數(shù)據(jù)
import {mapGetters} from 'vuex'
computed:{
...mapGetters({
show:'getShow',
items:'getFootItems',
cart:'getCart',
brands:'getBrands',
left_navs:'getLeft_nav'
})
},
在布局上,我的思路是:首頁(yè)三行,上下定高,中間自適應(yīng)高度,于是在app.vue的created()中設(shè)置事件委托
var self=this;
window.onload=()=>{
this.$store.dispatch('change_hw',{
h:document.documentElement.clientHeight||document.body.clientHeight,
w:document.documentElement.clientWidth||document.body.clientWidth
})
}
window.onresize=()=>{
if(self.timer){
clearTimeout(self.timer)
}
self.timer=setTimeout(function(){
self.$store.dispatch('change_hw',{
h:document.documentElement.clientHeight||document.body.clientHeight,
w:document.documentElement.clientWidth||document.body.clientWidth
})
},100)
}
window.onscroll=()=>{
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
if(scrollTop>10){
this.scroll=true;
}else{
this.scroll=false;
}
}
}
然后中間那行用的三欄布局,左右定寬中間自適應(yīng)寬度,再設(shè)置一個(gè)min-height不免得將中間的輪播弄來(lái)沒(méi)有了,具體見(jiàn)css
細(xì)節(jié):其中用data中的scroll,用來(lái)顯示可以讓頁(yè)面一鍵劃到頂端的按鈕,滑動(dòng)動(dòng)畫(huà)代碼如下
scrolltoTop:()=>{
if(document.documentElement.scrollTop){
var scrollTop=document.documentElement.scrollTop
var step=scrollTop/30;
var now=scrollTop-step;
var i=0;
var time=setInterval(function(){
i++;
if(i>32){
clearInterval(time)
}
document.documentElement.scrollTop=now;
scrollTop=document.documentElement.scrollTop
now=scrollTop-step;
},10)
}else if(document.body.scrollTop){
var scrollTop=document.body.scrollTop
var step=scrollTop/30;
var now=scrollTop-step;
var i=0;
var time=setInterval(function(){
i++;
if(i>32){
clearInterval(time)
}
document.body.scrollTop=now;
scrollTop=document.body.scrollTop
now=scrollTop-step;
},10)
}
},
這里比較坑的地方就是document.documentElement.scrollTop和document.documentElement.scrollTop需要注意
Home.vue###

這里給出了brands的樣式,也就是說(shuō)導(dǎo)航欄的home,clot,madness,bape,assc都有這個(gè)組件,
HomeFirst.vue###
2.21號(hào)修改
重新改了下輪播,通過(guò)改變left來(lái)實(shí)現(xiàn)無(wú)限輪播,思路如下:
<div class="wrapper-content" :class="{wrapper_trans:isTrans}" :style="{width:originalData.img_width*(originalData.num+2)+'px',height:originalData.img_height+'px',left:-originalData.img_width+'px'}" ref="wrapperContent">






</div>
共四張圖片,前后再加一張,變成六張,當(dāng)向后滾動(dòng)到第五張時(shí),index為4,下一次滾動(dòng),滾動(dòng)到第六張結(jié)束后立即跳到第二張,index依然為3。向前滑動(dòng)道理一樣
methods如下
export default {
data (){
return {
originalData:{
img_width:350,
img_height:350,
btn_width:40,
btn_height:40,
num:4,
delay:300
},
isTrans:true,//因?yàn)榈阶詈笠粡垐D片,index為1時(shí),需要立即跳到第二張index也為1的圖片,這個(gè)用來(lái)是否給出transition
index:1,
timer:null,//setInterval
clickdelay:false//用來(lái)防止連續(xù)點(diǎn)擊
}
},
computed:{
...mapGetters({
hw:'getHW'
}),
home_first_width:function(){
return parseInt(this.hw.w)-400;
},
home_first_height:function(){
var a= parseInt(this.hw.h)-200
return a<389?389:a
},
home_first_height_margin:function(){
return parseInt(this.home_first_height-300)/2
}
},
methods:{
next(){
if(this.clickdelay){
return
}
this.clickdelay=true
if(this.index==this.originalData.num){
this.index=1
}else{
this.index+=1
}
this.animate(this.originalData.img_width)
},
prev(){
if(this.clickdelay){
return
}
this.clickdelay=true
if(this.index==1){
this.index=this.originalData.num
}else{
this.index-=1
}
this.animate(-this.originalData.img_width)
},
animate(offset){
var node=this.$refs.wrapperContent
var self=this;
var left=parseInt(node.style.left)-offset
this.isTrans=true
node.style.left=left+'px'
setTimeout(function(){
if(left<-(self.originalData.num*self.originalData.img_width)){
self.isTrans=false
node.style.left=-self.originalData.img_width+'px'
self.clickdelay=false //當(dāng)?shù)竭_(dá)最后一張圖片時(shí)
}
if(left>-100){
self.isTrans=false
node.style.left=-self.originalData.num*self.originalData.img_width+'px'
self.clickdelay=false //當(dāng)?shù)竭_(dá)第一張圖片時(shí)
}
},this.originalData.delay)
},
play(){
var self=this;
this.timer=setInterval(function(){
self.next()
},2000)
},
stop(){
this.clickdelay=false//用來(lái)防止連續(xù)點(diǎn)擊
clearInterval(this.timer)
this.timer=null
},
turnTo(flag){
if(flag==this.index){
return
}else{
var offset=(flag-this.index)*this.originalData.img_width
this.index=flag
this.animate(offset)
}
}
},
mounted(){
/*下面是判斷過(guò)渡動(dòng)畫(huà)是否完成*/
var node=this.$refs.wrapperContent
var transitions = {
'transition':'transitionend',
'OTransition':'oTransitionEnd',
'MozTransition':'transitionend',
'WebkitTransition':'webkitTransitionEnd'
}
var self=this
for(var t in transitions){
if( node.style[t] !== undefined ){
var transitionEvent=transitions[t];
}
}
transitionEvent && node.addEventListener(transitionEvent, function() {
self.clickdelay=false
});
this.play()
},
created(){
this.$store.dispatch('changeShow','home')
}
}
Shop.vue###

methods:{
changeLike(index){
this.$store.dispatch('changeLike',index)//改變是否喜歡
},
changeFlagTrue(index){
this.$store.dispatch('changeFlagTrue',index)//改變是否顯示喜歡
},
changeFlagFalse(index){
this.$store.dispatch('changeFlagFalse',index)//改變是否顯示喜歡
},
changeSelectedItem(index){
this.$store.dispatch('changeSelectedItem',index)//改變進(jìn)入商品
}
}
每個(gè)商品被點(diǎn)擊時(shí)都要改變進(jìn)入的是哪個(gè)商品,changeSelectedItem來(lái)完成,這個(gè)頁(yè)面想法來(lái)源于1626潮牌網(wǎng),覺(jué)得挺好看的,于是自己寫(xiě)了下來(lái),尤其是mouseover顯示的是否喜歡,處理的還是可以,不過(guò)chrome和Firefox還是會(huì)有閃爍的效果沒(méi)有處理好
shoppingitem.vue###

這個(gè)組件中重要的就是數(shù)量的增減,因?yàn)槊總€(gè)商品都有一個(gè)對(duì)象存儲(chǔ)數(shù)據(jù),并且加入購(gòu)物車還需要判斷購(gòu)物車中是否有相同信息的商品,還有點(diǎn)擊加入購(gòu)物車后直接跳轉(zhuǎn)到購(gòu)物車頁(yè)面,方法如下
methods:{
changeSize(index){
this.$store.dispatch('changeSize',index)
},
changeColor(num){
this.$store.dispatch('changeColor',num)
},
changeNumSub(){
if(this.item.num>1){
this.$store.dispatch('changeNumSub')
}
},
changeNumAdd(){
if(this.item.num<8){
this.$store.dispatch('changeNumAdd')
}
},
addToCart(){
if(!!this.item.color&&!!this.item.size){
this.$store.dispatch('addToCart')
}
}
}
index.js中的方法如下
ADD_TO_CART(state){
var cart=state.cart;
var thing=mutations.clone(state.selectedItem);
//查看購(gòu)物車是否已經(jīng)有相同的商品,信息都一樣
if(!cart.length){
cart.push(thing)
}else{
var flag=cart.some(function(e){
return e.color==thing.color&&e.size==thing.size&&e.src==thing.src
})
try{
if(!flag){
cart.push(thing);
throw new Error("can't find")
}
cart.forEach(function(e,index){
if(e.color==thing.color&&e.size==thing.size&&e.src==thing.src){
cart[index].num+=thing.num;
foreach.break=new Error("StopIteration");
}
})
}catch(e){
//用于跳出循環(huán)
}
}
state.selectedItem={};
},
添加到購(gòu)物車中的方法中,我用try,catch來(lái)跳出forEach循環(huán),還有這句state.selectedItem={};如果state.selectedItem是直接引用別的對(duì)象,那么另一個(gè)對(duì)象也會(huì)跟著改變,為了避免引用,我用了如下方法
//js復(fù)制對(duì)象
clone(myObj){
if(typeof(myObj) != 'object') return myObj;
if(myObj == null) return myObj;
var myNewObj = new Object();
for(var i in myObj)
myNewObj[i] = mutations.clone(myObj[i]);
return myNewObj;
},
Brands.vue###

在created(){}中用this.$route.params.id來(lái)得到進(jìn)入那個(gè)路由,因?yàn)檫@四個(gè)brand布局樣式什么的大致都一樣,然后watch來(lái)檢測(cè)this.$route.params.id的改變,以此來(lái)getIntro也就是每個(gè)brand的數(shù)據(jù)
組件的介紹大致就是這些
四、Vuex##
我在vuex這里沒(méi)有做好,狀態(tài)和數(shù)據(jù)應(yīng)該分開(kāi),而且actions,mutations,getters,state,應(yīng)該分開(kāi),不然太冗余了
Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。Vuex 也集成到 Vue 的官方調(diào)試工具 devtools extension,提供了諸如零配置的 time-travel 調(diào)試、狀態(tài)快照導(dǎo)入導(dǎo)出等高級(jí)調(diào)試功能。
這個(gè)狀態(tài)自管理應(yīng)用包含以下幾個(gè)部分:
state,驅(qū)動(dòng)應(yīng)用的數(shù)據(jù)源;
view,以聲明方式將state映射到視圖;
actions,響應(yīng)在view上的用戶輸入導(dǎo)致的狀態(tài)變化。


index.js中的state###
大概羅列一點(diǎn)
const state={
loginway:'',
show:'home',
clientheight:0,
clientwidth:0,
footItems:[
{title:'ABOUT US',contents:{content_1:'contact us',content_2:'about vogue'}},
{title:'SERVICE',contents:{content_1:'payment methods',content_2:'track order'}},
{title:'POLICY',contents:{content_1:'privacy policy',content_2:'terms & condition'}},
{title:'FOLLOW US',contents:{content_1:'Facebook',content_2:'Instagram'}},
],
left_nav:{
home:'home',
news:'news',
collections:'collections',
shop:'shop'
},
]
index.js中的mutations###
const mutations={
CHANGE_HW(state,obj){
state.clientwidth=obj.w;
state.clientheight=obj.h;
},
CHANGE_SHOW(state,type){
state.show=type
},
CHANGE_NOWBRAND(state,type){
state.nowbrand=type+'Intro'
},
CHANGE_LIKE(state,index){
state.goods[index].isLike=!state.goods[index].isLike;
if(!state.goods[index].isLike){
state.goods[index].likes+=1
}else{
state.goods[index].likes-=1
}
},
]
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutations 非常類似于事件:每個(gè) mutation 都有一個(gè)字符串的 事件類型 (type) 和 一個(gè) 回調(diào)函數(shù) (handler)。這個(gè)回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方,并且它會(huì)接受 state 作為第一個(gè)參數(shù):
index.js中的actions###
const actions={
change_hw({commit},obj){
commit('CHANGE_HW',obj)
},
changeShow({commit},type){
commit('CHANGE_SHOW',type)
},
changeNowbrand({commit},type){
commit('CHANGE_NOWBRAND',type)
},
changeLike({commit},index){
commit('CHANGE_LIKE',index)
},
]
Action 類似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接變更狀態(tài)。
Action 可以包含任意異步操作。
index.js中的getters###
const getters={
getHW:function(state){
return {
h:state.clientheight,
w:state.clientwidth
}
},
getBrands:function(state){
return state.brandsArr
},
getLeft_nav:function(state){
return state.left_nav
},
getShow:function(state){
return state.show
}
]
有時(shí)候我們需要從 store 中的 state 中派生出一些狀態(tài),或用于得到信息
五、總結(jié)##
自己寫(xiě)的這個(gè)項(xiàng)目,蠻有收獲的,遇到了問(wèn)題到處問(wèn),都解決的差不多了,
下面羅列了一些收貨和本項(xiàng)目的不足
- Firefox中不支持 table 的 min-height
- CSS 的話 考慮用 normalize.css解決不同瀏覽器初始樣式不一樣的問(wèn)題
- css 的命名啥的可以參考一下 BEM 的命名規(guī)范
- 代碼組織有點(diǎn)雜亂
- vuex只要專心做頁(yè)面狀態(tài)管理,盡量不要摻雜頁(yè)面數(shù)據(jù)
- <input type="checkbox" @change="selectAll" id="selectAll" v-model="isAll"/>此處的isAll是從state中g(shù)et到得數(shù)據(jù),可以被改變,我自己嘗試得到的這個(gè)結(jié)論
- 輪播還需要改進(jìn)
- 第一次在gh-pages中顯示時(shí),發(fā)現(xiàn)圖片加載太慢 ,于是我把圖片壓縮了
- 在用git上傳代碼是出過(guò)差錯(cuò),解決了。
最后感謝您能閱讀到這里,本人小白,努力學(xué)習(xí)中,獻(xiàn)丑了。
參考資料##
- Vue2.0中文文檔:https://cn.vuejs.org/
- Vue-router2.0中文文檔:http://router.vuejs.org/zh-cn/essentials/getting-started.html
- Vuex2.0中文文檔:http://router.vuejs.org/zh-cn/essentials/getting-started.html
- git教程:http://www.liaoxuefeng.com/