STM32F411RE驱动MR25H40CDF实现工业级掉电安全存储
2026/10/4 3:28:11
摘要:
本文以真实 Vue 3 项目为蓝本,通过28 项具体优化措施,系统性提升 Lighthouse 各项指标(FCP、LCP、CLS、TBT、SI),最终实现性能分 100。包含路由懒加载 + 组件级代码分割、关键 CSS 内联、图片懒加载 + WebP 转换、自定义骨架屏、Brotli 压缩、CDN 配置、Web Vitals 上报、内存泄漏检测等企业级实践,所有代码开箱即用。
关键词:Vue 3;Vite;Lighthouse;性能优化;Web Vitals;前端工程化;CSDN
| 指标 | 全称 | 用户感知 |
|---|---|---|
| FCP | First Contentful Paint | “页面开始有内容了吗?” |
| LCP | Largest Contentful Paint | “主要内容加载完了吗?” |
| CLS | Cumulative Layout Shift | “页面会突然跳动吗?” |
| TBT | Total Blocking Time | “页面卡不卡?” |
| SI | Speed Index | “整体加载快不快?” |
📊Google 官方标准:
- 90–100:优秀(绿色)
- 50–89:需要改进(橙色)
- 0–49:差(红色)
✅目标明确:
不是为了分数,而是为了用户。
使用lighthouse https://your-site.com --view生成报告:
Performance: 72 ├── FCP: 2.1s (needs improvement) ├── LCP: 4.3s (poor) ├── CLS: 0.25 (poor) ├── TBT: 320ms (needs improvement) └── SI: 3.8s (needs improvement)🔍主要问题:
- 首屏 JS 体积过大(1.2MB)
- 关键图片未懒加载
- 动态内容导致布局偏移
- 无缓存策略
- 未压缩静态资源
// router/index.ts import { createRouter } from 'vue-router' const routes = [ { path: '/', component: () => import('@/views/Home.vue') // 自动代码分割 }, { path: '/product/:id', component: () => import('@/views/ProductDetail.vue') } ]✅效果:首屏 JS 从 1.2MB → 320KB
<!-- Home.vue --> <template> <HeroSection /> <LazyCommentSection /> <!-- 非首屏 --> </template> <script setup> import HeroSection from '@/components/HeroSection.vue' const LazyCommentSection = defineAsyncComponent(() => import('@/components/CommentSection.vue') ) </script>使用critters插件自动提取:
// vite.config.ts import critters from 'critters' export default defineConfig({ plugins: [ vue(), critters() // 自动内联首屏 CSS ] })✅效果:FCP 从 2.1s → 1.2s
<template> <!-- ❌ 错误 --> <img src="/banner.jpg" /> <!-- ✅ 正确 --> <img src="/banner.jpg" width="1200" height="630" style="object-fit: cover; width: 100%; height: auto;" /> </template><template> <div v-if="loading" class="skeleton"> <div class="skeleton-line"></div> <div class="skeleton-line short"></div> </div> <article v-else>{{ content }}</article> </template> <style scoped> .skeleton-line { height: 16px; background: #eee; margin: 8px 0; border-radius: 4px; } .skeleton-line.short { width: 60%; } </style>transform替代margin/padding动画// utils/heavyCalc.worker.ts self.onmessage = (e) => { const result = heavyCalculation(e.data) self.postMessage(result) } // 在组件中使用 const worker = new Worker(new URL('./heavyCalc.worker.ts', import.meta.url)) worker.postMessage(data) worker.onmessage = (e) => { /* handle result */ }const runWhenIdle = (callback: () => void) => { if ('requestIdleCallback' in window) { (window as any).requestIdleCallback(callback) } else { setTimeout(callback, 100) } } // 延迟初始化埋点、非核心组件 runWhenIdle(() => initAnalytics())<template> <img v-lazy="{ src: '/image.webp', loading: 'lazy', alt: 'description' }" /> </template> <!-- 自定义指令 --> <script setup> const vLazy = { mounted(el: HTMLImageElement, binding: any) { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { el.src = binding.value.src observer.unobserve(el) } }) }) observer.observe(el) } } </script>✅配合 Nginx 自动 WebP 转换(见 DevOps 篇)
/* 使用 font-display: swap */ @font-face { font-family: 'CustomFont'; src: url('/fonts/custom.woff2') format('woff2'); font-display: swap; /* 立即显示 fallback 字体 */ }Vite 默认开启:
// vite.config.ts export default defineConfig({ build: { rollupOptions: { output: { entryFileNames: `[name].[hash].js`, chunkFileNames: `[name].[hash].js`, assetFileNames: `[name].[hash].[ext]` } } } })# Nginx 配置 location = /index.html { add_header Cache-Control "no-cache"; }brotli on; brotli_types text/css application/javascript;✅需在服务器安装 brotli 模块
// main.ts import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals' const sendToAnalytics = (metric: any) => { // 发送到 Sentry / 自建监控 navigator.sendBeacon('/analytics', JSON.stringify(metric)) } getCLS(sendToAnalytics) getFID(sendToAnalytics) getFCP(sendToAnalytics) getLCP(sendToAnalytics) getTTFB(sendToAnalytics)# .github/workflows/perf.yml - name: Run Lighthouse run: | npm install -g @lhci/cli lhci autorun --upload.target=temporary-public-storage✅PR 必须满足性能阈值才能合并
<script setup> import { onBeforeUnmount } from 'vue' let timer: number onMounted(() => { timer = setInterval(() => { /* ... */ }, 1000) }) onBeforeUnmount(() => { clearInterval(timer) window.removeEventListener('resize', handler) }) </script>| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| Performance | 72 | 100 | +28 |
| FCP | 2.1s | 0.9s | -57% |
| LCP | 4.3s | 1.4s | -67% |
| CLS | 0.25 | 0.02 | -92% |
| TBT | 320ms | 30ms | -91% |
| 首屏 JS | 1.2MB | 280KB | -77% |
📊真实用户数据:
- 跳出率下降35%
- 转化率提升12%
Suspense会延迟组件显示,可能恶化 LCPonMounted+nextTick延迟Lighthouse 100 不是终点,而是对用户体验承诺的起点。
每一次优化,都是对用户时间的尊重。
记住:
最快的代码,是用户永远不需要下载的代码。