目錄
#1. 長列表效能最佳化
1. 不做響應式
Vue3 裡則是新增了響應式
參考vue-virtual-scroller、vue-virtual-scroll-list
4. 使用 v-show 复用 DOM
5. 无状态的组件用函数式组件
6. 子组件分割
7. 变量本地化
8. 第三方插件按需引入
9. 路由懒加载
10. keep-alive缓存页面
11. 事件的销毁
12. 图片懒加载
首頁 web前端 Vue.js Vue開發中如何進行效能最佳化? 12 個優化小技巧分享

Vue開發中如何進行效能最佳化? 12 個優化小技巧分享

Feb 24, 2022 am 11:35 AM
vue開發 效能最佳化

Vue開發中如何進行效能最佳化?這篇文章跟大家分享12 個 Vue 開發中的效能優化小技巧,希望對大家有幫助!

Vue開發中如何進行效能最佳化? 12 個優化小技巧分享

效能最佳化,是每個開發者都會遇到的問題,特別是現在越來越重視體驗,以及競爭越來越激烈的環境下,對於我們開發者來說,只完成迭代,把功能做好是遠遠不夠的,最重要的是把產品做好,讓更多人願意使用,讓用戶用得更爽,這不也是我們開發者價值與能力的體現嗎。

重視效能問題,優化產品的體驗,比起改幾個無關痛癢的bug 要有價值得多

本文記錄了我在Vue 專案日常開發中的一些小技巧,廢話不多說,我們開始吧! 【相關推薦:vuejs影片教學

#1. 長列表效能最佳化

1. 不做響應式

例如會員清單、商品列表之類的,只是純粹的資料展示,不會有任何動態改變的場景下,就不需要對資料做回應化處理,可以大幅提升渲染速度

例如使用 Object.freeze( ) 凍結一個對象,MDN的描述是該方法凍結的對像不能被修改;即不能向這個對象添加新屬性,不能刪除已有屬性,不能修改該對像已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值,以及該物件的原型也不能被修改

export default {
 data: () => ({
   userList: []
}),
 async created() {
   const users = await axios.get("/api/users");
   this.userList = Object.freeze(users);
}
};
登入後複製

Vue2 的響應式原始碼位址:src/core/observer/index. js - 144行 是這樣的

export function defineReactive (...){
   const property = Object.getOwnPropertyDescriptor(obj, key)
   if (property && property.configurable === false) {
       return
  }
   ...
}
登入後複製

可以看到一開始就判斷 configurable 為 false 的直接回傳不做回應式處理

configurable 為 false 表示這個屬性是不能被修改的,而凍結的物件的 configurable 就是為 false

##configurable

 就是為 Vue開發中如何進行效能最佳化? 12 個優化小技巧分享false

Vue3 裡則是新增了響應式

flag

#,用於標記目標物件類型

2. 虛擬滾動

如果是大數據很長的列表,全部渲染的話一次性創建太多DOM 就會非常卡,這時就可以用虛擬滾動,只渲染少部分(含可視區域)區域的內容,然後滾動的時候,不斷取代可視區域的內容,模擬出滾動的效果

<recycle-scroller
 class="items"
 :items="items"
 :item-size="24"
>
 <template v-slot="{ item }">
   <FetchItemView
     :item="item"
     @vote="voteItem(item)"
   />
 </template>
</recycle-scroller>
登入後複製

參考vue-virtual-scroller、vue-virtual-scroll-list

##原理是監聽滾動事件,動態更新需要顯示的DOM,併計算出在視圖中的位移,這也意味著在滾動過程需要即時計算,有一定成本,所以如果數據量不是很大的情況下,用普通的滾動就行

2. v-for 遍歷避免同時使用v-if為什麼要避免同時使用 v-for

 和 

v-if

在Vue2中 

v-for 優先權更高,所以編譯過程中會把列表元素全部遍歷產生虛擬DOM,再來透過v-if 判斷符合條件的才渲染,就會造成效能的浪費,因為我們希望的是不符合條件的虛擬DOM都不要產生

在Vue3 中 

v-if

 的優先順序較高,就表示當判斷條件是v-for 遍歷的列表中的屬性的話,v-if 是拿不到的

所以在一些需要同時用到的場景,就可以透過計算屬性來過濾一下列表,如下

