一個vue2.0+vuex+vue-router搭建的單頁潮流購物網(wǎng)站

項目demo地址
github源碼地址
覺得不錯叛买,給我的github源碼點個贊吧QAQ

首頁##

Paste_Image.png

前言##

這篇文章是總結(jié)自己寫項目時的思路砂代,遇到的問題,和學(xué)到的東西聪全,本文只截取一部分來講泊藕,源碼已奉上,覺得項目還行的點個贊吧难礼,謝謝

一娃圆、搭建環(huán)境##

  • 安裝vue-cli

npm install -g vue-cli

  • 創(chuàng)建webpack項目

vue init webpack vogue
cd vogue

  • 安裝依賴

npm install

  • 安裝vue-router

npm install vue-router --save-dev

  • 安裝vuex

npm install vuex --save-dev

  • 運行

npm run dev

二、目錄結(jié)構(gòu)##

Paste_Image.png
  • components中是所有頁面組件
  • store中的index.js存放了vuex狀態(tài)管理的東西蛾茉,此處本應(yīng)分成actions.js,mutations.js,getters.js的讼呢,可是我試了很多次沒成功,還是將他們放在一個文件中谦炬,顯得有點冗余了悦屏,這點失誤了,會找原因的
  • static中存放了圖片键思,圖片是壓縮了的础爬,網(wǎng)站是https://tinypng.com/,還存放了字體吼鳞,和一點css看蚜,css放在這里有一個原因就是,我想給某個元素設(shè)置background時赔桌,將style寫在static里才行供炎。
  • dist文件是后來npm run build后生成的渴逻,生成的dist中的index.html中的link都是沒有加引號的,我自己加上才可以直接運行

三音诫、項目開發(fā)##

開發(fā)過程中惨奕,頁面是一個一個寫的,不過還是要先確定路由竭钝,路由嵌套

main.js###

先說說路由吧梨撞,寫在了main.js中,直接上圖

Paste_Image.png

文章開頭有首頁蜓氨,home的路徑就是‘/home’聋袋,這里路由嵌套,用‘:id’來識別穴吹,Brands.vue組件在后文中會解釋如何得到id幽勒,home頁的八個導(dǎo)航,分別導(dǎo)向‘/home’,‘/news’港令,'/collections','/shop','/home/clot','/home/madness','/home/bape','/home/assc',購物車導(dǎo)向'/cart','login|register'導(dǎo)向‘/login’,'/newsarticle'是在news組件中導(dǎo)向的啥容,‘/shoppingitem’是shop組件中導(dǎo)向的

App.vue###

Paste_Image.png

Paste_Image.png

v-for列表渲染的數(shù)據(jù)如left_navs和contents均來自state
對象迭代

<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ī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è)置一個min-height不免得將中間的輪播弄來沒有了褥琐,具體見css

細節(jié):其中用data中的scroll朵纷,用來顯示可以讓頁面一鍵劃到頂端的按鈕炭臭,滑動動畫代碼如下

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###

Paste_Image.png

這里給出了brands的樣式,也就是說導(dǎo)航欄的home袍辞,clot鞋仍,madness,bape搅吁,assc都有這個組件威创,

HomeFirst.vue###

2.21號修改
重新改了下輪播,通過改變left來實現(xiàn)無限輪播谎懦,思路如下:

<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">
    ![]('../static/images/home_4.jpg')
    ![]('../static/images/home_1.jpg')
    ![]('../static/images/home_2.jpg')
    ![]('../static/images/home_3.jpg')
    ![]('../static/images/home_4.jpg')
    ![]('../static/images/home_1.jpg')
</div>

共四張圖片肚豺,前后再加一張,變成六張界拦,當(dāng)向后滾動到第五張時详炬,index為4,下一次滾動,滾動到第六張結(jié)束后立即跳到第二張呛谜,index依然為3。向前滑動道理一樣

methods如下

export default {
        data (){
            return {
                originalData:{
                    img_width:350,
                    img_height:350,
                    btn_width:40,
                    btn_height:40,
                    num:4,
                    delay:300
                },
                isTrans:true,//因為到最后一張圖片枪萄,index為1時隐岛,需要立即跳到第二張index也為1的圖片,這個用來是否給出transition
                index:1,
                timer:null,//setInterval
                clickdelay:false//用來防止連續(xù)點擊
            }
        },
        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ù)竭_最后一張圖片時 
                        }
                        if(left>-100){
                            self.isTrans=false
                            node.style.left=-self.originalData.num*self.originalData.img_width+'px'
                            self.clickdelay=false //當(dāng)?shù)竭_第一張圖片時  
                        }
                    },this.originalData.delay)
                },
                play(){
                
                    var self=this;
                    this.timer=setInterval(function(){
                        self.next()
                    },2000)
                },
                stop(){
                    this.clickdelay=false//用來防止連續(xù)點擊
                    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(){
                /*下面是判斷過渡動畫是否完成*/ 
                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###

Paste_Image.png
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)//改變進入商品
            }
        }

每個商品被點擊時都要改變進入的是哪個商品瓷翻,changeSelectedItem來完成聚凹,這個頁面想法來源于1626潮牌網(wǎng),覺得挺好看的齐帚,于是自己寫了下來妒牙,尤其是mouseover顯示的是否喜歡,處理的還是可以对妄,不過chrome和Firefox還是會有閃爍的效果沒有處理好

