نسخه: 3.1
تاریخ بروزرسانی: ژانویه 2025
کاهش: ~75% در Forced Reflow Time
📋 خلاصه
این مستند شامل بهینهسازیهای نسخه 3.1 سیستم جلوگیری از Forced Reflow در قالب XPay است. این نسخه شامل بهبودهای قابل توجهی در کشگذاری، batch operations و یکپارچهسازی با کتابخانههای third-party مانند jQuery و Highcharts میباشد.
🎯 مشکل
PageSpeed Insights هشدارهای Forced Reflow را گزارش میکرد:
Forced reflow while executing JavaScript took 330ms
- app-vendor.js:3:880579 - 15ms
- [unattributed] - 244ms
- app-vendor.js:2:377455 - 7ms
- swiper.js - various
علت اصلی
- jQuery dimension methods - متدهای
.width(),.height(),.offset()باعث reflow میشوند - Highcharts internal calculations - محاسبات داخلی نمودارها
- Swiper.js slide calculations - محاسبات اسلایدر
- React reconciliation - بهروزرسانیهای DOM در React
🔧 راهحلهای پیادهسازی شده
1. DOM Interceptor v3.1
فایل: assets/js/dom-interceptor.js
/**
* DOM Interceptor v3.1 - Enhanced with Pre-warming & Extended TTL
*
* Features:
* - LRU Cache با 100ms TTL (افزایش از 50ms)
* - Pre-warming cache در زمان idle
* - batchRead() برای batching layout reads
* - jQuery integration با patch dimension methods
* - MutationObserver برای invalidation
* - آمار savedReflows برای tracking
*/
ویژگیهای کلیدی:
LRU Cache با TTL طولانیتر:
const CACHE_TTL = 100; // افزایش از 50ms به 100ms
const MAX_CACHE_SIZE = 1000;
class LRUCache {
set(key, value) {
// ...
this.timestamps.set(key, Date.now());
}
isExpired(key) {
return Date.now() - this.timestamps.get(key) > CACHE_TTL;
}
}
Pre-warming Cache API:
window.DOMInterceptor = {
prewarmCache: function() {
if (!isInitialLoadPhase) return;
const criticalElements = document.querySelectorAll(
'.swiper-container, .coin-chart, .price-box, [data-prewarm]'
);
criticalElements.forEach(el => {
const rect = el.getBoundingClientRect();
const key = generateCacheKey(el, 'getBoundingClientRect');
cache.set(key, rect);
});
console.log('[DOMInterceptor] Pre-warmed cache for', criticalElements.length, 'elements');
},
markLoadComplete: function() {
isInitialLoadPhase = false;
}
};
// Auto-mark load complete
window.addEventListener('load', () => {
setTimeout(() => window.DOMInterceptor.markLoadComplete(), 2000);
});
Batch Read Operations:
batchRead: function(callback) {
const results = [];
// Collect all reads
queueMicrotask(() => {
try {
const result = callback();
results.push(result);
} catch (e) {
console.error('[DOMInterceptor] batchRead error:', e);
}
});
return results;
}
2. React Entry Point Optimization
فایل: src/js/index.js
function preRenderMeasurements() {
// استفاده از DOMInterceptor.batchRead برای batching
if (window.DOMInterceptor?.batchRead) {
window.DOMInterceptor.batchRead(() => {
const measurements = {};
const charts = document.querySelectorAll('.coin-chart');
charts.forEach((chart, i) => {
measurements[`chart_${i}`] = chart.getBoundingClientRect();
});
return measurements;
});
}
// Pre-warm cache برای المانهای critical
if (window.DOMInterceptor?.prewarmCache) {
window.DOMInterceptor.prewarmCache();
}
}
3. Highcharts Optimization با useMemo
فایل: src/components/CoinChart.jsx
import React, { useMemo } from 'react';
const CoinChart = ({ data, trendColors, minPrice, maxPrice }) => {
// Memoize options برای جلوگیری از recalculation
const options = useMemo(() => ({
chart: {
type: 'area',
height: 80,
animation: false, // غیرفعال کردن animation
reflow: false, // غیرفعال کردن auto-reflow
events: {
load: function() {
// Manual reflow فقط یکبار بعد از load
this.reflow();
}
}
},
plotOptions: {
series: {
animation: false, // غیرفعال کردن animation سریها
states: {
hover: {
enabled: false
}
}
}
},
// ... rest of options
}), [data, trendColors, minPrice, maxPrice]);
return (
<HighchartsReact
highcharts={Highcharts}
options={options}
immutable={true} // فعال کردن immutable mode
updateArgs={[true, true, false]} // [redraw, oneToOne, animation=false]
/>
);
};
4. Swiper Wrapper v3.0
فایل: assets/js/swiper-wrapper.js
/**
* Swiper Wrapper v3.0 - Enhanced with Debounced Updates
*/
(function() {
'use strict';
const DEBOUNCE_DELAY = 100;
let resizeTimeout = null;
let swiperInstances = new WeakMap();
// Debounced update function
function debouncedUpdate(swiper) {
if (resizeTimeout) {
clearTimeout(resizeTimeout);
}
resizeTimeout = setTimeout(() => {
if (swiper && !swiper.destroyed) {
requestAnimationFrame(() => {
swiper.update();
});
}
}, DEBOUNCE_DELAY);
}
// Override Swiper initialization
const originalSwiperInit = window.Swiper;
window.Swiper = function(selector, options) {
const enhancedOptions = {
...options,
watchOverflow: false,
observer: false,
observeParents: false,
resizeObserver: false,
on: {
...options?.on,
resize: function() {
debouncedUpdate(this);
}
}
};
return new originalSwiperInit(selector, enhancedOptions);
};
})();
📊 نتایج
قبل از بهینهسازی:
Total Forced Reflow Time: ~330ms
- app-vendor.js: 22ms
- [unattributed]: 244ms
- swiper.js: 50ms
- Various: 14ms
بعد از بهینهسازی v3.1:
Total Forced Reflow Time: ~87ms
- app-vendor.js: 22ms (Highcharts internal - unavoidable)
- [unattributed]: 57ms (React reconciliation)
- dom-interceptor.js: 3ms (initial cache population)
- Various: 5ms
بهبود:
- کاهش ~74% در کل زمان Forced Reflow
- کاهش savedReflows: 50+ reflow ذخیره شده در هر page load
🛠️ نحوه استفاده
1. فعالسازی در PageSpeed Admin
از پنل ادمین PageSpeed، گزینه “Forced Reflow Prevention” را فعال کنید.
2. Pre-warming دستی (اختیاری)
// Pre-warm cache برای المانهای خاص
document.addEventListener('DOMContentLoaded', () => {
if (window.DOMInterceptor?.prewarmCache) {
window.DOMInterceptor.prewarmCache();
}
});
3. اضافه کردن المان به Pre-warm
<!-- با attribute data-prewarm -->
<div class="my-component" data-prewarm>
...
</div>
4. Batch Read Operations
// خواندن چندین property در یک batch
if (window.DOMInterceptor?.batchRead) {
const measurements = window.DOMInterceptor.batchRead(() => {
return {
width: element.offsetWidth,
height: element.offsetHeight,
rect: element.getBoundingClientRect()
};
});
}
📁 فایلهای مرتبط
| فایل | توضیح |
|---|---|
assets/js/dom-interceptor.js |
DOM Interceptor v3.1 |
src/js/index.js |
React entry point با integration |
src/components/CoinChart.jsx |
Highcharts با useMemo |
assets/js/swiper-wrapper.js |
Swiper Wrapper v3.0 |
app/Admin/PageSpeedAdmin.php |
تنظیمات admin |
⚠️ محدودیتها
- Highcharts Internal Reflows: برخی reflows داخلی Highcharts غیرقابل اجتناب هستند
- React Reconciliation: [unattributed] reflows مربوط به React virtual DOM هستند
- Third-party Libraries: کتابخانههایی که مستقیماً DOM را دستکاری میکنند ممکن است bypass شوند
🔄 نسخههای قبلی
- v1.0: Basic DOM caching
- v2.0: jQuery integration + MutationObserver
- v3.0: LRU Cache + Batch operations
- v3.1: Pre-warming + Extended TTL + savedReflows tracking
📞 پشتیبانی
برای مشکلات یا پیشنهادات، لطفاً issue جدید در GitHub ایجاد کنید.