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:代表请求超时的时间5S
timeout: 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,提交mutation
async navList({
commit
}) {
// 调用reqNavList函数
let result = await reqNavList();
if (result.code == 200) {
// 提交的数据是result.data
commit("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-->
<div
class="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">
<!-- 二级分类 -->
<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>
</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发请求,获取数据,存储于仓库当中
// 派发action
this.$store.dispatch("navList");
},
// 映射为组件身上的一个实例对象,计算属性
computed: {
...mapState({
// 属性名:属性值(是一个函数)
// 右侧为啥是一个函数呢?当使用这个计算属性的时候,右侧的函数会立即执行一次,会注入一个参数state,state即为大仓库中的数据
// 箭头函数的简写形式,去掉return,去掉花括号
navList: (state) => state.home.navList,
}),
},
methods: {
// 鼠标进入修改响应式数据currentIndex
changeIndex(index) {
// index:鼠标移上某一级分类的元素的索引值
this.currentIndex = index;
},
leaveIndex() {
// index:鼠标移出的时候currentIndex,变为-1
this.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发请求,获取数据,存储于仓库当中
// 派发action
this.$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,变为-1
this.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-->
<div
class="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">
<!-- 二级分类 -->
<div
class="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);
}
},