当前位置: 首页 > Web前端 > vue.js

Vue开发中的12个性能优化技巧,看你用过多少

时间:2023-04-01 11:10:50 vue.js

性能优化,是每个开发者都会遇到的问题,尤其是现在越来越注重体验,竞争越来越激烈在激烈的环境下,对于我们开发者来说,仅仅完成迭代,完成功能是远远不够的。最重要的是把产品做好,让更多的人愿意用,让用户用得更舒心。不也是这样吗?是否体现了我们开发者的价值和能力?关注性能问题,优化产品体验,比修复几个无伤大雅的bug更有价值优化1.不做响应式,比如会员列表,产品列表等,只是纯数据展示,没有任何动态变化,有不需要对数据做响应式处理,可以大大提高渲染速度。例如,使用Object.freeze()冻结一个对象。MDN的描述是该方法冻结的对象不可修改;即不能向该对象添加新属性,不能删除已有属性,也不能修改对象已有属性的可枚举性。可配置性、可写性和已有属性的值不可修改,对象原型不可修改exportdefault{data:()=>({userList:[]}),asynccreated(){constusers=awaitaxios.get("/api/用户");this.userList=Object.freeze(users);}};Vue2的响应式源码地址:src/core/observer/index.js-第144行是这样的configurable===false){return}...}可以看到一开始configurable是falseconfigurable是false,也就是说这个属性不能修改,冻结对象的configurable是false。在Vue3中,增加了一个响应式标志来标记目标对象类型。Vue3的响应式细节可以看我的另一篇文章2,如果虚拟滚动是一个很长的大数据列表,如果全??部渲染的话,一次创建的DOM太多,会很卡。这时候可以使用虚拟滚动,只渲染一小部分(包括可视区域)区域的内容,然后在滚动的时候,不断替换可见区域的内容,模拟滚动的效果参考vue-virtual-scroller,vue-virtual-scroll-list原理是监听滚动事件,动态更新需要显示的DOM,计算位移在视图中,这也意味着在滚动过程中需要实时计算,有一定的开销,所以如果数据量不大,就用普通的滚动即可2.v-for遍历,避免使用同时使用v-if为什么要避免同时使用v-for而vue2中的v-if,v-for的优先级更高,所以在编译过程中,会遍历所有的list元素,生成一个虚拟DOM,然后通过v-if判断去渲染,这样会造成性能浪费,因为我们希望的是不生成不满足条件的虚拟DOM。在Vue3中,v-if的优先级更高,也就是说当判断条件是v-for遍历的列表中的属性时,无法获取到v-if。所以在一些需要同时使用的场景下,可以通过计算属性来过滤列表,如下3。该列表使用唯一键。比如有一个列表,我们需要在中间插入一个元素,如果不使用key或者使用index作为key会怎么样呢?我们先来看图。图中的li1和li2不会重新渲染。这个没有争议,但是li3、li4、li5都会重新渲染,因为当不使用key或者list的index作为key时,每个元素对应的位置关系是不同的。它是索引。上图的结果直接导致插入的元素和后面所有元素的对应位置关系发生变化。因此,在补丁过程中,所有这些都会被更新和重新渲染。这不是我们想要的。我们想要的是在不改变其他四个元素的情况下渲染添加的元素,所以不要重新渲染。在使用uniquekey的情况下,每个元素对应的位置关系为key,我们看一下使用uniquekeyvalue的情况,这样图中的li3和li4就不会重新渲染了,因为元素内容没有变化,对应的位置关系也没有变化。这就是为什么v-for一定要写key,开发中不建议使用数组的索引作为key。4、使用v-show复用DOMv-show:渲染组件,然后将组件的显示更改为block或nonev-if:渲染或不渲染组件,所以对于可以频繁改变条件的场景,使用v-show节省性能,尤其是DOM结构越复杂,好处越大。不过它也有缺点,就是v-show一开始,会渲染该分支内的所有组件,并执行相应的生命周期钩子函数,而v-if只会加载命中的组件判断条件,所以需要根据不同的场景使用合适的指令,比如下面用v-show复用DOM,比v-if/v-else原理是当条件发生变化时,使用v-if触发diffupdate,如果发现新旧vnode不一致,则移除整个vnode。旧的vnode,然后重新创建一个新的vnode,然后创建一个新的my-components组件,然后经过组件初始化、render、patch等过程,当v-show的条件发生变化时,新旧vnode是一致的,所以不会进行移除、创建等一系列过程。5.无状态组件的功能组件。对于一些纯展示,没有响应式数据,没有状态管理,没有生命周期钩子函数的,我们可以将它们设置为功能组件Component,提高渲染性能,因为会被当做一个函数,所以开销很低。原理是在patch过程中,功能组件render生成的虚拟DOM不会有递归子组件初始化的过程,所以渲染开销会低很多。它可以接受props,但是因为它不会创建实例,所以它不能使用this.xx在内部获取组件属性。写法如下<模板函数nal>

