10个技巧!实现vue.js极致性能优化(建议收藏)

QcloudCommunity QcloudCommunity     2023-01-04     800

关键词:

导语 | Vue是一套用于构建用户界面的渐进式的JavaScript框架。它具有体积小,更高的运行效率,双向数据绑定,生态丰富、学习成本低等优点,所以Vue也被广泛用在移动端跨平台框架上。接下来,我将为大家梳理10个实现Vue.js极致性能优化的技巧,以供大家在实际运用中使用。

Vue框架通过数据双向绑定和虚拟DOM技术,帮我们处理了前端开发中最脏最累的DOM操作部分,我们不再需要去考虑如何操作DOM以及如何最高效地操作DOM,但是我们仍然需要去关注Vue在跨平台项目性能方面的优化,使项目具有更高效的性能、更好的用户体验。

一、v-for遍历必须为item添加key,

       且避免同时使用v-if

在列表数据进行遍历渲染时,需要为每一项item设置唯一key值,方便Vue.js内部机制精准找到该条列表数据。当state更新时,新的状态值和旧的状态值对比,较快地定位到diff。

我们在使用的使用经常会使用index(即数组的下标)来作为key,但其实这是不推荐的一种使用方法。

举个例子:

var list = [
    
        id: 1,
        name: 'test1',
    ,
    
        id: 2,
        name: 'test2',
    ,
    
        id: 3,
        name: 'test3',
    ,
]


<div v-for="(item, index) in list" :key="index" >item.name</div>

在最后一条数据后再加一条数据:

var list = [
    
        id: 1,
        name: 'test1',
    ,
    
        id: 2,
        name: 'test2',
    ,
    
        id: 3,
        name: 'test3',
    ,
    
        id: 4,
        name: '我是在最后添加的一条数据',
    ,
]

此时前三条数据直接复用之前的,新渲染最后一条数据,此时用index作为key,没有任何问题。

在中间插入一条数据:

var list = [
    
        id: 1,
        name: 'test1',
    ,
    
        id: 4,
        name: '我是插队的那条数据',
    ,
    
        id: 2,
        name: 'test2',
    ,
    
        id: 3,
        name: 'test3',
    ,
]

此时更新渲染数据,通过index定义的key去进行前后数据的对比,发现:

之前的数据                         之后的数据


key: 0  index: 0 name: test1     key: 0  index: 0 name: test1


key: 1  index: 1 name: test2     key: 1  index: 1 name: 我是插队的那条数据


key: 2  index: 2 name: test3     key: 2  index: 2 name: test2


                                 key: 3  index: 3 name: test3

通过上面清晰的对比,发现除了第一个数据可以复用之前的之外,另外三条数据都需要重新渲染。

是不是很惊奇,我明明只是插入了一条数据,怎么三条数据都要重新渲染?而我想要的只是新增的那一条数据新渲染出来就行了。

最好的办法是使用数组中不会变化的那一项作为key值,对应到项目中,即每条数据都有一个唯一的id,来标识这条数据的唯一性;使用id作为key值,我们再来对比一下向中间插入一条数据,此时会怎么去渲染。

之前的数据                               之后的数据


key: 1  id: 1 index: 0 name: test1     key: 1  id: 1 index: 0  name: test1


key: 2  id: 2 index: 1 name: test2     key: 4  id: 4 index: 1  name: 我是插队的那条数据


key: 3  id: 3 index: 2 name: test3     key: 2  id: 2 index: 2  name: test2


                                       key: 3  id: 3 index: 3  name: test3

现在对比发现只有一条数据变化了,就是id为4的那条数据,因此只要新渲染这一条数据就可以了,其他都是就复用之前的。

总结:所以一句话,key的作用主要是为了高效的更新虚拟DOM。另外Vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让Vue可以区分它们,否则Vue只会替换其内部属性而不会触发过渡效果。

v-for遍历避免同时使用v-if,v-for比v-if优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成computed属性。

二、长列表性能优化

Vue会通过Object.defineProperty对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要Vue来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止Vue劫持我们的数据呢?可以通过Object.freeze方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。

export default 
  data: () => (
    users: 
  ),


  async created() 
    const users = await axios.get("/api/users");
    this.users = Object.freeze(users);
  
;

三、Vue组件中的data是函数而不是对象

export default 
  data() 
    // data是一个函数,data: function() 的简写
    return 
      // 页面要初始化的数据
      name: 'bartonwang',
    ;
  ,
;

而非如下所示:

export default 
  data: 
    // data是一个对象
    name: 'bartonwang',
  ,
;

当一个组件被定义,data必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例,复用在多个页面。

