1.Home模块组件拆分
- 先把静态页面完成
- 拆分出静态组件
- 获取服务器的数据进行展示
- 动态业务
在这里插入图片描述
导航栏这儿是个二级联动,拆分为全局组件,可以在项目的任意地方使用
import TypeNav from '@/pages/Home/TypeNav';
// 第一个参数:全局组件的名字,第二个参数:哪一个组件
Vue.component(TypeNav.name,TypeNav);
2.axios二次封装
XMLHttpRequest、Fetch、JQ、axios
为什么需要进行二次封装?
请求拦截器:可以在发请求之前处理一些业务
响应拦截器:当服务器数据返回以后,可以处理一些事情
cnpm install --save axios
api文件夹放axios请求
// 对于axios进行二次封装
import axios from 'axios';
// 请求和响应拦截器
// 1:利用axios对象的方法create,去创建一个axios实例
// 2:request就是axios,只不过我们可以自己配置一些属性
const requests = axios.create({// 创建配置对象// baseURL:基础路径,发请求的时候都带上/api,就不用自己手动写了baseURL: "/api",// timeOut:代表请求超时的时间5Stimeout: 5000,
})
// 拦截器(interceptors)
// 请求拦截器:在发请求之前可以监测到,可以做一些事情
requests.interceptors.request.use((config) => {// config:配置对象,对象里面有一个属性很重要,是headers请求头return config;
});
// 响应拦截器:成功的回调,会返回服务器带来的数据;失败的回调,终止promise链
requests.interceptors.response.use((res) => {return res.data;
}, (error) => {return Promise.reject(new Error('fail'));
});// 对外暴露
export default requests;
3.接口统一管理
- 项目很小:完全可以组件的生命周期函数中发请求,在mounted或者created中发请求,获取到服务器的数据,存储到data中。
3.1跨域问题
跨域:协议、域名、端口号不同请求,称之为跨域
办法:JSONP、CROS、代理
4.nprogress进度条插件的使用
cnpm i --save nprogress
5.vuex状态管理库
state
mutations
actions
getters
modules
import Vue from 'vue';
import Vuex from 'vuex';
// 需要使用插件一次
Vue.use(Vuex);
// state:仓库存储数据的地方
const state = {};
// mutations:修改state的唯一手段
const mutations = {};
// actions:处理actions,可以书写自己的业务逻辑,处理异步任务
const actions = {};
// getters:理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {};// 对外暴露store类的一个实例
export default new Vuex.Store({// Vuex的配置对象,key值不能瞎写state,mutations,actions,getters
});
注意vuex是3版本
5.1vuex实现模块式开发
如果项目过大,组件过多,接口也很多,数据也很多,如果有100个模块,state数据太多
模拟state数据
{count:1,search:{a:1},detail:{xxx},pay:{xxx},
}
大仓库拆分出许多小仓库
// 引入请求
import { reqNavList } from '@/api'
// home模块的小仓库
// state:仓库存储数据的地方
const state = {// state的初始值:根据接口的返回值写,是数组,还是对象navList: [],
};
// mutations:修改state的唯一手段
const mutations = {NAVLIST(state, navList) {// 修改state中navList的数据state.navList = navList;}
};
// actions:处理actions,可以书写自己的业务逻辑,处理异步任务
const actions = {// 通过API里面的接口函数,向服务器发请求,获取服务器的数据// 解构commit,提交mutationasync navList({commit}) {// 调用reqNavList函数let result = await reqNavList();if (result.code == 200) {// 提交的数据是result.datacommit("NAVLIST", result.data);}}
};
// getters:理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {};
export default {// Vuex的配置对象,key值不能瞎写state,mutations,actions,getters
}
三级分类
<div class="sort"><div class="all-sort-list2"><!-- 一级分类【替换】 --><!-- c1是一级分类的对象,index是索引值,navList是计算属性的属性,key值是找id--><div class="item" v-for="(c1,index) in navList" :key="index"><h3><a href="">{{ c1.categoryName }}</a></h3><div class="item-list clearfix"><!-- 二级分类 --><div class="subitem" v-for="(c2,index) in c1.categoryChild" :key="index"><dl class="fore"><dt><a href="">{{c2.categoryName}}</a></dt><dd ><em v-for="(c3,index) in c2.categoryChild" :key="index"><a href="">{{c3.categoryName}}</a></em></dd></dl></div></div></div></div></div>
6.完成一级分类动态添加背景颜色
给父盒子添加离开函数,事件委派
事件委派:指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件
<template><!-- 商品分类导航 --><div class="type-nav"><div class="container"><div @mouseleave="leaveIndex"><h2 class="all">全部信息分类</h2><div class="sort"><div class="all-sort-list2"><!-- 一级分类【替换】 --><!-- c1是一级分类的对象,index是索引值,navList是计算属性的属性,key值是找id--><divclass="item"v-for="(c1, index) in navList":key="index":class="{ cur: currentIndex == index }"><h3 @mouseenter="changeIndex(index)"><a href="">{{ c1.categoryName }}</a></h3><div class="item-list"><!-- 二级分类 --><divclass="subitem"v-for="(c2, index) in c1.categoryChild":key="index"><dl class="fore"><dt><a href="">{{ c2.categoryName }}</a></dt><!-- 三级分类 --><dd><em v-for="(c3, index) in c2.categoryChild" :key="index"><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>
// 开始从仓库中捞数据
// mapState辅助函数
import { mapState } from "vuex";
export default {name: "TypeNav",data() {return {currentIndex: -1,};},mounted() {// 通知Vuex发请求,获取数据,存储于仓库当中// 派发actionthis.$store.dispatch("navList");},// 映射为组件身上的一个实例对象,计算属性computed: {...mapState({// 属性名:属性值(是一个函数)// 右侧为啥是一个函数呢?当使用这个计算属性的时候,右侧的函数会立即执行一次,会注入一个参数state,state即为大仓库中的数据// 箭头函数的简写形式,去掉return,去掉花括号navList: (state) => state.home.navList,}),},methods: {// 鼠标进入修改响应式数据currentIndexchangeIndex(index) {// index:鼠标移上某一级分类的元素的索引值this.currentIndex = index;},leaveIndex() {// index:鼠标移出的时候currentIndex,变为-1this.currentIndex = -1;},},
};
</script>
7.通过js控制二三级商品分类的显示与隐藏
v-show=“currentIndex == index”
去掉css的display:none
8.完成三级联动节流的操作
依赖lodash插件
// 默认暴露的,不用加小括号{throttle}
import throttle from “lodash/throttle”;
changeIndex: throttle(function (index) {
// index:鼠标移上某一级分类的元素的索引值
this.currentIndex = index;
}, 50),
<script>
// 开始从仓库中捞数据
// mapState辅助函数
import { mapState } from "vuex";
// 引入lodash:是把lodash全部功能函数引入
// 最好的引入方式:按需加载
// 默认暴露的,不用加小括号{throttle}
import throttle from "lodash/throttle";
export default {name: "TypeNav",data() {return {currentIndex: -1,};},mounted() {// 通知Vuex发请求,获取数据,存储于仓库当中// 派发actionthis.$store.dispatch("navList");},// 映射为组件身上的一个实例对象,计算属性computed: {...mapState({// 属性名:属性值(是一个函数)// 右侧为啥是一个函数呢?当使用这个计算属性的时候,右侧的函数会立即执行一次,会注入一个参数state,state即为大仓库中的数据// 箭头函数的简写形式,去掉return,去掉花括号navList: (state) => state.home.navList,}),},methods: {// es5的写法,kv形式changeIndex: throttle(function (index) {// index:鼠标移上某一级分类的元素的索引值this.currentIndex = index;}, 50),leaveIndex() {// index:鼠标移出的时候currentIndex,变为-1this.currentIndex = -1;},},
};
</script>
9.三级联动组件的路由跳转与传递参数
Home模块跳转到seekfamily模块:
- 一级会把用户选择的名字在路由跳转的时候进行传递
-
path: “/SeekFamily/:keyword?“加问号,三级联动就可以直接跳转
< router-link to=”/SeekFamily”>{{ c1.categoryName }}< /router-link > - 但是容易出现卡顿的现象
利用事件委派存在一些问题:
4. 怎么确定点击一定是a标签
5. 路由跳转需要参数,如何获取
事件委派+编程式导航
利用自定义属性
如果a标签有data-categoryName一定就是a标签,其余子节点是没有的
HTML允许自定义属性以data-开头即为自定义属性
<div class="all-sort-list2" @click="goSeek"><!-- 一级分类【替换】 --><!-- c1是一级分类的对象,index是索引值,navList是计算属性的属性,key值是找id--><divclass="item"v-for="(c1, index) in navList":key="index":class="{ cur: currentIndex == index }"><h3 @mouseenter="changeIndex(index)"><a:data-categoryName="c1.categoryName":data-category1Id="c1.categoryId">{{ c1.categoryName }}</a></h3><!-- 二、三级分类 --><div class="item-list" v-show="currentIndex == index"><!-- 二级分类 --><divclass="subitem"v-for="(c2, index) in c1.categoryChild":key="index"><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="index"><a:data-categoryName="c3.categoryName":data-category3Id="c3.categoryId">{{ c3.categoryName }}</a></em></dd></dl></div></div></div></div>
goSeek(event) {// 最优解:编程式导航+事件委派let element = event.target;// 节点有一个属性dataset,可以获取节点的自定义属性和属性值// 浏览器自动识别data-xxx,注意获取到的xxx都是小写let { categoryname, category1id, category2id, category3id } =element.dataset;if (categoryname) {// 一级分类、二级分类、三级分类的判断// 整理路由跳转的参数let location = { name: "SeekFamily" };// query参数需要动态添加,没办法区分一级、二级、三级let query = { categoryName: categoryname };if (category1id) {query.category1Id = category1id;} else if (category2id) {query.category2Id = category2id;} else {query.category3Id = category3id;}console.log(location,query);// 整理完参数location.query = query;// 路由跳转this.$router.push(location);}},