{{value}}
//或Vue.component('my-component',{functional:true,//表示该组件是功能组件props:{...},//optional//第二个参数是上下文,没有这个渲染:function(createElement,context){//...}})6.先来看一个子组件分割的例子上面这样的代码中,每次父组件传递的数字发生变化时,每次都会重新渲染,重新执行someThing这个耗时的任务。因此,优化之一就是使用计算属性,因为计算属性本身具有缓存计算结果的特性。二是拆分成子组件,因为Vue的更新是组件粒度的。虽然第一次数据变化会引起父组件的重新渲染,但是子组件不会重新渲染,因为里面没有任何变化,很耗时,任务自然不会重新执行,所以性能更好。优化后的代码如下7.变量本地化简单来说就是保存会被多次引用的变量,因为每次访问this.xx,因为是响应式对象,所以每次都会触发getter,然后就是依赖收集的相关代码执行。如果变量被多次使用,性能将会提高。自然是越差越好。从需求的角度来说,一个函数中对一个变量执行一次依赖收集就够了,但是很多人习惯性的写一大堆这个。它导致了性能问题,例如下面的例子@/utils'exportdefault{props:['number'],computed:{base(){return100},result(){letbase=this.base,number=this.number//saveitfor(leti=0;i<1000;i++){number+=someThing(base)//避免频繁引用this.xx}returnnumber}}}8.第三方插件引入第三方组件库比如Element-UI可以按需导入,避免体积过大,尤其是项目不大的时候,不需要完全导入组件库//main.jsimportElement3from"plugins/element3";Vue.use(Element3)//element3.js//完成importimportelement3from"element3";import"element3/lib/theme-chalk/index.css";//根据需要导入//import"element3/lib/theme-chalk/button.css";//...//import{//ElButton,//ElRow,//ElCol,//ElMain,//.....//}from"element3";exportdefaultfunction(app){//完成导入app.use(element3)//按需引入//app.use(ElButton);}9.路由懒加载我们知道Vue是单页应用,所以如果不使用懒加载,会导致进入首页时加载的内容过多,而且会耗时过长,长白屏不利于用户体验,SEO也不友好,所以可以使用懒加载来分页,需要的时候加载对应的页面,分担加载减轻首页压力,减少首页加载时间路由懒加载:importHomefrom'@/components/Home'constrouter=newVueRouter({routes:[{path:'/home',component:Home}]})使用路由延迟加载:constrouter=newVueRouter({routes:[{path:'/home',component:()=>import('@/components/Home')},{path:'/login',component:require('@/components/Home').default}]})进入这条路由会去对应的组件,然后运行import编译加载组件,可以理解为Promise的解析机制。变量函数等要求:AMD规范、运行时调用、赋值过程、支持变量计算函数等。更多前端模块化的内容可以看我的另一篇文章。前端模块化规范详解总结10.表单输入页面进入下一步后的保活缓存页面,然后返回上一步到表单页面保留表单输入的内容,比如在列表页>详情页>列表页,这样我们就可以使用内置组件缓存组件,组件切换时不卸载,这样当它们再次返回时,可以快速从缓存中渲染,而不是重新渲染以节省性能,只需要包装你的组件想要缓存也可以使用include/exclude来缓存/不缓存缓存指定的组件,通过activated/deactivated两个生命周期获取当前组件状态11.事件的销毁当一个Vue组件被销毁时,会自动解除绑定它所有的指令和事件监听器,但仅限于组件本身的事件和注册定时器和addEventListener监听器等,需要手动销毁或解绑在组件销毁的生命周期钩子中避免内存泄漏12.图片延迟加载图片延迟加载适用于图片较多的页面。为了提高页面加载速度,只加载可见区域的图片,滚动到可见区域后加载可见区域以外的图片。一些UI框架有自己的功能。如果不是呢?推荐一个第三方插件vue-lazyloadnpmivue-lazyload-S//main.jsimportVueLazyloadfrom'vue-lazyload'Vue.use(VueLazyload)//然后就可以在页面使用v-lazy延迟加载图片或Make你自己的轮子并手动封装自定义命令。这是一个与各种浏览器兼容的版本。主要目的是判断浏览器是否支持IntersectionObserverAPI。如果支持,就用它来实现懒加载。如果不支持,就用它来监听滚动事件。+实现constLazyLoad={//安装方法install(Vue,options){constdefaultSrc=options.defaultVue.directive('lazy',{bind(el,binding){LazyLoad.init(el,binding.value,defaultSrc)},inserted(el){if(IntersectionObserver){LazyLoad.observe(el)}else{LazyLoad.listenerScroll(el)}},})},//初始化init(el,val,def){el.setAttribute('data-src',val)el.setAttribute('src',def)},//使用IntersectionObserver监控elobserve(el){vario=newIntersectionObserver((entries)=>{constrealSrc=el.dataset.srcif(entries[0].isIntersecting){if(realSrc){el.src=realSrcel.removeAttribute('data-src')}}})io.observe(el)},//监听滚动事件listenerScroll(el){consthandler=LazyLoad.throttle(LazyLoad.load,300)LazyLoad.load(el)window.addEventListener('scroll',()=>{handler(el)})},//加载图片load(el){constwindowHeight=document.documentElement.clientHeightconstelTop=el.getBoundingClientRect().topconstelBtm=el.getBoundingClientRect().bottomconstrealSrc=el.dataset.srcif(elTop-windowHeight<0&&elBtm>0){if(realSrc){el.src=realSrcel.removeAttribute('data-src')}}}},//节流throttle(fn,delay){lettimerletprevTimereturnfunction(...args){constcurrTime=Date.now()constcontext=thisif(!prevTime)prevTime=currTimeclearTimeout(timer)if(currTime-prevTime>delay){prevTime=currTimefn.apply(context,args)clearTimeout(timer)return}timer=setTimeout(function(){prevTime=Date.now()timer=nullfn.apply(context,args)},delay)}},}exportdefaultLazyLoad是这样用的,用v-LazyLoad而不是src13。我没有在项目中实践过SSR,所以不会出错。SSR优化可以看这篇文章:Vue-SSR优化方案的详细总结,记录在这里,就不搬过来了。前段时间精彩而简单。虚拟DOM和Diff算法,以及Vue2和Vue3的区别。Vue3的7个组件和Vue2的12个组件进行通信。值得收藏最新的Vue3.2更新了什么了解JavaScript进阶知识点前端异常监控与容灾20分钟助你打赢HTTP和HTTPS,巩固你的HTTP知识体系thanks