文章目录
1. NavigationDuplicated错误
- 声明式导航没有这类问题,因为vue-router底层已经处理好了
- 为什么编程式导航进行路由跳转的时候,就有这种错误警告
- 最新的vue-router引入了promise
- 通过push方法传递相应的成功、失败回调函数,可以解决
this.$router.push({
name:"search",
params:{
keyword:this.keyword
},
query:{
k:this.keyword.toUpperCase()
}
},()=>{},()=>{})
- 通过底部的代码。可以实现解决错误,但是治标不治本,将来在别的组件当中,push | replace,编程式导航还是有类似的错误
- this是当前组件实例(search),
- this.$router属性:当前这个属性,属性值是VueRouter类的一个实例,当入口文件注册路由时,给组件实例添加$router | $route 属性
- push:VueRouter类的一个方法
- 重写vue-router 3.5.1的push方法
2. Home模块组件拆分
先把静态页面完成 --> 拆分出静态组件 --> 获取服务器的数据进行展示 --> 动态业务
2.1 三级联动组件完成
- 由于三级联动在Home、Search、Detail都出现了,所以把三级联动注册为全局组件(在main.js只需要注册一次,就可以在羡慕任意地方使用)
// 三级联动组件--全局组件
import TypeNav from '@/pages/Home/TypeNav'
// 第一个参数:全局组件的名字,第二个参数:哪一个组件
Vue.component(TypeNav.name,TypeNav)
2.2 其余组件完成
- 注意结构、样式、图片资源
3. 接口测试
- 经过POSTMA接口测试,接口没有问题
- 如果服务器返回的数据code字段200,代表服务器返回数据成功
- 整个项目,接口前缀都有api字样
4. axios二次封装
-
XMLHttpRequest、fetch、JQ、axios都可以传递数据
-
为什么要对axios进行二次封装
- 为了请求拦截器、响应拦截器:前者可以在发请求之前处理一些业务,后者在当服务器返回以后,可以处理一些事情
-
安装axios:
npm install --save axios
-
在项目当中经常存在API文件夹,主要用于存放axios,在src目录下创建api文件夹
-
接口当中,路径都带有/api,所以baseURL就是在发请求的时候都带上/api,就不用自己书写了
-
可以参考git | NPM关于axios的文档
// 对于axios进行二次封装
import axios from "axios"
// 1.利用axios对象的方法create。去创建一个axios实例
// 2.request就是axios,只不过稍微配置一下
const requests = axios.create({
// 配置对象
// 基础路径,发请求的时候,路径当中会出现api
baseURL:"/api",
// 代表请求超时时间为5秒,若在五秒之内没有响应则请求失败
timeout:5000,
})
// 请求拦截器:在发请求之前,请求拦截器可以检测到,可以在请求发出去之前做一些事情
requests.interceptors.request.use((config)=>{
// config:配置对象,对象里面有个属性很重要:header请求头
return config
});
// 响应拦截器
requests.interceptors.response.use((res)=>{
// 成功的回调函数,服务器响应数据回来以后,响应拦截器可以检测到,可以做一些事情
return res.data
},(error)=>{
// 响应失败的回调函数
return Promise.reject(new Error('fail'))
});
// 对外暴露
export default requests
5. 接口统一管理
- 项目小:完全可以在组件的生命周期函数中发请求
- 项目大:需要统一管理
-
跨域问题:
- 协议、域名、端口号不同请求,称之为跨域
- http://localhost:8080/#/home 前端项目本地服务器
- http://gmall-h5-api.atguigu.cn 后台服务器
- 解决办法:JSONP、CROS、代理
-
代理配置:在vue.config.js中配置
// 代理跨域 devServer:{ proxy:{ '/api':{ target:'http://gmall-h5-api.atguigu.cn', // pathRewrite:{'^/api':''} } } }
6. nprogress的使用
-
安装:
npm i --save nprogress
-
start:进度条开始,done:进度条结束
-
在api/request.js里引用
// 引入进度条 import nProgress from "nprogress"; // 引入进度条的样式 import "nprogress/nprogress.css" // 请求拦截器 requests.interceptors.request.use((config)=>{ // config:配置对象,对象里面有个属性很重要:header请求头 // 进度条开始动 nProgress.start(); return config }); // 响应拦截器 requests.interceptors.response.use((res)=>{ // 成功的回调函数,服务器响应数据回来以后,响应拦截器可以检测到,可以做一些事情 // 进度条结束 nProgress.done(); return res.data },(error)=>{ // 响应失败的回调函数 return Promise.reject(new Error('fail')) });
-
修改样式可以到module里的nprogress的nprogress.css里修改
7. vuex状态管理库
- vuex是什么?
-
vuex是官方提供的一个插件,状态管理库,集中式管理项目中组件共用的资源
-
-
并不是所有的项目都用vuex,项目很大、组件很多、数据维护很费劲才使用
-
state、mutations、actions、getters、modules
-
安装vuex:
npm i --save vuex@3
-
- vuex的基本使用
- src目录下创建store文件夹
- 在main.js里引入仓库
- vux实现模块式开发
- 如果项目过大,组件过多,接口很多,数据也很多,可以让vux实现模块化开发
// 引入小仓库 import home from './home' import search from './search' // 对外暴露store类的一个实例 export default new Vuex.Store({ // 实现vuex仓库模块式开发存储数据 modules:{ home,search } })
8.完成TypeNav三级联动展示数据业务
// Components/TypeNav/index.vue
import { mapState } from 'vuex'
export default {
name: 'TypeNav',
// 组件挂载完毕:可以向服务器发请求
mounted() {
// 通知vuex发送请求,获取数据,存储于仓库当中
this.$store.dispatch('categoryList')
},
computed: {
// 对象的写法右侧需要的是一个函数,当使用这个计算属性的时候,右侧函数会立即执行一次
// 注入一个参数state,其实即为大仓库中的数据
...mapState({
categoryList: state => state.home.categoryList
})
}
}
// store/home/index.js
// search模块的小仓库
import {reqCategoryList} from '@/api'
const state = {
// state中数据默认初始值别瞎写,服务器返回的数据是什么类型,就写什么类型【根据接口的返回值初始化】
categoryList:[]
}
const mutations = {
CATGORYLIST(state,categoryList){
state.categoryList = categoryList;
}
}
const actions = {
// 通过API里面的接口函数调用,向服务器发送请求,获取服务器的数据
async categoryList({commit}){
let result = await reqCategoryList()
// console.log(result);
if(result.code == 200){
commit("CATGORYLIST",result.data)
}
}
}
8.1 完成一级分类动态添加背景颜色
- 采用样式完成
- hover
- 通过js完成:通过响应式数据控制谁该显示+事件的委派(委托给父元素)
data() {
return {
// 存储用户鼠标移到哪一个一级分类
currentIndex: -1,
}
},
methods: {
// 鼠标进入修改响应式数据currentIndex的数据
changeIndex(index) {
// index:鼠标移上某一个一级分类的元素的索引值
this.currentIndex = index
},
// 一级分类鼠标移除的事件回调
leaveIndex() {
// 鼠标移除,currentIndex变为-1
this.currentIndex = -1
}
},
8.2 通过js控制二三级分类的显示与隐藏
- 最开始的时候通过css样式display:block | none隐藏二三级商品分类
<div class="item-list clearfix" :style="{display:currentIndex==index?'block':'none'}">
8.3 演示卡顿现象+防抖与节流
-
正常:事件触发非常频繁,而且每一次的触发,回调函数都要去执行(如果时间很短,而回调函数内部有计算,那么很可能出现浏览器卡顿)
-
节流:在规定的间隔时间范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发(技能cd)
-
lodash插件:_.throttle函数(function(){ } , time}
// 引入方式:_ from 'lodash' 是把lodash全部功能函数引入 // 最好的引用方式是按需加载 import throttle from 'lodash/throttle' // 鼠标进入修改响应式数据currentIndex的数据 // throttle回调函数别用箭头函数,可能出现上下文this的问题 changeIndex:throttle(function(index){ // index:鼠标移上某一个一级分类的元素的索引值 this.currentIndex = index // 正常情况下(用户慢慢操作):鼠标进入,每一个一级分类h3,都会触发鼠标进入事件 // 非正常情况下(用户操作很快):本身全部的一级分类都应该触发鼠标进入事件,但经过调试,只有部分h3触发了 // 就是由于用户的行为过快,导致浏览器反应不过来,如果当前回调函数中有一些大量业务,有可能会出现卡顿现象 console.log(index); },50),
-
-
防抖:前面的所有的触发都被取消,最后一次执行在规定的时间之后才会触发,也就是说如果连续快速的触发只会执行一次(回城)
- lodash插件:_.debounce函数(function(){ } , time}
9. 三级联动路由跳转与传参
-
分析
-
用户点击三级分类,由home模块跳转到search模块,一级会把用户选中的产品(产品的名字和id),在路由跳转的时候进行传递
-
路由跳转的方式:声明式导航router-link,编程式导航push | replace
- 如果使用声明式导航router-link,可以实现路由的跳转和传递参数,但出现了卡顿现象
- router-link是一个组件,当服务器的数据返回之后,循环出很多routerlink组件,创建组件实例的时候,一瞬间创建1000+会很消耗内存,会出现卡顿现象
-
最好的解决办法:事件的委派 + 编程式导航
- 存在的问题:
- 事件的委派,是把全部的子节点(h3,dt,dl,em)的事件委派给父节点,点a标签的时候才会进行路由跳转,如何确定点击的一定是a标签
- 即使确定点击的是a表情,如何确定是一级、二级还是三级
- 存在的问题:
-
-
代码实现
- 第一个问题:把子节点中a标签加上自定义属性data-categoryName,其余的子节点没有
<a :data-categoryName="c1.categoryName" :data-category1Id="c1.categoryId">{{ c1.categoryName }}</a>
- 第二个问题:给a标签加上data-categoryId
goSearch(event){ // 把子节点中a标签加上自定义属性data-categoryName,其余的子节点没有 let element = event.target; // console.log(element); // 获取到当前触发这个事件的节点(h3、a、dt、dl),需要带有data-categoryname这样节点的一定是a标签 // 节点有一个属性dataset属性,可以获取节点的自定义属性与属性值 // 解构出来 let {categoryname,category1id,category2id,category3id} = element.dataset; // 如果标签上有categoryname属性,一定是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 } // 此时query和location是两个参数 需要整理参数 location.query = query // 路由跳转 this.$router.push(location) } }
10. search页面的三级菜单显示及动画
-
给三级菜单的父组件添加mouseleave和mouseenter函数
leaveIndex() { // 鼠标移除,currentIndex变为-1 this.currentIndex = -1 // 当鼠标离开的时候,让商品分类列表进行隐藏 if (this.$route.path != '/home') { this.show = false } }, // 当鼠标移入的时候,让商品分类列表进行展示 enterShow() { this.show = true }
-
过度动画:前提组件 | 元素务必要有v-if 或 v-show指令才可以进行过度动画
- 将v-show所在的组件用transition包裹
// 过度动画的样式 // 过度动画的开始状态(进入) .sort-enter{ height: 0px; } // 过度动画的结束状态(进入) .sort-enter-to{ height: 461px; } // 定义动画的时间和速率 .sort-enter-active{ transition: all .3s linear; }
-
优化:每次页面跳转都要请求一次nav,所以将请求nav放到根组件App.vue的mounted里面,只需要执行一次
- 注意:不能放到main.js上,因为main.js不是一个组件,不能使用this.$store
mounted() { // 通知vuex发送请求,获取商品分类的三级列表数据,存储于仓库当中 this.$store.dispatch('categoryList') },