shoppingitem.vue###

Paste_Image.png

這個組件中重要的就是數(shù)量的增減湘今,因為每個商品都有一個對象存儲數(shù)據(jù),并且加入購物車還需要判斷購物車中是否有相同信息的商品剪菱,還有點擊加入購物車后直接跳轉(zhuǎn)到購物車頁面摩瞎,方法如下

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);
        //查看購物車是否已經(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={};
    },

添加到購物車中的方法中孝常,我用try旗们,catch來跳出forEach循環(huán),還有這句state.selectedItem={};如果state.selectedItem是直接引用別的對象构灸,那么另一個對象也會跟著改變上渴,為了避免引用,我用了如下方法

//js復(fù)制對象
    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###

Paste_Image.png

在created(){}中用this.$route.params.id來得到進入那個路由,因為這四個brand布局樣式什么的大致都一樣喜颁,然后watch來檢測this.$route.params.id的改變稠氮,以此來getIntro也就是每個brand的數(shù)據(jù)

組件的介紹大致就是這些

四、Vuex##

我在vuex這里沒有做好洛巢,狀態(tài)和數(shù)據(jù)應(yīng)該分開括袒,而且actions,mutations稿茉,getters锹锰,state,應(yīng)該分開漓库,不然太冗余了

Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式恃慧。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化渺蒿。Vuex 也集成到 Vue 的官方調(diào)試工具 devtools extension痢士,提供了諸如零配置的 time-travel 調(diào)試、狀態(tài)快照導(dǎo)入導(dǎo)出等高級調(diào)試功能茂装。
這個狀態(tài)自管理應(yīng)用包含以下幾個部分:
state怠蹂,驅(qū)動應(yīng)用的數(shù)據(jù)源善延;
view,以聲明方式將state映射到視圖城侧;
actions易遣,響應(yīng)在view上的用戶輸入導(dǎo)致的狀態(tài)變化。

Paste_Image.png
Paste_Image.png

index.js中的state###

大概羅列一點

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 非常類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)豆茫。這個回調(diào)函數(shù)就是我們實際進行狀態(tài)更改的地方,并且它會接受 state 作為第一個參數(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
    }
]

有時候我們需要從 store 中的 state 中派生出一些狀態(tài),或用于得到信息

五火脉、總結(jié)##

自己寫的這個項目,蠻有收獲的茅特,遇到了問題到處問忘分,都解決的差不多了,
下面羅列了一些收貨和本項目的不足

  • Firefox中不支持 table 的 min-height
  • CSS 的話 考慮用 normalize.css解決不同瀏覽器初始樣式不一樣的問題
  • css 的命名啥的可以參考一下 BEM 的命名規(guī)范
  • 代碼組織有點雜亂
  • vuex只要專心做頁面狀態(tài)管理白修,盡量不要摻雜頁面數(shù)據(jù)
  • <input type="checkbox" @change="selectAll" id="selectAll" v-model="isAll"/>此處的isAll是從state中g(shù)et到得數(shù)據(jù)妒峦,可以被改變,我自己嘗試得到的這個結(jié)論
  • 輪播還需要改進
  • 第一次在gh-pages中顯示時兵睛,發(fā)現(xiàn)圖片加載太慢 肯骇,于是我把圖片壓縮了
  • 在用git上傳代碼是出過差錯,解決了祖很。

最后感謝您能閱讀到這里笛丙,本人小白,努力學(xué)習(xí)中假颇,獻丑了胚鸯。

參考資料##

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市笨鸡,隨后出現(xiàn)的幾起案子姜钳,更是在濱河造成了極大的恐慌,老刑警劉巖形耗,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哥桥,死亡現(xiàn)場離奇詭異,居然都是意外死亡激涤,警方通過查閱死者的電腦和手機拟糕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人送滞,你說我怎么就攤上這事侠草。” “怎么了犁嗅?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵梦抢,是天一觀的道長。 經(jīng)常有香客問我愧哟,道長,這世上最難降的妖魔是什么哼蛆? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任蕊梧,我火速辦了婚禮,結(jié)果婚禮上腮介,老公的妹妹穿的比我還像新娘肥矢。我一直安慰自己,他們只是感情好叠洗,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布甘改。 她就那樣靜靜地躺著,像睡著了一般灭抑。 火紅的嫁衣襯著肌膚如雪十艾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天腾节,我揣著相機與錄音忘嫉,去河邊找鬼。 笑死案腺,一個胖子當(dāng)著我的面吹牛庆冕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播劈榨,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼访递,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了同辣?” 一聲冷哼從身側(cè)響起拷姿,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎邑闺,沒想到半個月后跌前,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡陡舅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年抵乓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡灾炭,死狀恐怖茎芋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蜈出,我是刑警寧澤田弥,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站铡原,受9級特大地震影響偷厦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜燕刻,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一只泼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卵洗,春花似錦请唱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至酷勺,卻和暖如春本橙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸥印。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工勋功, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人库说。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓狂鞋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親潜的。 傳聞我的和親對象是個殘疾皇子骚揍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容