🔥前端性能优化9大杀招,第5招面试必挂?📉

1. 前端性能优化实战策略

%% 前端性能优化策略架构图 flowchart TD classDef core fill:#4e73df,stroke:#2e59d9,color:white classDef module fill:#f8f9fc,stroke:#5a67d8,stroke-width:2px classDef tech fill:#e9ecef,stroke:#495057,stroke-dasharray: 3 3 A[前端性能优化]:::core --> B[加载优化]:::module A --> C[渲染优化]:::module A --> D[执行优化]:::module B --> B1[资源压缩]:::tech B --> B2[代码分割]:::tech B --> B3[预加载]:::tech B --> B4[CDN加速]:::tech C --> C1[防阻塞]:::tech C --> C2[懒加载]:::tech C --> C3[骨架屏]:::tech D --> D1[防抖节流]:::tech D --> D2[Web Worker]:::tech D --> D3[虚拟列表]:::tech click B1 "资源压缩:使用Webpack+Terser压缩JS,CSSNano压缩CSS" click B2 "代码分割:通过动态import()实现路由级代码分割" click B3 "预加载:使用预加载关键资源" click B4 "CDN加速:通过CDN全球节点分发静态资源" click C1 "防阻塞:CSS放在,JS使用async/defer异步加载" click C2 "懒加载:图片使用loading='lazy',组件动态导入" click C3 "骨架屏:首屏加载时显示内容占位结构" click D1 "防抖节流:控制scroll/resize等事件触发频率" click D2 "Web Worker:将计算密集型任务移出主线程" click D3 "虚拟列表:仅渲染可视区域内的列表项" style A fill:#2b3a4c,stroke:#1a202c style B fill:#4e73df,stroke:#2e59d9 style C fill:#1cc88a,stroke:#17a673 style D fill:#f6c23e,stroke:#dda20a

✅ 加载优化

  • 资源压缩:Webpack + Terser(JS)、CSSNano
  • 代码分割import() 动态导入,按路由拆分
  • 预加载<link rel="preload"> 关键资源
  • CDN 加速:静态资源分发全球节点

✅ 渲染优化

  • 防阻塞 :CSS 放 head,JS 放 body 底部 or async/defer
  • 懒加载 :图片 loading="lazy",组件动态导入
  • 骨架屏:首屏快速渲染占位结构

✅ 执行优化

  • 防抖节流scroll/resize 事件控制频率
  • Web Worker:耗时计算移出主线程
  • 虚拟列表:长列表只渲染可视区域

Q1: defer 和 async 区别?

A1: defer 顺序执行,DOM 解析完再运行;async 加载完立即执行,不保证顺序 💥

Q2: 如何测量首屏时间?

A2: PerformanceObserver 监听 paint 条目,first-contentful-paint

👉继续看?Q3: 为什么说"减少 reflow & repaint"是伪命题?

A3: 现代浏览器会批量更新,关键应避免强制同步布局(Force Sync Layout)🤯


2. 如何判断变量是数组?

✅ 正确方式(按优先级):

js 复制代码
// 1. 最佳:Array.isArray()
Array.isArray([]) // true

// 2. 兼容老浏览器:Object.prototype.toString
Object.prototype.toString.call([]) === '[object Array]' // true

// 3. 原型链检查(不推荐)
arr.__proto__ === Array.prototype // 可被改写,不安全

❌ 错误方式:

js 复制代码
// 类型判断失败
typeof [] === 'object' // true,但无法区分对象和数组

// 构造函数检查(跨 iframe 失效)
arr.constructor === Array // iframe 中的数组 constructor 不同

Q4: 为什么 Array.isArray 是最安全的?

A4: ES5 原生方法,不可篡改,且正确处理跨 iframe 场景 ✅


3. 变量 a 和 b 如何交换?

✅ 方式一:ES6 解构(推荐)

js 复制代码
[a, b] = [b, a];

✅ 方式二:临时变量(最兼容)

js 复制代码
let temp = a;
a = b;
b = temp;

✅ 方式三:数学运算(仅限数字)

js 复制代码
a = a + b;
b = a - b;
a = a - b;

✅ 方式四:异或(仅限整数)

js 复制代码
a ^= b;
b ^= a;
a ^= b;

Q5: 解构赋值底层怎么实现的?

A5: 引擎创建临时数组 [b, a],再按位置赋值,本质还是用了临时空间 💡


9. 事件委托(Event Delegation)

利用事件冒泡,将子元素事件绑定到父元素

html 复制代码
<ul id="list">
  <li>Item 1</li>
  <li>Item 2</li>
  <!-- 动态添加更多 -->
</ul>
js 复制代码
document.getElementById('list').addEventListener('click', e => {
  if (e.target.tagName === 'LI') {
    console.log('Clicked:', e.target.textContent);
  }
});

优势:

  • 减少内存占用(一个监听器 vs 多个)
  • 自动支持动态元素
  • 提升性能

Q6: 如何阻止事件冒泡影响委托?

A6: e.stopPropagation(),但慎用,会破坏委托机制 ❗


10. 标签生成的 DOM 结构是类数组

