# 20260306面试准备
# 怎么用户LCP响应时间过长问题
1.服务器响应慢 HTML 首屏加载慢。 API 接口或 SSR(服务端渲染)数据延迟。
2.资源阻塞渲染 CSS、JS 文件阻塞首屏渲染。 大量第三方脚本(广告、统计、社交插件)。
3.图片/媒体文件过大 大图未压缩或未使用 WebP/AVIF。 图片尺寸超过显示区域。
4.客户端渲染阻塞 大量 JS 运算,阻塞主线程。 SPA 首屏渲染需要下载并执行过多 JS。
5.懒加载不合理 首屏关键内容被懒加载。 视口内的内容未优先加载。
2️⃣ 优化策略
- 1服务器端优化
使用 CDN 缓存静态资源。
开启 HTTP/2 或 QUIC 提升并行加载。
优化服务器渲染(SSR)或预渲染(SSG),缩短 TTFB(Time To First Byte)。
数据接口延迟高 → 考虑 缓存、压缩、分页。
- 资源优化
图片优化: 压缩图片(WebP / AVIF / JPEG 2000)。
设置 width 和 height 避免布局抖动。
对首屏图片使用 优先加载 ()。
CSS/JS 优化:
移除未使用的 CSS(Tree-shaking)。
拆分 JS(Code Splitting)并懒加载非关键脚本。
将关键 CSS 内联,减少阻塞。
压缩/合并 JS,开启 Gzip 或 Brotli。
减少第三方脚本
延迟加载广告、分析、社交插件。
3.客户端渲染优化
对 SPA 项目,考虑 SSR 或 SSR+CSR 混合,确保首屏 HTML 包含关键内容。
避免在首屏渲染中执行大量 JS 计算。
使用 requestIdleCallback 或 Web Worker 做耗时任务。
4.优先加载关键内容
确定 LCP 元素(通常是 或
)。
对 LCP 元素使用:
# 说一下Vue3的执行流程
# react组件之前怎么🧑通讯
父子通信:通过 props 传递数据。
子父通信:父组件传递回调函数,子组件调用。
兄弟组件通信:通过父组件作为中间层共享状态。
兄弟组件一般通过 父组件中转。ChildA → Parent → ChildB
function Parent() {
const [msg, setMsg] = useState("")
return (
<>
<ChildA setMsg={setMsg} />
<ChildB msg={msg} />
</>
)
}
- 跨层级通信:使用 Context API。 当组件层级很深时,可以使用 Context API。
const ThemeContext = React.createContext()
function App() {
return (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
)
}
function Child() {
const theme = useContext(ThemeContext)
return <div>{theme}</div>
}
全局状态管理:使用 Redux、Zustand 等状态管理库。
Ref 通信:父组件通过 ref 调用子组件方法。
Ref / Imperative Handle,父组件可以通过 ref 调用子组件方法。
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
focus() {
console.log("focus")
}
}))
})
function Parent() {
const ref = useRef()
return (
<>
<Child ref={ref} />
<button onClick={() => ref.current.focus()}>
click
</button>
</>
)
}
# 什么是链表?
链表(Linked List)是一种线性数据结构,由多个节点(Node)组成,每个节点包含两部分:数据(data)、指向下一个节点的指针(next)
class Node {
constructor(value) {
this.value = value
this.next = null
}
}
// 创建链表:
const a = new Node("A")
const b = new Node("B")
const c = new Node("C")
a.next = b
b.next = c
console.log(a)
// 结构:
A → B → C
- 链表常见类型
1: 单链表(Singly Linked List)只有 next。
A → B → C → D
2: 双向链表(Doubly Linked List), 节点:prev,next
A ⇄ B ⇄ C ⇄ D
3: 循环链表(Circular Linked List)
A → B → C
↑ ↓
└───────┘
# 为什么 React Fiber用链表
可以随时暂停遍历, 不依赖递归调用栈, 方便实现可中断渲染
App
↓ child
Header
↓ sibling
Main
↓ sibling
Footer
# React Fiber 架构
React Fiber 架构是 React 从 v16 开始引入的一套新的协调(reconciliation)架构,主要解决旧架构在大型应用中 渲染阻塞、无法中断、页面卡顿的问题。
在 React 16 之前,React 使用的是:Stack Reconciler,React 更新组件树时会 一次性递归执行完所有 diff。如果组件树很大:就会导致:掉帧
Fiber 的目标是:把一次长任务拆成多个小任务,允许:暂停,恢复,优先级调度
# next路由有哪些
Pages Router(传统路由):Next.js 13 之前的主要路由方式。
- 1 静态路由:文件名就是路径。
pages/ ├ index.tsx ├ about.tsx └ blog/ └ [id].tsx
- 2 动态路由:通过文件名生成路由。使用 [param]
pages/post/[id].tsx
- 3 catch-all 路由
pages/docs/[...slug].tsx
可以匹配:
/docs/a
/docs/a/b
/docs/a/b/c
返回:
['a','b','c']
Pages Router 在大型应用中存在一些架构限制,比如布局复用困难、数据获取方式复杂、以及无法很好支持 React Server Components。
- App Router(新路由)
因此 Next.js 在 13 版本推出了 App Router,它引入了 Server Components、Nested Layout、Streaming Rendering 等能力,使得数据获取更加简单,同时减少客户端 JS 体积,提高页面性能和开发体验。
- 1 支持 Server Components(最大变化重点)
// 不会打包到浏览器, JS 体积更小, 更快
export default async function Page() {
const data = await fetch("https://api.com/data")
return <div>{data.title}</div>
}
- 2 真正的嵌套路由 + Layout
app/
├ dashboard/
│ ├ layout.js
│ ├ page.js
│ ├ settings/page.js
│ └ users/page.js
- 3 更简单的数据获取
async function getData() {
const res = await fetch("https://api.com")
return res.json()
}
- 4 Streaming(渐进渲染)
以前页面必须:
数据全部加载完
↓
再返回 HTML
现在:
Header 先渲染
↓
内容慢慢加载
- 5 更强的路由能力
| 功能 | 作用 |
|---|---|
| Layout | 共享布局 |
| Route Groups | 路由分组 |
| Parallel Routes | 并行渲染 |
| Intercepting Routes | Modal 路由 |
| 特性 | Pages Router | App Router | |
|---|---|---|---|
| 默认组件 | Client Component | Server Component | |
| 数据获取 | getStaticProps | getServerSideProps | 直接 async/await |
| Layout | 需要手动写 | 内置 layout.tsx | |
| Loading | 手动控制 | loading.tsx | |
| Streaming | 不支持 | 支持 | |
| 缓存机制 | 较弱 | 内置 fetch cache |
# 什么事MCP
MCP 通常指 Model Context Protocol,是 AI 领域最近很火的一个协议。 MCP 是让 AI 可以像插件一样调用工具、访问数据的一种标准协议。
如:用户输入的文本
AI:无法访问你的数据库
AI:无法读取你的文件
AI:无法调用API
如果想让 AI:
查数据库
读本地文件
调用 GitHub
控制 IDE
开发者必须:
自己写一堆 API glue code
每个 AI 工具都要重新集成一次。
| AI Plugin | MCP | |
|---|---|---|
| 标准 | 各家不同 | 统一协议 |
| 连接方式 | API | 工具协议 |
| 生态 | 分散 | 统一 |
# Cursor,和Claude 有哪些优缺点?
Cursor 是基于 Visual Studio Code 改的,所以开发体验非常完整。
比如可以:
直接改整个项目
AI 自动理解项目结构
自动改多个文件
自动生成代码
- 缺点
有时候改代码太“激进” Cursor 有时候会:
改很多文件
改错逻辑
破坏代码结构
- Claude 的优缺点
超强代码理解能力
Claude 在代码理解方面非常强。
尤其:
大型代码
架构分析
代码解释
比如你丢 5000 行代码,它依然能理解。
- 缺点
不能直接运行代码
不像 Cursor:
Cursor 可以直接改项目
Claude 只能给代码
# 为什么 React 要从 Stack Reconciler 升级到 Fiber?
Stack Reconciler 是同步递归的,无法中断,导致大组件树渲染时页面卡顿;Fiber 让渲染变成可中断、可调度的任务。
React 在 React 16 引入了 Fiber 架构,把递归调用 变成可暂停的任务
| Stack Reconciler | Fiber | |
|---|---|---|
| 渲染方式 | 递归 | 链表 |
| 执行方式 | 同步 | 可中断 |
| 任务拆分 | ❌ | ✅ |
| 优先级调度 | ❌ | ✅ |
| 并发渲染 | ❌ | ✅ |
Stack Reconciler 使用同步递归,一旦开始渲染必须执行完,无法中断, 当组件树很大时会长时间占用主线程,导致页面卡顿。
Fiber 将组件树转换成链表结构,把渲染拆分为可中断的小任务, 并引入调度机制和优先级系统,使 React 可以在浏览器空闲时继续渲染, 从而提升用户体验。
# 说一下这个代码的核心逻辑
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React</title>
<script
src="https://unpkg.com/react@18/umd/react.development.js"
crossorigin
></script>
<script
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
crossorigin
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect } = React;
// The core library code --- // zustand
const create = (createState) => {
let state;
const listeners = new Set();
const set = (partial) => {
const nextPartial =
typeof partial === 'function' ? partial(state) : partial;
state = { ...state, ...nextPartial };
listeners.forEach((listener) => listener());
};
debugger
state = createState(set);
const useStore = () => {
const [, setState] = useState(0);
useEffect(() => {
const forceUpdate = () => setState((c) => c + 1);
listeners.add(forceUpdate);
return () => {
listeners.delete(forceUpdate);
};
}, []);
return state;
};
return useStore;
};
const useBearStore = create((set) => ({
bears: 0,
honey: 10,
increaseBears: () => set((state) => {
debugger
return { bears: state.bears + 1 }
}),
increaseHoney: () => set((state) => ({ honey: state.honey + 1 })),
}));
function BearCounter() {
const { bears, increaseBears } = useBearStore('increaseBears');
console.log('BearCounter re-rendered');
return (
<div>
<h1>Bears: {bears}</h1>
<button onClick={increaseBears}>Increase Bears</button>
</div>
);
}
function HoneyPot() {
const { honey, increaseHoney } = useBearStore();
console.log('HoneyPot re-rendered');
return (
<div>
<h2>Honey: {honey}</h2>
<button onClick={increaseHoney}>Increase Honey</button>
</div>
);
}
function App() {
console.log('App re-rendered');
return (
<div>
<BearCounter />
<hr />
<HoneyPot />
</div>
);
}
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App />);
</script>
</body>
</html>
# 代码题
实现一个 requestScheduler 函数,假设有 10 个请求任务,但为了性能,要求同时执行的任务数不能超过 2 个。当其中一个完成后,自动补位执行下一个,直到全部完成。
async function requestScheduler(requests) {
let limit = 2;
let poolList = []
for(let i = 0; i < requests.length; i++) {
const p = request().then(() => {
// 2. 任务完成后,从执行池中移除自己
poolList.splice(poolList.indexOf(p), 1);
});
poolList.push(pool)
if(poolList.length >= limit) {
await Promise.race(poolList)
}
}
await Promise.all(poolList)
}
# react的生命周期
Mounting(挂载)
Updating(更新)
Unmounting(卸载)
创建组件
↓
Mounting
↓
Updating(可能多次)
↓
Unmounting
# 为什么 Hooks 不能写在 if 里面?
React Hooks 不是通过变量名管理 state,而是通过:调用顺序
// React 内部实现类似这样:
let hookIndex = 0
function useState() {
const state = hooks[hookIndex]
hookIndex++
}
React Hooks 是通过 调用顺序来管理 state 的。 如果 Hooks 写在 if、for 等条件语句中,就可能导致 每次渲染调用顺序不一致。 React 就无法正确匹配对应的 state,从而导致 状态错乱。 所以 React 要求 Hooks 必须写在 组件顶层。
# 为什么 React setState / useState 是异步的?
- 提高性能 如果每次 setState 都立即更新并重新渲染,会非常慢。
setCount(1)
setCount(2)
setCount(3)
如果是同步更新:会触发 3次渲染。所以 React 会先 收集更新,然后一次性执行。
React 的 setState / useState 并不是立即更新 state,而是通过 调度机制进行批量更新。 这样可以把多次 state 更新合并成一次渲染,提高性能。同时 React16 之后引入 Fiber 架构,可以根据任务优先级进行调度,避免长时间阻塞 UI。因此 React 采用了这种异步更新策略。
# 1.什么是 React Server Components
组件在服务器运行 结果发送给客户端
好处: 1)减少 JS bundle 2)提升性能 3)更好的数据获取方式
# 2.为什么 RSC 不能使用 useState
Server Component 不会在浏览器执行,而 useState 是浏览器交互状态。
# 3.什么是 Hydration
Hydration 是 React 在服务端渲染中的一个过程。服务器先生成 HTML 返回给浏览器,浏览器展示静态页面。随后客户端加载 React JavaScript,并将 React 组件与已有 DOM 结构绑定,同时注册事件监听器,使页面变成交互式应用。这个过程就叫 Hydration。
| Hydration | Re-render | |
|---|---|---|
| DOM | 复用已有 DOM | 重新生成 |
| 发生时间 | 首次加载 | 状态变化 |
| 目的 | 绑定 React | 更新 UI |
# 4. React Streaming
React Streaming 是 React 18 提供的流式服务端渲染能力,它允许服务器在 HTML 完整生成之前就开始向客户端发送内容。通过 Suspense 机制,React 可以先返回已经准备好的组件,而延迟加载较慢的部分,从而提升首屏加载速度和用户体验。
# 5. React 缓存
同一个请求, 只会请求一次,React 内部做了:request deduplication
await fetch('/api/user')
await fetch('/api/user')
- Data Cache(数据缓存)
// 默认cache: force-cache
fetch('/api/posts')
// 关闭缓存
fetch('/api/posts', {
cache: "no-store"
})
- 4 Router Cache(客户端缓存)
用户访问: /proudcts
在返回:/home
再回去:/proudcts
不会重新请求。
Next.js 13 引入了多层缓存机制来提升性能,包括 Request Memoization、Data Cache、Full Route Cache 和 Router Cache。React 会自动去重相同的请求,Next.js 默认缓存 fetch 请求数据,同时缓存页面 HTML,并在客户端缓存路由数据。通过 revalidate、cache 和 dynamic 等配置,可以控制缓存策略,实现 SSR、SSG 和 ISR 等不同渲染模式。
# nest.js 面试题
# NestJS 有 6个核心概念:
x| 概念 | 作用 | | ---------- | ------- | | Module | 模块 | | Controller | 控制器 | | Service | 业务逻辑 | | Provider | 依赖注入 | | Middleware | 中间件 | | Pipe | 数据转换/校验 |
# 1.怎么写一个装饰器
装饰器本质是一个函数,可以作用在类、方法、属性或参数上。 在 TypeScript 中通常通过 Reflect Metadata 存储元数据。 NestJS 的装饰器例如 @Controller、@Get、@Body 本质上都是在类或方法上挂载 metadata,然后在运行时通过 Reflect.getMetadata 读取并执行对应逻辑。
# 怎么理解NestJS 请求生命周期顺序
Middleware
↓
Guard
↓
Interceptor (before)
↓
Pipe
↓
Controller
↓
Service
↓
Interceptor (after)
↓
Response
1: 用一个真实请求来理解
/users/123
- 1 Middleware(中间件)
最先执行。
作用: 日志 cors token解析
function logger(req, res, next) {
console.log(req.url)
next()
}
- 2 Guard(守卫)
如:权限控制 是否登录 是否管理员 是否有权限访问
@UseGuards(AuthGuard)
核心方法:
canActivate(context: ExecutionContext)
返回:
true → 继续执行
false → 拒绝访问
- 3 Interceptor (before) 拦截器开始执行。
作用: 请求日志 请求缓存 修改 response
@UseInterceptors(LoggingInterceptor)
Interceptor 是 AOP思想。
- 4 Pipe(管道) 数据转换 / 参数校验
Pipe 可以:string → number
@Get(':id')
find(@Param('id', ParseIntPipe) id: number)
- 5 Controller(控制器)
真正的路由处理。
负责:
接收请求 调用 service 返回数据
@Get(':id')
getUser(@Param('id') id: number) {
return this.userService.getUser(id)
}
6 Service(业务逻辑) 业务逻辑: 查数据库 调用 API 计算逻辑
7 Interceptor (after)
Controller 执行完之后。
Interceptor 会继续执行。
作用:
修改返回数据
统一 response 格式
记录接口耗时
{
"code": 200,
"data": {}
}
- 8 Response(响应)
HTTP 200
# webpack 编译流程
解析入口文件,找到哪些文件需要添加到webpack的依赖中
调用const EntryPlugin = require("webpack/lib/EntryPlugin")的EntryPlugin函数,这个是webpack的内部插件,然后将入口文件添加到依赖中
输出的时候,我们可以控制输入打包的文件名称,大小,输出的路径等
Compiler 是webpack的主要引擎,可以创建一个compilation。Compiler他是继承Tapable类,是为了注册和调用插件。大多数面向用户的插件会首先在Compiler上注册。
Compilation 模块会被 Compiler 用来创建新的 compilation 对象(或新的 build 对象)。 compilation 实例能够访问所有的模块和它们的依赖(大部分是循环依赖)。 它会对应用程序的依赖图中所有模块, 进行字面上的编译(literal compilation)。 在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、 分块(chunk)、哈希(hash)和重新创建(restore)。
# 手写Primise原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function MyPromise(excutor) {
let self = this;
self.status = 'pending';
self.resolveCallBackFn = [];
self.rejectCallBackFn = [];
function resolveFn(value) {
setTimeout(() => {
if(self.status == 'pending') {
self.status = 'fulfilled'
self.value = value;
self.resolveCallBackFn.forEach(fn => fn(self.value))
}
})
}
function rejectFn(reson) {
self.status = 'rejected'
self.reson = reson;
self.rejectCallBackFn.forEach(fn => fn(self.reson))
}
excutor(resolveFn, rejectFn);
}
function resolveMyPrimise(x, resolve, reject) {
if(typeof x === 'object') {
resolve(x)
}
}
MyPromise.prototype.then = function(resolveFn, rejectFn) {
let promise2,
self = this;
if(self.status == 'pending') {
promise2 = new MyPromise((resolve, reject) => {
self.resolveCallBackFn.push((value) => {
let x = resolveFn(value);
resolveMyPrimise(x, resolve, reject)
})
self.rejectCallBackFn.push(value => {
let x = rejectFn(value);
resolveMyPrimise(x, resolve, reject)
})
})
}
return promise2
}
var test = new MyPromise((res, rej) => {
setTimeout(() => {
res({name: 'wzh', age: 23})
}, 1000)
})
test.then((res) => {
res['address'] = 'sz'
return res;
}).then(res => {
res['emial'] = '147@qq,com'
return res
}).then(res => {
console.log(res)
})
</script>
</body>
</html>
# nextjs 性能优化
- 选择正确的渲染策略
| 渲染方式 | 作用 | 使用场景 |
|---|---|---|
| CSR | 客户端渲染 | 浏览器渲染页面,动态交互或私有页面 |
| SSR | 服务端渲染 | 每次请求生成 HTML,实时数据、个性化页面 |
| SSG | 静态生成 | 构建时生成 HTML,内容静态、不常 |
| ISR | 增量静态生成 | SSG + 定时刷新 内容偶尔更新 |
其中:基于用户请求触发的重新生成 + 带时间控制的缓存失效。不是后台定时任务 而是请求触发更新
缓存过期 + 有用户访问
↓
才触发重新生成
如果:一天没人访问, 页面:不会更新
- 代码分割
动态组件加载
import dynamic from 'next/dynamic'
const Chart = dynamic(() => import('./Chart'), {
ssr: false
})
减少首屏 JS
按需加载组件
- 缓存优化 Next.js 支持 多层缓存。
1.HTTP Cache:
Cache-Control: s-maxage=60
2: API Cache
fetch(url, {
next: { revalidate: 60 }
})
- 减少 JS 体积 Tree Shaking,删除未使用代码。
- 优化数据请求 瀑布流请求
- CDN 加速
静态资源,可以直接 CDN 缓存。
JS
CSS
Image