<template>
   <ul>
     <li v-for="item in activeList" :key="item.id">
      {{ item.title }}
     </li>
   </ul>
</template>
<script>
// Vue2.x
export default {
   computed: {
     activeList() {
       return this.list.filter( item => {
         return item.isActive
      })
    }
  }
}

// Vue3
import { computed } from "vue";
const activeList = computed(() => {
 return list.filter( item => {
   return item.isActive
})
})
</script>
登入後複製

3. 列表使用唯一key

Vue開發中如何進行效能最佳化? 12 個優化小技巧分享例如有一個列表,我們需要在中間插入一個元素,在不使用key 或使用index 作為key 會發生什麼變化呢?先看個圖片

如圖的 li1 和 li2

 不會重新渲染,這沒有爭議的。而 

li3、li4、li5 都會重新渲染因為在不使用 key 或清單的 index

 作為 

key

的時候,每個元素對應的位置關係都是index,上圖中的結果直接導致我們插入的元素到後面的全部元素,對應的位置關係都發生了變更,所以在patch 過程中會將它們全都執行更新操作,再重新渲染。

這可不是我們想要的,我們希望的是渲染添加的那一個元素,其他四個元素不做任何變更,也就不要重新渲染而在使用唯一 key  的情況下,每個元素對應的位置關係就是 key

,來看看使用唯一 ###key### 值的情況下###

Vue開發中如何進行效能最佳化? 12 個優化小技巧分享

这样如图中的 li3li4 就不会重新渲染,因为元素内容没发生改变,对应的位置关系也没有发生改变。

这也是为什么 v-for 必须要写 key,而且不建议开发中使用数组的 index 作为 key 的原因

4. 使用 v-show 复用 DOM

v-show:是渲染组件,然后改变组件的 display 为 block 或 none v-if:是渲染或不渲染组件

所以对于可以频繁改变条件的场景,就使用 v-show 节省性能,特别是 DOM 结构越复杂收益越大

不过它也有劣势,就是 v-show 在一开始的时候,所有分支内部的组件都会渲染,对应的生命周期钩子函数都会执行,而 v-if 只会加载判断条件命中的组件,所以需要根据不同场景使用合适的指令

比如下面的用 v-show 复用DOM,比 v-if/v-else 效果好

<template>
 <div>
   <div v-show="status" class="on">
     <my-components />
   </div>
   <section v-show="!status" class="off">
     <my-components >
   </section>
 </div>
</template>
登入後複製

原理就是使用 v-if 当条件变化的时候,触发 diff 更新,发现新旧 vnode 不一致,就会移除整个旧的 vnode,再重新创建新的 vnode,然后创建新的 my-components 组件,又会经历组件自身初始化,renderpatch 等过程,而 v-show 在条件变化的时候,新旧 vnode 是一致的,就不会执行移除创建等一系列流程

5. 无状态的组件用函数式组件

对于一些纯展示,没有响应式数据,没有状态管理,也不用生命周期钩子函数的组件,我们就可以设置成函数式组件,提高渲染性能,因为会把它当成一个函数来处理,所以开销很低

原理是在 patch 过程中对于函数式组件的 render 生成的虚拟 DOM,不会有递归子组件初始化的过程,所以渲染开销会低很多

它可以接受 props,但是由于不会创建实例,所以内部不能使用 this.xx 获取组件属性,写法如下

<template functional>
 <div>
   <div class="content">{{ value }}</div>
 </div>
</template>
<script>
export default {
 props: [&#39;value&#39;]
}
</script>

// 或者
Vue.component(&#39;my-component&#39;, {
 functional: true, // 表示该组件为函数式组件
 props: { ... }, // 可选
 // 第二个参数为上下文,没有 this
 render: function (createElement, context) {
   // ...
}
})
登入後複製

6. 子组件分割

先看个例子

<template>
 <div :style="{ opacity: number / 100 }">
   <div>{{ someThing() }}</div>
 </div>
