Vue常见优化手段

永远不要过早优化,要做到因地制宜,见招拆招。

使用key

对于通过循环生成了列表,应该给每个列表项一个稳定且唯一的key,这有利于在列表变动时,尽量少的删除、新增、改动元素。

使用冻结对象

冻结的对象不会被响应化。有的时候没有必要将数据变成响应式的数据,因为在数据的响应化的过程中需要递归遍历数据,需要耗时。

JavaScript 复制代码
Object.freeze(this.data);

使用函数式组件

参见函数式组件

使用函数式组件,在js执行时间以及渲染时间上稍有减少,但是差别不大,同时在内存占用(消耗)方面会比普通组件占用少,这是因为使用函数式组件不会在vue的组件树中生成该组件,不会为函数组件创建实例,只是纯渲染。对于普通组件,vue内部会通过new VueComponent()对每一个组件创建新的组件实例

使用计算属性

如果模板中某个数据会使用多次,并且该数据是通过计算得到的,使用计算属性以缓存它们

非实时绑定的表单项

当使用v-model绑定一个表单项时,当用户改变表单项的状态时,也会随之改变数据,从而导致vue发生重渲染(rerender),这会带来一些性能的开销。

特别是当用户改变表单项时,页面有一些动画正在进行中,由于JS执行线程和浏览器渲染线程是互斥的,最终会导致动画出现卡顿。

我们可以通过使用lazy或不使用v-model的方式解决该问题,但要注意,这样可能会导致在某一个时间段内数据和表单项的值是不一致的。

保持对象引用稳定

在绝大部分情况下,vue触发rerender的时机是其依赖的数据发生变化 ,若数据没有发生变化,哪怕给数据重新赋值了,vue也是不会做出任何处理的,下面是vue判断数据没有变化的源码

JavaScript 复制代码
export function hasChanged(x:unknow,y:unknown):boolean {
   if(x === y) {
     return x === 0 && 1/x !=== 1 / (y as number)  // +0 === -0 --> true 
   } else {
     return x === x || y === y // NaN !== NaN --> true
   }
}

因此,如果需要,只要能保证组件的依赖数据不发生变化,组件就不会重新渲染。

对于原始数据类型,保持其值不变即可

对于对象类型,保持其引用不变即可

从另一方面来说,由于可以通过保持属性引用稳定来避免子组件的重渲染,那么我们应该细分组件来尽量避免多余的渲染

使用v-show替代v-if

对于频繁切换显示状态的元素,使用v-show可以保证虚拟dom树的稳定,避免频繁的新增和删除元素,特别是对于那些内部包含大量dom元素的节点,这一点极其重要

关键字:频繁切换显示状态、内部包含大量dom元素

使用延迟装载(defer)

首页白屏时间主要受到两个因素的影响:

  • 打包体积过大

    巨型包需要消耗大量的传输时间,导致JS传输完成前页面只有一个<div>,没有可显示的内容

  • 需要立即渲染的内容太多

    JS传输完成后,浏览器开始执行JS构造页面。

    但可能一开始要渲染的组件太多,不仅JS执行的时间很长,而且执行完后浏览器要渲染的元素过多,从而导致页面白屏

一个可行的办法就是延迟装载组件,让组件按照指定的先后顺序依次一个一个渲染出来

延迟装载是一个思路,本质是利用requestAnimationFrame事件分批渲染内容,它的具体实现多种多样。

使用keep-alive

keep-alive组件是vue的内置组件,用于缓存内部组件实例。被包裹的组件在切换的时候不会被销毁,直接使用缓存中的实例,好处是避免创建组件带来的开销、而且可以保留组件的状态,但是会因此占用更多的内存。

属性
  • include 哪些组件缓存
  • exclude 哪些组件不缓存
  • max 最大缓存数,超出vue会自动移除最久没有使用的组件缓存
vue 复制代码
// 写法1
<keep-alive :include="['Comp1','Comp2']">
    <component :is="comps[curIndex]">
</keep-alive>
// 写法2
 <keep-alive include="Comp1,Comp2" :max="2">
    <component :is="comps[curIndex]">
