vuex 模块式开发
vuex 是官方提供的一个插件,状态管理库,集中式管理项目中组件共用的数据。
如果项目很小,完全不需要 vuex
如果项目很大,组件很多、数据很多、数据维护很费劲,就用 vuex
安装 vuex
npm install --save vuex
vuex的基本使用
在 src 文件夹下创建 store 文件夹,并在里面创建 home 文件夹、search 文件夹和相应的 index.js 文件
home/index.js
//home 模块的小仓库
//state:仓库存储数据的地方
const state = {};
//mutations:修改state的唯一手段
const mutations = {};
//actions:处理action,可以书写自己的业务逻辑,也可以处理异步
const actions = {};
//getters:理解为计算属性,用于简化仓库数据,让组件获取仓库数据更加方便
const getters = {};
export default{
state,
mutations,
actions,
getters
}
search/index.js
//search 模块的小仓库
const state = {}
const mutations = {}
const actions = {}
const getter = {}
export default{
state,
mutations,
actions,
getter
}
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
//需要使用插件一次
Vue.use(Vuex);
//引入小仓库
import home from './home'
import search from './search'
//对外暴露 Store 类的一个实例
export default new Vuex.Store({
//实现Vuex仓库模块式开发存储数据
modules:{
home,
search
}
})
打开 main.js 把 store仓库 引入
为了规范写法,我们将 pages > Home >TypeNav 文件夹移动到 conponents 文件夹里
ps:全局组件放在 components 文件夹里
同时将 main.js 里引入 TypeNav 的路径改为
import TypeNav from '@/components/TypeNav'
实现 三级联动组件 TypeNav 动态渲染数据
将 请求到的数据放到 store > home 模块的小仓库中
TypeNav / index.vue
home / index.js
//home 模块的小仓库
import { reqCategoryList } from "@/api";
//state:仓库存储数据的地方
const state = {
//根据接口返回值初始化,返回的是对象,就是{} 返回的是数组,就是[]
categoryList:[]
};
//mutations:修改state的唯一手段
const mutations = {
CATEGORYLIST(state,categoryList){
state.categoryList = categoryList
}
};
//actions:处理action,可以书写自己的业务逻辑,也可以处理异步
const actions = {
//通过API里面的接口函数调用,向服务器发送请求,获取服务器的数据
async categoryList({commit}){
let result = await reqCategoryList()
if(result.code==200){
commit("CATEGORYLIST",result.data)
}
}
};
//getters:理解为计算属性,用于简化仓库数据,让组件获取仓库数据更加方便
const getters = {};
export default{
state,
mutations,
actions,
getters
}
数据拿到之后就可以渲染页面了
TypeNav / index.vue
<template>
<!-- 商品分类导航 -->
<div class="type-nav">
<div class="container">
<div @mouseleave="leaveIndex">
<h2 class="all">全部商品分类</h2>
<div class="sort">
<div class="all-sort-list2">
<div class="item" v-for="(c1,index) in categoryList" :key="c1.categoryId" :class="{cur:currentIndex==index}">
<h3 @mouseenter="changeIndex(index)">
<a href="">{{c1.categoryName}}</a>
</h3>
<div class="item-list clearfix">
<div class="subitem" v-for="c2 in c1.categoryChild" :key="c2.categoryId">
<dl class="fore">
<dt>
<a href="">{{c2.categoryName}}</a>
</dt>
<dd>
<em v-for="c3 in c2.categoryChild" :key="c3.categoryId">
<a href="">{{c3.categoryName}}</a>
</em>
</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
</div>
<nav class="nav">
<a href="###">服装城</a>
<a href="###">美妆馆</a>
<a href="###">尚品汇超市</a>
<a href="###">全球购</a>
<a href="###">闪购</a>
<a href="###">团购</a>
<a href="###">有趣</a>
<a href="###">秒杀</a>
</nav>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name:'TypeNav',
data(){
return{
//存储用户鼠标移上哪一个一级分类
currentIndex:-1
}
},
//组件挂载完毕:可以向服务器发送请求
mounted(){
//通知Vuex发请求,获取数据,存储于仓库当中
this.$store.dispatch('categoryList')
},
computed:{
...mapState({
//右侧需要的是一个函数,当使用这个计算属性的时候,右侧函数会立即执行一次
//注入一个参数 state ,其实即为大仓库中的数据
categoryList:state=>state.home.categoryList
})
},
methods:{
//鼠标进入 修改响应式数据 currentIndex 属性
changeIndex(index){
//index:鼠标移上某一个一级分类的元素的索引值
this.currentIndex = index
},
//一级分类鼠标移除的时间回调
leaveIndex(){
//鼠标移出 currentIndex ,变为-1
this.currentIndex = -1
}
}
}
</script>
<style scoped lang="less">
.type-nav {
border-bottom: 2px solid #e1251b;
.container {
width: 1200px;
margin: 0 auto;
display: flex;
position: relative;
.all {
width: 210px;
height: 45px;
background-color: #e1251b;
line-height: 45px;
text-align: center;
color: #fff;
font-size: 14px;
font-weight: bold;
}
.nav {
a {
height: 45px;
margin: 0 22px;
line-height: 45px;
font-size: 16px;
color: #333;
}
}
.sort {
position: absolute;
left: 0;
top: 45px;
width: 210px;
height: 461px;
position: absolute;
background: #fafafa;
z-index: 999;
.all-sort-list2 {
.item {
h3 {
line-height: 30px;
font-size: 14px;
font-weight: 400;
overflow: hidden;
padding: 0 20px;
margin: 0;
a {
color: #333;
}
}
.item-list {
display: none;
position: absolute;
width: 734px;
min-height: 460px;
background: #f7f7f7;
left: 210px;
border: 1px solid #ddd;
top: 0;
z-index: 9999 !important;
.subitem {
float: left;
width: 650px;
padding: 0 4px 0 8px;
dl {
border-top: 1px solid #eee;
padding: 6px 0;
overflow: hidden;
zoom: 1;
&.fore {
border-top: 0;
}
dt {
float: left;
width: 54px;
line-height: 22px;
text-align: right;
padding: 3px 6px 0 0;
font-weight: 700;
}
dd {
float: left;
width: 415px;
padding: 3px 0 0;
overflow: hidden;
em {
float: left;
height: 14px;
line-height: 14px;
padding: 0 8px;
margin-top: 5px;
border-left: 1px solid #ccc;
}
}
}
}
}
&:hover {
.item-list {
display: block;
}
}
}
.cur{
background-color: skyblue;
}
}
}
}
}
</style>
引入防抖与节流
防抖:就是规定在一段时间内,事件只执行一次,如果在规定的时间里面,再次触发该事件,那么将会重新计时
例子:老师告诉学生们,如果在一个星期内不会上课睡觉,就奖励一颗棒棒糖,如果在这段时间内,有人违规了,那么就从这一刻开始重新计时,过一个星期就可以领到棒棒糖了
节流:规定在一段时间内执行一次
例子:老师告诉学生们,一周内在课堂上只允许放一次影片观看,如果在这一周内观看了一次影片,那么接下来学生观看电影的请求,老师都不会答应的
一级,二级,三级分类的跳转
我们采用编程式导航 + 事件委托 来进行路由跳转
存在的一些问题:事件委托,是把全部的子节点【h3,dt,dl,em】的事件委托给父亲节点
点击 a 标签的时候,才会进行路由跳转【怎么确定点击的一定是a标签】
存在另一个问题:即使你能确定点击的是a标签,如何区分是一级、二级、三级分类的标签
我们的解决方式如下图:
开发Search模块中的TypeNav商品分类菜单(过渡动画效果)
在Home路由页面 商品分类是默认打开的
在Search路由页面 商品分类是默认关闭的
实现,如下图:
在Search页面时,鼠标移入商品分类,展开,移出时,则关闭 且不影响Home页面
实现,如下图:
添加过渡动画
实现,如下图:
TypeNav商品分类列表的优化
因为 Home 组件和 Search 组件都用到 TypeNav 组件了
所以会导致重复的向服务器发送两次请求
优化方式:在根组件发送请求
TypeNav
这行代码剪切到根组件(App.vue)