這是我參與更文挑戰的第9天,活動詳情查看: 更文挑戰css
開心,T恤到手~前端
策之不以其道,食之不能盡其材,鳴之而不能通其意,執策而臨之,曰:「天下無馬!」嗚呼!其真無馬邪?其真不知馬也!vue
運營人員反饋某商品列表頁面,每頁1000條時,頁面卡頓嚴重,已經影響使用,經實際測試,1000個條記錄時,頁面加載長達2分鐘+,而且會出現卡死狀況。遂開始優化之旅~~java
頁面主體部分截圖以下,每一個元素包括一張主圖,一些基本的信息字段。運營反饋當設置分頁數量爲1000時,會出現卡死,通過實際測試,能夠重現。數據庫
主體前端代碼分爲兩個部分,一個是包裹這些元素的父元素,一個是元素組件:後端
<!-- 父元素關鍵代碼 -->
<div v-loading="productsLoading" class="choosingproduct-products">
<!-- product-card爲子組件,使用v-model綁定列表元素 -->
<product-card
v-for="(item, index) in result.list"
:key="index"
v-model="result.list[index]"
:showpoint="false"
/>
<div style="width: 100%;margin-top: 15px;margin-bottom: 85px" class="center">
<pagination
:total="result.totalRow"
:page.sync="query.pageNum"
:limit.sync="query.numPerPage"
@pagination="getSkus"
/>
</div>
</div>
複製代碼
子組件ProductCard
的關鍵代碼以下:markdown
<template>
<div class="productcard flex-col-start-start" @click="changeChoosed">
<div class="flex-row-center-spacebetween" style="width: 100%">
<!-- 關鍵點1:狀態切換使用了visibility -->
<el-checkbox v-model="value.choosed" class="productcard-checkbox " :class="{'productcard-delete' : !value.choosed}" @change="changeChoosed" />
</div>
<div style="width: 100%" class="center">
<!-- 關鍵點2: 圖片過多 -->
<el-image :src="value.picurl" style="height: 114px;width: 114px" />
</div>
<div .... />
</div>
</template>
<script>
export default {
name: 'ProductCard',
props: {
value: {
type: Object,
default() {
return {}
}
},
},
data() {
return {
product: {
choosed: false
}
}
},
watch: {
// 關鍵點3: 也是最重要的一點,watch監聽不當,致使性能與卡頓
product: {
deep: true,
handler: function(newValue, oldValue) {
// 向上層組件發送改變後的數據
this.$emit('input', this.product)
}
},
value: {
deep: true,
handler: function() {
this.product = this.value
}
}
},
created() {
this.product = this.value
},
methods: {
changeChoosed() {
// 改變選中狀態
this.product.choosed = !this.product.choosed
}
}
}
</script>
<style scoped lang="scss">
.productcard {
.productcard-checkbox {
visibility: hidden;
}
.productcard-delete {
visibility: hidden;
}
}
.productcard:hover {
.productcard-checkbox {
visibility: visible;
}
.productcard-delete {
visibility: visible;
}
}
</style>
複製代碼
後端根據前端選擇的條件,執行查詢SQL,返回數據,使用的數據庫鏈接池Druid+ActiveRecord模式。(對這一點感興趣的能夠檢索jfinal相關)curl
public Page<Record> getData() {
......
Page<Record> result = Db.paginate(pageNum, numPerPage, select, exceptSelect);
for(Record r : result.getList()) {
display(r);
}
return result;
}
複製代碼
由於後端的性能更好分析一些,本人也更熟悉後端,因此先從後端入手:svn
通過測試,接口返回1000條數據的時間大約是17秒。post
使用Chrome-Performance監聽加載1000條數據的性能狀況,咱們獲得以下幾張圖:
從而肯定了卡頓問題主要在前端,並且從renderList
咱們猜想,卡頓主要在於ProductCard
這個組件的渲染上。
定位到了ProductCard
組件後,咱們首先根據Network查看到圖片的加載時間較長,猜想多是圖片過多致使加載過長。
第二個,咱們注意到選中與不選中,咱們在CSS中是經過變換組件的visibility屬性來實現的。是不是visibility切換會致使組件渲染卡頓呢?咱們檢索資料發現visibility:hidden
的性能實際比display:none
要好。
第三個(終於找到你!),咱們在組件中使用了watch
,同時監聽組件內部元素product
與props
中的value
,當product變更時,會經過this.$emit('input', this.product)
傳遞給外部的value
,而value
變更,又會觸發this.product = this.value
,進而又致使product
變更,造成近乎死循環。形成嚴重性能問題。
咱們獲得如下結論:
watch
咱們使用parallelStream
優化後端性能,優化後代碼以下。接口響應時間從17秒左右降低到5秒。
public Page<Record> getData() {
......
Page<Record> result = Db.paginate(pageNum, numPerPage, select, exceptSelect);
result.getList().parallelStream().forEach(r -> {
display(r);
});
return result;
}
複製代碼
針對分析中的三點,咱們分別使用圖片懶加載和修復watch
後,1000條數據頁面耗時大約爲10秒,且不在出現卡死現象。
優化後的watch
以下:
.......
watch: {
value: {
deep: true,
handler: function() {
this.product = this.value
}
}
},
created() {
this.product = this.value
},
methods: {
changeChoosed() {
this.product.choosed = !this.product.choosed
// 向上層發送數據
this.$emit('input', this.product)
}
......
}
複製代碼
前端沒有什麼優化經驗,歡迎指正!
若不吝可點個贊!