【Vue项目】商品汇前台(二)进度条插件+Vuex模块化仓库+函数的防抖与节流+路由传参

前言

1 nprogress进度条的使用

当请求发出进度条出现并向前走,请求成功后进度条消失。nprogress是一种进度条插件

1.1 nprogress进度条插件安装

npm i --save nprogress

随后可以在package.json中查看到安装的nprogress插件。

1.2 nprogress进度条插件的使用

在对axios封装的请求和响应拦截器中使用nprogress的start和done方法

引入进度条的样式,否则进度条不会显示

api/request.js

// 对axios进行二次封装
import axios from 'axios'
// 引入进度条
import nprogress from 'nprogress'
// 引入进度条样式
import 'nprogress/nprogress.css'

// 1 利用axios create创建一个axios实例
// 2 requests就是配置参数后的axios实例
const requests = axios.create({
    // 基础路径,请求发出的时候,路径前面就会出现api
    baseURL: '/api',
    // 设置请求超出事件
    timeout: 5000
})
// 请求拦截器,在请求发出之前能够检测到,做一些指定的业务
requests.interceptors.request.use((config)=>{
    nprogress.start();
    // 参数为config的回调函数
    // config为配置对象,包含一个中要的属性headers请求头
    return config;
});
requests.interceptors.response.use((res)=>{
    nprogress.done();
    // 响应成功的回调函数
    return res.data;
}, error=>{
    // 响应失败的回调函数
    // 终止promise链
    return Promise.reject(new Error('faile'))
})

export default requests;

通过修改样式文件改变进度条的颜色

#nprogress .bar {
  background: #e1251b;

2 Vuex模块式开发

Vuex是Vue官方提供的一个状态管理库插件,集中式管理项目中共用的数据。具体内容可以参考我之前写的这篇博文:【Vue】Vuex - Tod4 - 博客园

2.1 Vuex的安装

npm i vuex@3

2.2 Vuex的模块化

当项目很大的时候,几百个数据都放在数据仓库的state中就会显得十分臃肿,因此需要采用模块化的方法对这些数据进行模块化管理。

Vuex的模块化,就是将整个的大仓库按照模块划分为小仓库:在store文件夹下建立各模块文件夹和index.js,模块文件夹中的index.js包含模块的state,actions,mutations,getters并将它们都进行默认暴露,最后由总仓库外层index.js整合并暴露为一个vuex.Store。

search模块的index.js:

const actions = {

}
const mutations = {

}
const getters = {

}
const state = {

}

// 将模块仓库内容对外暴露
export default {
    state,
    actions,
    mutations,
    getters
}

home模块的index.js:

const actions = {

}
const mutations = {

}
const getters = {

}
const state = {

}

// 将模块仓库内容对外暴露
export default {
    state,
    actions,
    mutations,
    getters
}

/store/index.js

import vue from 'vue'
import vuex from 'vuex'
import home from './home'
import search from './search'

vue.use(vuex)

export default new vuex.Store({
    modules: {
        home, search
    }
})

3 typeNav三级联动数据动态展示

3.1 组件通知Vuex向服务器发送数据

<script>
export default {
    name: 'TypeNav',
    mounted() {
        // 通知vuex向服务器发送数据,并存储于home数据仓库中
        this.$store.dispatch('CategoryList');
    }
}
</script>

组件向Vuex dispatch CategoryList会调用Vuex actions中的CategoryList()方法

3.2 Vuex actions 通过api中的函数调用,向服务器发送请求,获取服务器数据

const actions = {
    // 通过api中的函数调用,向服务器发送请求,获取服务器数据
    async CategoryList(context) {
        let result = await reqCategoryList();
        if(result.code === 200) {
            context.commit('CATEGORYLIST', result.data)
        }
    }
}

actions方法主要包含处理action和一些服务器请求,因此可以在actions的CategoryList()方法中通过api中的函数调用,向服务器发送请求,获取服务器数据;然后actions commit到mutations将获取到的数据进行修改

此外这里的async和await,是因为axios之前进行了二次封装,当请求成功时会返回一个promise,以此来实现异步操作的执行顺序,因此需要使用await得到promise中成功的数据。具体promise的介绍看一看我之前写的博客。

3.3 Vuex mutations修改仓库数据

const mutations = {
    CATEGORYLIST(state, CateGoryList) {
        state.CateGoryList = CateGoryList;
    }
}

3.4Vuex state仓库的初始化定义

const state = {
    CateGoryList: [],
}

当数据是对象的时候使用{},是数组的时候使用[]