如果data是一个纯碎的对象,则所有的实例将共享引用同一份data数据对象,无论在哪个组件实例中修改data,都会影响到所有的组件实例。

如果data是函数,每次创建一个新实例后,调用data函数,从而返回初始数据的一个全新副本数据对象。

这样每复用一次组件,会返回一份新的data数据,类似于给每个组件实例创建一个私有的数据空间,让各个组件的实例各自独立,互不影响,保持低耦合。


四、Vue钩子函数之钩子事件hookEvent,

       监听组件简化代码

用法:

  1. 通过$on(eventName, eventHandler) 侦听一个事件。

  2. 通过$once(eventName,eventHandler) 一次性侦听一个事件。

  3. 通过$off(eventName, eventHandler) 停止侦听一个事件。

通常实现一个定时器的调用与销毁我可能会以以下方式实现:

export default
  data()
    timer:null  // 需要创建实例
  ,


  mounted()
      this.timer = setInterval(()=>
      //具体执行内容
      console.log('1');
    ,1000);
  


  beforeDestory()
    clearInterval(this.timer);
    this.timer = null;
  

这种方法存在的问题是:

vue实例中需要有这个定时器的实例,感觉有点多余。创建的定时器代码和销毁定时器的代码没有放在一起,不容易维护,通常很容易忘记去清理这个定时器。

使用$on(‘hook:’)监听beforeDestory生命周期可以避免该问题,并且因为只需要监听一次,所以使用$once进行注册监听。

export default
  methods:
    fn()
      const timer = setInterval(()=>
        console.log('1');
      ,1000);


      this.$once('hook:beforeDestory',()=> // 监听一次即可
        clearInterval(timer);
        timer = null;
      )
    
  

五、组件懒加载

在单页应用中,如果没有应用懒加载,运用webpack打包后的文件将会异常地大,造成进入首页时需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时。

Vue.js 2.0组件级懒加载方案:

  • 支持组件可见或即将可见时懒加载

  • 支持组件延时加载

  • 支持加载真实组件前展示骨架组件,提高用户体验

  • 支持真实组件代码分包异步加载

安装:

npm install@xunlei/vue-lazy-component

在组件中实现局部注册组件:

import  component as VueLazyComponent  from '@xunlei/vue-lazy-component'


export default 
  components: 
    'vue-lazy-component': VueLazyComponent
  

需要懒加载的组件将其包裹在vue-lazy-component中,slot值为skeleton指的是在懒加载过程中显示的加载状态组件。

<vue-lazy-component :timeout="5000" tagName="div">
      <child1 slot="skeleton" />
      <child2 />
      <child3 />
      <child4 />
      <child5 />
</vue-lazy-component>

六、非响应式数据

初始化时,Vue会对data做getter、setter改造。在Vue的文档中介绍数据绑定和响应时,特意标注了对于经过Object.freeze()方法的对象无法进行更新响应。

性能提升对比

在基于Vue的一个big table benchmark里,可以看到在渲染一个一个1000x10的表格的时候,开启Object.freeze()前后重新渲染的对比。

开启优化之前

开启优化之后

在这个例子里,使用了Object.freeze()比不使用快了4倍。

为什么Object.freeze()的性能会更好,不使用Object.freeze()的CPU开销?

使用Object.freeze()的CPU开销:

对比可以看出,使用了Object.freeze()之后,减少了observer的开销。


七、不要将所有的数据都放到data中

data中的数据都会增加getter和setter,又会收集watcher,这样还占内存。不需要响应式的数据我们可以定义在实例上。

八、v-for元素绑定事件代理

事件代理作用主要是2个:

  1. 将事件处理程序代理到父节点,减少内存占用率。

  2. 动态生成子节点时能自动绑定事件处理程序到父节点。

  • 不使用事件代理,每个span节点绑定一个click事件,并指向同一个事件处理程序:

<div>
      <span 
        v-for="(item,index) of 100000" 
        :key="index" 
        @click="handleClick">
        item
      </span>
 </div>
  • 不使用事件代理,每个span节点绑定一个click事件,并指向不同的事件处理程序

<div>
      <span 
        v-for="(item,index) of 100000" 
        :key="index" 
        @click="function () ">
        item
      </span>
  </div>
  • 使用事件代理

<div  @click="handleClick">
      <span 
        v-for="(item,index) of 100000"  
        :key="index">
        item
      </span>
 </div>

可以看到使用事件代理无论是监听器数量和内存占用率都比前两者要少,同时对比3个图中监听器的数量并没有发现Vue会自动做事件代理,但是一般给v-for绑定事件时,都会让节点指向同一个事件处理程序(第二种情况可以运行,但是eslint会警告),一定程度上比每生成一个节点都绑定一个不同的事件处理程序性能好,但是监听器的数量仍不会变,所以使用事件代理会更好一点。

