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

項目demo地址
github源碼地址css

首頁

以爲不錯,給個人github源碼點個贊吧QAQhtml

圖片描述

前言

這篇文章是總結本身寫項目時的思路,遇到的問題,和學到的東西,本文只截取一部分來說,源碼已奉上,以爲項目還行的點個贊吧,謝謝vue

1、搭建環境

  • 安裝vue-clinode

npm install -g vue-cliwebpack

  • 建立webpack項目git

vue init webpack vogue
cd voguegithub

  • 安裝依賴web

npm installvue-router

  • 安裝vue-routervuex

npm install vue-router --save-dev

  • 安裝vuex

npm install vuex --save-dev

  • 運行

npm run dev

2、目錄結構

圖片描述

  • components中是全部頁面組件

  • store中的index.js存放了vuex狀態管理的東西,此處本應分紅actions.js,mutations.js,getters.js的,但是我試了不少次沒成功,仍是將他們放在一個文件中,顯得有點冗餘了,這點失誤了,會找緣由的

  • static中存放了圖片,圖片是壓縮了的,網站是https://tinypng.com/,還存放了字體,和一點css,css放在這裏有一個緣由就是,我想給某個元素設置background時,將style寫在static裏才行。

  • dist文件是後來npm run build後生成的,生成的dist中的index.html中的link都是沒有加引號的,我本身加上才能夠直接運行

3、項目開發

開發過程當中,頁面是一個一個寫的,不過仍是要先肯定路由,路由嵌套

main.js

先說說路由吧,寫在了main.js中,直接上圖
圖片描述

文章開頭有首頁,home的路徑就是‘/home’,這裏路由嵌套,用‘:id’來識別,Brands.vue組件在後文中會解釋如何獲得id,home頁的八個導航,分別導向‘/home’,‘/news’,'/collections','/shop','/home/clot','/home/madness','/home/bape','/home/assc',購物車導向'/cart','login|register'導向‘/login’,'/newsarticle'是在news組件中導向的,‘/shoppingitem’是shop組件中導向的

App.vue

圖片描述
圖片描述
v-for列表渲染的數據如left_navs和contents均來自state
對象迭代

<div v-for="(value, key, index) in object">
  {{ index }}. {{ key }} : {{ value }}
</div>

如何獲得state中的數據

import {mapGetters} from 'vuex'
    computed:{
    ...mapGetters({
      show:'getShow',
      items:'getFootItems',
      cart:'getCart',
      brands:'getBrands',
      left_navs:'getLeft_nav'
    })
  },

在佈局上,個人思路是:首頁三行,上下定高,中間自適應高度,因而在app.vue的created()中設置事件委託

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;
            }
        
      }
   }

而後中間那行用的三欄佈局,左右定寬中間自適應寬度,再設置一個min-height難免得將中間的輪播弄來沒有了,具體見css

細節:其中用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

圖片描述
這裏給出了brands的樣式,也就是說導航欄的home,clot,madness,bape,assc都有這個組件,

HomeFirst.vue

2.21號修改
從新改了下輪播,經過改變left來實現無限輪播,思路以下:

<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">
    <img class="wrapper-content_img" alt="4" :src="'../static/images/home_4.jpg'" :style="{width:originalData.img_width+'px',height:originalData.img_height+'px'}"/>
    <img class="wrapper-content_img" alt="1" :src="'../static/images/home_1.jpg'" :style="{width:originalData.img_width+'px',height:originalData.img_height+'px'}"/>
    <img class="wrapper-content_img" alt="2" :src="'../static/images/home_2.jpg'" :style="{width:originalData.img_width+'px',height:originalData.img_height+'px'}"/>
    <img class="wrapper-content_img" alt="3" :src="'../static/images/home_3.jpg'" :style="{width:originalData.img_width+'px',height:originalData.img_height+'px'}"/>
    <img class="wrapper-content_img" alt="4" :src="'../static/images/home_4.jpg'" :style="{width:originalData.img_width+'px',height:originalData.img_height+'px'}"/>
    <img class="wrapper-content_img" alt="1" :src="'../static/images/home_1.jpg'" :style="{width:originalData.img_width+'px',height:originalData.img_height+'px'}"/>
