前端必须有印象的四个词:LCP、CLS、Tree Shaking、Code Splitting

5 阅读11分钟
文章前提是看到一篇文章,对文章中的内容有的词熟悉又陌生,就问问ds解答,觉得回答的挺不错的发在掘金记录日后再看

那篇掘金文章地址 # 我为什么说全栈正在杀死前端?:

以下内容由ds生成:

Q:文章中:“当一个全栈工程师的注意力,被数据库索引、Nginx缓存、Docker镜像大小给占满时,他还有多少脑容量,去关心LCP、CLS、Tree Shaking、Code Splitting?” LCP、CLS、Tree Shaking、Code Splitting分别什么呢?这句话怎么理解

LCP、CLS、Tree Shaking、Code Splitting详解与语境分析

一、这四个概念是什么?

1. LCP(最大内容绘制)

定义:页面主要内容加载完成的时间点,是衡量用户感知加载速度的关键指标。

通俗理解

你打开一个网页,什么时候能看到"真正有用的东西"(比如文章正文、产品图片)?

技术细节

// 使用Web Vitals API监测LCP
import {getLCP} from 'web-vitals';
​
getLCP((metric) => {
  console.log('LCP:', metric.value); // 单位:毫秒
  // 良好:< 2.5秒
  // 需要改进:2.5-4秒
  // 差:> 4秒
});
​
// 常见影响因素:
// 1. 服务器响应时间
// 2. 图片/视频资源大小
// 3. 字体加载阻塞
// 4. 客户端渲染延迟

优化策略

<!-- 优化前:大图延迟加载 -->
<img src="hero.jpg" alt="大图"><!-- 优化后 -->
<!-- 1. 使用下一代图片格式 -->
<img src="hero.webp" alt="大图" loading="eager" fetchpriority="high"><!-- 2. 预加载关键资源 -->
<link rel="preload" as="image" href="hero.webp"><!-- 3. 内联关键CSS -->
<style>/* 关键CSS直接内联 */</style><!-- 4. 使用CDN -->
<img src="https://cdn.example.com/hero.webp">

2. CLS(累计布局偏移)

定义:页面元素在加载时意外移动的程度,是衡量视觉稳定性的指标。

通俗理解

你正要点击一个按钮,结果按钮突然跳走了,你点到了别的东西上。这就是布局偏移!

技术细节

// CLS测量的是"意外移动"
// 公式:CLS = 影响比例 × 距离比例
// 影响比例:元素移动影响的视口面积
// 距离比例:元素移动的距离// 监测CLS
import {getCLS} from 'web-vitals';
​
getCLS((metric) => {
  console.log('CLS:', metric.value);
  // 良好:< 0.1
  // 需要改进:0.1-0.25
  // 差:> 0.25
});

常见罪魁祸首与修复

<!-- 问题1:未指定尺寸的图片/视频 -->
<img src="ad.jpg"> <!-- 加载后才撑开布局 --><!-- 修复 -->
<img src="ad.jpg" width="300" height="150">
<!-- 或 -->
<img src="ad.jpg" style="aspect-ratio: 2/1"><!-- 问题2:动态插入内容 -->
<script>
// 突然插入广告
document.body.appendChild(adElement);
</script><!-- 修复:预留空间 -->
<div class="ad-placeholder" style="height: 250px"></div>
<script>
// 内容在预留位置插入
document.querySelector('.ad-placeholder').appendChild(adElement);
</script><!-- 问题3:字体变化导致的布局偏移 -->
<style>
/* 字体加载前后大小不同 */
@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2');
}
​
h1 {
  font-family: 'CustomFont', sans-serif;
  /* 修复:设置字体显示策略 */
  font-display: swap;
  /* 或预留空间 */
  min-height: 1.2em;
}
</style>

3. Tree Shaking(摇树优化)

定义:打包工具自动删除未使用的代码,减少最终文件大小。

通俗理解

就像摇苹果树,把没用的枯枝烂叶摇掉,只留下好苹果(有用的代码)。

工作原理