</template>
<script>
export default {
 props:[&#39;number&#39;],
 methods: {
   someThing () { /* 耗时任务 */ }
}
}
</script>
登入後複製

上面这样的代码中,每次父组件传过来的 number 发生变化时,每次都会重新渲染,并且重新执行 someThing 这个耗时任务

所以优化的话一个是用计算属性,因为计算属性自身有缓存计算结果的特性

第二个是拆分成子组件,因为 Vue 的更新是组件粒度的,虽然第次数据变化都会导致父组件的重新渲染,但是子组件却不会重新渲染,因为它的内部没有任何变化,耗时任务自然也就不会重新执行,因此性能更好,优化代码如下

<template>
<div>  
 <my-child />
</div>
</template>
<script>
export default {
components: {  
 MyChild: {  
  methods: {    
   someThing () { /* 耗时任务 */ }    
  },   
   render (h) {  
    return h(&#39;div&#39;, this.someThing())  
  } 
 }
}
}
</script>
登入後複製

7. 变量本地化

简单说就是把会多次引用的变量保存起来,因为每次访问 this.xx 的时候,由于是响应式对象,所以每次都会触发 getter,然后执行依赖收集的相关代码,如果使用变量次数越多,性能自然就越差

从需求上说在一个函数里一个变量执行一次依赖收集就够了,可是很多人习惯性的在项目中大量写 this.xx,而忽略了 this.xx 背后做的事,就会导致性能问题了

比如下面例子

<template> 
 <div :style="{ opacity: number / 100 }"> {{ result
}}</div>
</template>
<script>
import { someThing } from &#39;@/utils&#39;
export default {
 props: [&#39;number&#39;], 
 computed: {  
  base () { return 100 },  
  result () {   
   let base = this.base, number = this.number // 
保存起来    
  for (let i = 0; i < 1000; i++) {   
   number += someThing(base) // 避免频繁引用
this.xx   
  }   
   return number 
  }
}
}
</script>
登入後複製

8. 第三方插件按需引入

比如 Element-UI 这样的第三方组件库可以按需引入避免体积太大,特别是项目不大的情况下,更没有必要完整引入组件库

// main.js
import Element3 from "plugins/element3";
Vue.use(Element3)

// element3.js
// 完整引入
import element3 from "element3";
import "element3/lib/theme-chalk/index.css";

// 按需引入
// import "element3/lib/theme-chalk/button.css";
// ...
// import { 
// ElButton, 
// ElRow, 
// ElCol, 
// ElMain, 
// .....
// } from "element3";

export default function (app) { 
// 完整引入 
app.use(element3)  

// 按需引入 
// app.use(ElButton);
}
登入後複製

9. 路由懒加载

我们知道 Vue 是单页应用,所以如果没有用懒加载,就会导致进入首页时需要加载的内容过多,时间过长,就会出现长时间的白屏,很不利于用户体验,SEO 也不友好

所以可以去用懒加载将页面进行划分,需要的时候才加载对应的页面,以分担首页的加载压力,减少首页加载时间

没有用路由懒加载:

import Home from &#39;@/components/Home&#39;
const router = new VueRouter({  
 routes: [  
  { path: &#39;/home&#39;, component: Home }
]
})
登入後複製

用了路由懒加载:

const router = new VueRouter({
routes: [ 
 { path: &#39;/home&#39;, component: () => 
import(&#39;@/components/Home&#39;) }, 
 { path: &#39;/login&#39;, component: 
require(&#39;@/components/Home&#39;).default }
]
})
登入後複製

在进入这个路由的时候才会走对应的 component,然后运行 import 编译加载组件,可以理解为 Promiseresolve 机制

  • import:Es6语法规范、编译时调用、是解构过程、不支持变量函数等
  • require:AMD规范、运行时调用、是赋值过程,支持变量计算函数等

更多有关前端模块化的内容可以看我另一篇文章 前端模块化规范详细总结

10. keep-alive缓存页面

比如在表单输入页面进入下一步后,再返回上一步到表单页时要保留表单输入的内容、比如在列表页>详情页>列表页,这样来回跳转的场景等

我们都可以通过内置组件 <keep-alive></keep-alive> 来把组件缓存起来,在组件切换的时候不进行卸载,这样当再次返回的时候,就能从缓存中快速渲染,而不是重新渲染,以节省性能

只需要包裹想要缓存的组件即可

<template>
 <div id="app"> 
  <keep-alive>  
   <router-view/> 
  </keep-alive>
</div>
</template>
登入後複製
  • 也可以用 include/exclude 来 缓存/不缓存 指定组件
  • 可通过两个生命周期 activated/deactivated 来获取当前组件状态

11. 事件的销毁

Vue 组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件

而对于定时器addEventListener 注册的监听器等,就需要在组件销毁的生命周期钩子中手动销毁或解绑,以避免内存泄露

<script>
export default {  
 created() {   
  this.timer = setInterval(this.refresh, 2000)  
  addEventListener(&#39;touchmove&#39;, 
this.touchmove, false) 
 }, 
  beforeDestroy() {  
   clearInterval(this.timer)   
   this.timer = null   
   removeEventListener(&#39;touchmove&#39;, 
this.touchmove, false) 
 }
}
</script>
登入後複製

12. 图片懒加载

图片懒加载就是对于有很多图片的页面,为了提高页面加载速度,只加载可视区域内的图片,可视区域外的等到滚动到可视区域后再去加载

这个功能一些 UI 框架都有自带的,如果没有呢?

推荐一个第三方插件 vue-lazyload

npm i vue-lazyload -S

// main.js
import VueLazyload from &#39;vue-lazyload&#39;
Vue.use(VueLazyload)

// 接着就可以在页面中使用 v-lazy 懒加载图片了
<img  v-lazy="/static/images/Vue開發中如何進行效能最佳化? 12 個優化小技巧分享" alt="Vue開發中如何進行效能最佳化? 12 個優化小技巧分享" >
登入後複製

或者自己造轮子,手动封装一个自定义指令,这里封装好了一个兼容各浏览器的版本的,主要是判断浏览器支不支持 IntersectionObserver API,支持就用它实现懒加载,不支持就用监听 scroll 事件+节流的方式实现

const LazyLoad = { 
// install方法 
install(Vue, options) {  
 const defaultSrc = options.default 
 Vue.directive(&#39;lazy&#39;, {  
  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(&#39;data-src&#39;, val)  
 el.setAttribute(&#39;src&#39;, def)
}, 
// 利用IntersectionObserver监听el
observe(el) {  
 var io = new IntersectionObserver((entries) => {  
  const realSrc = el.dataset.src    
 if (entries[0].isIntersecting) {   
  if (realSrc) {      
   el.src = realSrc       
   el.removeAttribute(&#39;data-src&#39;)    
  }   
 } 
}) 
 io.observe(el)
}, 
// 监听scroll事件
listenerScroll(el) {  
 const handler =
LazyLoad.throttle(LazyLoad.load, 300) 
 LazyLoad.load(el)  
 window.addEventListener(&#39;scroll&#39;, () => {   
  handler(el) 
 })
}, 
// 加载真实图片 
load(el) {  
 const windowHeight =
document.documentElement.clientHeight
 const elTop = el.getBoundingClientRect().top  
 const elBtm = 
el.getBoundingClientRect().bottom 
 const realSrc = el.dataset.src  
 if (elTop - windowHeight < 0 && elBtm > 0) {  
  if (realSrc) {     
   el.src = realSrc    
   el.removeAttribute(&#39;data-src&#39;)  
  } 
 }
}, 
// 节流 
throttle(fn, delay) {  
 let timer  
 let prevTime  
 return function (...args) {   
  const currTime = Date.now() 
  const context = this    
  if (!prevTime) prevTime = currTime  
  clearTimeout(timer)   
  
  if (currTime - prevTime > delay) {     
   prevTime = currTime      
   fn.apply(context, args)    
   clearTimeout(timer)      
   return   
 }  

 timer = setTimeout(function () {   
  prevTime = Date.now()  
  timer = null   
  fn.apply(context, args)   
}, delay) 
}
},
}
export default LazyLoad
登入後複製

使用上是这样的,用 v-LazyLoad 代替 src

<img  v-LazyLoad="xxx.jpg" / alt="Vue開發中如何進行效能最佳化? 12 個優化小技巧分享" >
登入後複製

(学习视频分享:vuejs教程web前端

以上是Vue開發中如何進行效能最佳化? 12 個優化小技巧分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Go 框架的效能優化與橫向擴展技術? Go 框架的效能優化與橫向擴展技術? Jun 03, 2024 pm 07:27 PM

為了提高Go應用程式的效能,我們可以採取以下優化措施:快取:使用快取減少對底層儲存的存取次數,提高效能。並發:使用goroutine和channel並行執行冗長的任務。記憶體管理:手動管理記憶體(使用unsafe套件)以進一步優化效能。為了橫向擴展應用程序,我們可以實施以下技術:水平擴展(橫向擴展):在多個伺服器或節點上部署應用程式實例。負載平衡:使用負載平衡器將請求指派到多個應用程式執行個體。資料分片:將大型資料集分佈在多個資料庫或儲存節點上,提高查詢效能和可擴充性。

C++ 效能最佳化指南:探索提高程式碼執行效率的秘訣 C++ 效能最佳化指南:探索提高程式碼執行效率的秘訣 Jun 01, 2024 pm 05:13 PM

C++效能最佳化涉及多種技術,包括:1.避免動態分配;2.使用編譯器最佳化標誌;3.選擇最佳化資料結構;4.應用快取;5.並行程式設計。優化實戰案例展示如何在整數數組中找到最長上升子序列時應用這些技術,將演算法效率從O(n^2)提升至O(nlogn)。

優化之道:探尋java框架的效能提升之旅 優化之道:探尋java框架的效能提升之旅 Jun 01, 2024 pm 07:07 PM

透過實作快取機制、平行處理、資料庫最佳化和減少記憶體消耗,可以提升Java框架的效能。快取機制:減少資料庫或API請求次數,提高效能。並行處理:利用多核心CPU同時執行任務,提高吞吐量。資料庫最佳化:最佳化查詢、使用索引、設定連接池,提升資料庫效能。減少記憶體消耗:使用輕量級框架、避免洩漏、使用分析工具,減少記憶體消耗。

NGINX性能調整:針對速度和低潛伏期進行優化 NGINX性能調整:針對速度和低潛伏期進行優化 Apr 05, 2025 am 12:08 AM

Nginx性能調優可以通過調整worker進程數、連接池大小、啟用Gzip壓縮和HTTP/2協議、使用緩存和負載均衡來實現。 1.調整worker進程數和連接池大小:worker_processesauto;events{worker_connections1024;}。 2.啟用Gzip壓縮和HTTP/2協議:http{gzipon;server{listen443sslhttp2;}}。 3.使用緩存優化:http{proxy_cache_path/path/to/cachelevels=1:2k

利用 C++ 優化火箭引擎性能 利用 C++ 優化火箭引擎性能 Jun 01, 2024 pm 04:14 PM

通过建立数学模型、进行模拟和优化参数,C++可显著提高火箭发动机性能:建立火箭发动机的数学模型,描述其行为。模拟发动机性能,计算关键参数(如推力和比冲)。识别关键参数并使用优化算法(如遗传算法)搜索最佳值。根据优化后的参数重新计算发动机性能,提高其整体效率。

如何快速診斷 PHP 效能問題 如何快速診斷 PHP 效能問題 Jun 03, 2024 am 10:56 AM

快速診斷PHP效能問題的有效技術包括:使用Xdebug取得效能數據,然後分析Cachegrind輸出。使用Blackfire查看請求跟踪,產生效能報告。檢查資料庫查詢,識別低效率查詢。分析記憶體使用情況,查看記憶體分配和峰值使用。

Java 中如何使用輪廓分析來優化效能? Java 中如何使用輪廓分析來優化效能? Jun 01, 2024 pm 02:08 PM

Java中的輪廓分析用於確定應用程式執行中的時間和資源消耗。使用JavaVisualVM實作輪廓分析:連線至JVM開啟輪廓分析,設定採樣間隔執行應用程式停止輪廓分析分析結果顯示執行時間的樹狀視圖。優化效能的方法包括:識別熱點減少方法呼叫最佳化演算法

程式效能優化有哪些常見的方法? 程式效能優化有哪些常見的方法? May 09, 2024 am 09:57 AM

程式效能最佳化方法包括:演算法最佳化:選擇時間複雜度較低的演算法,減少迴圈和條件語句。資料結構選擇:根據資料存取模式選擇合適的資料結構,例如查找樹和雜湊表。記憶體最佳化:避免建立不必要對象,釋放不再使用的內存,使用記憶體池技術。執行緒優化:識別可並行化任務,優化執行緒同步機制。資料庫最佳化:建立索引加快資料檢索,優化查詢語句,使用快取或NoSQL資料庫提升效能。

See all articles