vue、vuex、vue-router结合的电商网站

最近在简单的学习了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获取代码资源。

感谢您的阅读!

猜你喜欢

转载自blog.csdn.net/yanggreater/article/details/82429197
今日推荐