文章目录
- 15.动态展示三级菜单联动
- 15.1动态调用展示三级菜单步骤
- 15.2完成一级菜单鼠标划入显示背景色
- 15.3控制二三级商品分类的显示与隐藏
- 15.4演示卡顿现象引入防抖与节流
- 15.5三级联动组件的路透跳转与传递参数
- 本人其他相关文章链接
15.动态展示三级菜单联动
问题1:
代码:
<script>
export default {name: "TypeNav",computed:{...mapState({categoryList:state=>state.home.categoryList})}
}
</script>
原因分析:因为缺少mapState引入,所以报错mapState不认识,添加引入即可
import { mapState } from "vuex";
15.1动态调用展示三级菜单步骤
简单总结使用步骤:
- 在字模块actions、mutations中定义函数
- 在App.vue中触发调用
- 编写计算属性,这样state中就已经保存最新的数据了
- 在模板代码处遍历动态拼接最新的数据,进行展示
详细使用步骤:
- 第1步:/store/home/index.js引入方法定义actions、mutations中的函数。
import {getCategoryList} from '@/api'
//Home模块的小仓库
//actions代表一系列动作,可以书写自己的业务逻辑,也可以处理异步
const actions = {//getCategoryList返回的是一个Promise对象//需要用await接受成功返回的结果,await必须要结合async一起使用(CP)async getCategoryList(context) {let response = await getCategoryList();if (response.code == 200) {context.commit("GETCATEGORYLIST", response.data)}}
}
//mutations代表维护,操作维护的是state中的数据,且state中数据只能在mutations中处理
const mutations = {GETCATEGORYLIST(state, categoryList) {state.categoryList = categoryList}
}
//state代表仓库中的数据
const state = {//home仓库中存储三级菜单的数据categoryList:[]
}
//getters理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {}//创建并暴露store
export default {actions,mutations,state,getters
}
- 第2步:在App.vue中定义mounted钩子函数,派发一个action,获取商品分类的三级列表的数据。
mounted() {//派发一个action||获取商品分类的三级列表的数据this.$store.dispatch("getCategoryList")}
- 第3步:/components/TypeNav/index.vue,在要使用state数据的地方使用mapState生成计算函数,然后模板中循环遍历使用数据。
<div class="item bo" v-for="(c1,index) in categoryList" :key="c1.categoryId"><h3><a href="">{{c1.categoryName}}</a></h3>
</div>computed:{...mapState({categoryList:state=>state.home.categoryList})}
问题2:为啥actions定义的getCategoryList函数要使用async+await?
答案:因为getCategoryList函数函数最后返回的是一个promise的对象,如图,而我们直接想要的是成功的回调函数,所以用async+await可直接获取到。
15.2完成一级菜单鼠标划入显示背景色
效果如下:
共有2种方式:
- 方式1:css直接使用:hover添加背景色,知识点:hover在鼠标移到链接上时添加的特殊样式
//方式1::hover
.item:hover {background: skyblue;
}
- 方式2:通过定义鼠标划入事件添加背景色,思路说明,每一项都有自己的index,我们初始化currentIndex为-1,当鼠标划入时修改currentIndex的值,当判断currentIndex == index时动态绑定样式,同时搭配@mouseenter和@mouseleave即可实现划入划出的背景效果。
<div @mouseleave="leaveIndex" >
<h3 @mouseenter="changeIndex(index)" :class="{cur: currentIndex == index }">methods: {//用于修改组件实例身上的currentIndex的属性值//当用户鼠标移入到h3身上的时候就会立即出发一次changeIndex(index) {this.currentIndex = index;},//当鼠标离开的时候,让商品分类列表进行隐藏leaveIndex() {this.currentIndex = -1;}
}.cur {background: skyblue;
}
注意点1
:mouseenter:当鼠标移入元素本身(不包含元素的子元素)会触发事件,事件不会叠加。对应的移除事件是 mouseleave。
注意点2
:现在想实现个新效果,鼠标从第一项划入“全部商品分类”也不会去除背景色,具体效果如图,具体方案可采用“事件委派”指的是把事件委派给父亲元素去控制,逻辑就是把“全部商品分类”和一级菜单用一个div单独包起来,在外侧进行划出控制。
15.3控制二三级商品分类的显示与隐藏
共有2种方式:
- 方式1:通过css的:hover控制display的block|none显示与隐藏
.item-list {display: none;...
}
&:hover {.item-list {display: block;}
}
- 方式2:通过js的方式控制display的显示与隐藏
<div class="item-list clearfix" :style="{display: currentIndex == index ? 'block' : 'none'}">
问题:css中的&:hover,中的&啥意思?
答案:
贴个CSS代码进行讲解
.box{&:before{border-color: red;}&:after{border-color: green;}}
& 表示在嵌套层次中回溯一层,即&:before相当于.box:before
15.4演示卡顿现象引入防抖与节流
正常:事件触发非常频繁,而且每一次的触发,回调函数都要去执行(如果时间很短,而回调函数内部有计算,那么很可能出现浏览器卡顿现象),正常效果如图1,错误效果如,2。
节流:在规定的间隔时间范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发【给浏览器有充裕的时间解析代码】。
防抖:前面的所有的触发都被取消,最后一次执行在规定的时间之后才会触发,也就是说如果连续快速的触发只会执行一次。
注意点0:
安装命令:cnpm install --save lodash
使用方式:
//引入lodash:是把lodash全部封装好的函数全都引入进来了
//按需引入:只是引入节流函数,其他的函数没有引入(模块),这样做的好处是,当你打包项目的时候体积会小一些
import throttle from "lodash/throttle";changeIndex: throttle(function(index) {//修改当前currentIndex索引值//函数节流:在20MS时间之内只能执行一次this.currentIndex = index;}, 20),
注意点1
:可使用lodash插件:里面封装函数的防抖与节流的业务方法【闭包+延迟器】
注意点2
:防抖与节流区别是啥?
答案:“节流”是规定时间内只执行一次,而“防抖”是无论多长时间我都是最后执行一次。
注意点3
:lodash官网:https://www.lodashjs.com/
防抖:debounce(func, [wait=0], [options=])
节流:throttle(func, [wait=0], [options=])
15.5三级联动组件的路透跳转与传递参数
三级联动用户可以点击的:一级分类、二级分类、三级分类,当你点击的时候Home模块跳转到Search模块,一级会把用户选中的产品(产品名字,产品ID)在路由跳转的时候,进行传递。
路由跳转:
-
声明式导航:router-link
-
编程式导航:push|replace
问题1:选用“声明式导航”还是“编程式导航“?
答案:应该选用编程式导航。如果使用声明式导航router-link,可以实现路由的跳转与传递参数,但是需要注意,会出现卡顿现象,因为三级联动菜单是循环遍历产生的,如果循环遍历1000次那得新生成/标签,很占用内存,肯定会卡顿。
问题2:既然选择”编程式导航“,那么是/标签绑定点击事件?还是采用其他方式?
答案:不能采用a标签绑定事件方式,应该采用“父元素委托事件”方式。因为这样只需定义一次函数即可,而采用a标签我还得循环遍历定义1000次函数。
问题3:我们想要效果是点击a标签才跳转路由,如何确认a标签?
答案:给a标签绑定自定义属性,使用event.target.dataset可以获取自定义属性名称,能获取自定义属性名称的一定是我们要的a标签。
问题4:采用“父元素委托事件”方式后,会出现如图错误效果:当点击1级菜单时 -> 先跳转到搜索页 -> 又自动跳转到首页
答案:把/标签的href属性必须删掉
问题5:自定义属性名为啥叫 data-xxx,输出打印的dataset名称全是小写,但是为啥前面的data-没了?举例说明自定义属性名称叫:data-categoryName,为啥最后dataset获取的名称却是categoryname,明显前缀data-没了,而且莫名变成小写了?
答案:JS中要想使用dataset获取自定义属性,那么在HTML模板中自定义属性名必须叫“data-xxx”。
下方为别人博客摘抄:
HTML5 中添加了 data-* 的方式来自定义属性,实际上就是在自定义属性名前加上前缀data- 即可,使用这样的结构可以进行数据存放。使用data-* 可以解决自定义属性混乱无管理的现状。
一个自定义一旦加上了前缀 data- ,那么在 JS 中就可以通过 elementNodeObject.dataset拿到这个属性,显然 dataset 是 attribute 集合的一个子集。
dataset 属性的值是 DOMStringMap的一个实例,也就是一个名值对儿的映射。在这个映射中,每个 data-name 形式的属性都会有一个对应的属性,只不过没有 data- 前缀。
完整代码 src/commponents/TypeNav/index.vue
<!--1级联动菜单--><div class="sort"><div class="all-sort-list2" @click="goSearch"><div class="item bo" v-for="(c1,index) in categoryList" :key="c1.categoryId"><h3 @mouseenter="changeIndex(index)" :class="{cur: currentIndex == index }"><a :data-categoryName="c1.categoryName" :data-category1Id="c1.categoryId">{{c1.categoryName}}</a></h3><!--方式2:通过js的方式控制display的显示与隐藏--><div class="item-list clearfix" :style="{display: currentIndex == index ? 'block' : 'none'}"><div class="subitem"><!--2级联动菜单--><dl class="fore" v-for="(c2,index) in c1.categoryChild" :key="c2.categoryId"><dt><a :data-categoryName="c2.categoryName" :data-category2Id="c2.categoryId">{{c2.categoryName}}</a></dt><dd><!--3级联动菜单--><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></div></div></div>
------------------------------------------------------------------------------------------------------
//进行路由跳转的回调函数goSearch(event) {//最好的解决方案:编程式导航+事件委派//存在一些问题:事件委派,是把全部的子节点【h3、dt、dl、em】的事件委派给父亲节点//问题1:点击a标签的时候,才会进行路由跳转【怎么能确认点击的一定是a标签?】//答案:给a标签绑定自定义data-categoryName属性,只要能获取到自定义属性就代表是a标签//event.target:获取到的是触发事件的元素(div、h3、a、em、dt、dl)let element = event.target;//节点有一个属性dataset,可以过去节点的自定义属性与属性值let {categoryname, category1id, category2id, category3id} = element.dataset;//如果标签身上带有categoryname一定是a标签,且当前这个if语句:一定是a标签才会进入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属性location.query = query;//路由跳转this.$router.push(location);}}
问题6:Vue项目启动默认打开http://0.0.0.0:8080,浏览器显示无法访问,而输入http://localhost:8080就能展示页面
解决方案:在vue.config.js文件中新增一行:host: 'localhost’即可。
module.exports = {productionSourceMap:false,// 关闭ESLINT校验工具lintOnSave: false,devServer: {//代理服务器解决跨域proxy: {"/api": {target: "http://39.98.123.211:8510"},},//解决默认打开浏览器,会出现0.0.0.0:8080,浏览器显示无法访问host: 'localhost'}
};
本人其他相关文章链接
1.vue尚品汇商城项目-day02【9.Home组件拆分+10.postman测试接口】
2.vue尚品汇商城项目-day02【vue插件-13.nprogress进度条的使用】
3.vue尚品汇商城项目-day02【11.对axios二次封装+12.接口统一管理】
4.vue尚品汇商城项目-day02【14.vuex状态管理库】
5.vue尚品汇商城项目-day02【15.动态展示三级菜单联动】