- socket 快速推送数据过来可能是秒级,可能是毫秒级,数据量可能大可能小,如何在保证数据即时性的同时优化性能,让 DOM渲染不卡顿?
- 做个简单的自我介绍
- 谈谈你对水合的理解以及SSR的时候服务端返回的 html 如何跟前端 js 进行水合的?
- React中为什么不能再if里面使用hook?
- 说说浏览器强缓存和协商缓存
- 你进行首屏优化的时候FPS控制在多少指标
- 在性能优化方面你做过哪些性能优化都展开说说,不限制vue和react
- react和vue中key的作用是什么?原理是什么
- SSR的时候首屏加载都会遇到短暂白屏你怎么处理
- SSR都会遇到非短暂白屏你怎么处理
- 首屏渲染优化你是怎么做的 其他不记得了~~可能是我会的,只记得这几个印象深刻的了
以下是答案
1. react和vue中key的作用是什么?原理是什么
使用index造成多余的DOM更新,当key相同内容不同的时候会重新更新dom,但如果嵌套输入框,输入框又不会重新更新,如果输入框里面有值则值就会发送错乱
requestIdleCallback
2. react为什么不能再if里面使用hook
在 React 中,Hook 不能在 if 语句、循环或嵌套函数中调用的主要原因是 React 依赖 Hook 的调用顺序来正确管理状态。这条规则由 React 官方明确规定,并且由 eslint-plugin-react-hooks 插件进行检查。
大白话:react的hooks都具有特定的执行顺序,if改变了hook原本的执行顺序,让状态管理变得混乱和不可预测
核心问题:Hook 调用的顺序必须一致
React 通过 调用顺序(Call Order) 来管理每个组件的 Hook,如果 Hook 在 if 语句或循环中使用,可能会导致调用顺序变化,从而引发 状态错乱。
示例:错误的 Hook 使用
function MyComponent(props) {
if (props.show) {
useState(0); // ❌ 错误,Hook 在 if 语句中
}
return <div>Test</div>;
}
假设 props.show 初始为 true,useState(0) 被调用,但如果 props.show 变为 false,useState 将不会再被调用,导致 React 失去对 Hook 顺序的追踪,从而引发 状态错乱。
正确的使用方式
React 期望 Hook 在组件的 顶层作用域 调用,确保它们始终按照相同的顺序执行:
function MyComponent(props) {
const [count, setCount] = useState(0); // ✅ Hook 在组件顶层
if (props.show) {
console.log("show is true");
}
return <div>Count: {count}</div>;
}
这样,不管 props.show 是 true 还是 false,useState(0) 总是会按照固定顺序调用,React 才能正确管理状态。
如何在条件中使用 Hook?
虽然 Hook 不能直接放在 if 语句中,但你可以使用 条件逻辑 来决定是否使用某个状态或副作用:
方案 1:条件值
function MyComponent({ show }) {
const [count, setCount] = useState(0);
const message = show ? "显示内容" : "隐藏内容";
return <div>{message}</div>;
}
这种方式不会破坏 Hook 调用顺序,但仍然能根据 show 的值来调整渲染内容。
方案 2:使用自定义 Hook
如果你确实需要基于条件使用某个 Hook,可以封装成 自定义 Hook,然后在顶层调用:
function useConditionalEffect(show) {
useEffect(() => {
if (show) {
console.log("Effect is running");
}
}, [show]);
}
function MyComponent({ show }) {
useConditionalEffect(show);
return <div>Test</div>;
}
这样 useConditionalEffect 始终会在组件的顶层调用,避免了 Hook 顺序改变的问题。
方案 3:使用 useMemo 或 useEffect
对于 useEffect、useMemo 等 Hook,你可以直接在回调内部处理逻辑:
function MyComponent({ show }) {
useEffect(() => {
if (show) {
console.log("Show is true");
}
}, [show]); // ✅ 依赖 show,确保 Hook 顺序不变
return <div>Test</div>;
}
这样 useEffect 总是按照相同的顺序执行,但内部逻辑会根据 show 进行调整。
总结
✅ Hook 只能在组件的顶层作用域调用,不能放在 if、循环或函数内部,以确保 Hook 调用顺序始终一致。
✅ 可以在 Hook 内部使用条件逻辑,如 useEffect 或 useMemo,避免 Hook 直接出现在 if 语句中。
✅ 可以使用自定义 Hook,将条件逻辑封装起来,让 Hook 始终在顶层被调用。
这样能保证 React 正确管理状态,避免意外的 bug 🚀
3.强缓存和协商缓存
强缓存(Strong Cache)与协商缓存(Negotiated Cache)详解
1. 强缓存(Strong Cache)
强缓存是指浏览器在一定时间内直接从本地缓存读取资源,而不会向服务器发送请求。只有当缓存过期或因某些因素失效时,才会向服务器请求最新资源。
1.1 关键 HTTP 头
强缓存主要通过以下 HTTP 响应头 进行控制:
ExpiresCache-Control
1.2 具体实现
-
Expires(HTTP/1.0,已逐渐被Cache-Control替代)Expires头的值是一个绝对时间(GMT 格式)。- 例如: Expires: Fri, 29 Mar 2025 12:00:00 GMT
- 表示资源在 2025 年 3 月 29 日 12:00 之前,浏览器无需向服务器请求资源,而是直接从缓存读取。
- 缺点:
- 受客户端时间影响,如果本地时间错误,可能导致缓存失效或过早过期。
-
Cache-Control(HTTP/1.1,优先级高于Expires)Cache-Control头提供更精细的缓存控制,主要指令包括:max-age=<seconds>:表示资源在 X 秒内 直接使用缓存,无需请求服务器。public:可以被任何缓存存储(如 CDN)。private:仅浏览器可缓存,CDN 不能缓存。no-cache:浏览器必须向服务器请求验证资源是否过期(即协商缓存)。no-store:不进行缓存,每次都请求服务器。
- 例如:
Cache-Control: max-age=3600, public
- 表示 3600 秒(1 小时)内浏览器不会重新请求资源。
1.3 使用场景
-
适用于静态资源(CSS、JS、图片、字体):
- 例如:
https://example.com/style.csshttps://example.com/logo.png
- 服务器返回:
Cache-Control: max-age=86400
- 资源会在 24 小时内(86400 秒) 从缓存加载,减少服务器请求,提高页面加载速度。
- 例如:
-
CDN 资源(例如字体、JS 库)
- CDN 一般会设置较长的
max-age,如: Cache-Control: max-age=31536000, public- 资源会被缓存 一年(31536000 秒),减少带宽使用。
- CDN 一般会设置较长的
2. 协商缓存(Negotiated Cache)
协商缓存是在浏览器请求资源时,向服务器发送请求,但服务器会验证资源是否更新,如果没有更新,则返回 304 状态码,浏览器继续使用缓存的资源。
2.1 关键 HTTP 头
协商缓存使用以下 请求头 & 响应头:
Last-Modified/If-Modified-SinceETag/If-None-Match
2.2 具体实现
-
Last-Modified/If-Modified-Since-
服务器响应头: Last-Modified: Fri, 22 Mar 2025 10:00:00 GMT
- 表示资源最后修改时间为 2025 年 3 月 22 日 10:00。
-
浏览器请求头: If-Modified-Since: Fri, 22 Mar 2025 10:00:00 GMT
- 浏览器向服务器询问:“自从 3 月 22 日 10:00 之后,资源有没有更新?”
-
服务器响应:
- 如果资源没变:
HTTP/1.1 304 Not Modified
- 服务器告诉浏览器资源未修改,继续使用缓存。
- 如果资源已更新:
HTTP/1.1 200 OK
Last-Modified: Fri, 23 Mar 2025 10:00:00 GMT
- 服务器返回新资源,并更新
Last-Modified头。
- 服务器返回新资源,并更新
- 如果资源没变:
HTTP/1.1 304 Not Modified
-
-
ETag/If-None-Match(更精确的缓存机制)ETag是资源的唯一标识符(哈希值),如果资源改变,ETag也会变。- 服务器响应头: ETag: "abc123"
- 浏览器请求头: If-None-Match: "abc123"
- 服务器响应:
- 资源未修改:
HTTP/1.1 304 Not Modified
- 继续使用缓存。
- 资源已修改:
HTTP/1.1 200 OK
ETag: "xyz789"
- 返回新资源。
- 资源未修改:
HTTP/1.1 304 Not Modified
2.3 使用场景
-
适用于动态内容(HTML、API 数据)
- 例如:
https://example.com/news.htmlhttps://api.example.com/data
- 服务器返回: Last-Modified: Fri, 22 Mar 2025 10:00:00 GMT
- 如果内容没变,服务器返回 304,节省带宽,提高加载速度。
- 例如:
-
适用于 CDN 或用户敏感数据
- ETag 适用于文件精确比较,如果一个资源可能被多个用户修改,
ETag能更可靠地识别更新,如: ETag: "file-version-1"
- ETag 适用于文件精确比较,如果一个资源可能被多个用户修改,
3. 强缓存 vs 协商缓存对比
| 特性 | 强缓存(Strong Cache) | 协商缓存(Negotiated Cache) |
|---|---|---|
| 是否发送请求 | ❌ 不发送请求,直接使用缓存 | ✅ 发送请求,服务器验证 |
| 服务器负载 | ✅ 低(减少请求) | ⚠️ 略高(需要处理 304) |
| 响应速度 | ✅ 快(无需等待服务器) | ⚠️ 略慢(需服务器验证) |
| 控制方式 | Expires / Cache-Control | Last-Modified / ETag |
| 适用场景 | 静态资源(JS、CSS、图片) | 动态内容(HTML、API) |
4. 具体案例
案例 1:静态资源(强缓存)
HTTP/1.1 200 OK
Cache-Control: max-age=31536000, public
- 资源 一年内 直接使用缓存,不请求服务器。
案例 2:动态数据(协商缓存)
HTTP/1.1 200 OK
ETag: "data-123456"
Last-Modified: Mon, 25 Mar 2025 10:00:00 GMT
- 浏览器下次请求时: If-None-Match: "data-123456" If-Modified-Since: Mon, 25 Mar 2025 10:00:00 GMT
- 如果数据没变,返回 304,节省流量。
5. 总结
- 强缓存(Strong Cache):完全不请求服务器,适用于 静态资源。
- 协商缓存(Negotiated Cache):请求服务器验证,适用于 动态数据。
- 实际应用:
- 静态资源(CSS、JS、图片) → 强缓存
- HTML 页面、API 数据 → 协商缓存
合理使用缓存策略,可以优化性能、减少服务器压力,提高网站加载速度 🚀
4.谈谈你对nextjs水合的理解
如果我作为面试官,询问对Next.js中水合(Hydration)的理解,期望得到以下内容的答案:
1. 概念解释
- 定义阐述:面试者应清晰指出在Next.js的服务器端渲染(SSR)场景中,水合指的是在服务器将React组件渲染为HTML并发送到客户端后,客户端JavaScript代码与已有的HTML进行“融合”,使静态的HTML转变为可交互的React应用的过程。形象地说,就像是给静态的HTML“注入活力”,让它能够响应用户操作,实现完整的交互功能。
2. 工作原理
- 渲染过程描述:要说明服务器首先使用
ReactDOMServer等方法将React组件渲染为HTML字符串,这个HTML包含了页面的基本结构和初始数据。当浏览器接收到这个HTML后,会加载Next.js应用的客户端JavaScript代码。客户端代码中的React会重新挂载组件,并将其与服务器渲染生成的HTML进行比对。通过比对,React能够确定哪些部分需要更新,哪些部分可以复用已有的HTML结构,然后为这些元素添加事件监听器等交互功能,从而实现页面的交互性。例如,服务器渲染出一个包含按钮的列表页面,客户端水合时会给按钮添加点击事件处理函数,使其能在用户点击时执行相应逻辑。 - 差异处理机制:提及React如何高效处理服务器渲染的HTML与客户端重新渲染的差异。React利用虚拟DOM技术,通过对比服务器端生成的虚拟DOM和客户端重新生成的虚拟DOM,找出最小的差异集,并仅更新实际需要改变的部分,避免不必要的DOM操作,提高水合的效率和性能。
3. 重要性及优势
- 性能方面:阐述水合对于提升应用性能的作用。由于服务器端已经生成了HTML,用户可以快速看到页面内容,然后通过水合逐步增强页面的交互性,这种方式极大地减少了首屏加载时间,提升了用户体验。与完全在客户端渲染的应用相比,用户无需等待所有JavaScript代码下载、解析和执行完毕才能看到页面内容,特别是在网络环境较差或设备性能较低的情况下,优势更为明显。
- SEO友好:说明水合与SEO的关系。服务器端渲染生成的HTML可被搜索引擎爬虫直接抓取,而水合过程不会影响搜索引擎对页面内容的索引。这使得采用Next.js进行SSR和水合的应用在搜索引擎优化方面具有显著优势,能够提高网站在搜索结果中的排名。
4. 可能遇到的问题及解决方法
- 水合不匹配问题:能指出在水合过程中可能出现的“水合不匹配”错误。这通常是由于服务器端和客户端渲染的结果不一致导致的,比如在服务器端和客户端执行代码的环境不同,或者数据获取逻辑存在差异。解决方法可以包括确保服务器端和客户端使用相同的数据、在客户端渲染前进行数据校验和同步,以及避免在服务器端和客户端执行依赖于不同环境的代码等。
- 性能优化:讨论如何进一步优化水合性能,如代码拆分,将JavaScript代码拆分成更小的块,按需加载,减少初始加载的代码量,从而加快水合速度;还可以通过合理使用
next/script组件来控制脚本的加载顺序和时机,确保关键脚本优先加载,提高水合效率。 -
1. 什么是水合(Hydration)?
水合(Hydration)是指在 服务器端渲染(SSR) 后,客户端的 JavaScript 重新接管并激活静态 HTML,使其变为可交互的页面 的过程。
- 在 SSR 场景下,服务器返回的是已经渲染好的 HTML,但其中的事件监听等动态交互逻辑 尚未生效。
- 客户端的 JS(React/Vue等框架)会重新解析 HTML 结构并绑定事件,这个过程就是 水合(Hydration)。
2. SSR 中服务端返回的 HTML 如何与前端 JS 进行水合?
💡 SSR 水合的核心流程:
-
服务器渲染 HTML:
- 在 服务端(Node.js/Next.js/Nuxt.js) 运行 React/Vue 组件,并生成 完整的 HTML 页面。
- 服务器返回的 HTML 结构和客户端的 React/Vue 组件渲染结果一致。
-
前端加载并执行 JavaScript:
- 浏览器解析 HTML 后,会加载
bundle.js(客户端 JS 代码)。 - 前端 React/Vue 框架在
window.onload或DOMContentLoaded事件后执行。
- 浏览器解析 HTML 后,会加载
-
框架执行 Hydration(对 HTML 结构进行重新绑定):
- React/Vue 会对比 HTML 结构,发现页面已经有完整的 DOM,而不是空白页面。
- React 使用
ReactDOM.hydrate(),Vue 使用createSSRApp()来复用已有 DOM,避免二次渲染。
-
绑定事件监听,激活交互逻辑:
- 例如
<button>标签已经在 HTML 中,但它的onClick事件 需要 JS 重新绑定,否则用户点击不会生效。 - 这一步完成后,页面就完全恢复到 CSR(客户端渲染)的状态。
- 例如
3. React SSR 的 Hydration 实现
在 React 中,SSR 渲染时通常使用 ReactDOM.hydrate() 来进行水合:
import React from "react";
import { hydrateRoot } from "react-dom/client";
import App from "./App";
// 服务器已经返回了 HTML,只需要绑定事件
hydrateRoot(document.getElementById("root"), <App />);
🔹 hydrateRoot() 会复用已有的 HTML 结构,并绑定事件,而不会重新创建整个 DOM。
4. Vue SSR 的 Hydration 实现
在 Vue 3 SSR 中,我们使用 createSSRApp() 来激活页面:
import { createSSRApp } from "vue";
import App from "./App.vue";
const app = createSSRApp(App);
app.mount("#app");
🔹 Vue 的 createSSRApp() 会复用服务端渲染的 HTML,并仅仅为其绑定事件,不会重新生成 DOM。
5. 为什么 SSR 水合可能会导致问题?
(1) 可能出现 "内容不匹配" 错误
如果 服务器返回的 HTML 与客户端渲染的 HTML 不一致,会导致 React/Vue 重新渲染整个页面,甚至出现警告:
Warning: Text content did not match. Server: "Hello" Client: "Hi"
❌ 错误示例(SSR 与 CSR 内容不一致)
// 服务器端渲染
<p>{new Date().toISOString()}</p>
// 客户端渲染
<p>{new Date().toLocaleString()}</p>
🔹 解决方案:确保 SSR 和 CSR 逻辑一致,避免 Math.random()、new Date() 这种在客户端和服务端不同步的变量。
(2) SSR 水合会导致页面短暂不可交互
在 hydrate() 之前,页面虽然有 HTML,但事件未绑定,导致短暂无法交互。
🔹 解决方案:骨架屏(Skeleton Screen) + 渐进增强(Partial Hydration)。
(3) SSR 页面可能变得更慢
- 水合过程中,React/Vue 需要重新绑定事件,如果页面很大,JS 计算量大,水合时间会较长。
- 解决方案:
- 代码拆分(Code-Splitting)
- 选择性水合(Selective Hydration)
- 流式渲染(Streaming Rendering)(React 18 新特性)
6. 进阶:如何优化 SSR Hydration 过程?
✅ 方式 1:选择性水合(Selective Hydration)
React 18 允许 局部水合,即 只激活用户交互的部分,而不是整个页面:
import { createRoot, hydrateRoot } from 'react-dom/client';
hydrateRoot(document, <App />, {
onHydrated: () => console.log("Hydration completed"),
});
🔹 这样可以加速交互的可用性。
✅ 方式 2:流式渲染(Streaming SSR)
在 React 18,支持流式 HTML 传输,客户端 按需水合:
import { renderToPipeableStream } from 'react-dom/server';
const { pipe } = renderToPipeableStream(<App />, {
onShellReady() {
pipe(res);
},
});
🔹 流式渲染可以更快让页面可见,同时逐步绑定交互逻辑。
7. 结论
- SSR Hydration 是将服务器返回的 HTML 变成可交互页面的过程。
- React 使用
hydrateRoot(),Vue 使用createSSRApp()进行水合。 - 避免 CSR 和 SSR 渲染不一致,否则会导致 水合失败 或 页面被重新渲染。
- 优化方案:选择性水合、流式渲染、代码拆分,提升 SSR 页面性能。
🚀 掌握 SSR Hydration 原理和优化技巧,可以在面试和项目开发中脱颖而出!
5.做一个简单的自我介绍
在面试中,一个精彩的自我介绍能迅速吸引面试官的注意,展现你的专业能力和沟通表达能力。你的简历已经非常优秀,我建议你突出你的核心竞争力、项目经验和亮点技术,同时保持逻辑清晰、简洁流畅。
🎯 自我介绍的结构
- 简要介绍个人背景(1-2 句话)
- 概括你的技术栈和核心能力(3-4 句话)
- 突出关键项目经历和技术亮点(3-4 句话)
- 总结你的个人特质和职业目标(1-2 句话)
💡 示例:
👋 开场 "您好,我是 [你的名字],一名拥有 9 年经验的前端开发工程师,擅长 Vue、React、Angular,并在多个企业级项目中负责前端架构设计和团队协作。"
💻 技术能力 "我的技术栈涵盖 Vue 生态(Vue2/Vue3、Vuex/Pinia)、React 生态(React Hooks、Redux/Zustand、Next.js),以及跨端开发(React Native、Taro、UniApp)。在工程化方面,我熟悉 Webpack、Vite、Rollup,并在多个项目中主导过性能优化和 CI/CD 流程改进。"
🏆 关键项目经历 "在 [悦居生活智慧社区服务平台] 项目中,我主导 Vue2 迁移 Vue3,优化前端架构,提升代码复用率 40%,并通过 Vite 替换 Webpack,使构建速度提高 80%。
在 [威胁检测平台] 项目中,我优化了百万级数据的渲染性能,使用虚拟列表减少 DOM 开销,使日志加载时间减少 70%。
此外,我在多个项目中实践了前端低代码方案、自研组件库,并优化了 WebSocket 实时通信性能。"
🎯 个人优势 & 发展目标 "我不仅关注技术实现,还注重团队协作和工程化提升,曾负责 3 人前端团队的技术规划和 Code Review 机制。未来,我希望在前端架构、性能优化、微前端等方向持续深入,同时带领团队构建更高效、可扩展的前端体系。"
💪 结束语 "期待在贵公司能有更多技术上的挑战,与团队共同进步,谢谢!"
✨ 如何让面试官觉得你的表达能力强?
- 逻辑清晰,条理分明(使用“我擅长 A、在 B 项目做了 C、优化了 D”这种结构)。
- 突出亮点,避免流水账(强调你的架构能力、性能优化经验、团队管理经验)。
- 语速适中,语气自信(不要念简历,而是像分享经历一样流畅表达)。
- 适当加入量化数据(如“性能提升 80%”“减少 70% 加载时间”),更具说服力。
- 最后表达期待和感谢,给面试官留下良好印象。
这个自我介绍不仅能展现你的技术深度,还能凸显你的沟通能力和职业规划,让面试官对你留下深刻印象!🎯 🚀
6.SSR都会遇到短暂白屏你怎么处理
📌 短暂白屏的原因与解决方案
短暂白屏 指的是页面加载过程中,用户可能会看到短暂的空白状态,随后内容才逐渐出现。这种情况通常发生在 SSR(服务器端渲染)+ Hydration 过程中,或者 CSR(客户端渲染)首屏渲染 过程中。
🔹 1. 短暂白屏的常见原因
✅ 1.1. JavaScript 资源加载延迟
问题:
- 服务器返回 HTML 后,JavaScript 代码加载较慢,导致 Hydration 迟滞。
- 如果 JavaScript 资源未及时执行,页面虽然有 HTML 但无交互。
- 可能的情况:
- 前端 JS 体积过大,影响加载速度。
- 网络波动,CDN 资源加载慢。
🚀 解决方案: ✅ 优化 JavaScript 体积
- 使用 代码拆分(Code Splitting),按需加载:
const LazyComponent = lazy(() => import("./HeavyComponent")); - Tree Shaking 移除未使用的代码:
"sideEffects": false
✅ 开启 preload 加快关键资源加载
<link rel="preload" href="/app.js" as="script">
✅ 使用 fallback 组件
<Suspense fallback={<p>加载中...</p>}>
<LazyComponent />
</Suspense>
✅ 1.2. 服务器返回 HTML 过慢
问题:
- 服务器端渲染 HTML 时间过长,导致页面白屏。
- 可能的情况:
- API 响应慢(数据请求延迟)。
- SSR 渲染耗时(如数据过大)。
- 服务器性能低(CPU 负载高)。
🚀 解决方案: ✅ 并行数据请求
const [data1, data2] = await Promise.all([fetchData1(), fetchData2()]);
✅ 缓存 SSR HTML,避免重复渲染
cache.set("homepage", renderedHTML);
✅ 使用 stale-while-revalidate 让旧数据可用
Cache-Control: s-maxage=600, stale-while-revalidate
✅ 流式渲染(Streaming)
- React 18 支持流式渲染,减少白屏:
import { renderToPipeableStream } from "react-dom/server"; - Vue 3 SSR 也支持流式渲染
import { renderToNodeStream } from "@vue/server-renderer";
✅ 1.3. Hydration 过慢
问题:
- 服务器端返回的 HTML 页面可见,但 React/Vue Hydration 需要时间,导致短暂白屏。
- Hydration 过程中 React/Vue 需要绑定事件、解析 DOM,这可能会花费几百毫秒。
🚀 解决方案: ✅ 分块 Hydration(React 18 / Vue 3)
- 避免一次性 Hydration,改用并行 Hydration:
import { createRoot } from "react-dom/client"; createRoot(document.getElementById("root"), { hydrate: true });
✅ 只 Hydration 关键区域
<Suspense fallback={<Loading />}>
<MainContent />
</Suspense>
✅ 避免 Hydration 失败
- 确保服务器端 HTML 与客户端 UI 一致
- 避免
Math.random()、Date.now()影响 Hydration。
🔹 2. 短暂白屏的 UI 解决方案
即使优化了服务器渲染速度,我们仍然可以在 UI 设计上减少白屏带来的不良体验。
✅ 2.1. 使用 Skeleton 屏(骨架屏)
❌ 不好的做法:
return data ? <Component /> : null;
✅ 改进:
return data ? <Component /> : <Skeleton />;
示例 Skeleton 代码
const Skeleton = () => (
<div className="skeleton">
<div className="skeleton-box"></div>
<div className="skeleton-line"></div>
</div>
);
✅ 骨架屏优点:
- 减少白屏感知,让用户感觉内容正在加载,而不是完全空白。
✅ 2.2. 预渲染(Prefetch + Preload)
预加载资源,加快首屏渲染:
<link rel="preload" href="/main.css" as="style">
<link rel="prefetch" href="/next-page.js">
✅ 预加载 CSS、JS,避免资源加载迟滞。
✅ 2.3. 延迟渲染非关键组件
某些不重要的组件可以在页面加载后再渲染:
const HeavyComponent = lazy(() => import("./HeavyComponent"));
export default function Page() {
return (
<div>
<Header />
<Suspense fallback={<Skeleton />}>
<HeavyComponent />
</Suspense>
</div>
);
}
✅ 先加载 Header,HeavyComponent 在后台加载,避免白屏。
✅ 2.4. 在 SSR 中渲染基础 UI 结构
即使数据未加载完成,也应该先渲染页面基本结构,避免全白:
export default function Page({ data }) {
return (
<div>
<h1>欢迎来到我的网站</h1>
{data ? <Content /> : <p>数据加载中...</p>}
</div>
);
}
✅ 这样,用户至少能看到标题,而不是完全空白。
📌 3. 解决短暂白屏的最佳实践
| 问题 | 解决方案 |
|---|---|
| JS 资源加载慢 | 代码拆分、Preload、CDN 加速 |
| 服务器端渲染慢 | 并行请求、缓存、流式渲染 |
| Hydration 过慢 | 部分 Hydration、Suspense |
| 用户感知白屏 | Skeleton 屏、加载动画 |
🎯 结论
- 短暂白屏 主要由 JavaScript 加载、SSR 响应慢、Hydration 过慢 导致。
- 解决方案
- 优化 JavaScript 体积(代码拆分、Tree Shaking)。
- 使用 Skeleton 屏(用户不会感觉到白屏)。
- 开启流式渲染(React 18/Vue 3)。
- 缓存数据(避免 SSR 重新计算)。
- 预加载关键资源(减少 FCP 时间)。
如果面试官问你 "SSR 如何优化短暂白屏?",你可以从 技术优化(流式渲染、缓存)、前端优化(Skeleton、预加载) 两个角度全面解答,展示你的架构能力和前端优化思维!🚀
7.SSR都会遇到非短暂白屏你怎么处理
📌 SSR(服务器端渲染)白屏问题及解决方案
💡 什么是 SSR 白屏问题?
在 SSR(Server-Side Rendering) 应用中,白屏 通常指的是:
- 服务器端已经返回 HTML,但前端 Hydration 失败,导致页面无法交互。
- 在网络慢或 JavaScript 资源未加载时,用户看到的是空白页面。
- 在数据获取失败或错误处理不当时,导致前端无法正确渲染 UI。
🔹 1. SSR 白屏的常见原因
✅ 1.1. 服务器返回的 HTML 和客户端渲染的 DOM 不匹配
- 服务器端渲染的 HTML 和前端 Hydration 时的 HTML 结构不同,React / Vue 可能会报错: Warning: Hydration failed because the initial UI does not match what was rendered on the server.
- 原因:
- 服务器端 渲染了动态数据,但前端渲染时数据未同步。
- 服务器端和客户端使用 不同的时间格式 / 随机数,导致 HTML 不一致。
🚀 解决方案 ✅ 确保服务器端和客户端的渲染逻辑一致
// ❌ 不要这样,Date 每次渲染都会不同
const now = new Date().toLocaleString();
✅ 改为在前端动态计算
const [now, setNow] = useState("");
useEffect(() => setNow(new Date().toLocaleString()), []);
✅ 1.2. JavaScript 资源加载失败
- 服务器端返回的 HTML 可以显示,但 JS 代码未执行,页面无法交互(比如按钮点击无效)。
- 可能的原因:
- CDN 资源未加载(如
https://cdn.example.com/app.js404)。 - JS 解析错误(如
SyntaxError)。 - 网络问题导致 JS 未下载。
- CDN 资源未加载(如
🚀 解决方案 ✅ 开启 Service Worker 进行离线缓存
navigator.serviceWorker.register("/sw.js");
✅ 在 HTML 添加 onerror 监听
<script src="/app.js" onerror="location.reload();"></script>
✅ 检查浏览器 Console 中的错误信息,找出 JS 失败原因。
✅ 1.3. 数据请求失败
- SSR 依赖的 API 请求失败,导致返回的 HTML 没有正确的内容。
- 症状:
- HTML 结构为空,导致白屏。
- React / Vue 在 Hydration 过程中崩溃。
🚀 解决方案 ✅ 在服务器端增加错误兜底逻辑
export async function getServerSideProps() {
try {
const res = await fetch("https://api.example.com/data");
if (!res.ok) throw new Error("数据请求失败");
return { props: { data: await res.json() } };
} catch (error) {
return { props: { data: null, error: "获取数据失败,请稍后再试" } };
}
}
✅ 前端组件兜底
export default function Page({ data, error }) {
if (error) return <p>{error}</p>;
return <div>{data ? data.title : "加载中..."}</div>;
}
✅ 避免 fetch 在 useEffect 里直接运行
useEffect(() => {
fetch("/api/data")
.then(res => res.json())
.then(setData)
.catch(() => setError("加载失败"));
}, []);
✅ 1.4. 前端 Hydration 失败
- SSR HTML 和客户端的 React / Vue 代码不匹配,导致 Hydration 失败,页面无法交互。
- 错误案例:
export default function Page() {
return <h1>{Math.random()}</h1>;
}
- 服务器端
Math.random()返回的是0.1234,客户端可能返回0.5678,导致 React Hydration 失败。
🚀 解决方案 ✅ 在服务器端确保一致性
export default function Page({ randomNumber }) {
return <h1>{randomNumber}</h1>;
}
export async function getServerSideProps() {
return { props: { randomNumber: Math.random() } };
}
✅ 避免 Hydration 失败
const [random, setRandom] = useState(0);
useEffect(() => setRandom(Math.random()), []);
🔹 2. SSR 白屏的解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| HTML 与前端不匹配 | 服务器端动态渲染的数据在客户端不同 | 确保服务器端数据和前端一致 |
| JS 资源未加载 | 网络问题或 JS 解析错误 | 兜底逻辑 + 错误上报 |
| API 请求失败 | 数据获取失败,导致页面为空 | 服务器端兜底 + 前端错误提示 |
| Hydration 失败 | React / Vue 代码不匹配 | 避免 Math.random() 等动态数据 |
🔹 3. SSR 预加载 & 骨架屏
白屏问题的另一个优化手段是 “骨架屏 + 预加载”,确保用户体验流畅。
✅ 3.1. 使用骨架屏
在数据请求未完成前,显示 骨架屏(Skeleton Screen),避免白屏:
function Skeleton() {
return <div className="skeleton-box">加载中...</div>;
}
export default function Page({ data }) {
if (!data) return <Skeleton />;
return <div>{data.title}</div>;
}
✅ 用户不会看到白屏,而是看到“加载中”动画。
✅ 3.2. 预加载关键资源
<link rel="preload" href="/styles.css" as="style">
<link rel="preload" href="/app.js" as="script">
✅ 减少 CSS / JS 加载时间,优化首屏渲染。
✅ 3.3. 开启 stale-while-revalidate 缓存
Cache-Control: s-maxage=600, stale-while-revalidate
✅ 即使 API 请求失败,也能先展示旧数据。
🎯 结论
SSR 白屏通常由以下问题导致:
- HTML 和前端 Hydration 不匹配 → 确保服务器和客户端数据一致。
- JavaScript 资源加载失败 → 兜底逻辑 + Service Worker 缓存。
- API 请求失败 → 服务器端和前端都要做好错误处理。
- Hydration 失败 → 避免
Math.random()等动态数据。
✅ 最佳实践
- 使用骨架屏(Skeleton) 代替白屏。
- 预加载关键资源(Preload) 提升渲染速度。
- 开启缓存策略(stale-while-revalidate) 确保数据可用。
8.socket 快速推送数据过来可能是秒级,可能是毫秒级,数据量可能大可能小,如何在保证数据即时性的同时优化性能,让 DOM渲染不卡顿?
当 WebSocket 快速推送数据时,为了保证数据的 即时性 但又不让 DOM 渲染 卡顿,需要采用 批量处理、虚拟列表、异步任务分流等策略 来优化性能。
1. 采用 requestAnimationFrame 进行批量渲染
当数据频繁推送时,避免每次都更新 DOM,而是将数据存入队列,利用 requestAnimationFrame 批量处理,每一帧只更新一次,减少渲染压力。
示例
let dataQueue = [];
let isProcessing = false;
socket.on("message", (data) => {
dataQueue.push(data);
if (!isProcessing) {
isProcessing = true;
requestAnimationFrame(processData);
}
});
function processData() {
if (dataQueue.length > 0) {
updateUI(dataQueue.splice(0, 10)); // 批量更新 10 条,避免一次性渲染过多
requestAnimationFrame(processData);
} else {
isProcessing = false;
}
}
✅ 好处:
- 限制每帧渲染的数据量,避免大数据量瞬间渲染导致卡顿。
2. 使用 Web Worker 处理数据
WebSocket 推送的 数据解析、计算 可能会占用主线程,导致渲染卡顿。可以用 Web Worker 处理计算逻辑,主线程只负责 UI 渲染。
示例
// 主线程
const worker = new Worker("worker.js");
socket.on("message", (data) => {
worker.postMessage(data); // 发送数据到 Worker
});
worker.onmessage = (event) => {
updateUI(event.data); // 只处理最终渲染
};
// worker.js(Web Worker)
self.onmessage = (event) => {
const processedData = processData(event.data);
self.postMessage(processedData);
};
✅ 好处:
- 计算任务在 Worker 中执行,不阻塞主线程,提升渲染流畅度。
3. 使用节流 & 防抖 控制数据更新频率
如果 WebSocket 毫秒级 推送数据,可以使用 节流(throttle) 或 防抖(debounce) 限制更新频率,减少不必要的 UI 变更。
节流:限制更新频率
import throttle from "lodash/throttle";
const updateThrottled = throttle((data) => {
updateUI(data);
}, 100); // 100ms 内最多更新一次
socket.on("message", (data) => {
updateThrottled(data);
});
✅ 好处:
- 控制数据更新的频率,防止 UI 频繁 re-render。
4. 虚拟列表(Virtual List)
如果 WebSocket 传输的是长列表数据,不要一次性渲染全部数据,使用 虚拟列表(Virtual Scroll) 只渲染可视区域的数据。
React 示例(react-window)
import { FixedSizeList as List } from "react-window";
const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);
<List height={500} itemCount={10000} itemSize={35}>
{Row}
</List>
Vue 示例(vue-virtual-scroller)
<template>
<RecycleScroller :items="dataList" :item-size="50">
<template #default="{ item }">
<div>{{ item }}</div>
</template>
</RecycleScroller>
</template>
✅ 好处:
- 仅渲染可视区域的内容,即使有 10W+ 条数据 也不会卡顿。
5. 使用 requestIdleCallback 进行非紧急计算
有些数据可能不需要 立即 处理(比如日志记录、统计等),可以用 requestIdleCallback 在 浏览器空闲时 执行。
示例
socket.on("message", (data) => {
requestIdleCallback(() => logData(data));
});
✅ 好处:
- 避免主线程阻塞,提高 UI 响应速度。
6. 避免不必要的 re-render(React/Vue)
在 React/Vue 项目中,如果组件的状态更新过于频繁,可能会导致 重复渲染,可以通过 useMemo / useCallback(React) 或 computed(Vue) 来优化。
React
const memoizedData = useMemo(() => processData(data), [data]);
Vue
computed(() => processData(data));
✅ 好处:
- 避免不必要的渲染,减少性能开销。
7. 总结
| 优化方案 | 适用场景 | 效果 |
|---|---|---|
requestAnimationFrame 批量渲染 | 高频推送数据 | 限制每帧渲染量,提升流畅度 |
| Web Worker | 计算量大的数据处理 | 分离计算,避免主线程阻塞 |
| 节流 throttle | 高频小数据 | 控制更新频率,减少无效渲染 |
| 虚拟列表 Virtual List | 长列表渲染 | 只渲染可视区域,提升滚动性能 |
| requestIdleCallback | 低优先级任务 | 在空闲时处理数据,提升主线程性能 |
| useMemo / computed | 避免无效 re-render | 减少不必要的状态更新 |
最终效果
✅ 数据实时推送,不卡顿,页面渲染流畅! 🚀
9.你进行首屏优化的时候FPS控制在多少指标
在前端开发中,性能优化是提高用户体验的重要环节。我在 Vue、React 以及其他前端框架的开发过程中,做过以下 多个层面 的性能优化,包括但不限于 首屏加载优化、渲染优化、网络优化、资源优化、代码优化 等。以下是详细的展开:
1. 首屏加载优化
(1)优化资源加载
✅ 关键点: 让用户尽快看到页面内容,减少首屏白屏时间。
- SSR(服务端渲染)
- Vue 使用
Nuxt.js,React 使用Next.js,服务端返回完整 HTML,前端执行水合(Hydration),避免客户端渲染造成的白屏。
- Vue 使用
- 静态生成(SSG)
- 对于 SEO 友好的页面,使用
getStaticProps(Next.js)或Nuxt generate预渲染 HTML,提高加载速度。
- 对于 SEO 友好的页面,使用
- 骨架屏(Skeleton Screen)
- 在数据未加载完成前,展示占位 UI(如骨架屏)降低用户感知的等待时间。
(2)减少首屏 JS 体积
✅ 关键点: 让关键代码尽快执行,非必要代码延迟加载。
- Code Splitting(代码拆分)
- 动态引入(Dynamic Import)
const Component = React.lazy(() => import('./Component'));- Webpack
splitChunks配置拆分代码,减少主包体积。
- Tree Shaking(摇树优化)
- 仅打包用到的代码,移除无用代码。
- 避免
import * as lodash,而使用import { debounce } from 'lodash-es'。
- Preload / Prefetch
rel="preload"加载关键资源,rel="prefetch"预加载未来可能用到的资源。
(3)优化图片加载
✅ 关键点: 图片是首屏加载的重要影响因素,需要合理优化。
- 懒加载(Lazy Load)
<img loading="lazy" />(现代浏览器支持)- Vue:
vue-lazyload,React:react-lazy-load
- 使用 WebP 代替 PNG/JPG
- WebP 体积更小,加载更快。
- CDN 加速
- 使用 CDN 提供图片存储,减少服务器压力。
2. 渲染优化
(1)减少 DOM 操作
✅ 关键点: DOM 操作昂贵,避免不必要的回流和重绘。
- 使用 Virtual DOM
- Vue/React 通过Diff 算法减少 DOM 更新,避免全量更新。
- 使用
key提高 Diff 计算效率- 在
v-for或map中使用唯一key,避免无意义的重渲染。
- 在
- 避免频繁修改样式
- 使用
classList.add/remove,避免style直接赋值导致频繁回流。
- 使用
(2)优化列表渲染
✅ 关键点: 大数据列表渲染时,避免性能消耗过大。
- 虚拟列表(Virtual List)
- 仅渲染可视区域的数据,提升渲染效率。
- Vue:
vue-virtual-scroller,React:react-window
- 分页加载
- 使用
Infinite Scroll(懒加载)方式分页加载数据。
- 使用
(3)减少组件重新渲染
✅ 关键点: 避免无意义的重复渲染,提高性能。
- React:使用
memo进行组件缓存const MemoComponent = React.memo(MyComponent); - Vue:使用
keep-alive缓存组件<keep-alive> <MyComponent /> </keep-alive> - 使用
useCallback/useMemo- 避免不必要的函数重新创建,提高性能。
3. 网络优化
(1)减少 HTTP 请求
✅ 关键点: 降低请求数量,提高加载速度。
- 合并请求(Batch Requests)
- GraphQL 批量请求 vs RESTful 多次请求。
- 使用 HTTP2
- 允许多个请求复用同一个 TCP 连接,减少请求开销。
(2)缓存优化
✅ 关键点: 充分利用浏览器缓存,减少重复请求。
- 强缓存(Expires / Cache-Control)
- 设置
Cache-Control: max-age=31536000,让静态资源长期缓存。
- 设置
- 协商缓存(ETag / Last-Modified)
- 资源变更后,浏览器自动重新请求,减少带宽消耗。
(3)CDN 加速
✅ 关键点: 让用户从最近的节点加载资源,提高加载速度。
- 使用
Aliyun OSS + CDN或Cloudflare部署静态资源,提高访问速度。
4. 代码优化
(1)减少主线程阻塞
✅ 关键点: 让 JS 执行更高效,避免影响 UI 线程。
- 使用 Web Worker
- 将复杂计算(如音视频处理)放入 Web Worker,避免主线程阻塞。
- 避免长任务
- 将任务拆分,使用
requestIdleCallback处理低优先级任务。
- 将任务拆分,使用
(2)减少 JavaScript 解析和执行
✅ 关键点: 让 JS 加载更快,减少阻塞渲染。
- 使用 ES 模块
<script type="module">让浏览器并行解析 JS。
- 减少 polyfill
- 只针对老旧浏览器加载 polyfill,提高现代浏览器性能。
5. FPS 和流畅度优化
(1)保持 60 FPS
✅ 关键点: 让动画和交互更流畅。
- 使用
requestAnimationFrame- 替代
setTimeout或setInterval,让动画帧率与浏览器同步。
- 替代
- GPU 加速
- 使用
will-change: transform启用 GPU 加速,提升动画流畅度。
- 使用
(2)减少动画阻塞
✅ 关键点: 让 UI 更新更流畅,不影响交互。
- 使用 CSS 动画
- CSS3
transform比top/left性能更优。
- CSS3
- 避免
position: fixed触发回流- 使用
transform: translateZ(0)让元素开启 GPU 加速。
- 使用
总结
- 首屏优化:SSR、静态生成、骨架屏、懒加载。
- 渲染优化:虚拟 DOM、列表优化、减少重渲染。
- 网络优化:HTTP 缓存、CDN、合并请求。
- 代码优化:Tree Shaking、Code Splitting、减少 JS 执行时间。
- FPS 优化:
requestAnimationFrame、GPU 加速、避免大规模回流。
这套性能优化方案可以针对 Vue、React、甚至原生 JS 开发时使用。你有没有遇到性能瓶颈,我可以帮你分析!😊
10.在性能优化方面你做过哪些性能优化都展开说说,不限制vue和react
在前端开发中,性能优化是提高用户体验的重要环节。我在 Vue、React 以及其他前端框架的开发过程中,做过以下 多个层面 的性能优化,包括但不限于 首屏加载优化、渲染优化、网络优化、资源优化、代码优化 等。以下是详细的展开:
1. 首屏加载优化
(1)优化资源加载
✅ 关键点: 让用户尽快看到页面内容,减少首屏白屏时间。
- SSR(服务端渲染)
- Vue 使用
Nuxt.js,React 使用Next.js,服务端返回完整 HTML,前端执行水合(Hydration),避免客户端渲染造成的白屏。
- Vue 使用
- 静态生成(SSG)
- 对于 SEO 友好的页面,使用
getStaticProps(Next.js)或Nuxt generate预渲染 HTML,提高加载速度。
- 对于 SEO 友好的页面,使用
- 骨架屏(Skeleton Screen)
- 在数据未加载完成前,展示占位 UI(如骨架屏)降低用户感知的等待时间。
(2)减少首屏 JS 体积
✅ 关键点: 让关键代码尽快执行,非必要代码延迟加载。
- Code Splitting(代码拆分)
- 动态引入(Dynamic Import)
const Component = React.lazy(() => import('./Component'));- Webpack
splitChunks配置拆分代码,减少主包体积。
- Tree Shaking(摇树优化)
- 仅打包用到的代码,移除无用代码。
- 避免
import * as lodash,而使用import { debounce } from 'lodash-es'。
- Preload / Prefetch
rel="preload"加载关键资源,rel="prefetch"预加载未来可能用到的资源。
(3)优化图片加载
✅ 关键点: 图片是首屏加载的重要影响因素,需要合理优化。
- 懒加载(Lazy Load)
<img loading="lazy" />(现代浏览器支持)- Vue:
vue-lazyload,React:react-lazy-load
- 使用 WebP 代替 PNG/JPG
- WebP 体积更小,加载更快。
- CDN 加速
- 使用 CDN 提供图片存储,减少服务器压力。
2. 渲染优化
(1)减少 DOM 操作
✅ 关键点: DOM 操作昂贵,避免不必要的回流和重绘。
- 使用 Virtual DOM
- Vue/React 通过Diff 算法减少 DOM 更新,避免全量更新。
- 使用
key提高 Diff 计算效率- 在
v-for或map中使用唯一key,避免无意义的重渲染。
- 在
- 避免频繁修改样式
- 使用
classList.add/remove,避免style直接赋值导致频繁回流。
- 使用
(2)优化列表渲染
✅ 关键点: 大数据列表渲染时,避免性能消耗过大。
- 虚拟列表(Virtual List)
- 仅渲染可视区域的数据,提升渲染效率。
- Vue:
vue-virtual-scroller,React:react-window
- 分页加载
- 使用
Infinite Scroll(懒加载)方式分页加载数据。
- 使用
(3)减少组件重新渲染
✅ 关键点: 避免无意义的重复渲染,提高性能。
- React:使用
memo进行组件缓存const MemoComponent = React.memo(MyComponent); - Vue:使用
keep-alive缓存组件<keep-alive> <MyComponent /> </keep-alive> - 使用
useCallback/useMemo- 避免不必要的函数重新创建,提高性能。
3. 网络优化
(1)减少 HTTP 请求
✅ 关键点: 降低请求数量,提高加载速度。
- 合并请求(Batch Requests)
- GraphQL 批量请求 vs RESTful 多次请求。
- 使用 HTTP2
- 允许多个请求复用同一个 TCP 连接,减少请求开销。
(2)缓存优化
✅ 关键点: 充分利用浏览器缓存,减少重复请求。
- 强缓存(Expires / Cache-Control)
- 设置
Cache-Control: max-age=31536000,让静态资源长期缓存。
- 设置
- 协商缓存(ETag / Last-Modified)
- 资源变更后,浏览器自动重新请求,减少带宽消耗。
(3)CDN 加速
✅ 关键点: 让用户从最近的节点加载资源,提高加载速度。
- 使用
Aliyun OSS + CDN或Cloudflare部署静态资源,提高访问速度。
4. 代码优化
(1)减少主线程阻塞
✅ 关键点: 让 JS 执行更高效,避免影响 UI 线程。
- 使用 Web Worker
- 将复杂计算(如音视频处理)放入 Web Worker,避免主线程阻塞。
- 避免长任务
- 将任务拆分,使用
requestIdleCallback处理低优先级任务。
- 将任务拆分,使用
(2)减少 JavaScript 解析和执行
✅ 关键点: 让 JS 加载更快,减少阻塞渲染。
- 使用 ES 模块
<script type="module">让浏览器并行解析 JS。
- 减少 polyfill
- 只针对老旧浏览器加载 polyfill,提高现代浏览器性能。
5. FPS 和流畅度优化
(1)保持 60 FPS
✅ 关键点: 让动画和交互更流畅。
- 使用
requestAnimationFrame- 替代
setTimeout或setInterval,让动画帧率与浏览器同步。
- 替代
- GPU 加速
- 使用
will-change: transform启用 GPU 加速,提升动画流畅度。
- 使用
(2)减少动画阻塞
✅ 关键点: 让 UI 更新更流畅,不影响交互。
- 使用 CSS 动画
- CSS3
transform比top/left性能更优。
- CSS3
- 避免
position: fixed触发回流- 使用
transform: translateZ(0)让元素开启 GPU 加速。
- 使用
总结
- 首屏优化:SSR、静态生成、骨架屏、懒加载。
- 渲染优化:虚拟 DOM、列表优化、减少重渲染。
- 网络优化:HTTP 缓存、CDN、合并请求。
- 代码优化:Tree Shaking、Code Splitting、减少 JS 执行时间。
- FPS 优化:
requestAnimationFrame、GPU 加速、避免大规模回流。
这套性能优化方案可以针对 Vue、React、甚至原生 JS 开发时使用。你有没有遇到性能瓶颈,我可以帮你分析!😊
11.首屏渲染优化你是怎么做的
首屏渲染优化(First Paint/First Contentful Paint/Time to Interactive)是前端性能优化的重要一环,目标是让用户尽快看到页面的主要内容,提高交互体验。我在 Vue 和 React 项目中做过多种优化,以下是一些关键优化策略:
1. 资源优化(减少加载时间)
(1)静态资源按需加载
-
代码分割(Code Splitting)
- Vue:使用
webpack或Vite的动态import()进行按需加载。
const AsyncComponent = defineAsyncComponent(() => import('@/components/MyComponent.vue'));- React:使用
React.lazy()+Suspense
const LazyComponent = React.lazy(() => import('./LazyComponent')); - Vue:使用
-
首屏不需要的资源延迟加载
- 图片懒加载(
loading="lazy",或者使用IntersectionObserver) - 非关键 CSS/JS 延迟加载(如
defer和async)
- 图片懒加载(
(2)减少 HTTP 请求
- 使用 HTTP/2 或 HTTP/3 进行多路复用,减少连接开销。
- 雪碧图(Sprite)合并小图标 或使用 SVG Sprites。
- 字体优化:
- 只加载必要的字体子集(
font-display: swap;)。 - WebFont 预加载 (
<link rel="preload" as="font">)。
- 只加载必要的字体子集(
2. 渲染优化(减少白屏时间)
(1)SSR(服务端渲染)或 SSG(静态生成)
适用于:SEO 需求强的项目
- Vue:Nuxt.js (
nuxt generate) - React:Next.js (
getServerSideProps() / getStaticProps())
作用:
- 服务器预渲染 HTML,减少前端计算量,提高首屏渲染速度。
- 但 SSR 需要解决水合(Hydration)问题,避免 CSR 二次渲染造成的白屏。
(2)骨架屏(Skeleton Screen)
适用于:数据量大、接口返回慢的场景
- Vue:使用
vue-skeleton-loader - React:使用
react-loading-skeleton或placeholder
示例:
{loading ? <Skeleton height={40} /> : <h1>{data.title}</h1>}
✅ 用户不会看到白屏,而是一个预加载 UI,提高体验。
(3)首屏关键 CSS 提前加载
减少 FOUC(闪烁无样式内容)
- CSS 提前加载
<link rel="preload" href="/styles/main.css" as="style" /> - CSS Critical Rendering Path
- 提取首屏关键 CSS,直接嵌入
<style>,非关键 CSS 采用lazyload。
- 提取首屏关键 CSS,直接嵌入
3. 交互优化(提高可用性)
(1)减少 JavaScript 执行时间
- 减少不必要的 JavaScript 计算(比如避免大数据遍历)。
- 使用
requestIdleCallback让低优先级任务在空闲时间执行:requestIdleCallback(() => { console.log('执行低优先级任务'); });
(2)首屏渲染时减少阻塞
- 避免
setTimeout和requestAnimationFrame影响首屏 - 使用
defer让非关键 JS 延迟加载<script src="analytics.js" defer></script> - 优先渲染主内容,推迟非核心部分
- 例如,在 React/Vue 里先渲染核心 UI,再通过
useEffect处理次要内容:
useEffect(() => { fetchMoreData(); }, []); - 例如,在 React/Vue 里先渲染核心 UI,再通过
4. 监控与优化
(1)使用 Web 性能指标
- FP(First Paint):首个像素绘制
- FCP(First Contentful Paint):首个内容绘制
- TTI(Time to Interactive):可交互时间
- LCP(Largest Contentful Paint):最大内容绘制
- CLS(Cumulative Layout Shift):累积布局偏移
(2)使用性能分析工具
- Chrome DevTools Performance
- Lighthouse
- WebPageTest
- Google PageSpeed Insights
5. 具体优化实战总结
| 优化点 | Vue | React | 作用 |
|---|---|---|---|
| 代码分割 | defineAsyncComponent | React.lazy() | 减少首屏 JS 体积 |
| SSR | Nuxt.js | Next.js | 预渲染 HTML,减少前端计算 |
| 图片懒加载 | v-lazy | loading="lazy" | 避免一次性加载所有图片 |
| 骨架屏 | vue-skeleton-loader | react-loading-skeleton | 让用户看到占位内容 |
| 关键 CSS 提前加载 | <style> 内嵌 | <link rel="preload"> | 避免 FOUC |
requestIdleCallback | ✅ | ✅ | 低优先级任务异步执行 |
defer/async | ✅ | ✅ | 非关键 JS 延迟执行 |
| 性能监控 | performance.now() | Lighthouse | 监控与优化 |
6. 总结
- 减少 HTTP 请求(合并资源、使用 CDN、HTTP/2)。
- 优化 JS 解析与执行(Tree Shaking、懒加载)。
- 减少白屏时间(SSR、骨架屏、关键 CSS)。
- 异步加载非核心内容(
defer、requestIdleCallback)。 - 监控性能指标并优化(LCP、CLS、TTI)。
这些优化手段结合使用,可以显著提升首屏加载速度,让用户获得更流畅的体验!🚀