js 复制代码
const divs = document.getElementsByTagName('div');
// divs 是 HTMLCollection,长这样:
{
  0: <div>,
  1: <div>,
  length: 2,
  [Symbol.iterator]: function() { ... }
}

⚠️它能调用 push 吗?


11. 类数组和数组的区别

特性 数组 类数组
原型 Array.prototype Element.prototype
方法 有 push/pop 无数组方法
length 自动更新 手动维护(部分自动)
迭代 可迭代 部分可迭代(如 NodeList)
js 复制代码
// 共同点:有 length + 数字索引
arr[0] === list[0]; // true

Q7: Arguments 是类数组吗?

A7: 是!但箭头函数中不存在,已被 ...args 取代 ✅


12. DOM 类数组如何转成数组?

✅ 方式一:Array.from(推荐)

js 复制代码
const arr = Array.from(document.querySelectorAll('div'));

✅ 方式二:扩展运算符

js 复制代码
const arr = [...document.querySelectorAll('div')];

✅ 方式三:call 调用数组方法

js 复制代码
const arr = Array.prototype.slice.call(document.getElementsByTagName('li'));

✅ 方式四:for 循环手动构建

js 复制代码
const list = document.querySelectorAll('p');
const arr = [];
for (let i = 0; i < list.length; i++) {
  arr.push(list[i]);
}

Q8: 为什么 NodeList 有些能迭代有些不能?

A8: querySelectorAll 返回静态 NodeList(可迭代),getElementsByTagName 返回动态集合(实时更新)💥


13. 单页面应用 vs 多页面应用

%% 单页面应用 vs 多页面应用 架构对比 flowchart LR classDef spa fill:#e8f5e9,stroke:#4caf50,stroke-width:2px classDef mpa fill:#ffebee,stroke:#f44336,stroke-width:2px classDef tech fill:#f5f5f5,stroke:#9e9e9e,stroke-dasharray: 3 3 SPA[单页面应用]:::spa --> FRAMEWORK[Vue/React]:::tech FRAMEWORK --> ROUTING[路由切换]:::tech ROUTING --> PARTIAL[局部更新]:::tech MPA[多页面应用]:::mpa --> HTML[HTML页面]:::tech HTML --> NAVIGATION[页面跳转]:::tech NAVIGATION --> FULL[全量加载]:::tech click SPA "单页面应用(SPA): 通过路由切换实现局部更新,提升用户体验" click MPA "多页面应用(MPA): 传统页面跳转+全量加载,首屏加载慢" click ROUTING "核心:使用前端路由实现无刷新页面切换" click FULL "痛点:每次跳转都需要重新加载整个页面资源"
维度 SPA MPA
体验 流畅,像原生应用 有白屏,传统网页感
SEO 需 SSR 支持 天然友好
首屏加载 初次大,后续快 每次独立加载
构建复杂度 高(路由、状态管理) 低(HTML + JS)
适用场景 后台系统、Web App 内容站、营销页

Q9: 如何让 SPA 支持 SEO?

A9: 服务端渲染(SSR)或预渲染(Prerender)✅


15. localStorage API 全解析

js 复制代码
// ✅ 增
localStorage.setItem('name', '张三');

// ✅ 查
localStorage.getItem('name'); // "张三"

// ✅ 改 → 同 setItem

// ✅ 删
localStorage.removeItem('name');
localStorage.clear(); // 清空所有

// ✅ 监听变化(跨标签页)
window.addEventListener('storage', e => {
  console.log(e.key, e.oldValue, e.newValue);
});

注意事项:

  • 存储字符串,对象需 JSON.stringify
  • 容量约 5-10MB
  • 同源策略限制
  • 同步操作,大数据阻塞主线程

Q10: 如何存储对象?

A10: localStorage.setItem('user', JSON.stringify({name:'张三'})),读取时 JSON.parse 💡

Q11: localStorage 和 sessionStorage 区别?

A11: 前者永久存储(除非清除),后者关闭标签页即销毁 🌟

相关推荐
苏格拉没有底了8 分钟前
由频繁创建3D火焰造成的内存泄漏问题
前端
阿彬爱学习9 分钟前
大模型在垂直场景的创新应用:搜索、推荐、营销与客服新玩法
前端·javascript·easyui
我是哪吒13 分钟前
分布式微服务系统架构第164集:架构懂了就来了解数据库存储扩展千亿读写
后端·面试·github
UrbanJazzerati28 分钟前
PowerShell 自动化实战:自动化为 Git Staged 内容添加 Issue 注释标记
后端·面试·shell
橙序员小站35 分钟前
通过trae开发你的第一个Chrome扩展插件
前端·javascript·后端
Lazy_zheng35 分钟前
一文掌握:JavaScript 数组常用方法的手写实现
前端·javascript·面试
是晓晓吖36 分钟前
关于Chrome Extension option的一些小事
前端·chrome
wave77739 分钟前
feign的bean创建过程-底层请求过程-源码走读
后端·面试
MrSkye39 分钟前
🔥从菜鸟到高手:彻底搞懂 JavaScript 事件循环只需这一篇(下)
前端·javascript·面试
方佑39 分钟前
✨ Nuxt 混合渲染实践: MemOS前端体验深度优化指南
前端