</div>

共四張圖片,先後再加一張,變成六張,當向後滾動到第五張時,index爲4,下一次滾動,滾動到第六張結束後當即跳到第二張,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//用來防止連續點擊
            }
        },
        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 //當到達最後一張圖片時 
                        }
                        if(left>-100){
                            self.isTrans=false
                            node.style.left=-self.originalData.num*self.originalData.img_width+'px'
                            self.clickdelay=false //當到達第一張圖片時  
                        }
                    },this.originalData.delay)
                },
                play(){
                
                    var self=this;
                    this.timer=setInterval(function(){
                        self.next()
                    },2000)
                },
                stop(){
                    this.clickdelay=false//用來防止連續點擊
                    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

圖片描述

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潮牌網,以爲挺好看的,因而本身寫了下來,尤爲是mouseover顯示的是否喜歡,處理的仍是能夠,不過chrome和Firefox仍是會有閃爍的效果沒有處理好

shoppingitem.vue

圖片描述
這個組件中重要的就是數量的增減,由於每一個商品都有一個對象存儲數據,而且加入購物車還須要判斷購物車中是否有相同信息的商品,還有點擊加入購物車後直接跳轉到購物車頁面,方法以下

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);
        //查看購物車是否已經有相同的商品,信息都同樣
        
        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){
                //用於跳出循環
            }
            
        }
        state.selectedItem={};
    },

添加到購物車中的方法中,我用try,catch來跳出forEach循環,還有這句state.selectedItem={};若是state.selectedItem是直接引用別的對象,那麼另外一個對象也會跟着改變,爲了不引用,我用了以下方法

//js複製對象
    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來獲得進入那個路由,由於這四個brand佈局樣式什麼的大體都同樣,而後watch來檢測this.$route.params.id的改變,以此來getIntro也就是每一個brand的數據

組件的介紹大體就是這些

4、Vuex

我在vuex這裏沒有作好,狀態和數據應該分開,並且actions,mutations,getters,state,應該分開,否則太冗餘了

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。
這個狀態自管理應用包含如下幾個部分:
state,驅動應用的數據源;
view,以聲明方式將state映射到視圖;
actions,響應在view上的用戶輸入致使的狀態變化。

clipboard.png

clipboard.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 中的狀態的惟一方法是提交 mutation。Vuex 中的 mutations 很是相似於事件:每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是咱們實際進行狀態更改的地方,而且它會接受 state 做爲第一個參數:

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,而不是直接變動狀態。
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 中派生出一些狀態,或用於獲得信息

5、總結

本身寫的這個項目,蠻有收穫的,遇到了問題處處問,都解決的差很少了,
下面羅列了一些收貨和本項目的不足

  • Firefox中不支持 table 的 min-height

  • CSS 的話 考慮用 normalize.css解決不一樣瀏覽器初始樣式不同的問題

  • css 的命名啥的能夠參考一下 BEM 的命名規範

  • 代碼組織有點雜亂

  • vuex只要專心作頁面狀態管理,儘可能不要摻雜頁面數據

  • <input type="checkbox" @change="selectAll" id="selectAll" v-model="isAll"/>此處的isAll是從state中get到得數據,能夠被改變,我本身嘗試獲得的這個結論

  • 輪播還須要改進

  • 第一次在gh-pages中顯示時,發現圖片加載太慢 ,因而我把圖片壓縮了

  • 在用git上傳代碼是出過差錯,解決了。

最後感謝您能閱讀到這裏,本人小白,努力學習中,獻醜了。

參考資料

相關文章
相關標籤/搜索