记一次大量数据渲染vue dom时导致的卡死问题

上周,分配任务的同事指着我们的网站,拽我过来,快速地切换着菜单,对我说,来,你看看为什么我们现在的网站会卡成这样,emmmm
分配任务的同事说:“他说每当我快速切换菜单的时候,就卡了,你去把这个问题解决一下吧!”
我:"放心交给我,我是专业的!”
分配任务的同事:“为了让你更快地知道问题出在了哪儿,我给你两条思路吧。”

  1. axios 向后端请求接口太慢时候就卡住了。
  2. 当一段脚本长时间占用着处理机,就会挂起浏览器的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~~

总结:

  1. vue dom 是由js 来操作渲染的,当有数据改变时会走 diff 算法来判断是哪个节点的数据变更了。
  2. 当一段脚本长时间占用着处理机,就会挂起浏览器的GUI更新,主线程不要长时间执行大量数据。

终于写完了,要睡觉了… (~﹃~)~zZ
大家晚安

你可能感兴趣的:(报错分享)