之前自己写的前端数据的axios请求和处理都是放在script中的,写完之后非常地乱自己都不想再看,写完这部分后才明白当数据量较大时,数据请求和处理应该由模块化的vuex完成并且存储于模块化的数据仓库。

3.5 在typeNav中使用辅助函数mapState存储并显示home仓库数据

方式一:直接$store.state.home.xxx

<div class="item-list clearfix">
    <div class="subitem" v-for="(c2, index) in c1.categoryChild" :key="c2.categoryId">
        <dl class="fore">
            <dt>
                <a href="">{{c2.categoryName}}</a>
            </dt>
            <dd>
                <em v-for="(c3, index) in c2.categoryChild" :key="c3.categoryId">
                    <a href="">{{c3.categoryName}}</a>
                </em>
            </dd>
        </dl>
    </div>
</div>

方式二:使用辅助函数mapState生成计算属性

具体mapState的介绍可以参考这篇博客:vuex 中辅助函数mapState的基本用法详解 - 只争朝夕,不负韶华 - 博客园

    computed: {
        // mapState自动生成计算属性
        ...mapState({
            // 当使用cateGroyList时,右边的函数就会执行
            cateGroyList:(state)=>{
                return state.home.CateGoryList
            }
        })
    }

当映射的计算属性的名称与 state 的子节点名称相同时,也即是没有模块化satate的时候,可以直接给 mapState 传一个字符串数组:

...mapState(["count", "name"]),

然后直接使用计算属性即可:

<div class="item" v-for="(c1, index) in CateGoryList" :key="c1.categoryId">
    <h3>
        <a href="">{{c1.categoryName}}</a>
    </h3>
    <div class="item-list clearfix">
        <div class="subitem" v-for="(c2, index) in c1.categoryChild" :key="c2.categoryId">
            <dl class="fore">
                <dt>
                    <a href="">{{c2.categoryName}}</a>
                </dt>
                <dd>
                    <em v-for="(c3, index) in c2.categoryChild" :key="c3.categoryId">
                        <a href="">{{c3.categoryName}}</a>
                    </em>
                </dd>
            </dl>
        </div>
    </div>
</div>

3.6 三级联动动态背景颜色

方式一:使用css样式实现

                    .item:hover{
                        background: skyblue;
                    }

方式二:通过js实现

①用一个变量存储当前鼠标所在的索引值

    data() {
        return {
            curIndex: -1
        }
    },

②使用鼠标事件绑定函数changeIndex

<h3 @mouseenter="changeIndex(index)">
    <a href="">{{c1.categoryName}}</a>
</h3>

③在methods中创建changeIndex方法

methods: {
    changeIndex(index) {
        this.curIndex = index
    }
}

如此一来,当鼠标滑动到一个地方,组件就能够获取到所在的索引

④为索引值为当前索引变量值的行添加cur样式

<div class="item"  v-for="(c1, index) in CateGoryList" :key="c1.categoryId" :class="{cur:index==curIndex}">

⑤添加移出效果

<div @mouseleave="leaveIndex()">

⑥移出函数

    leaveIndex() {
      this.curIndex = -1;
    },

3.5 通过Js控制二三级分类的显示与隐藏

<div class="item-list clearfix" :style="{display:index==curIndex?'block':'none'}">

4 解决卡顿现象

卡顿现象:事件触发非常平凡,而且每一次触发,回调函数都要去执行,如果时间很短,而且回调函数内部有计算,那么可能会使浏览器反应不过来产生卡顿现象。

4.1 函数防抖、节流 lodash插件的使用

Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。中文文档地址:https://www.lodashjs.com/

4.1.1 lodash的安装
npm i --save lodash
4.1.2 lodash的使用

lodash插件里面封装了函数的防抖与节流的业务【闭包+延迟器】,对外暴露_函数。

4.1.3 lodash的防抖函数 _.debounce

防抖:前面的所有触发都会被取消,最后一次执行在规定时间之后才会触发,也就是说连续快速的多次触发,只会执行最后一次。

_.debounce(function, wait),第一个参数为防抖的函数,第二个参数为上面说的规定的时间,返回的函数即为可以防抖的函数。

4.1.3 lodash的节流函数 _.throttle

节流:在规定的时间内不会重复触发回调,只有大于这个时间间隔才会触发函数回调,把频繁触发变为少量触发。

创建一个节流函数,在 wait 秒内最多执行 func 一次的函数。 该函数提供一个 cancel 方法取消延迟的函数调用以及 flush 方法立即调用。 可以提供一个 options 对象决定如何调用 func 方法, options.leading 与|或 options.trailing 决定 wait 前后如何触发。 func 会传入最后一次传入的参数给这个函数。 随后调用的函数返回是最后一次 func 调用的结果。

