上周,分配任务的同事指着我们的网站,拽我过来,快速地切换着菜单,对我说,来,你看看为什么我们现在的网站会卡成这样,emmmm
分配任务的同事说:“他说每当我快速切换菜单的时候,就卡了,你去把这个问题解决一下吧!”
我:"放心交给我,我是专业的!”
分配任务的同事:“为了让你更快地知道问题出在了哪儿,我给你两条思路吧。”
- axios 向后端请求接口太慢时候就卡住了。
- 当一段脚本长时间占用着处理机,就会挂起浏览器的GUI更新。
然后.......我就成功地跑偏了 n(≧▽≦)n
解决问题第一步,我去看了vue-router的 beforeEach 函数,确认肯定不是它的问题,然后又去 fetch.ts 这个封装axios 的类里查看,还在http请求接口的回调函数中加了 setTimeout 来模拟延迟请求。O(∩_∩)O~~ 并没有效果!
第二步,转换一下思路,既然是快速点击页面卡死的,那么,当这一次的路由切换的时候,把上一次的路由取消掉就可以了呀,Σ( ° △ °|||)︴ 于是,用axios 的 canceltoken 一顿猛如虎的撸完了。下面是代码:( 此处仅是cacalToken 的例子,没写请求配置,大家可以根据自己公司的需要写 。 )
// fetch.ts 文件
import axios, { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios'
//定义全局变量clearRequest,在main.ts中要用到
const clearRequest:any = {
source: {
token: null,
cancel: null
}
}
class Fetch {
CancelToken:any = axios.CancelToken
source:any = this.CancelToken.source();
// Axios实例对象
private _axios: AxiosInstance
// Axios配置
private readonly _config: AxiosRequestConfig = {
baseURL: process.env.BASE_API,
timeout: 1000 * 60 * 10,
responseType: 'json',
cancelToken: this.source.token,// 这句很重要
}
// 构造函数,初始化Axios
constructor() {
// axios实例
this._axios = axios.create(this._config)
// axios请求拦截
this._axios.interceptors.request.use(
(config: AxiosRequestConfig) => {
config.cancelToken = clearRequest.source.token;// 这句很重要
// start 此处 若是有做鉴权token , 就给头部带上token
//------------------------------------
// end
return config
},
(error: any) => {
// start 此处可以写 错误判断
//------------------------------------
// end
}
)
// 此处写 axios 响应拦截 以及对响应的状态处理
//------------------------------------
// end
)
}
//main.ts 文件
// beforeEach 请求拦截
router.beforeEach((to, from, next) => {
// 调用next方法之前,可以进行授权拦截
if (to.meta.requireAuth) {
// start 此处判断是否有token
//------------------------------------
// end
if (token && token !== '') {
next()
} else {
next({
// start 此处写未登录时要跳转的页面
//------------------------------------
// end
})
}
} else {
// 加上 这句,取消上一级路由的所有请求
const cancelTokenObj = Fetch.CancelToken;
clearRequest.source.cancel && clearRequest.source.cancel();
clearRequest.source = cancelTokenObj.source();
next()
}
})
刷新页面,带着要解决完问题的激动的心情,再次快速点击树形菜单,发现并没有效果,它依然坚定地卡着。emmmm ,不过无意中却解决了另一个问题
就是————上一级路由返回的数据将不再走回调函数,也不会在页面上显示不必要的数据了。 n(≧▽≦)n 好吧,这也算是一个发现。
接下来,再次踏上找bug之旅——————
我发现,快速点击树形菜单栏时,总有一个页面点完之后,再点其他的面就开始卡了,于是我主要开始研究这个页
于是我打开了Network, 惊喜地发现
当前页有三个下拉列表接口,后端给我一次性返回了 两万三千多条数据!
两万三千多条数据! 划重点!
我们都知道,vue 的vendor函数它是用js生成的一串字符串,本质就是js 来操作的,那这两万三千多条数据,就是js在页面上创建了两万三千多个dom呀,它不卡死那简直天理不容啊!
于是,我开始了优化之路…(其实,这个需要后端小哥哥给你返回少一些数据,然后动态查找就可以)
但是,当时情况紧急,没有时间重新写接口,于是…就在本地将前五十条数据显示,剩下的数据存在一个备用变量中,当用户手动输入下拉列表查找时,我们再去这个备用变量中查找数据,显示在页面中。
// 下拉框
<el-select
v-model="searchForm.project"
default-first-option
placeholder="请选择名称"
:loading="projectLoading"
filterable
remote
reserve-keyword
clearable
size="small"
:remote-method="onProjectSelectChange">
<el-option v-for="item in projectOptions" :key="item.id" :label="item.name" :value="item.name"></el-option>
</el-select>
// 以下为 typescript 写法
import Vue from "vue";
import { Component } from "vue-property-decorator"; // 导入组件装饰器
import { AxiosResponse } from 'axios'
import { getProjectList } from 'api/project
import { SearchForm } from 'api/modules'
@Component({})
export default class ReportUpload extends Vue {
searchForm: SearchForm = new SearchForm(); //实例化 数据模板
projectOptions: any[] = []; // 下拉列表显示数据
projectOptionsM: any[] = []; // 全部下拉列表数据
created() {
this.getProjectList();
}
getProjectList(){
let data = {
name: this.searchForm.project
}
getProjectList(data).then((res:AxiosResponse ){
this.projectOptions = res.data.split(1, 50);
this.projectOptionsM = res.data
})
}
onProjectSelectChange(quer){
this.projectUtil = query
if (query !== ''|| this.projectOptions.length<=0) {
this.projectLoading = true;
this.projectOptions = this.projectOptionsM.filter(item=>{
return item.project.indexOf(query) + 1
})
}
}
}
撸完了码子,让我们回到浏览器,刷新一下页面,这时候,我们发现,已经可以流畅地切换菜单页不卡了。O(∩_∩)O~~
总结:
- vue dom 是由js 来操作渲染的,当有数据改变时会走 diff 算法来判断是哪个节点的数据变更了。
- 当一段脚本长时间占用着处理机,就会挂起浏览器的GUI更新,主线程不要长时间执行大量数据。
终于写完了,要睡觉了… (~﹃~)~zZ
大家晚安
更多推荐
记一次大量数据渲染vue dom时导致的卡死问题
发布评论