代码使用:

<ul @click="meths">
      <li v-for="(item,key) in 10" :key="key" :data-index="key">item</li>
 </ul>


meths(e) 
      if (e.target.nodeName.toLowerCase() === 'li') 
        console.log(e.target.innerHTML)
        console.log(e.target.dataset)
      


九、函数式组件

函数式组件是无状态,它无法实例化,没有任何的生命周期和方法。创建函数式组件也很简单,只需要在模板添加functional声明即可。一般适合只依赖于外部数据的变化而变化的组件,因其轻量,渲染性能也会有所提高。

组件需要的一切都是通过context参数传递。它是一个上下文对象,具体属性查看文档。这里props是一个包含所有绑定属性的对象。

函数式组件

十、函数式组件provide和inject组件通信

痛点:常用的父子组件通信方式都是父组件绑定要传递给子组件的数据,子组件通过props属性接收,一旦组件层级变多时,采用这种方式一级一级传递值非常麻烦,而且代码可读性不高,不便后期维护。

Vue提供了provide和inject帮助我们解决多层次嵌套嵌套通信问题。在provide中指定要传递给子孙组件的数据,子孙组件通过inject注入祖父组件传递过来的数据,可以轻松实现跨级访问父组件的数据。

provide:是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。注意:子孙层的provide会掩盖祖父层provide中相同key的属性值

inject:一个字符串数组,或者是一个对象。属性值可以是一个对象,包含from和default默认值,from是在可用的注入内容中搜索用的key (字符串或Symbol),意思就是祖父多层provide提供了很多数据,from属性指定取哪一个key;default指定默认值。

从上面这个例子可以看出,只要在父组件中调用了,那么在这个父组件生效的生命周期内,所有的子组件都可以调用inject来注入父组件中的值。

在使用场景中,肯定是希望父组件的数据一旦发生改变,子孙组件获取到的也是父组件更新后的数据。那么,怎么实现父组件与子孙组件所绑定的数据动态响应呢?

-------------------parent.vue----------------------


provide()


    return 


   // keyName: name:this.name, // value 是对象才能实现响应式,也就是引用类型


      keyName: this.changeValue // 通过函数的方式也可以[注意,这里是把函数作为value,而不是this.changeValue()]


   // keyName: 'test' value 如果是基本类型,就无法实现响应式


    
  ,


data()


  return 
      name:'张三'
  
  
  ,


  methods: 


  changeValue()
      this.name = '改变后的名字-李四'
   
   
    


  -------------grandson.vue-----------------


  inject:['keyName']
  create()
     console.log(this.keyName) // 改变后的名字-李四

 作者简介

王雄

腾讯客户端开发工程师

腾讯客户端开发工程师,目前在IEG增值服务部从事掌上道聚城app开发工作,有丰富的跨平台weex,react-native,flutter开发经验。

 推荐阅读

为什么WebAssembly不是JavaScript的终结者,而是它的“助推器”?

快人一步掌握vue源码解读,搞定diff算法!(超详细)

Linux入门必看:如何在60秒内分析Linux性能?

“Docker VS Kubernetes”是共生还是相爱相杀?


java中的5个代码性能提升技巧,最高提升近10倍(代码片段)

...Star。这篇文章介绍几个Java开发中可以进行性能优化的小技巧,虽然大多数情况下极致优化代码是没有必要的,但是作为一名技术开发者,我们还是想追求代码的更小、更快,更强。如果哪天你发现程序的运行速... 查看详情

高性能极致用户体验前端开发实战

...优化思路,并给出开发高性能极致体验网页的通用方法和技巧。 课程官方博客:前端学堂 在开始学习本课程之前,先提2个基本要求:了解业务作为一名合格的前端开发,我们的开发工作不是盲目的,我们的优化目标需... 查看详情

聊聊服务器性能优化~(建议收藏)

...们开发的软件服务需要在服务器上运行,所以服务器性能代表了软件的性能上限,因此服务器性能调优是个十分重要的环节,然而大部分同学对服务器性能调优关注的较少,今天从3个部分对服务器性能调优进行介... 查看详情

聊聊服务器性能优化~(建议收藏)

...们开发的软件服务需要在服务器上运行,所以服务器性能代表了软件的性能上限,因此服务器性能调优是个十分重要的环节,然而大部分同学对服务器性能调优关注的较少,今天从3个部分对服务器性能调优进行介... 查看详情

