Performance: Inter-Font auf 60KB Subset reduziert (vorher 344KB)
- Font Subsetting mit fonttools für Latin + German Zeichen - 83% Reduktion der Font-Datei - PageSpeed API Script hinzugefügt 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+5
-4
@@ -107,13 +107,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Font-Loading Optimierung */
|
||||
/* Font-Loading Optimierung - Subset für Latin + German */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
src: url('/fonts/inter-var.woff2') format('woff2-variations');
|
||||
src: url('/fonts/inter-subset.woff2') format('woff2-variations');
|
||||
unicode-range: U+0020-007E, U+00A0-00FF, U+0100-017F, U+2013-2014, U+2018-201A, U+201C-201E, U+2026, U+20AC;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -121,8 +122,8 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Preload Font -->
|
||||
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Preload Font (60KB subset statt 344KB vollständig) -->
|
||||
<link rel="preload" href="/fonts/inter-subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
|
||||
<!-- Preload wichtiger Assets -->
|
||||
<link rel="preload" href="/portrait-224.webp" as="image" type="image/webp" fetchpriority="high" media="(max-width: 640px)" />
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* PageSpeed Insights API Check
|
||||
* Prüft die Performance einer URL mit der Google PageSpeed Insights API
|
||||
*/
|
||||
|
||||
import https from 'https';
|
||||
|
||||
// Disable SSL verification for local testing (network proxy issue)
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
|
||||
const API_KEY = 'AIzaSyA4qo0LfLy_ps-SyTQTQpFrFRjDnAUn_K4';
|
||||
const URL_TO_TEST = 'https://www.damjan-savic.com/';
|
||||
|
||||
function httpsGet(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get(url, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
resolve(JSON.parse(data));
|
||||
} catch (e) {
|
||||
reject(new Error(`JSON Parse Error: ${e.message}`));
|
||||
}
|
||||
});
|
||||
}).on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
async function runPageSpeedTest(url, strategy = 'mobile') {
|
||||
const apiUrl = `https://www.pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent(url)}&key=${API_KEY}&strategy=${strategy}&category=performance&category=accessibility&category=best-practices&category=seo`;
|
||||
|
||||
console.log(`\n🔍 Analysiere ${url} (${strategy})...\n`);
|
||||
console.log('⏳ Bitte warten, die Analyse kann 30-60 Sekunden dauern...\n');
|
||||
|
||||
try {
|
||||
const data = await httpsGet(apiUrl);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(`API Error: ${data.error.message}`);
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('❌ Fehler bei der API-Anfrage:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function formatScore(score) {
|
||||
const percentage = Math.round(score * 100);
|
||||
if (percentage >= 90) return `\x1b[32m${percentage}\x1b[0m`; // Grün
|
||||
if (percentage >= 50) return `\x1b[33m${percentage}\x1b[0m`; // Gelb
|
||||
return `\x1b[31m${percentage}\x1b[0m`; // Rot
|
||||
}
|
||||
|
||||
function formatTime(ms) {
|
||||
if (ms < 1000) return `${Math.round(ms)}ms`;
|
||||
return `${(ms / 1000).toFixed(1)}s`;
|
||||
}
|
||||
|
||||
function displayResults(data, strategy) {
|
||||
const lighthouse = data.lighthouseResult;
|
||||
const categories = lighthouse.categories;
|
||||
const audits = lighthouse.audits;
|
||||
|
||||
console.log('═'.repeat(60));
|
||||
console.log(`📊 PageSpeed Insights Ergebnis (${strategy.toUpperCase()})`);
|
||||
console.log('═'.repeat(60));
|
||||
|
||||
// Scores
|
||||
console.log('\n📈 SCORES:');
|
||||
console.log('─'.repeat(40));
|
||||
console.log(` Performance: ${formatScore(categories.performance.score)}`);
|
||||
console.log(` Barrierefreiheit: ${formatScore(categories.accessibility.score)}`);
|
||||
console.log(` Best Practices: ${formatScore(categories['best-practices'].score)}`);
|
||||
console.log(` SEO: ${formatScore(categories.seo.score)}`);
|
||||
|
||||
// Core Web Vitals
|
||||
console.log('\n⚡ CORE WEB VITALS:');
|
||||
console.log('─'.repeat(40));
|
||||
|
||||
const fcp = audits['first-contentful-paint'];
|
||||
const lcp = audits['largest-contentful-paint'];
|
||||
const tbt = audits['total-blocking-time'];
|
||||
const cls = audits['cumulative-layout-shift'];
|
||||
const si = audits['speed-index'];
|
||||
|
||||
console.log(` First Contentful Paint (FCP): ${formatTime(fcp.numericValue)}`);
|
||||
console.log(` Largest Contentful Paint (LCP): ${formatTime(lcp.numericValue)}`);
|
||||
console.log(` Total Blocking Time (TBT): ${formatTime(tbt.numericValue)}`);
|
||||
console.log(` Cumulative Layout Shift (CLS): ${cls.displayValue}`);
|
||||
console.log(` Speed Index: ${formatTime(si.numericValue)}`);
|
||||
|
||||
// Opportunities
|
||||
const opportunities = Object.values(audits)
|
||||
.filter(audit => audit.details?.type === 'opportunity' && audit.details?.overallSavingsMs > 0)
|
||||
.sort((a, b) => (b.details?.overallSavingsMs || 0) - (a.details?.overallSavingsMs || 0))
|
||||
.slice(0, 5);
|
||||
|
||||
if (opportunities.length > 0) {
|
||||
console.log('\n💡 OPTIMIERUNGSMÖGLICHKEITEN:');
|
||||
console.log('─'.repeat(40));
|
||||
opportunities.forEach(opp => {
|
||||
const savings = opp.details?.overallSavingsMs;
|
||||
console.log(` • ${opp.title}`);
|
||||
if (savings) {
|
||||
console.log(` Potenzielle Einsparung: ${formatTime(savings)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Diagnostics
|
||||
const diagnostics = Object.values(audits)
|
||||
.filter(audit => audit.details?.type === 'table' && audit.score !== null && audit.score < 0.9)
|
||||
.slice(0, 5);
|
||||
|
||||
if (diagnostics.length > 0) {
|
||||
console.log('\n🔧 DIAGNOSE:');
|
||||
console.log('─'.repeat(40));
|
||||
diagnostics.forEach(diag => {
|
||||
const scoreIcon = diag.score >= 0.5 ? '⚠️' : '❌';
|
||||
console.log(` ${scoreIcon} ${diag.title}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Passed Audits Count
|
||||
const passedAudits = Object.values(audits).filter(a => a.score === 1).length;
|
||||
const totalAudits = Object.values(audits).filter(a => a.score !== null).length;
|
||||
|
||||
console.log('\n✅ BESTANDENE PRÜFUNGEN:');
|
||||
console.log('─'.repeat(40));
|
||||
console.log(` ${passedAudits} von ${totalAudits} Prüfungen bestanden`);
|
||||
|
||||
console.log('\n' + '═'.repeat(60));
|
||||
|
||||
return {
|
||||
performance: Math.round(categories.performance.score * 100),
|
||||
accessibility: Math.round(categories.accessibility.score * 100),
|
||||
bestPractices: Math.round(categories['best-practices'].score * 100),
|
||||
seo: Math.round(categories.seo.score * 100),
|
||||
lcp: lcp.numericValue,
|
||||
fcp: fcp.numericValue,
|
||||
tbt: tbt.numericValue,
|
||||
cls: parseFloat(cls.displayValue)
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('\n🚀 PageSpeed Insights API Test');
|
||||
console.log('================================\n');
|
||||
console.log(`URL: ${URL_TO_TEST}`);
|
||||
console.log(`API Key: ${API_KEY.substring(0, 10)}...`);
|
||||
|
||||
try {
|
||||
// Mobile Test
|
||||
const mobileData = await runPageSpeedTest(URL_TO_TEST, 'mobile');
|
||||
const mobileResults = displayResults(mobileData, 'mobile');
|
||||
|
||||
// Desktop Test
|
||||
console.log('\n\n');
|
||||
const desktopData = await runPageSpeedTest(URL_TO_TEST, 'desktop');
|
||||
const desktopResults = displayResults(desktopData, 'desktop');
|
||||
|
||||
// Summary
|
||||
console.log('\n\n📋 ZUSAMMENFASSUNG:');
|
||||
console.log('═'.repeat(60));
|
||||
console.log(`\n 📱 Mobile:`);
|
||||
console.log(` Performance: ${formatScore(mobileResults.performance / 100)}`);
|
||||
console.log(` LCP: ${formatTime(mobileResults.lcp)}`);
|
||||
console.log(` TBT: ${formatTime(mobileResults.tbt)}`);
|
||||
console.log(`\n 🖥️ Desktop:`);
|
||||
console.log(` Performance: ${formatScore(desktopResults.performance / 100)}`);
|
||||
console.log(` LCP: ${formatTime(desktopResults.lcp)}`);
|
||||
console.log(` TBT: ${formatTime(desktopResults.tbt)}`);
|
||||
console.log('\n' + '═'.repeat(60));
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Test fehlgeschlagen:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user