基于vue的前端性能优化归纳总结

vue框架经过近几年的发展,越来越受公司和广大开发人员的喜爱。用vue进行项目开发,当项目越来越庞大时,性能问题将显得尤为重要。经过多年对vue的使用积累,本文将从多方面,对vue的性能优化问题进行归纳和经验总结。

1.建议使用v-show提高dom的复用率

v-show通过简单的控制元素的 display 属性来显示与隐藏;v-if 是通过条件渲染,进行创建或销毁来控制显示与隐藏,两种各有优缺点, v-show 的首次渲染开销,要比 v-if 的首次渲染开销高的多,而首次渲染结束后,v-show 切换开销远远小于v-if。如果界面上要对元素节点进行频繁的显隐操作,建议使用v-show,否则相反。

2.v-for 遍历避免同时使用 v-if

在vue中,v-for优先于v-if被解析,如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,很影响性能。要避免这种性能消耗,有两种情况:①.如果条件出现在循环体外面,则在循环体外层嵌套template,在template上进行v-if判断;②.如果条件出现在循环内部,可通过计算属性提前过滤掉那些不需要显示的数据项。

<!--① 情况一 -->
<template v-if="isTrue">
    <span v-for="(item,index) in baseData">{{ item.name }}</span>
</template>
<script>
  export default {
    data:{
      baseData:[
        { id:1,name:"陕AQU091",disabled:false},
        { id:2,name:"陕ASDS91",disabled:false},
        { id:3,name:"陕A56778",disabled:true},
        { id:4,name:"陕BJJ556",disabled:false}
      ]
    },
    computed:{
      isTrue(){
        return this.baseData && this.baseData.length > 0
      }
    }
  }
</script>

<!--② 情况二 -->
<span v-for="(item,index) in baseData">{{ item.name }}</span>
<script>
  export default {
    data:{
      baseData:[
        { id:1,name:"陕AQU091",disabled:false},
        { id:2,name:"陕ASDS91",disabled:false},
        { id:3,name:"陕A56778",disabled:true},
        { id:4,name:"陕BJJ556",disabled:false}
      ]
    },
    mounted(){
      this.baseData=this.baseData.filter(val=>!val.disabled)
    }
  }
</script>

3.keep-alive缓存页面

对于一些页面组件,如果页面中的数据不是实时或者经常更新,可以放到keep-alive中缓存起来,可以减少频繁的页面dom渲染。如果部分页面在特殊情况下需要更新页面数据,可以在被keep-alive包裹的组件中使用activated钩子函数进行页面数据激活。

4.for循环时加上key

对于数据循环类的dom,需要加上key,更高效的更新虚拟DOM,vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。若不设置key,可能在列表更新时引发一些隐蔽的bug,如果标签名相同,则会导致只更新其内部属性,从而出现一些奇怪的问题。

5.路由懒加载

路由在加载的时候,往往都是面向大的页面级模块组件,如果用提前引入的方式,会造成一次性加载出现首屏留白等待时间过长,加载吃力。如果路由文件以懒加载的形式引入,在打包时会发现包的体积会大幅的缩减,页面组件在使用的时候会按需加载,从而大大提高性能。

6.图片懒加载

图片懒加载
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等滚动到可视区域后再去加载,可以使使用第三方懒加载插件vue-lazyload。

<img v-lazy="assets/images/a.png">

7.长列表数据,使用object.freeze进行冻结

对于纯粹展示的列表类数据,一般不会有任何改变,这种情况就不需要做响应化。处理方式有很多,这里列举常用的两种:

①我们可以在通过接口请求获取到数据后,使用object.freeze来冻结数据。

②把得到的数据重新再处理一遍,组成一个新的map,如:使用definedproperty进行重新定义,然后将configurable属性改为false,也一样达到对数据进行冻结锁死。

8.大数据长列表下的虚拟加载

如果数据量过大,比如接口请求了好几万条数据,我们可以采用滚动虚拟加载的方式,只渲染少部分区域的内容。如每次只渲染20条数据,在页面边滚动时边渲染,并且dom节点始终保持20个节点,滚出区域外的节点自动销毁,进入区域的数据自动渲染,可以大大减小页面的渲染压力。【此场景的使用频率非常广泛,可以自行封装成组件使用,也可以使用本人封装的基于vue的vfan-ui组件库中的组件】。

9.变量本地化

在方法中使用data中的数据时,不要频繁的使用this.xxx,如果通过这种方式频繁的获取变量,当该变量是通过其他方式处理得来的,那么就会造成频繁的计算和数据处理,积少成多,从而影响性能。