聊聊服务器性能优化~(建议收藏)(代码片段)

...们开发的软件服务需要在服务器上运行,所以服务器性能代表了软件的性能上限,因此服务器性能调优是个十分重要的环节,然而大部分同学对服务器性能调优关注的较少,今天从3个部分对服务器性能调优进行介... 查看详情

java中的5个代码性能提升技巧,最高提升近10倍(代码片段)

...Star。这篇文章介绍几个Java开发中可以进行性能优化的小技巧,虽然大多数情况下极致优化代码是没有必要的,但是作为一名技术开发者,我们还是想追求代码的更小、更快,更强。如果哪天你发现程序的运行速... 查看详情

java中的5个代码性能提升技巧,最高提升近10倍(代码片段)

这篇文章介绍几个Java开发中可以进行性能优化的小技巧,虽然大多数情况下极致优化代码是没有必要的,但是作为一名技术开发者,我们还是想追求代码的更小、更快,更强。如果哪天你发现程序的运行速度不尽... 查看详情

7000字前端性能优化总结|干货建议收藏(代码片段)

为什么要做性能优化?性能优化到底有多重要?网站的性能优化对于用户的留存率、转化率有很大的影响,所以对于前端开发来说性能优化能力也是重要的考察点。性能优化的点非常的多,有的小伙伴觉得记起来... 查看详情

c语言实现九大排序算法(建议收藏!)(代码片段)

...一个排序是否发生跳跃式的交换也是判断是否稳定的一个技巧。一个稳定的排序算法可以变成一个不稳定的排序算法一个本身就不稳定的排序算法是不可以变成一个稳定的排序算法的1.插入排序直接插入排序是一种最简单的排序... 查看详情

9个java性能调优技巧,yyds!

点击上方关注“终端研发部”设为“星标”,和你一起掌握更多数据库知识大多数开发者认为性能优化是一个复杂的话题,它需要大量的工作经验和相关知识理论。好吧,这也不完全错。优化一个应用做到性能最优化... 查看详情

mysql高性能优化规范建议,速度收藏(代码片段)

数据库命令规范所有数据库对象名称必须使用小写字母并用下划线分割所有数据库对象名称禁止使用MySQL保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来)数据库对象的命名要能做到见名识意,并且最后不... 查看详情

程序员必备的10个unity小技巧!!!

游戏技巧游戏优化是游戏成功的必要条件。即使这个游戏设计的很好,但它仍可能存在性能问题。要确保用户的Unity游戏经过优化,请尝试以下提示。1、性能问题的来源在开始优化游戏之前,用户需要找出性能问题的... 查看详情

嗨,这52条sql性能优化解密,建议收藏!

...学最好的别人,做最好的我们本文将全面揭秘SQL语句性能优化策略,直接上干货!-   52条SQL语句性能优化策略   -1、对查询进行优化,应尽量避免全表扫描,首先应考虑在where及orderby涉及的列上建立索引。... 查看详情

生产级基于springcloud微服务架构性能优化实战,建议收藏

参考技术A本文将从Tomcat性能优化,SpringCloud开启重试机制,Zuul网关性能参数优化,Ribbon性能参数优化,Feign与Hystrix性能优化等五个方面分享在生产环境如何做好SpringCloud性能优化。一般基于SpringCloud的微服务能够脱离传统的tomcat... 查看详情

go语言最全优化技巧总结,值得收藏!

...维护go基础库过程中,用到或者见到的一些性能优化技巧,现将一些理解梳理撰写成文,和大家探讨。一、常规手段(一)sync.Pool临时对象池应该是对可读性影响最小且优化效果显著的手段。基本上,业内... 查看详情

熬夜总结了53个python干货技巧,建议收藏

...#xff1a;zhuanlan.zhihu.com/p/48293468下面是老司机总结的一些干货技巧,非常有价值,尤其是对比c/c++有其他语言编程基础的小伙伴,记得收藏哦!1.易混淆操作本节对一些Python易混淆的操作进行对比。1.1有放回随机... 查看详情

全网最全性能优化总结!!(冰河吐血整理,建议收藏)

...示:如果是面试中高级的Java岗,基本上都需要懂性能优化的相关知识。今天,我们就一起来聊聊如何进行性能优化这个话 查看详情

discuz优化10个小技巧

  Discuz论坛是国内使用最多的论坛系统,现在最新版为X3.4,X3.4从2018年1月1日起只在官方Git发布,地址:https://gitee.com/ComsenzDiscuz/DiscuzX,说明已经开源了。discuz有那么多的粉丝,跟它完善的功能有很大关系,开箱即用,很多优... 查看详情