面试大综合
面试大综合
八股
1、setTimeOut 定时器的回调函数在主栈执行,需要闭包保存上下文
2、fn.bind(obj) 绑定后会返回新函数,call、apply立即执行(apply要求数组传参)
3、bind 基于 apply + 闭包实现,底层是暂时把 fn 变成 obj 的一个成员,调用一次之后就销毁
- 不能被bind改变上下文的情况:箭头函数、已经被bind过一次的函数、静态方法(static函数是类属性,不能被调用)
4、var 定义的变量有全局作用域,每次迭代不创建新对象;let 是块级作用域,每次迭代都是新对象
5、const x=1; x++ 错误,x++ 等效于 x=x+1
6、ES6的类写法是运行时语法糖,TS的类型是编译期扩展,即ES6的类如果出错只能在运行时发现,TS会直接编译不通过,抽象类、接口也是TS才有
7、防抖:维护一个倒计时定时器,n秒执行一次。每次执行时都先清空这个计时器再重新计
8、异步(等待的时间可以做别的)解决方案:
- 回调:后一步需要前一步的结果,把下一步的动作写在回调里
- promise 把未来的结果变成对象,控制权回到调用方
- async、await本质是promise的语法糖
9、XHR:XMLHttpRequest,早期浏览器异步网络api,基本用法:xhr.open('GET', '/url'); 属于回调式,无promise支持
10、fetch:现代api,支持promise
11、axios封装了XHR/fetch,浏览器端用XHR,比较重
12、ky是原生fetch+少量的填坑,例如 res.json 自动化、HTTP错误自动reject(本来不会认为404等是错误),轻量
13、ajax是不请求全部网页而只做局部更新的网页技术,没有ajax的时代必须请求完整的html,全盘销毁旧HTML。ajax拿到数据后用js去更新dom,它的异步来自于浏览器架构(使用浏览器本身的网络进程)
14、XHR的请求流程
xhr.send() -> js调用浏览器的api -> 注册请求 -> 函数返回
浏览器会用专门的网络线程,响应返回后把回调包装成任务塞入队列
15、事件循环:同步代码 -> 清微任务 -> 渲染 -> 一个宏任务 -> 清微任务 -> 渲染 -> 一个宏任务 -> ...
设计原因:先清微任务保证promise的行为可预测,渲染之前把状态更新到最新
16、事件捕获冒泡
document -> html -> body -> ... -> A -> ... -> body -> html -> document
A的捕获监听器和冒泡监听器都执行。默认监听器是冒泡阶段,如果要改捕获,传个true参数:
addEventListener('click', handler, true) // 捕获监听器冒泡用来做事件委托,捕获用来做事件拦截器:为document加一个监听器,可以先拦截任何事件
17、reactRouter 用来实现单页面应用切换url时不刷新页面的情况下改变内容,reactRouterDom 在它的基础上新加了一些浏览器功能
18、BrowserRouter 和 HashRouter
BrowserRouter:history模式,可以用pushState、replaceState实现url导航,可以用浏览器箭头控制,会更新历史记录;HashRouter:hash模式,监听url中 # 之后的值,模拟路由变化,不会发请求
19、react组件生命周期
主要三个过程:mounting -> updating -> unmounting
- 第一次渲染:挂载,执行render(函数组件本身),useEffect在首次渲染后执行
- 有更新时:props/state变化时,再次执行函数组件,useEffect按依赖表决定是否执行(fiber也是作用在这一步)
- 移除时:卸载,useEffect返回的清理函数执行
20、组件间值传递
- 父传子:props
- 子传父:父组件在props里写一个回调,子组件调用它传值
- 用
useContext依赖注入,向环境提供或从环境读取值,提供者创建一个context,消费者直接用,可以跨多层透传 - 状态管理库
21、hook:react的设计理念是任何组件必须是纯函数,如果要带副作用(记忆一个状态、引入外部依赖)就要通过hook:
- useState: 确保每次函数组件重新执行时组件都能获取到最新的状态值,这个值实际上是保存在一个外部结构中的,不受组件刷新影响
- useEffect:通用副作用hook,渲染后按依赖执行
22、fiber包含一个组件的相关信息,类似一个树节点,react会沿着这棵树完成props比较、更新等任务,这个过程可以随时中断去处理更重要的事(用户交互等)。
在React 16之前的版本中,是使用递归的方式处理组件树更新,称为堆栈调和。这种方法一旦开始就不能中断,直到整个组件树都被遍历完。
23、受控组件:react管理(动态表单、按钮在表单有效前禁用);非受控组件:DOM自身管理(文件输入、轻量表单)
24、react的事件机制:对原生事件的跨浏览器封装。把所有的事件都绑定到最外层统一监听,组件挂载、卸载都只是在统一监听器上插入、删除对象。
顺序:react把事件都绑定到document -> 真实dom触发,先执行原生事件 -> 冒泡到document,处理react事件
25、react19的更新
- 有服务端渲染(SSR),减少发到客户端的js bundle,首屏性能高,SEO好
- 流式渲染,先发部分HTML,后续补全,提升首屏体验
- 渲染优化,降低对useMemo的依赖
- 现代化的JSX转换,无需import React
26、居中做法:绝对定位;若知道父元素宽高,用负margin;flex:justify-content(默认主轴水平),align-items
27、单位:em:相对当前对象内文本尺寸(默认1em=16px);rem:相对HTML根元素的font-size
28、webpack流程、原理
从入口文件出发,构建依赖图 -> 打包js、css、图片、资源 -> 启动热更新服务器(HMR)
webpack的HMR实际上是在整体打包后的bundle里替换要热更新的部分,比较慢
29、vite流程、原理
读取 vite.config.ts,合并默认配置 -> TS转JS(babel) -> TSX转JS(esbuild,组件转成虚拟dom) -> 模块解析,形成模块依赖图 -> 执行rollup插件 -> TreeShaking(移除引入但未使用的包) -> 代码分割(按一定规则把不同的包分成多个js文件) -> 处理资源(图片转base64或hash) -> 处理css -> 输出(js bundle、css、assets)
好处:不会一次性打包,网页请求哪里打包哪里(与其有关的一切依赖一并打包),HMR启动超快
30、babel转译:把TS、ES6语法编译成js
parse(源码 -> AST) -> Transform(插件修改AST,把JSX转为普通函数) -> Generate(AST -> JS)
31、重排(回流):重新计算元素位置、布局,改变元素宽高等会影响布局的属性时触发;重绘:改变元素颜色、background等属性时触发
32、useMemo用来避免无谓渲染,只有当依赖项变化时才触发,例如父组件更新时子组件不一定需要更新,就可以加useMemo
33、浏览器缓存
- 强缓存:命中时不向服务器发请求。请求资源 -> 检查本地缓存 -> 如果未过期(cache-control,expires(已过时)) -> 200 -> 如果过期 -> 走协商缓存
- 协商缓存:需要服务器来判断是否用缓存。发现强缓存过期 -> 带一个缓存标识E-tag发请求 -> 未修改过,304 -> 如果修改了,返回最新值,200
34、同源策略:协议、域名、端口号
35、cookie(4kb),sessionStorage(5MB)、localStorage(5MB)都是浏览器存储方案。cookie会随请求发送(主要做身份验证),storage只用于前端存储,不参与请求
36、浏览器多进程、多线程架构
- 浏览器进程(主进程):显示、交互、进程管理
- 渲染进程:js执行、dom操作,包含的线程:
- 主线程:js执行、dom操作、网络请求
- 工作线程:大文件、复杂算法
- 合成线程:滚动、动画、优化渲染
- 光栅化:图层转换为像素,图片解码
- GPU进程:3D、硬件加速(会触发gpu参与合成、光栅化的css属性:(3d)transform、opacity、filter、will change)
- 网络进程:资源加载
- 插件进程:处理浏览器插件
设计原因:进程间隔离,互不影响,利用多核CPU性能,独立管理内存,便于垃圾回收
37、输入url到呈现总流程:
输入url -> 走缓存流程 -> 浏览器解析url -> 浏览器组装HTTP请求 -> 获取服务器ip(有缓存用缓存,否则走dns解析) -> 打开socket,建立tcp连接* -> 建立连接后发http请求 -> 服务器解析并处理请求 -> 服务器把响应报文发回浏览器 -> 浏览器接收、渲染* -> 如果要关闭,tcp四挥手
38、tcp连接与断开
- 三握手:
- 客户端:SYN=1,seq=X
- 服务端:SYN=1,ACK=X+1,seq=Z
- 客户端:ACK=Y+1,seq=Z
- 四挥手
- 客户端:Fin=1,ACK=Z,seq=X
- 服务端:ACK=X+1,seq=Z
- 服务端发完剩余的数据包
- 服务端:Fin=1,ACK=X,seq=Y
- 客户端:ACK=Y,seq=X
39、浏览器收到服务端发来的打包好的东西后的渲染流程
构建HTML DOM树 -> CSS树 -> 合并成渲染树(仅有可见元素,display: none不在此列) -> 计算元素位置、大小(回流) -> 绘制(重绘) -> 合成(opacity、transform相关)
40、HTTP版本:
- 1.0:每个请求建立一个TCP,连接不能复用
- 1.1:有缓存,连接可复用,但是单路的,不能并发(管道),存在队头阻塞(一个返回才发下一个)
- 2.0:多路复用,带头部压缩
- 3.0:传输层使用UDP,服务端可以主动推送消息
41、HTTPS加密流程
客户端发HTTPS请求 -> 服务器返回包含公钥的证书(由CA公证机构签名) -> 客户端验证证书 -> 客户端生成对称密钥 -> 用服务端公钥(证书上的)加密发回服务器 -> 服务器用私钥解密,得到对称密钥 -> 后续用对称加密
中间人不能掉包证书,因为证书包含网站信息;也不能篡改,因为中间人无CA机构的私钥,不能签名
42、网络攻击
- XSS:跨站脚本攻击,script注入(用输入验证、转义。react自带,不执行script标签。如果要写html标签需要专门的hook)
- SCRF:用户登录A网站,拿到cookie,未登出时无意中访问黑网,黑网可以用这个cookie假冒用户进A网(用验证码防止用户无意操作;请求带一个token验证,不要放在cookie里)
43、JWT登录原理:把用户信息加密成token发给客户端,每次请求都带着,服务端只验证,不存储登录状态
44、第三方登录(微信、google):本质上仍然是JWT。用户前端登录后,第三方重定向回系统,带一个授权码。服务端用授权码换一个access token(防止access token暴露在浏览器),可以凭这个获取用户信息(头像、昵称等),关联或创建本地用户,生成自己的JWT。
45、promise原理
promise基于状态机:pending、fulfilled、rejected
主要是then方法:返回一个新promise,实现链式调用。then方法会判断传入的是不是函数,如果不是,给默认(fulfilled返回原值,rejected抛错),如果是,根据内部状态决定何时回调:
- fulfilled:已有结果,把回调放入宏任务队列,执行onFulfilled,拿到结果,调resolve promise处理其是否依然是promise;
- rejected:已有结果,执行onRejected
- pending:还没结果,把成功、失败回调存进队列,未来状态变化时再用微任务处理回调
46、react函数组件渲染流程
本质:根据state和props计算UI描述(虚拟DOM)
先执行函数组件,顺序调用hook,计算虚拟DOM,这部分只有纯计算,不执行副作用;diff阶段对比新旧虚拟dom,fiber比较更新真实DOM方案;提交阶段更新真实DOM,执行useEffect
47、Hook按调用顺序识别,所有的hook会被放进一个hooks列表,每次render时按顺序执行,不能放在控制结构内,会影响顺序,导致真实情况和hooks表错位
48、Hook的原理
hook会被当作fiber节点渲染函数,渲染阶段调用组件,拿到JSX,调用前react会把全局指针指向正在渲染的fiber节点,并重置hooks链表指针。每次调用useState,react会在全局指针处挂一个hook节点。
- useState:取hooks表下一个位置,每次setState都生成更新对象,放入更新队列,通知react重新调度
- useEffect:不会在渲染阶段回调,先记录副作用对象,存在fiber节点的effect表,提交阶段遍历effect,对比依赖决定是否执行回调
49、虚拟DOM
概念:代码层面是一个js对象,由
react.createElement创建,是对真实DOM的描述作用:提升性能,dom运行在渲染引擎,js运行在js引擎,操作真实dom相当于跨系统调用,开销巨大,如果先算好最小更新方式再一次性更新能带来巨大提升;虚拟DOM还可以实现跨平台:同一套虚拟DOM可以适配浏览器dom控制或移动端的原生控件,做到解耦。
网易高频
1、meta标签:meta 标签主要用于描述文档的元信息,例如字符集、页面描述、关键字、视口信息等。 它不会直接渲染到页面,但会影响浏览器解析、SEO、移动端适配等行为。name=viewport用于控制移动端页面的布局视口,决定页面如何缩放和渲染
2、BFC(块级格式化上下文)是一个独立的布局环境,其内部布局不会影响外部
3、DocumentFragment:是一个轻量级的 DOM 容器,不会触发回流重绘。适合用于批量 DOM 操作后一次性插入页面,提高性能(类似缓冲区)。
4、requestAnimationFrame:js实现动画的一种方式,会在浏览器下一次重绘前执行回调,与刷新率同步,性能更好,且在页面隐藏时会自动暂停。
5、泛型约束:指定泛型变量必须具有某些特征才能通过编译:
function fn<T extends { id: number }>(arg: T) {}6、ESM 和 CJS 区别
| ESM | CJS | |
|---|---|---|
| 加载 | 静态 | 动态 |
| 运行时 | 编译期分析 | 运行时 |
| this | undefined | module |
项目
1、TanstackQuery
可以理解为专门管理服务端数据的状态管理库,状态的过期时间由前端配置。典型用处:数据缓存,列表页的数据请求完先存在tanstack里,从别的页面回来后可以直接凭key获取,无需再发请求
2、MutationHook
我在项目中封装过自定义的 mutation hook,主要是出于一致性、复用性和可维护性的考虑。
设计原因上,一方面是项目里新增、编辑、删除这类写操作非常多,如果每个地方都直接使用 useMutation,会导致请求逻辑、错误处理、提示文案和缓存更新方式分散在各个组件中;另一方面,不同接口对成功后的缓存刷新、乐观更新策略是固定的,很适合做统一抽象。
在设计上,我通常会基于 useMutation 封装一层业务 hook,比如 useCreateUser、useUpdateMenu,在内部统一处理:
- 接口请求函数
- 成功后的
invalidateQueries或setQueryData - 通用的成功 / 失败提示
这个自定义 mutation hook 的作用是让组件层只关注“什么时候触发变更”,而不用关心“变更之后如何维护缓存一致性”,从而降低心智负担,也减少重复代码。
在实际效果上,这种方式能明显提升代码可读性,也让后期调整缓存策略或错误处理时,只需要改一个地方。
3、性能瓶颈调试(performance)
首先,我在无干扰环境下录制一次用户真实操作(比如页面加载、点击、滚动),观察整体时间轴,判断问题是发生在加载阶段还是交互阶段。
接着我重点查看Main 线程,关注是否存在长任务(Long Task,>50ms),并展开调用栈,定位是脚本执行、样式计算、布局还是绘制导致的阻塞。
如果看到频繁的回流,我会判断是否存在强制同步布局或不合理的 DOM 操作;如果是大量重绘,则考虑是否需要用 transform、opacity 优化动画。
最后,我会在优化后再次录制,对比前后的时间分布和帧率变化,验证优化是否真正生效,而不是只凭感觉判断。
4、agent skill
是Agent 可复用的一段“可调用能力单元”,= 明确输入 → 稳定处理流程 → 可预期输出,比较像“对于prompt来说的函数”。
一个skill通常包含:
Skill Name
├── 1. Purpose(做什么)
├── 2. Input Schema(输入结构)
├── 3. Output Schema(输出结构)
├── 4. Reasoning Steps(内部步骤)
├── 5. Constraints(约束)
├── 6. Failure Handling(失败兜底)如果用cline,直接告诉claude读取.skills/image-to-html.md这个文档,Cline 会读取 workspace 文件,会把它当成“长期规则”,稳定性比临时 prompt 高很多。
