性能优化
自身就是一个很大的话题,并且它没有一个定式,最好是在具体的我的项目中具体分析,而不是说看到一个优化技巧肯定要用在我的项目当中,这篇文章次要聊聊在vue编码阶段有哪些常见的优化伎俩
应用 key
对于key
在这篇 请论述vue的diff算法文章有说到,key值在比照新旧虚构节点时能够辨识虚构节点,在更新子节点的时候,须要将旧虚构节点列表与新虚构节点雷同的节点进行更新。如果在比照过程中设置了key值,那么比照的速度就会快很多。对于通过循环生成的列表,应该给每个列表项增加一个稳固且惟一的key,这样有利于在列表发生变化时,尽量少删除、新增、改变元素。
应用解冻对象
什么是解冻对象?解冻对象其实就是通过Object.freeze(传一个对象)
将对象解冻,解冻之后,这个对象的属性就不能批改增加了,是不可变的,当然数组也能解冻,解冻后什么操作都不行,增删改就不要想了,因为解冻对象后不可变,vue会对解冻对象进行优化解决,vue不会将解冻的对象解决成响应式。咱们在理论我的项目开发中可能会解决不会扭转的数据,它只须要渲染到页面上就行了,所以这些数据是没必要变成响应式的,这时应用解冻对象能够缩小vue将对象变成响应式过程这个工夫。
当然它也有一个弊病,就是未来想批改对象中的数据,因为不是响应式的,所以也不会渲染在页面上。
⏳ 应用计算属性
如果模板中某个数据会应用屡次,并且该数据是通过计算失去的,尽量应用计算属性,咱们都晓得计算属性是有缓存的,计算属性函数依赖的数据在没有发生变化的状况下,会重复读取缓存数据,而计算属性函数并不会重复执行,但也有缺点,就是不能传参数。
非实时绑定的表单项
当应用v-model绑定一个表单项时,当用户扭转表单项的状态时,也会随之扭转数据,从而导致vue产生从新渲染(rerender),这会带来一些性能的开销。
特地是当用户扭转表单项时,页面有一些动画正在进行中,因为JS执行线程和浏览器渲染线程是互斥的,最终会导致动画呈现卡顿。
咱们能够通过应用lazy或不应用v-model的形式解决问题,但要留神,这样可能导致在某个时间段内数据和表单项的值不统一。
一个简略例子: 插入一个工作到列表中
当咱们间接应用v-model
进行双向绑定,先不加lazy
修饰符,而后将transition
过渡工夫调整为5s
<template> <div id="app"> <input type="text" placeholder="明天的工作实现了吗?" v-model="message" @change="addContent" /> <button @click="shuffle">随机排序</button> <transition-group name="nums" tag="ul" class="box"> <li v-for="(item, i) in likeList" :key="item" class="task"> <span>{{ item }}</span> <button @click="deleteContent(i)">实现</button> </li> </transition-group> </div></template><script>export default { data() { return { likeList: ["写代码", "看书", "吃饭", "追女孩子"], message: "", }; }, methods: { deleteContent(i) { this.likeList.splice(i, 1); }, shuffle() { this.likeList.sort(() => Math.random() - 0.5); }, addContent() { if (this.likeList.includes(this.message.replace(/\s+/g, ""))) { alert("当前任务已存在,请从新输出!"); this.message = ""; return; } this.likeList.unshift(this.message.replace(/\s+/g, "")); this.message = ""; }, },};</script><style>#app { width: 500px; margin: 0 auto;}* { list-style: none;}input { width: 600px; height: 40px; font-size: 24px; border: none; outline: none; border-style: solid; border-color: #ddd; background: paleturquoise; margin-bottom: 15px; text-indent: 1em;}.box { width: 500px; padding: 0 20px; margin-bottom: 200px;}.task { width: 100%; height: 50px; line-height: 50px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #ddd;}.task button { width: 70px; height: 30px;}.nums-enter { opacity: 0; transform: translateX(-300px);}.nums-enter-active,.nums-leave-active,.nums-move { /* 批改这里的工夫即可 */ transition: 5s;}.nums-leave-to { opacity: 0; transform: translateX(300px);}.nums-leave-active { position: absolute;}</style>
成果如下:
咱们能够很显著看到当增加一项内容的时候,不停的输出内容会导致页面渲染变慢
而后给v-model
加上lazy
修饰符再看看,是不是跟不加有很大区别
应用v-show代替v-if
对于频繁切换显示状态的元素,应用v-show能够保障虚构dom树的稳固,防止频繁的新增和删除元素,尤其是当外部蕴含大量的dom元素的节点。
应用提早装载(defer)
应用提早装载次要解决白屏问题,首页白屏工夫次要受两个因素的影响:
打包体积过大
包的体积过大须要耗费大量的传输工夫,导致Js传输实现前页面只有一个
<div>
,没有能够显示的内容。- 须要立刻渲染的内容过多
JS传输实现后,浏览器开始执行JS结构页面。然而可能一开始要渲染的组件太多了,不仅会导致Js执行工夫很长,而且执行完后浏览器要渲染的元素过多,从而导致白屏
打包体积过大须要自行优化打包体积,这里就不说了,次要聊聊渲染内容过多的问题。
一个可行的方法就是提早装载组件,让组件依照指定的先后顺序顺次一个一个渲染进去。
提早装载是一个思路,实质上就是利用requestAnimationFrame
事件分批渲染内容,它的具体实现多种多样。
keep-alive组件
对于keep-alive
能够看看这篇文章:请论述keep-alive组件的作用和原理
应用长列表优化
有的时候咱们须要在页面上显示特地长的列表,这种状况次要产生在挪动端或者后盾治理的页面中,在挪动端往往有个下拉刷新内容的性能,不停地往上翻,到底后会加载更多内容,这样会导致列表中会有很多元素,从而导致页面的卡顿,因为元素多了当前,浏览器渲染也须要工夫,特地是新增了一些元素,也会触发浏览器的重排重绘
,因而无论是内存的占用或者GPU的渲染都会给性能带来一些损耗。
举个栗子:
假如咱们须要在页面长列表中渲染10000条数据,代码如下:
//APP.vue<template> <div class="app"> <div class="scroller"> <Listltem v-for="item in item" :key="item.id" :item="item" /> </div> </div></template><script>import Listltem from "./components/Listltem.vue";var item = [];for (var i = 0; i < 10000;i++) { item.push({ id:i + 1, count:i + 1, })}export default { components:{ Listltem, }, data() { return { item, }; }, };</script><style lang="less" scoped></style>
//组件Listltem.vue<template> <div class="list-container"> <span>id{{item.id}}</span> <span>name{{item.count}}</span> <span>age{{item.count}}</span> </div></template><script>export default { props: { item:Object, }, data() { return {}; },};</script><style lang="less" scoped> .list-container{ margin: 0 auto; width: 500px; text-align: center; height: 54px; padding: 1em; display: grid; box-sizing: border-box; grid-template-columns: repeat(3,1fr); box-shadow: 0 0 3px rgba(0, 0, 0, 0.5); }</style>
成果如图:
接下来,咱们看下页面加载性能剖析图:
从图中能够分明的看到脚本的0执行占用6s
多,渲染用时将近1s
,而且CPU占用从开始的46MB
始终到196MB
那怎么解决这个问题呢?总体思路是这样的:让页面只显示咱们能看到的货色,看不到的货色不显示,而后监听滚动条的变动,当滚动条变动的时候从新显示可见区域就完事了,简略画个图:
初始样子:
当滑动了一个地位:
咱们只察看绿色边框区域就行了,当挪动一个地位后,示意1的数据条隐没了,示意7的数据条又呈现了,其实只是地位产生了变动,这就是次要实现的思路。
代码实现:
APP.vue
//APP.vue<template> <div id="app"> <RecycleScroller :items="items" :itemSize="54" class="scroller" v-slot="{item}" > <ListItem :item="item" /> </RecycleScroller> </div></template><script>import ListItem from "./components/Listltem.vue";import RecycleScroller from "./components/RecycleScroller.vue";var items = [];for (var i = 0; i < 10000; i++) { items.push({ id: i + 1, count: i + 1, });}export default { name: "App", components: { RecycleScroller, ListItem, }, data() { return { items, }; },};</script><style>#app { width: 100%; margin: 0 auto;}.scroller { width: 500px; height: 500px; margin: 0 auto;}</style>
ListItem.vue 组件
//ListItem.vue<template> <div class="list-container"> <span>id{{item.id}}</span> <span>name{{item.count}}</span> <span>age{{item.count}}</span> </div></template><script>export default { props: { item:Object, },};</script><style > .list-container{ margin: 0 auto; width: 500px; text-align: center; height: 54px; padding: 1em; display: grid; box-sizing: border-box; grid-template-columns: repeat(3,1fr); box-shadow: 0 0 3px rgba(0, 0, 0, 0.5); }</style>
RecycleScroller.vue 组件
// RecycleScroller.vue<template> <div class="recycle-scroller-container" @scroll="setPool" ref="container"> <div class="recycle-scroller-wrapper" :style="{ height: `${totalSize}px` }"> <div class="recycle-scroller-item" v-for="poolItem in pool" :key="poolItem.item[keyField]" :style="{ transform: `translateY(${poolItem.position}px)`, }" > <slot :item="poolItem.item"></slot> </div> </div> </div></template><script>export default { props: { //数据的数组 items: { type: Array, default: () => [], }, //每一条数据的高度 itemSize: { type: Number, default: 0, }, keyField: { //给我的items数组中,每个对象哪个属性代表惟一且稳固的编号 type: String, default: "id", }, }, data() { return { pool: [], //渲染池,保留以后须要渲染的数据 }; }, mounted() { this.setPool(); }, computed: { totalSize() { return this.items.length * this.itemSize; //总高度,每一个元素的高度 * 数量 }, }, methods: { //拿到须要被渲染的数据增加到 pool数组中 setPool() { let scrollTop = this.$refs.container.scrollTop; let height = this.$refs.container.clientHeight; let startIndex = Math.floor(scrollTop / this.itemSize);//获取到要截取数据的终点 let endIndex = Math.ceil((scrollTop + height) / this.itemSize);//获取到要截取数据的起点 let scrollPos = startIndex * this.itemSize; this.pool = this.items.slice(startIndex, endIndex).map((it, i) => ({ item: it, position: scrollPos + i * this.itemSize, })); }, },};</script><style>.recycle-scroller-container { overflow: auto;}.recycle-scroller-wrapper { position: relative;}.recycle-scroller-item { position: absolute; width: 100%; left: 0; top: 0;}</style>
最终成果
同样是渲染10000条数据,咱们来看下这种计划性能图:
从图中能够分明的看到脚本的执行用了335ms
,渲染用时6ms
,内存占用14.3MB到30MB,跟第一次性能图堪称天差地别。
下面的代码大家没必要记,对于长列表优化这块是有一个插件的,名字叫vue-virtual-scroller
,链接地址
接下来,咱们在我的项目中应用这个插件:
- 装置
npm i vue-virtual-scroller
- 批改代码,将原先
RecycleScroller
移除,导入新装置的组件,别忘了还要引入css文件import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
APP.vue
//APP.vue<template> <div id="app"> <RecycleScroller :items="items" :itemSize="54" class="scroller" v-slot="{item}" > <ListItem :item="item" /> </RecycleScroller> </div></template><script>import ListItem from "./components/Listltem.vue";import {RecycleScroller} from "vue-virtual-scroller";import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';//记得还有引入css// import RecycleScroller from "./components/RecycleScroller.vue";var items = [];for (var i = 0; i < 10000; i++) { items.push({ id: i + 1, count: i + 1, });}export default { name: "App", components: { RecycleScroller, ListItem, }, data() { return { items, }; },};</script><style>#app { width: 100%; margin: 0 auto;}.scroller { width: 500px; height: 500px; margin: 0 auto;}</style>
导入插件后,成果是一样的。
好了, 以上就是我的分享,欢送大家在评论区探讨鸭~
心愿小伙伴们点赞 反对一下哦~ ,我会更有能源的