</keep-alive>

关于max属性,vue是这么判断的

js 复制代码
cacheVNode() {
      const { cache, keys, vnodeToCache, keyToCache } = this
      if (vnodeToCache) {
        const { tag, componentInstance, componentOptions } = vnodeToCache
        cache[keyToCache] = {
          name: _getComponentName(componentOptions),
          tag,
          componentInstance
        }
        keys.push(keyToCache)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) { 
          pruneCacheEntry(cache, keys[0], keys, this._vnode) // 移除keys中的第一个
        }
        this.vnodeToCache = null
      }
    }
特有的生命周期
  • activated 组件激活时调用,第一次调用是在mounted之后调用
  • deactivated 组件失活时调用
常见的应用场景

后台管理系统的选项卡,当菜单添加到选项卡时,就对选项卡中的菜单进行缓存,提高用户的使用体验。

原理:

keep-alive在内部维护一个keys数组和一个缓存对象cache

js 复制代码
created() {
  this.cache = Object.create(null)
  this.keys = []
},

keys数组记录缓存组件的key值,如果组件没有,就会为组件自动生成一个唯一的key值

js 复制代码
const { cache, keys } = this
const key =
  vnode.key == null
    ? // same constructor may get registered as different local components
      // so cid alone is not enough (#3269)
      componentOptions.Ctor.cid +
      (componentOptions.tag ? `::${componentOptions.tag}` : '')
    : vnode.key

cache以key值为键,以vnode为值,用于缓存组件对应的虚拟DOM

js 复制代码
keys:[1,2,3]
cache:{
    1:vnode1,
    2,vnode2,
    3:vnode3
}

大致流程:

js 复制代码
render() {
  const slot = this.$slots.default // 获取默认插槽
  const vnode = getFirstComponentChild(slot) // 得到插槽中第一个组件的vnode
  const componentOptions = vnode && vnode.componentOptions // 获取vnode的信息
  if (componentOptions) {
    // check pattern
    const name = _getComponentName(componentOptions) // 获取组件名字
    const { include, exclude } = this
    // 判断是否需要缓存
    if (
      // not included 
      (include && (!name || !matches(include, name))) ||
      // excluded
      (exclude && name && matches(exclude, name))
    ) {
      return vnode
    }
    // 来到这里,说明组件需要缓存,有缓存复用组件实例,无缓存则进行缓存
    const { cache, keys } = this
    // 获取组件的key
    const key =
      vnode.key == null
        ? componentOptions.Ctor.cid +
          (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
    if (cache[key]) {
    } else {
      // 无缓存,进行缓存
      this.vnodeToCache = vnode
      this.keyToCache = key
    }
    // @ts-expect-error can vnode.data can be undefined
    vnode.data.keepAlive = true
  }
  return vnode || (slot && slot[0])
}

长列表优化

相关推荐
sewinger7 分钟前
区间合并算法详解
算法
XY.散人10 分钟前
初识算法 · 滑动窗口(1)
算法
韬. .32 分钟前
树和二叉树知识点大全及相关题目练习【数据结构】
数据结构·学习·算法
Word码37 分钟前
数据结构:栈和队列
c语言·开发语言·数据结构·经验分享·笔记·算法
五花肉村长41 分钟前
数据结构-队列
c语言·开发语言·数据结构·算法·visualstudio·编辑器
一线青少年编程教师1 小时前
线性表三——队列queue
数据结构·c++·算法
Neituijunsir1 小时前
2024.09.22 校招 实习 内推 面经
大数据·人工智能·算法·面试·自动驾驶·汽车·求职招聘
希望有朝一日能如愿以偿2 小时前
力扣题解(飞机座位分配概率)
算法·leetcode·职场和发展
丶Darling.2 小时前
代码随想录 | Day26 | 二叉树:二叉搜索树中的插入操作&&删除二叉搜索树中的节点&&修剪二叉搜索树
开发语言·数据结构·c++·笔记·学习·算法
JustCouvrir2 小时前
代码随想录算法训练营Day15
算法