AMP • TR
2025'te web sitesi hız optimizasyonu, performans artırma teknikleri ve Core Web Vitals iyileştirme rehberi. PageSpeed ve loading time optimization.
Web sitesi hızı artık bir lüks değil, zorunluluk! Google'ın ranking faktörü, kullanıcı deneyiminin temelive dönüşüm oranlarının belirleyicisi. Bu rehberde, 2025'te web performansını maksimuma çıkarmanın tüm yollarını öğreneceksiniz.
📊 İstatistikler:
Google'ın resmi performans metrikleri:
Hedef: < 2.5 saniye
En büyük içeriğin yüklenme süresi.
// LCP tracking
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log('LCP:', lastEntry.renderTime || lastEntry.loadTime);
}).observe({ entryTypes: ['largest-contentful-paint'] });
Optimizasyon:
<!-- Priority hint -->
<img src="hero.jpg" fetchpriority="high" loading="eager">
<!-- Preload critical images -->
<link rel="preload" as="image" href="hero.jpg">
<!-- WebP format -->
<picture>
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero">
</picture>
Hedef: < 100ms (FID) / < 200ms (INP)
İlk etkileşime yanıt süresi.
// INP measurement
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const delay = entry.processingStart - entry.startTime;
console.log('Input delay:', delay);
}
});
observer.observe({ type: 'first-input', buffered: true });
Optimizasyon:
// Code splitting
const HeavyComponent = lazy(() => import('./HeavyComponent'));
// Debounce expensive operations
const debouncedSearch = debounce((query) => {
// Expensive search operation
}, 300);
// Use Web Workers for heavy computations
const worker = new Worker('heavy-calc.js');
worker.postMessage(data);
Hedef: < 0.1
Görsel kararlılık - beklenmeyen kaymalara karşı.
/* Reserve space for images */
img {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
}
/* Font loading optimization */
@font-face {
font-family: 'MyFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* Prevent layout shift */
}
/* Ad placeholder */
.ad-container {
min-height: 250px; /* Reserve space */
}
Modern Formats:
<picture>
<!-- AVIF (best compression, 2025 standard) -->
<source srcset="image.avif" type="image/avif">
<!-- WebP (good fallback) -->
<source srcset="image.webp" type="image/webp">
<!-- JPEG (universal fallback) -->
<img src="image.jpg" alt="Description">
</picture>
Responsive Images:
<img
srcset="
small.jpg 400w,
medium.jpg 800w,
large.jpg 1200w
"
sizes="
(max-width: 600px) 400px,
(max-width: 900px) 800px,
1200px
"
src="large.jpg"
alt="Responsive image"
loading="lazy"
decoding="async"
>
Image CDN:
// Cloudinary example
const imageUrl = `https://res.cloudinary.com/demo/image/upload/
w_800, // Width
f_auto, // Format auto (AVIF/WebP/JPEG)
q_auto, // Quality auto
c_fill, // Crop mode
g_auto // Gravity auto (smart crop)
/sample.jpg`;
/* Font loading strategy */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
/* Optional: unicode-range for subsetting */
unicode-range: U+0020-007F;
}
/* Preload critical fonts */
<link
rel="preload"
href="/fonts/inter.woff2"
as="font"
type="font/woff2"
crossorigin
>
Variable Fonts (2025 Best Practice):
@font-face {
font-family: 'Inter Variable';
src: url('inter-variable.woff2') format('woff2');
font-weight: 100 900; /* All weights in one file! */
font-display: swap;
}
Critical CSS:
<style>
/* Inline critical above-the-fold CSS */
body { margin: 0; font-family: sans-serif; }
.header { background: #000; color: #fff; }
.hero { height: 100vh; }
</style>
<!-- Async load rest -->
<link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'">
CSS Minification & Purging:
// PostCSS config
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
require('cssnano')({
preset: ['advanced', {
discardComments: { removeAll: true },
reduceIdents: true,
zindex: false
}]
}),
require('@fullhuman/postcss-purgecss')({
content: ['./src/**/*.html', './src/**/*.js'],
safelist: ['active', 'hover']
})
]
};
Code Splitting:
// Route-based code splitting
const routes = [
{
path: '/',
component: () => import('./pages/Home.js')
},
{
path: '/about',
component: () => import('./pages/About.js')
}
];
// Dynamic import
button.addEventListener('click', async () => {
const module = await import('./heavy-module.js');
module.init();
});
Tree Shaking:
// ❌ Import all
import _ from 'lodash';
// ✅ Import only what you need
import { debounce } from 'lodash-es';
// ✅ Even better: individual package
import debounce from 'lodash.debounce';
Minification & Compression:
// Webpack config
module.exports = {
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // Remove console.logs
pure_funcs: ['console.info', 'console.debug']
}
}
})
],
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10
}
}
}
}
};
# Nginx configuration
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.(html)$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
// service-worker.js
const CACHE_NAME = 'v1';
const urlsToCache = [
'/',
'/styles.css',
'/script.js',
'/logo.png'
];
// Install
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
);
});
// Fetch with cache-first strategy
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => response || fetch(event.request))
);
});
<!-- Static assets via CDN -->
<script src="https://cdn.example.com/js/app.min.js"></script>
<link rel="stylesheet" href="https://cdn.example.com/css/style.min.css">
<!-- DNS prefetch for external domains -->
<link rel="dns-prefetch" href="https://cdn.example.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
image/svg+xml;
# Brotli compression (better than gzip)
brotli on;
brotli_comp_level 6;
brotli_types
text/plain
text/css
application/javascript
application/json;
# Enable HTTP/2
listen 443 ssl http2;
# Enable HTTP/3 (QUIC)
listen 443 quic reuseport;
add_header Alt-Svc 'h3=":443"; ma=86400';
// Next.js example
export async function getServerSideProps() {
const data = await fetchData();
return {
props: { data }
};
}
// Static Generation (even better!)
export async function getStaticProps() {
const data = await fetchData();
return {
props: { data },
revalidate: 60 // ISR: Revalidate every 60 seconds
};
}
-- ❌ BAD: N+1 query problem
SELECT * FROM users;
-- Then for each user:
SELECT * FROM posts WHERE user_id = ?;
-- ✅ GOOD: Join query
SELECT users.*, posts.*
FROM users
LEFT JOIN posts ON users.id = posts.user_id;
-- Add indexes
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at DESC);
// Redis caching example
const redis = require('redis');
const client = redis.createClient();
async function getUser(id) {
// Check cache first
const cached = await client.get(`user:${id}`);
if (cached) return JSON.parse(cached);
// If not in cache, query database
const user = await db.query('SELECT * FROM users WHERE id = ?', [id]);
// Store in cache
await client.setex(`user:${id}`, 3600, JSON.stringify(user));
return user;
}
// Web Vitals tracking
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric) {
// Send to Google Analytics
gtag('event', metric.name, {
value: Math.round(metric.value),
metric_id: metric.id,
metric_value: metric.value,
metric_delta: metric.delta
});
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
// Webpack performance budget
module.exports = {
performance: {
maxAssetSize: 244000, // 244 KB
maxEntrypointSize: 244000,
hints: 'error' // Fail build if exceeded
}
};
// manifest.json
{
"name": "My App",
"short_name": "App",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Web performans optimizasyonu sürekli bir süreç:
Hedef Skorlar:
Web sitenizin performansını birlikte optimize edelim! 🚀
📧 iletisim@cesayazilim.com
📞 +90 850 225 53 34