中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue開發中怎么進行性能優化

發布時間:2022-02-24 13:34:39 來源:億速云 閱讀:147 作者:iii 欄目:編程語言

本篇內容介紹了“Vue開發中怎么進行性能優化”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

Vue開發中怎么進行性能優化

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

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
  }
   ...
}

可以看到一開始就判斷 configurablefalse 的直接返回不做響應式處理

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

Vue開發中怎么進行性能優化

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-forv-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

比如有一個列表,我們需要在中間插入一個元素,在不使用 key 或者使用 index 作為 key 會發生什么變化呢?先看個圖

Vue開發中怎么進行性能優化

如圖的 li1li2 不會重新渲染,這個沒有爭議的。而 li3、li4、li5 都會重新渲染

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

這可不是我們想要的,我們希望的是渲染添加的那一個元素,其他四個元素不做任何變更,也就不要重新渲染

而在使用唯一 key  的情況下,每個元素對應的位置關系就是 key,來看一下使用唯一 key 值的情況下

Vue開發中怎么進行性能優化

這樣如圖中的 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: ['value']
}
</script>

// 或者
Vue.component('my-component', {
 functional: true, // 表示該組件為函數式組件
 props: { ... }, // 可選
 // 第二個參數為上下文,沒有 this
 render: function (createElement, context) {
   // ...
}
})

6. 子組件分割

先看個例子

<template>
 <div :style="{ opacity: number / 100 }">
   <div>{{ someThing() }}</div>
 </div>
</template>
<script>
export default {
 props:['number'],
 methods: {
   someThing () { /* 耗時任務 */ }
}
}
</script>

上面這樣的代碼中,每次父組件傳過來的 number 發生變化時,每次都會重新渲染,并且重新執行 someThing 這個耗時任務

所以優化的話一個是用計算屬性,因為計算屬性自身有緩存計算結果的特性

第二個是拆分成子組件,因為 Vue 的更新是組件粒度的,雖然第次數據變化都會導致父組件的重新渲染,但是子組件卻不會重新渲染,因為它的內部沒有任何變化,耗時任務自然也就不會重新執行,因此性能更好,優化代碼如下

<template>
<div>  
 <my-child />
</div>
</template>
<script>
export default {
components: {  
 MyChild: {  
  methods: {    
   someThing () { /* 耗時任務 */ }    
  },   
   render (h) {  
    return h('div', this.someThing())  
  } 
 }
}
}
</script>

7. 變量本地化

簡單說就是把會多次引用的變量保存起來,因為每次訪問 this.xx 的時候,由于是響應式對象,所以每次都會觸發 getter,然后執行依賴收集的相關代碼,如果使用變量次數越多,性能自然就越差

從需求上說在一個函數里一個變量執行一次依賴收集就夠了,可是很多人習慣性的在項目中大量寫 this.xx,而忽略了 this.xx 背后做的事,就會導致性能問題了

比如下面例子

<template> 
 <div :style="{ opacity: number / 100 }"> {{ result
}}</div>
</template>
<script>
import { someThing } from '@/utils'
export default {
 props: ['number'], 
 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 '@/components/Home'
const router = new VueRouter({  
 routes: [  
  { path: '/home', component: Home }
]
})

用了路由懶加載:

const router = new VueRouter({
routes: [ 
 { path: '/home', component: () => 
import('@/components/Home') }, 
 { path: '/login', component: 
require('@/components/Home').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('touchmove', 
this.touchmove, false) 
 }, 
  beforeDestroy() {  
   clearInterval(this.timer)   
   this.timer = null   
   removeEventListener('touchmove', 
this.touchmove, false) 
 }
}
</script>

12. 圖片懶加載

圖片懶加載就是對于有很多圖片的頁面,為了提高頁面加載速度,只加載可視區域內的圖片,可視區域外的等到滾動到可視區域后再去加載

這個功能一些 UI 框架都有自帶的,如果沒有呢?

推薦一個第三方插件 vue-lazyload

npm i vue-lazyload -S

// main.js
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)

// 接著就可以在頁面中使用 v-lazy 懶加載圖片了
<img v-lazy="/static/images/1.png">

或者自己造輪子,手動封裝一個自定義指令,這里封裝好了一個兼容各瀏覽器的版本的,主要是判斷瀏覽器支不支持 IntersectionObserver API,支持就用它實現懶加載,不支持就用監聽 scroll 事件+節流的方式實現

const LazyLoad = { 
// install方法 
install(Vue, options) {  
 const defaultSrc = options.default 
 Vue.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監聽el
observe(el) {  
 var io = new IntersectionObserver((entries) => {  
  const realSrc = el.dataset.src    
 if (entries[0].isIntersecting) {   
  if (realSrc) {      
   el.src = realSrc       
   el.removeAttribute('data-src')    
  }   
 } 
}) 
 io.observe(el)
}, 
// 監聽scroll事件
listenerScroll(el) {  
 const handler =
LazyLoad.throttle(LazyLoad.load, 300) 
 LazyLoad.load(el)  
 window.addEventListener('scroll', () => {   
  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('data-src')  
  } 
 }
}, 
// 節流 
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" />

“Vue開發中怎么進行性能優化”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

vue
AI

阿坝县| 广德县| 新晃| 伊宁市| 个旧市| 鄂州市| 台州市| 出国| 凭祥市| 阿拉善盟| 哈巴河县| 儋州市| 隆林| 临海市| 岱山县| 东阿县| 台山市| 松潘县| 恭城| 故城县| 东城区| 长丰县| 庆元县| 清水河县| 永春县| 芮城县| 栾城县| 富民县| 枝江市| 新源县| 济宁市| 全椒县| 香格里拉县| 鲜城| 岳西县| 永胜县| 左权县| 扶风县| 兴安盟| 平江县| 湟源县|