4.2 实现三级联动的节流

①引入lodash

import _ from 'lodash'

按需引入,throttle采用的默认暴露:

import throttle from "lodash/throttle";

②采用es5的写法对原函数进行节流封装

methods: {
    changeIndex: throttle(function(index){
        this.curIndex = index;
    }, 50),
    leaveIndex() {
      this.curIndex = -1;
    },
  },),

5 typeNav三级联动组件路由跳转和传参

5.1 分析

方式一:对三级标签使用声明式路由导航router-link

router-link会被当做一个组件,当服务器数据返回之后,会循环出很多的router-link组件去创建组件实例,一瞬间创建这么多组件非常消耗内存,可能会造成卡顿现象

方式二:使用编程式路由导航

这样做的缺陷是每一个标签都有自己的回调函数,循环之后可能出现上千个回调函数。

5.2 使用事件委派到父元素 + 编程式导航

①将三级分类元素的事件委派给父元素

<div class="item"  
    v-for="(c1, index) in CateGoryList" :key="c1.categoryId" 
    :class="{cur:index==curIndex}"
    @click="goSearch"
>

但是这样存在着两个问题,一个是点击父元素中的所有元素都会触发事件,而我们只是想让三级分类元素的a标签触发;另一个是这样无法获取元素的参数。

②通过自定义属性筛选三级分类子元素

即只为三级分类元素的a标签添加一个自定义属性cateGoryName

<h3 @mouseenter="changeIndex(index)">
    <a :data-cateGoryName="c1.categoryName">{{c1.categoryName}}</a>
</h3>
<div class="item-list clearfix" :style="{display:index==curIndex?'block':'none'}">
    <div class="subitem" v-for="(c2, index) in c1.categoryChild" :key="c2.categoryId">
        <dl class="fore">
            <dt>
                <a :data-cateGoryName="c2.categoryName">{{c2.categoryName}}</a>
            </dt>
            <dd>
                <em v-for="(c3, index) in c2.categoryChild" :key="c3.categoryId">
                    <a :data-cateGoryName="c3.categoryName">{{c3.categoryName}}</a>
                </em>
            </dd>
        </dl>
    </div>
</div>

然后在goSearch方法中通过dataset获取自定义属性判断有没有cateGoryName的方式,判断元素是不是三级分类的a标签,如此一来便实现了只有三级分类的a标签才能进行路由跳转的功能。

只有使用 data- 声明的自定义属性,dataset才能够检测到

    goSearch(event) {
        let element = event.target;
        let {categoryname} = element.dataset;
        if(categoryname)
            this.$router.push({name:'search'})
    }

自定义属性经过编译之后全部变为了小写,所以这是写的是categoryname

③通过添加自定义属性分别三级分类

通过添加一个自定义属性cateGoryId判断当前是哪一级标签触发了事件。

<h3 @mouseenter="changeIndex(index)">
    <a :data-cateGoryName="c1.categoryName" :data-cateGory1Id="c1.categoryId">{{c1.categoryName}}</a>
</h3>
<div class="item-list clearfix" :style="{display:index==curIndex?'block':'none'}">
    <div class="subitem" v-for="(c2, index) in c1.categoryChild" :key="c2.categoryId">
        <dl class="fore">
            <dt>
                <a :data-cateGoryName="c2.categoryName" :data-cateGory2Id="c2.categoryId">{{c2.categoryName}}</a>
            </dt>
            <dd>
                <em v-for="(c3, index) in c2.categoryChild" :key="c3.categoryId">
                    <a :data-cateGoryName="c3.categoryName" :data-cateGory3Id="c3.categoryId">{{c3.categoryName}}</a>
                </em>
            </dd>
        </dl>
    </div>
</div>

在goSearch方法中进行参数真理和路由转发

goSearch(event) {
      let element = event.target;
      let { categoryname, category1id, category2id, category3id } =
        element.dataset;
      if (categoryname) {
        // 整理参数
        let location = {
            name: 'search',
        };
        let query = {
            categoryname: categoryname
        };
        if (category1id) {
            query.category1id = category1id;
        } else if (category2id) {
            query.category2id = category2id;
        } else {
            query.category3id = category3id;
        }
        location.query = query;
        // 转发路由传递参数
        this.$router.push(location)
      }
    },
posted @ 2022-07-14 10:06  Tod4  阅读(302)  评论(0编辑  收藏  举报