vue、vuex、vue-router结合的电商网站
最近在简单的学习了vue之后,通过实践去理解vue的用法和它的优点。首先先简单介绍一下文件的目录components是公共的组件,里面包含cart.vue(购物车)、product.vue(list.vue文件要用到的产品公用组件)、productDetail.vue(产品详情页面)views文件是视图文件(就是list.vue主页文件),libs下的utils是一些公共函数,rout...
最近在简单的学习了vue之后,通过实践去理解vue的用法和它的优点。
首先先简单介绍一下文件的目录
components是公共的组件,里面包含cart.vue(购物车)、product.vue(list.vue文件要用到的产品公用组件)、productDetail.vue(产品详情页面)
views文件是视图文件(就是list.vue主页文件),libs下的utils是一些公共函数,router.js是路由配置文件,style.css是App.vue的样式文件(写在App.vue里也没关系)
一:首先在list.vue文件下需要循环显示商品信息,那么我就先介绍一下product.vue组件
重点在,这一个product组件就是一个链接,可以点击后查看此商品的详细信息所有我们直接设置一个router-link对于每个商品都会有一个自己的id,在路由的id我们就通过list.vue父组件传给它相对应的商品对象,这样顺理成章接下来的商品名称、价格等信息就直接可以在html获取到。同时,在每个商品组件有个添加到购物车的功能,因为用到vuex,所以,对于修改购物车里信息的,一律交给vuex去统一修改,因此在handleCart函数里用过commit提交给vuex中mutation里面的函数。具体代码如下:
<template>
<div class="product">
<router-link :to="'/productDetail/'+info.id" class="product-main">
<img :src="info.image">
<h4>{{info.name}}</h4>
<div class="product-color" :style="{background:colors[info.color]}"></div>
<div class="product-cost">¥ {{info.cost}}</div>
<div class="product-add-cart" @click.prevent="handleCart">加入购物车</div>
</router-link>
</div>
</template>
<script>
export default {
props:{
info:{
type:Object
}
},
methods:{
handleCart () {//添加到购物车
this.$store.commit('addCart',this.info.id);
}
},
data() {
return{
colors:{
'白色':'#ffffff',
'金色':'#dac272',
'蓝色':'#233472',
'红色':'#f2352e'
}
}
}
}
</script>
二:list.vue 主页代码思路
1、前面我们已经提到,我们要循环显示商品,就要给product循环传商品对象,那么在list.vue中怎样去获取到所有的商品信息?其实,在vuex的管理下所有的数据都是由vuex去统一管理,包括数据的获取,因此只要通过计算属性去获取vuex中state中的productList数组即可(切记不可在data属性中获取,文章末尾会提到原因),那么现在循环显示商品的信息就解决了。
2、那么对商品的排序我们如何去实现,首先,就是排序类别的显示,在list.vue中我们是获取到全部的商品信息,由于商品颜色和品牌等信息有重复,那么我们就要排重,获得到所有的颜色和品牌类别,在这里既可以在list.vue组件中获取,又可以在vuex中获取(到底应该放在哪里,文章末尾后给出几条建议),对于功能实现,就是通过过滤返回过滤的商品数组即可,那么现在所有问题解决,看一下代码是如何实现功能
<template>
<div v-show="list.length">
<div class="list-control">
<div class="list-control-filter">
<span>品牌:</span>
<span
class="list-control-filter-item"
:class="{on:item === filterBrand}"
v-for="item in brands"
@click="handleFilterBrand(item)"
>{{item}}</span>
</div>
<div class="list-control-filter">
<span>颜色:</span>
<span
class="list-control-filter-item"
:class="{on:item === filterColor}"
v-for="item in colors"
@click="handleFilterColor(item)"
>{{item}}</span>
</div>
<div class="list-control-order">
<span>排序:</span>
<span
@click="handleDefault"
class="list-control-order-item"
:class="{on:order ===''}"
>默认</span>
<span
@click="handleOrderSales"
class="list-control-order-item"
:class="{on:order === 'sales'}"
>销售</span>
<span
@click="handleOrderByprice"
class="list-control-order-item"
:class="{on:order.indexOf('cost')>-1}">
价格
<template v-if="order === 'cost-desc'">↓</template>
<template v-if="order === 'cost-asc'">↑</template>
</span>
</div>
</div>
<Product v-for="item in filteredAndOrderedList" :info="item" :key="item.id"></Product>
<div v-if="!filteredAndOrderedList.length" class="product-not-found">
暂无相关商品
</div>
</div>
</template>
<script>
import Product from '../components/product.vue'
export default {
data() {
return{
order:'',
filterBrand:'',
filterColor:''
}
},
components:{
Product
},
computed:{
list() {
return this.$store.state.productList;
},
filteredAndOrderedList() {
let list = [...this.list];
if(this.filterBrand!==''){
list = list.filter(item => item.brand ===this.filterBrand);
}
if(this.filterColor !==''){
list = list.filter(item => item.color === this.filterColor);
}
if(this.order !== '') {
if(this.order ==='sales'){
list.sort((a,b) => {return b.sales-a.sales})
}else if(this.order === 'cost-desc'){
list.sort((a,b) => b.cost-a.cost)
}else if(this.order === 'cost-asc'){
list.sort((a,b) => a.cost-b.cost)
}
}
return list;
},
brands() {
return this.$store.getters.brands;
},
colors() {
return this.$store.getters.colors;
}
},
methods:{
handleDefault() {
this.order = ''
},
handleOrderSales () {
this.order = 'sales'
},
handleOrderByprice () {
if(this.order === 'cost-desc'){
this.order = 'cost-asc'
}else{
this.order ='cost-desc'
}
},
handleFilterBrand (brand) {
if(this.filterBrand === brand){
this.filterBrand = ''
}else{
this.filterBrand = brand
}
},
handleFilterColor (color) {
if(this.filterColor === color){
this.filterColor = ''
}else{
this.filterColor = color
}
}
},
mounted () {
this.$store.dispatch('getProductList');
}
}
</script>
功能实现的主要思想就是,通过点击过滤条件的按钮时改变data中的数据,然后根据data里面的过滤条件分别通过filter函数过滤,最后返回过滤数据即可。
三:productDetail.vue 商品详情信息
现在直接看代码:
<template>
<div v-if="product">
<div class="product">
<div class="product-image">
<img :src="product.image">
</div>
<div class="product-info">
<h1 class="product-name">{{product.name}}</h1>
<div class="product-cost">¥{{product.cost}}</div>
<div
class="product-add-cart"
@click="handleClickAdd"
>加入购物车</div>
</div>
</div>
<div class="product-desc">
<img
v-for="n in 10"
:src="'http://ordfm6aah.bkt.clouddn.com/shop/'+n+'.jpeg'"
:key="n"
>
</div>
</div>
</template>
<script>
import product_data from '../project.js'
export default {
data () {
return {
id:parseInt(this.$route.params.id),
product:null
}
},
methods:{
getProduct () {
setTimeout(()=> {
this.product = product_data.find(item => item.id === this.id)
},500)
},
handleClickAdd () {
this.$store.commit('addCart',this.id);
}
},
mounted () {
this.getProduct();
}
}
</script>
在这里有人会问,不是在vuex获取到了数据了吗?为什么还要在此组件获取数据(解释一下,获取数据没有直接用axios,而是模拟了异步获取数据),这是因为,为了使业务组件逻辑更好的解耦,使他们彼此关系不那么密切(优点是便于维护),在这里只通过路由里面的id再次获取相应的数据,这样,即使vuex里面的数据出错,维护productDetali.vue时只需要维护路由里面的id即可;但是修改数据还是要在vuex中进行
个人的总结:
1、数组的排重
我们首先想到的是循环里面套循环,但是这样做的话效率会慢很多,所以我么怎样在一个循环中处理好呢?可以申请一个空对象作为参照,当访问过时,在这个对象添加key为循环出的值,对象值设为1,表示我已经读取过了,那么判断条件也就自然而然为对象对应key的值是不是1,是的话继续下一个循环,不是那么就push到新创建的数组中,并且在对象中push值为1的对象表示读过了,最后将数组返回。是不是觉得这个思想很6?
const common = {
getFilterArray(array) {//数组排重
let res = [];
let josn = {};
for(let i=0;i<array.length;i++){
const self = array[i];
if(!josn[self]){
res.push(self);
josn[self] = 1;
}
}
return res;
}
}
export default common;
2、有一个大数组a1,小数组a2,我要在a1中找到a2的所有值,同样先想到的是双循环.新的思路为将a1数组做成数据字典,即对象数组,里面的对象中的key为原数组元素的一个值,对象的值直接为原数组的元素,这样在循环a2中时就可以找到数据字典相对应的信息,代码中productDictList为a1,cartList为a2
productDictList () {
const dict = {};
this.productList.forEach(item => {
dict[item.id] = item;
});
return dict;
},
class="cart-content">
<div class="cart-content-main" v-for="(item,index) in cartList" :key="item.id">
<div class="cart-info">
<img :src="productDictList[item.id].image">
<span>{{productDictList[item.id].name}}</span>
</div>
<div class="cart-price">
¥ {{productDictList[item.id].cost}}
</div>
<div class="cart-count">
<span class="cart-count-minus" @click="handleCount(index,-1)">-</span>
{{item.count}}
<span class="cart-count-add" @click="handleCount(index,1)">+</span>
</div>
<div class="cart-cost">¥{{productDictList[item.id].cost*item.count}}</div>
<div class="cart-delete">
<span class="cart-control-delete" @click="handleDelete(index)">删除</span>
</div>
</div>
3、vuex中actions和mutations看起来很像,但是mutations只是单纯的修改state的值,而actions主要是异步处理操作,如获取数据和提交数据等,然后再提交mutation。还有就是为何不能在组件data中不能获取state是因为,data只会在created执行前赋值一次,相当于js中的字面量,不会在随着state的值改变而改变,也就是所谓的没有更新检测,但是在计算属性中computed 则是通过【依赖追踪】实现的,在 computed 求值时引用的 Vue 变量变化时,会触发对 computed 的重新计算,所以你可以使用 computed 去引用 Vuex 状态变量,从而使得依赖追踪生效。
4、获取vuex中state的部分数据到底在组件内部实现还是在vuex的getters获取
如果数据还有其他组件用,放在vuex中
如果要跨多级组件传递数据,放在vuex
如果对于持久化的数据(如:登录用户的信息),放在vuex中
数据和当前组件有很强的相关性或者是数据只在当前组件中使用,建议放在组件内
最后由于篇幅的原因没有全部介绍所有的组件,有兴趣的可以评论留言或直接在https://github.com/yanggreater/Vue-project/tree/master/%E7%94%B5%E5%95%86%E7%BD%91%E7%AB%99获取代码资源。
感谢您的阅读!
更多推荐
所有评论(0)