<script>
  export default {
    data:{
      baseData:[
        { id:1,name:"陕AQU091",disabled:false},
        { id:2,name:"陕ASDS91",disabled:false},
        { id:3,name:"陕A56778",disabled:true},
        { id:4,name:"陕BJJ556",disabled:false}
      ]
    },
    computed:{
      base(){
        return this.baseData.filter(val=>!val.disabled)
      }
    },
    methods:{
      getData(){
      // ①  
      const getBase = this.base
        let result = []
        this.newData = getBase
        // ②
        this.base.forEach(element => {
          result.push(element)
        });
        /*正确写法:
        getBase.forEach(element => {
          result.push(element)
        });
        */
      }
    }
  }
</script>

上面的方式是不可取的,因为①处通过this.base引用到了变量数据,并使用getBase存起来了,而②处又再次的使用this.base引用变量获取数据,而base又是经过二次处理的数据变量,这种调用方式会造成内部数据多次计算,正确的方法是开始使用getBase变量将this.base存起来,后面都直接使用getBase进行处理,可以避免内部数据的多次计算。

10.无状态的组件标记为functional

vue中的函数式组件也称为无状态组件,与Flutter中的无状态组件类似,其内部没有任何状态。函数式组件中只接受一个props参数,没有其他参数。对于无状态的组件在template上添加functional来指定为函数式组件,渲染时开销更小。

<!-- 声明为一个函数式组件 -->
<template functional>
	{{ data }}
<template>
<script>
export default {
	props: {
		data: ""
	}
}
</script>

11.第三方组件按需引入

对于element-ui,iview,ant-design等UI组件,一次性引入体积过大,使用按需引入的方式可以大大减小代码代码冗余。

import Vue from 'vue'
import { Button, Card } from 'element-ui'
 Vue.use(Button)
 Vue.use(Card)

12.watch和computed的合理使用

watch和computed的选择用结合实际场景合理的选择使用,切记滥用。一般情况下,建议能用computed的不要用watch,过多用watch会增加性能的开销。计算属性主要是做一些简单运算,不能用于方法的调用。watch 的作用就是监听数据变化去改变数据或触发事件,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的,如数据发生变化时,调用接口方法获取新的数据等。

13.组件细化(高内聚,低耦合)

在封装组件时,尽量对组件进行细分,一个组价只完成一件事,比如按钮,卡片,表单,输入框,轮播图等,这样可以解决当组件代码量庞大时,vue 的数据驱动视图更新比较慢,造成渲染过慢,体验效果比较差的问题。对于组件封装如何优化呢?下面提出几点建议:
1.组件尽量合理细分,一个组件只完成一件事。
2.增强组件的可配置性,配置性越强,可复用性越高。
3.封装组件遵循配置 props 细化的规则。

14.打包优化

在打包 vender 时不打包element-ui、 vue、vuex、vue-router、axios 等,换用国内的bootcdn或私服直接引入到根目录的 index.html 中。在 webpack 里配置externals,忽略不需要打包的库:

externals: {
  'element-ui': 'element-ui',
  'vue': 'Vue',
  'vue-router': 'VueRouter',
  'vuex': 'Vuex',
  'axios': 'axios'
}

15.文件进行gzip压缩

对于gzip压缩问题,较小资源不需要进行压缩,较大文件开启gzip压缩。但是对于图片文件不建议采取gzip方式压缩 ,因为图片压缩并不能实际减少文件大小,反而会导致打包后生成很多同大小的gz文件,得不偿失。针对图片可以手动的使用压缩工具进行压缩效果更好。

在webpack中配置gzip压缩也很简单,首先安装插件(compression-webpack-plugin),然后在vue.config.js文件中进行webpack配置:

const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
        plugins: [
            new CompressionPlugin({
                algorithm: 'gzip', // 使用gzip压缩
                test: /\.js$|\.html$|\.css$/, // 匹配文件名
                filename: '[path].gz[query]', // 压缩后的文件名(保持原文件名,后缀加.gz)
                minRatio: 1, // 压缩率小于1才会压缩
                threshold: 10240, // 对超过10k的数据进行压缩
                deleteOriginalAssets: false, // 是否删除未压缩的源文件,谨慎设置,如果希望提供非gzip的资源,可不设置或者设置为false(比如删除打包后的gz后还可以加载到原始资源文件)
            }),
        ],
    },
}

16.打包时关闭productionSourceMap

productionSourceMap是资源地图,作用就是定位。source map定位的是打包后代码语句在项目文件的位置。当开启productionSourceMap打包时,就会生成很多的map文件,而此文件在生产环境里是没有实际价值的,反而会增加包的体积,所以打包时建议关闭productionSourceMap。

原创文章,作者:Ferrycoln,如若转载,请注明出处:https://ms200.cn/archives/1833

发表评论

邮箱地址不会被公开。 必填项已用*标注