// 源代码
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;
export const divide = (a, b) => a / b;
​
// main.js
import { add } from './math.js';
console.log(add(1, 2));
​
// Tree Shaking后,打包文件只包含:
const add = (a, b) => a + b;
console.log(add(1, 2));
// subtract, multiply, divide 都被"摇掉"了

现代构建工具配置

// webpack.config.js
module.exports = {
  mode: 'production', // 生产模式自动开启Tree Shaking
  optimization: {
    usedExports: true, // 标记未使用的导出
    sideEffects: true, // 识别有副作用的模块
  }
};
​
// package.json中标记模块无副作用
{
  "name": "your-package",
  "sideEffects": false, // 或指定有副作用的文件
  "sideEffects": [
    "*.css",
    "*.scss"
  ]
}
​
// Vite(默认开启)
// 无需配置,自动使用Rollup进行Tree Shaking

常见问题与解决

// 问题:某些库无法Tree Shaking
import _ from 'lodash'; // 导入整个库
console.log(_.add(1, 2));
​
// 解决1:按需导入
import add from 'lodash/add';
​
// 解决2:使用babel插件
// .babelrc
{
  "plugins": ["lodash"] // 按需转换
}
​
// 问题:副作用模块被错误删除
// utils.js
import './styles.css'; // 副作用:修改全局样式export const util = () => {
  console.log('工具函数');
};
​
// 在package.json中声明
{
  "sideEffects": ["**/*.css"]
}

4. Code Splitting(代码分割)

定义:将代码拆分成多个小块,按需加载,而不是打包成一个巨大的文件。

通俗理解

就像搬家时把东西分箱打包,需要什么拿什么,而不是把所有东西塞进一个巨大的箱子。

实现方式

// 1. 动态导入(最常用)
// Vue中的路由懒加载
const routes = [
  {
    path: '/dashboard',
    component: () => import('./views/Dashboard.vue') // 按需加载
  },
  {
    path: '/admin',
    component: () => import(/* webpackChunkName: "admin" */ './views/Admin.vue')
  }
];
​
// React中的懒加载
import React, { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
const Admin = lazy(() => import('./Admin'));
​
function App() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/admin" element={<Admin />} />
      </Routes>
    </Suspense>
  );
}
​
// 2. 入口点分割(多页面应用)
// webpack.config.js
module.exports = {
  entry: {
    main: './src/main.js',
    admin: './src/admin.js'
  },
  output: {
    filename: '[name].bundle.js'
  }
};
​
// 3. 提取公共依赖
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\/]node_modules[\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
};

Vue3 + Vite的现代实践

// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        // 手动分块策略
        manualChunks: {
          'react-vendor': ['react', 'react-dom'],
          'ui-library': ['element-plus', 'ant-design-vue'],
          'utils': ['lodash', 'dayjs']
        }
      }
    }
  }
};
​
// 组件级分割 + 预加载
const UserProfile = defineAsyncComponent({
  loader: () => import('./UserProfile.vue'),
  loadingComponent: LoadingSpinner,
  delay: 200, // 延迟显示loading
  timeout: 3000, // 超时时间
  // 预加载策略
  webpackPrefetch: true, // 空闲时预加载
  webpackPreload: true, // 高优先级预加载
});

二、这句话的深层含义分析

这句话的背景

"当一个全栈工程师的注意力,
被数据库索引、Nginx缓存、Docker镜像大小给占满时,
他还有多少脑容量,
去关心LCP、CLS、Tree Shaking、Code Splitting?"

三层含义解读

1. 技术栈广度 vs 深度困境

问题:一个人的认知带宽有限,当后端复杂度占据大部分精力时,前端性能优化这种"细节"很容易被忽视。

