項目demo地址
github源碼地址
覺得不錯叛买,給我的github源碼點個贊吧QAQ
首頁##
前言##
這篇文章是總結(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)##
- 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中,直接上圖
文章開頭有首頁蜓氨,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###
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###
這里給出了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###
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###
這個組件中重要的就是數(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###
在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)變化。
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í)中假颇,獻丑了胚鸯。
參考資料##
- 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/