项目中提到的知识点需要熟悉,非常大的概率会被问到
为什么使用vuex的模块开发
根据不同的业务,将该业务state、mutations、action、getters 的封装在一个js文件中,让多种数据分类更加明确,代码更好维护。 --> 将store大仓库分为一个一个业务(功能) 的小仓库
vuex模块化开发的使用
1.在store大仓库的modules配置下挂载小仓库,小仓库都开启了命名空间
export default new Vuex.Store({
modules:{
home,
search
}
})
2.获取数据的时候写清楚来自哪个小仓库
封装的目的
/mock开头的前缀进行了哪些封装?
请求拦截器
res.data,请求失败终止promise链mockjs 生成随机数据,当前端使用mock模拟的数据接口时,mockjs进行数据返回,并拦截ajax请求不发送给后台。
封装一个mock请求的axios
const mockRequests = axios.create({
baseURL:"/mock",
timeout:5000, //请求超时的时间5s
})
//请求拦截器
mockRequests.interceptors.request.use((config)=>{
//config:配置对象,对象里面有一个属性很重要,header请求头
nprogress.start();//进度条开始
return config;
})
//响应拦截器
//参数1成功的回调,参数2失败的回调
mockRequests.interceptors.response.use((res)=>{
nprogress.done();//进度条结束
return res.data;//返回服务器返回的数据
},(error)=>{
return Promise.reject(new Error('fail')) //终止promise链
})
export default mockRequests;
//采用mock发送请求
import mockRequests from "./request";
//获取Home首页轮播图banner的结构
export const getBannerList = () => mockRequests.get('/banner');
分页器需要哪些数据?
1.当前是第几页 pageNo
2.每一页需要展示多少数据 pagesize
3.分页器一共有多少条数据 total
4.分页器显示的连续页码个数:5 | 7
对于分页器,很重要的点是计算出连续显示页面号起始数字和结束数字。 当前页在连续页的正中间
Math.ceil(total/pagesize) computed: {
//总共多少页
totalPage() {
return Math.ceil(this.total / this.pageSize);
},
//计算出连续的页码的起始数字与结束数字[连续页码的数字:至少是5]
startNumAndEndNum() {
const { continues, pageNo, totalPage } = this;
//先定义两个变量存储起始数字与结束数字
let start = 0,
end = 0;
//不正常现象【总页数没有连续页码多】
if (continues > totalPage) {
start = 1;
end = totalPage;
} else {
//正常现象【连续页码5,但是你的总页数一定是大于5的】
start = pageNo - parseInt(continues / 2);
end = pageNo + parseInt(continues / 2);
//把出现不正常的现象【start数字出现0|负数】纠正
if (start < 1) {
start = 1;
end = continues;
}
//把出现不正常的现象[end数字大于总页码]纠正
if (end > totalPage) {
end = totalPage;
start = totalPage - continues + 1;
}
}
return { start, end };
},
}
可以提到JWT(JSON Web Token) 对比 session认证机制
JSON Web Token:https://blog.csdn.net/qq_41370833/article/details/125052440
路由守卫 - 控制访问权限
1.在每次路由跳转时,根据token和用户信息判断用户是否登录,从而判断该路由是否可以跳转。
用户信息是因为页面需要使用,token来判断用户是否登录。如果token失效了,需要重新登录,也就是可以执行退出登录的流程。
2.没有登录时,有些路由不能进入(通过全局路由守卫实现),登录之后应该跳转到想要访问的路由。所以可以先把登录的路由作为query参数,点击登录的时候根据url有无query参数再决定往哪里跳转(在登陆组件中点击登录事件触发的回调中实现)。
路由守卫笔记:https://blog.csdn.net/qq_41370833/article/details/124300162
将多个子元素的同类事件监听委托给(绑定在)共同的一个父组件上。
好处
①减少内存占用(事件监听的回调变少)
②动态添加的内部元素也能响应
问题描述
给一级菜单绑定了鼠标的事件监听,当鼠标频繁进入时,事件回调被频繁执行。当用户操作很快时,移入的一级分类都应该触发鼠标进入事件,但是经过测试,只有部分的一级分类被触发了。原因是用户行为过快,导致浏览器没有反应过来。如果当前回调中有大量业务,有可能出现浏览器卡顿现象。
问题解决
节流(throttle)函数
控制的是给事件绑定的回调函数执行的频率,那么该函数的返回值应该是一个函数。
参数有两个1.获取到的回调函数 2. 设置的时间间隔
注意点
1.返回函数使用了闭包,闭包会永远在内存中保存所以这个pre都是记录的上一次的结果
2.修改this的目的是让函数的指向绑定事件的DOM
//使用形式,绑定时候throttle函数就会执行,所以this是window
window.addEventListener('scroll',throttle(()=>{},500))
//自定义
function throttle(callback,wait){
let pre=0;
//console.log(this);window
//节流函数/真正的事件回调函数
return function(...args){
const now = Date.now();
if(now-pre>wait){
//callback()是window调用的,所以callback函数里的this是window,这里要修改指向事件源,
//console.log('this2',this); //DOM
callback.apply(this,args);
pre = now;
}
}
}
防抖(debounce)函数
控制的是给事件绑定的回调函数执行的频率,那么该函数的返回值应该是一个函数。
参数有两个1.获取到的回调函数 2. 设置的规定时间
思路
1.返回一个函数,在函数中设置定时器,在定时器中执行回调函数,注意this指向的改变
2.当频繁点击的时候,如果此时已经开启定时器了说明之前触发了回调,我们需要删除定时器
3. 注意定时器的timeId不能在返回函数中定义,如果在返回函数中定义,那么每次触发回调的时候,都会重新定义。而我们的需求是对于当前触发的回调,timeId需要记录之前的结果,通过timeId来判断之前是不是已经开启了定时器。所以这里需要利用闭包实现。
//使用形式:绑定时debounce立即执行
window.addEventListener('scroll',debounce(()=>{},500));
//防抖函数
function debounce(callback, wait){
let timeId=null;
return funtion(...args){
if(timeId){//之前已经有一个定时器了,这里再一次触发事件,重新开始即使
clearTimeout(timer);
}
timeId = setTimeout(()=>{
callback.apply(this,args);
//执行成功之后,重置timeId,所以这里可以起作用
timeId = null;
},wait)
}
}
当组件之间进行切换时,会销毁旧组件,创建新组件。所以组件中的子组件导航也会重新创建实例、重新挂载,重新发送数据请求。
如何优化
对于导航组件来说,一般都是不变的,所以我们希望只发送一次数据请求。所以可以把数据请求放在根组件,根组件只会实例化一次。
图片懒加载:https://blog.csdn.net/qq_41370833/article/details/125284975 重点
路由懒加载:https://blog.csdn.net/qq_41370833/article/details/125299151
$router.push/replace,可以进行路由跳转问题描述
编程式路由重复点击(参数不变),多次执行会抛出NavigationDuplicated警告报错
问题分析,为什么会报错?
Vue router3.1之后,$router.push()返回Promise,返回的promise没有设置失败的回调,没有对错误进行处理
解决办法
1.对每个router.push()进行错误捕获
router.push('xxxx').catch(err => {err})
push方法还可以传入成功和失败的回调
this.$router.push({
name:'search',//路由记得命名
params:{keyword:this.keyword},
query:{keyword:this.keyword.toUpperCase()}
},()=>{},(err)=>{if(如果是NavigationDuplicated错误)console.log(err)})
2.重写push()方法
①先保存VueRouter原型上的push方法
②重写push|repalce
import VueRouter from 'vue-router' //引入插件
Vue.use(VueRouter) //使用插件
let origiPush = VueRouter.prototype.push
VueRouter.prototype.push = function(location,onResolved=()=>{},onRejected=(err )=>err){
return origiPush.call(this,location,onResolved,onRejected)
}
原因
在new Swiper实例之前,页面中的结构必须存在
刚开始将数据请求放在mounted里,但是ajax请求是异步的,数据是动态获取的,new Swiper时可能数据还没有获取到,页面还没有根据数据重新渲染,结构还不完整。
mounted() {
//派发action,通过vuex发起aja请求
this.$store.dispatch('home/reqBannerList');
new Swiper(document.querySelector('.swiper-container'),{
loop:true,
pagination:{
el:".swiper-pagination"
},
navigator:{
nextEl:".swiper-button-next",
prevEl:".swiper-button-prev"
}
})
}
解决办法
watch: 数据监听,监听已有数据变化。 此时只能保证数据已经获取到了,不能保证v-fordom渲染完毕了
$nextTick:将回调延迟到下次DOM更新周期之后执行,下一次DOM更新周期之后指下次微任务执行更新DOM操作之后,此时保证dom渲染完毕
更新DOM的回调和vm.$nextTick注册的回调,都是添加到微队列中。所以DOM会先更新完毕,然后再执行$nextTick的回调
$nextTick原理:https://blog.csdn.net/qq_41370833/article/details/124830714
问题描述
当从页面跳转到新路由时,滚动条保持原有位置
原因
路由切换时没有重新刷新页面
解决办法
使用前端路由,当切换到新路由时,想要页面滚动到顶部或者保持原先的滚动位置,vue-router可以实现,只支持在history.pushState的浏览器使用。 —引出H5接口的新方法pushState和replaceState,或者hash模式和history模式的区别
//配置路由
export default new VueRouter({
routes,
scrollBehavior (to, from, savedPosition) {
return {x:0,y:0} //每次路由切换时的滚动条位置
}
})