2. 前后端关注点的本质差异
后端/基础设施前端/性能优化
思维模式:确定性、稳定性、可预测性思维模式:概率性、体验性、用户感知
优化目标:QPS、响应时间、资源利用率优化目标:加载速度、交互流畅度、视觉稳定性
验证方式:压测、监控、日志分析验证方式:真实用户监测、实验室测试、用户体验
影响范围:所有用户一致影响范围:不同设备/网络差异巨大
3. 现代Web开发的割裂现实
// 一个典型全栈工程师的一天
const fullStackEngineer = {
  morning: [
    '优化SQL查询,添加复合索引',
    '调整Nginx缓存策略,减少DB压力',
    '精简Docker镜像,节省CI/CD时间'
  ],
  
  afternoon: [
    '修复生产环境Bug',
    '参与技术方案评审',
    '编写API文档'
  ],
  
  // 什么时候考虑前端性能?
  evening: '勉强修复一下首屏加载问题',
  
  // 前端性能优化需要:
  frontendOptimization: {
    timeNeeded: '持续关注,不断调整',
    tools: ['Lighthouse', 'WebPageTest', 'Chrome DevTools'],
    metrics: ['LCP < 2.5s', 'CLS < 0.1', 'FID < 100ms'],
    reality: '通常被挤压到项目后期或出现明显问题时'
  }
};

这句话的现实意义

1. 个人成长建议
// 不要成为"全栈但都浅"的工程师
// 而应该成为"T型人才"或"一专多能"// ❌ 错误的职业路径:
const shallowFullStack = {
  skills: ['一切都知道一点点'],
  depth: '每个领域都停留在表面',
  value: '容易被替代',
  stress: '高(要学的东西太多)'
};
​
// ✅ 推荐的职业路径:
const tShapeEngineer = {
  vertical: '前端性能优化专家', // 深度专精
  horizontal: [
    '后端开发',      // 了解
    'DevOps',        // 了解
    '数据库设计'     // 了解
  ],
  value: '稀缺且高价值',
  growth: '有明确发展方向'
};
2. 团队协作启示
// 方案1:专业化分工
const teamStructure = {
  frontendSpecialist: {
    focus: '用户体验与性能优化',
    metrics: 'LCP, CLS, FID',
    tools: 'Lighthouse, Web Vitals'
  },
  
  backendSpecialist: {
    focus: '系统稳定与性能',
    metrics: 'QPS, P95延迟, 错误率',
    tools: '监控系统, 压测工具'
  },
  
  devOpsEngineer: {
    focus: '部署与基础设施',
    metrics: '构建时间, 镜像大小, 资源使用率',
    tools: 'Docker, Kubernetes, CI/CD'
  }
};
​
// 方案2:建立共享知识库
const performanceCulture = {
  checklist: {
    frontend: [
      'LCP优化:预加载关键资源',
      'CLS优化:图片尺寸预留',
      '代码分割:路由懒加载'
    ],
    backend: [
      'API响应时间 < 200ms',
      '数据库查询使用索引',
      'Redis缓存热点数据'
    ]
  },
  
  // 定期审计
  audit: '每季度性能健康检查',
  
  // 工具支持
  automation: {
    ci: 'Lighthouse集成到CI/CD',
    monitoring: '真实用户性能监控',
    alert: '关键指标异常报警'
  }
};

三、面试中的回答策略

当被问到这个问题时,你可以这样回答:

"这句话深刻反映了现代全栈工程师面临的认知过载挑战。我理解它有三层含义:

第一层是技术广度与深度的矛盾: 像LCP(加载速度)、CLS(视觉稳定性)这些前端指标,需要持续关注和精细调优,但后端问题(数据库、缓存、容器)往往更紧急、更显性,容易挤占前端优化的时间。

第二层是前后端思维模式的差异: 后端优化更关注确定性的系统指标(如响应时间99分位值),而前端性能受用户设备、网络环境等不确定因素影响更大,需要不同的方法论。

第三层是团队协作与个人成长的启示: 我的实践是:

  1. 建立个人知识体系:我在前端性能领域深入钻研(Lighthouse评分从60提升到95+),同时保持后端技能的实用水平
  2. 推动团队性能文化:在前公司,我主导建立了性能监控看板,将LCP、CLS纳入SLA
  3. 善用自动化工具:把性能检查集成到CI/CD,让机器帮我们关注这些指标

例如,我们最近的项目:

  • 通过代码分割,首屏加载时间减少了40%
  • 优化图片和字体加载,LCP从3.2s降到1.8s
  • 预留广告位尺寸,CLS从0.35降到0.05

我认为真正的全栈不是什么都做,而是有能力理解整个技术栈的权衡,并在团队中推动最佳实践。"

如果你是面试官,可以用这个问题考察:

  1. 候选人是否了解现代前端性能优化
  2. 候选人是否有全栈开发的实践经验
  3. 候选人对团队协作和技术分工的理解
  4. 候选人的自我认知和学习能力

四、实际工作中的应对策略

1. 建立性能预算

// 在项目中定义明确的性能指标
// .performance-budget.json
{
  "metrics": {
    "lcp": "2500ms",
    "cls": "0.1",
    "fid": "100ms",
    "fcp": "1800ms"
  },
  "resourceSizes": {
    "js": "200kb",
    "css": "50kb",
    "images": "500kb",
    "fonts": "100kb"
  },
  "budgets": {
    "mainBundle": "100kb",
    "vendorBundle": "150kb",
    "totalJs": "300kb"
  }
}// 集成到构建流程
// package.json
{
  "scripts": {
    "build": "vite build",
    "audit": "lighthouse-ci --budget=.performance-budget.json"
  }
}

2. 自动化监控

// 持续监控用户真实性能
// 使用工具如:Google Analytics + Web Vitals
import {getCLS, getFID, getLCP} from 'web-vitals';
​
function sendToGoogleAnalytics({name, delta, id}) {
  gtag('event', name, {
    event_category: 'Web Vitals',
    value: Math.round(name === 'CLS' ? delta * 1000 : delta),
    event_label: id,
    non_interaction: true,
  });
}
​
getCLS(sendToGoogleAnalytics);
getFID(sendToGoogleAnalytics);
getLCP(sendToGoogleAnalytics);
​
// 设置报警
const performanceAlerts = {
  lcp: {
    threshold: 4000, // 4秒
    action: '发送Slack通知'
  },
  cls: {
    threshold: 0.25, // 超过0.25
    action: '自动创建Jira工单'
  }
};

3. 团队协作流程

## 性能优化流程### 设计阶段
- [ ] 审核设计稿,识别潜在CLS问题
- [ ] 制定资源加载策略(关键/非关键)
​
### 开发阶段
- [ ] 代码分割方案(路由级/组件级)
- [ ] 图片优化策略(格式/尺寸/懒加载)
- [ ] Tree Shaking验证
​
### 测试阶段
- [ ] Lighthouse自动化测试
- [ ] 不同网络环境测试(3G/4G)
- [ ] 真实设备测试
​
### 发布阶段
- [ ] 性能基准测试
- [ ] 监控报警配置
- [ ] A/B测试(性能改进对比)
​
### 复盘阶段
- [ ] 性能数据分析
- [ ] 用户反馈收集
- [ ] 优化方案迭代

五、总结

核心观点

  1. 认知带宽有限:全栈工程师需要明智分配注意力
  2. 前后端关注点不同:需要不同的思维方式和工具链
  3. 性能优化是系统工程:需要从设计到运维的全流程关注
  4. 自动化和流程化是关键:用工具和流程保证性能不被忽视

实际建议

  • 如果你是个人开发者/小团队:建立简化的性能检查清单,至少关注LCP和CLS
  • 如果你是团队领导:明确性能负责人,建立监控和报警机制
  • 如果你是企业决策者:将性能指标纳入KPI,投入专门的工具和培训

最终答案

这四种技术代表现代前端性能优化的核心方向:

  • LCP:让用户更快看到内容
  • CLS:让用户操作不被干扰
  • Tree Shaking:让代码更精简
  • Code Splitting:让加载更智能

而那句话提醒我们:在追求技术广度的同时,不要忽视专业深度;在解决后端问题的同时,不要忘记前端体验。真正的全栈价值,在于能够理解这些权衡并做出明智的决策。