React
- 有哪些常用的react hooks
- 由一延展:为何 React Hooks 不能放在条件或循环之内
- React的渲染机制
- 由3延展:说说什么是虚拟dom,diff算法?
- 由3延展:什么是fiber架构,它解决了什么问题?
- react性能存在哪些问题?如何优化?
- rudex对比context api,rudex对比mobx
- react对比vue
- nextjs是如何实现ssr的
- 什么是高阶组件、受控组件、非受控组件?
- useState的参数可以使用函数吗?
- 如何避免useEffect的闭包陷阱?
- react如何做错误边界(类组件)
- react如何实现vue的keep-alive
- 有没有自己实现过hook,hook和普通函数有什么区别
- 使用过rudex的哪些中间件,是分布式的吗?
- jest都是ai写的用例吗?能涵盖ui类的测试吗?比如点击按钮弹窗或者动画?
react-router
- 说说两种react-router的区别
Js
- 如何判断类型?intanceof确定能判断准吗?有没有一定准确的方法?
- 如何实现深拷贝?除了递归之外呢?如果这个元素里面又调用了它本身该怎么做?
- 闭包是什么?闭包会产生什么问题?
- 什么是垃圾回收机制?说说两种垃圾回收机制
- js的事件循环机制?介绍一下promise,如果.then()里面有一个Error该如何捕获?.catch()之后还能做链式调用吗?
- for in和for of的区别
- ajax或者axios如何取消请求?
- js的继承,除了extends还有什么办法?
浏览器和http相关
- 跨域问题,如何只在前端处理跨域?
- cookie,sessionStorage,localStorage和index DB的区别?除了大小,时限,会随请求发送给后端还有什么区别?后端能否调用本地存储?
webpack
- webpack的优化
如何准确判断js类型
原型修改后,instanceof 可能失效,toString 只返回 [object Object],不够细。我用 constructor.name 获取具体类型,像 Dog 或 Foo,哪怕原型变也能认。篡改 constructor 时,结合 Object.getPrototypeOf 遍历原型链验证。在 React 中,我用它判 props 类型,确保组件逻辑准。
/**
* 获取变量的精确类型
* @param {any} value - 要判断类型的变量
* @returns {string} - 类型名称(小写)
*/
function getType(value) {
// 1. 处理 null 和 undefined
if (value === null) {
return 'null'; // 直接返回,避免 typeof 误判为 "object"
}
if (value === undefined) {
return 'undefined'; // 明确区分 undefined
}
// 2. 用 typeof 判断基本类型(除 null 外)
const type = typeof value;
if (type !== 'object' && type !== 'function') {
return type; // 返回 number, string, boolean, bigint, symbol
}
// 3. 处理对象类型,用 Object.prototype.toString 获取内置类型
const toStringType = Object.prototype.toString.call(value); // 如 "[object Array]"
const baseType = toStringType.slice(8, -1).toLowerCase(); // 提取类型名并转为小写
// 4. 如果是普通对象,进一步检查自定义类型或包装对象
if (baseType === 'object') {
// 检查包装对象(new Number, new String 等)
if (value instanceof Number) return 'numberobject';
if (value instanceof String) return 'stringobject';
if (value instanceof Boolean) return 'booleanobject';
// 检查自定义类型,通过 constructor.name
if (value.constructor && value.constructor.name !== 'Object') {
return value.constructor.name.toLowerCase(); // 返回自定义类名,如 "dog"
}
}
// 5. 返回内置类型或其他对象类型
return baseType; // array, date, regexp, function, object 等
}
// 测试用例
console.log(getType(42)); // "number"
console.log(getType("hello")); // "string"
console.log(getType(true)); // "boolean"
console.log(getType(null)); // "null"
console.log(getType(undefined)); // "undefined"
console.log(getType([])); // "array"
console.log(getType(new Date())); // "date"
console.log(getType(/test/)); // "regexp"
console.log(getType(function() {})); // "function"
console.log(getType(new Number(42))); // "numberobject"
console.log(getType({})); // "object"
// 自定义类型测试
class Dog {}
const dog = new Dog();
console.log(getType(dog)); // "dog"
// 修改原型测试
function Foo() {}
const foo = new Foo();
Foo.prototype = {};
console.log(getType(foo)); // "foo"(仍准确)
前端处理跨域
- 有几种方法。开发时,我用 Webpack 代理,在 package.json 加 proxy,把 /api 转到目标域名。生产环境可以用 JSONP,动态加
<script>
加载,API 返回回调函数,适合 GET 请求。若目标域名可控,用 iframe 和 postMessage 通信。还能搭个前端 Node 代理,转发请求。我在 React 中用代理开发,JSONP 调用第三方 API,灵活选方案
本地存储
Cookie 是 4KB 的会话或持久存储,随请求发送,适合认证。sessionStorage 和 localStorage 是 5-10MB,HTML5 API,前者会话级,后者永久,前端专用。IndexedDB 是大容量数据库,异步操作,适合复杂数据。区别在数据结构、操作方式、作用域等,Cookie 用字符串,其他支持对象,IndexedDB 最强。后端不能直接调用本地存储,但可通过 API 间接控制。我在 React 用 localStorage 存主题,Cookie 同步后端
什么是垃圾回收机制?说说两种机制。
- “垃圾回收是 JS 自动管理内存,回收不可用对象。两种机制是标记清除和引用计数。标记清除从根标记可达对象,清除不可达的,能处理循环引用,V8 用它,像 React 组件卸载清理引用后回收。引用计数跟踪引用数,计数为 0 回收,但循环引用会导致泄漏,已淘汰。我在 React 中用标记清除原理优化内存,确保大对象及时释放。”
如何实现深拷贝?除了递归呢?循环引用怎么办?
- “深拷贝我用递归遍历对象,嵌套对象再递归复制。非递归可以用JSON.parse(JSON.stringify()),简单但不支持函数,或者lodash的_.clonedeep方法
- 循环引用用 WeakMap 存已拷贝对象,遇到重复返回缓存。我在 React 中用它复制状态,确保修改不影响原数据。”
Js事件循环机制
-
注意浏览器事件循环和node事件循环有不同
-
执行当前调用栈中的同步代码,直到栈清空。
-
检查微任务队列,执行所有微任务直到队列清空。
-
执行一个宏任务(比如setTimeout回调)。
-
重复2-3,直到所有任务完成。
解释:同步代码先执行(start、end),然后微任务(promise),最后宏任务(timeout)。
new操作符的执行过程:
(1)首先创建了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
for in和for of的区别
-
for...in:
- 用途:遍历对象的可枚举属性(包括自身和继承的)。
- 对象:主要用于对象(Object),也可以用在数组(但不推荐)。
- 返回值:属性的键(key)。
-
for...of:
- 用途:遍历可迭代对象(如数组、字符串、Map、Set)的值。
- 对象:适用于实现了 Iterable 接口的数据结构。
- 返回值:元素的值(value)。
Hook 函数和普通函数有什么区别?
- “普通函数是 JavaScript 的通用函数,处理任意逻辑,不依赖框架,像格式化数据。Hook 函数是 React 提供的特殊函数,如 useState 和 useEffect,管理状态和副作用,只能组件顶层调用,依赖 React 的 Fiber。普通函数随意用,Hook 有规则限制。我用 Hook 管理状态,普通函数处理工具逻辑,两者结合提升代码复用。”
如何在项目中取消请求?
如果每个组件都手动取消,代码重复且易漏。我封装了个 useFetch Hook,用 AbortController 自动在 useEffect 清理,组件只管用。中大型项目我用 RequestManager 类,集中存 controller,路由切换时 cancelAll。或者用 Axios 拦截器统一加 CancelToken,自动管理。React Query 也是好选择,内置取消和缓存,我根据项目选最合适的。
数组有哪些原生方法?
- 数组和字符串的转换方法:toString()、toLocalString()、join() 其中 join() 方法可以指定转换为字符串时的分隔符。
- 数组尾部操作的方法 pop() 和 push(),push 方法可以传入多个参数。
- 数组首部操作的方法 shift() 和 unshift() 重排序的方法 reverse() 和 sort(),sort() 方法可以传入一个函数来进行比较,传入前后两个值,如果返回值为正数,则交换两个参数的位置。
- 数组连接的方法 concat() ,返回的是拼接好的数组,不影响原数组。
- 数组截取办法 slice(),用于截取数组中的一部分返回,不影响原数组。
- 数组插入方法 splice(),影响原数组查找特定项的索引的方法,indexOf() 和 lastIndexOf() 迭代方法 every()、some()、filter()、map() 和 forEach() 方法
- 数组归并方法 reduce() 和 reduceRight() 方法
ES6
什么是高阶组件、受控组件、非受控组件?
高阶组件
是个函数,输出结果是个新组件,可以对输入的组件进行加工,并返回一个具有特定功能的组件。
受控组件
相当于input中的value值通过state值获取,onChange事件改变state中的value值。实现了双向绑定,任意一方的数据发生变化,另一方也会随之改变 。
非受控组件
不需要设置对应的state属性,可通过ref来直接操作真实的dom。
如何在react中实现vue的keep-alive
React 没有内置 keep-alive,但可以用 useRef 和自定义组件实现。Vue 的 keep-alive 缓存组件实例,我在 React 用 Map 存组件,useRef 保持引用,切换时只改 display 不销毁。加 useEffect 模拟 activated 和 deactivated。在 React 路由中,我用它缓存页面状态,像 Tab 切换保留输入值
useState的深度使用,它的初始值可以用函数,用函数有什么好处?
可以,useState 支持函数作为参数,叫初始状态函数,只在初次渲染时执行,返回初始值,后续忽略。我用它处理复杂初始值,像基于 props 计算或深拷贝对象,能避免每次渲染重复跑。比如 useState(() => initialValue * 2),性能更好。在 React 中,我用它初始化表单数据或读取 localStorage,很实用
如何避免 useEffect 的闭包陷阱?
- “useEffect 的闭包陷阱是回调捕获旧状态,像定时器里用初始值。我有几种方法避免:一是加依赖项,状态变时重跑;二是用函数式更新,setState(prev => prev + 1) 不依赖旧值;三是用 useRef 存最新值,ref.current 不受闭包限;四是把逻辑移出回调。我在计数器中用 Ref,确保定时器读最新状态。”
React Router DOM的两种路由模式
- 有 BrowserRouter 和 HashRouter 两种模式。BrowserRouter 用 HTML5 History API,URL 干净像 /home,适合现代 SPA,但需服务器配置重定向。HashRouter 用哈希路径如 #/home,无需配置,适合静态托管,但 SEO 不友好。我在项目中用 BrowserRouter 做官网,URL 直观;用 HashRouter 部署 GitHub Pages,省配置
- BrowserRouter 用 HTML5 History API 管理路由。pushState 添加历史记录,改 URL 不刷新,像点击 跳到 /home。popState 监听前进后退,捕获路径变化,同步组件渲染。React Router 封装这些 API,用 history 库监听和更新状态,匹配 渲染。我用它实现 SPA 导航,URL 干净又可控。
redux对比context api,rudex对比mobx
特性 | Redux | MobX | Context API |
---|---|---|---|
状态管理方式 | 集中式,单一 Store | 分散式,可观察对象 | 分散式,Provider |
数据流 | 单向(Action) | 双向(直接修改) | 无强制规则 |
学习曲线 | 较陡峭 | 平缓 | 简单 |
代码量 | 较多(样板代码) | 少 | 少 |
性能优化 | Reselect、浅比较 | 自动响应式 | 需手动优化(如 Memo) |
调试工具 | 优秀(Redux DevTools) | 一般 | 无专用工具 |
适用规模 | 大型应用 | 中小型应用 | 小型应用 |
-
如果问“为什么选 Redux?”
“Redux 适合需要严格状态管理和调试的大型项目,它的单一数据源和时间旅行功能在复杂场景下很有优势。但如果是简单项目,我可能会选 Context API 来减少依赖,或者用 MobX 来提升开发效率。” -
如果问“Redux 和 MobX 的区别?”
“Redux 是函数式编程风格,状态不可变,更新通过纯函数;MobX 是面向对象风格,状态可变,直接修改更直观。Redux 更规范但代码多,MobX 更灵活但可控性稍弱。” -
如果问“Context API 能替代 Redux 吗?”
“Context API 可以替代 Redux 在简单场景下的全局状态管理,比如主题切换。但它没有 Redux 的 Middleware 支持异步,也没有状态变化的严格规则,大型项目中可能不够。”
为何 React Hooks 不能放在条件或循环之内
“React Hooks 的状态存储在一个单向链表中,React 靠调用顺序来记住每个 Hook 的状态。第一次渲染时,React 按顺序构建链表,后续渲染时按相同顺序读取。如果把 Hooks 放在条件或循环里,调用顺序可能变,比如条件跳过某个 Hook 或循环次数改变,React 就无法正确匹配链表中的状态,导致报错或数据错乱。所以 Hooks 必须放在顶层,保证每次渲染顺序一致。
react性能有哪些问题?如何优化?
React 性能问题主要有几点:一是父组件更新导致子组件不必要渲染;二是复杂计算每次渲染都重复执行;三是事件函数重复创建,影响子组件缓存;四是无限滚动列表渲染过多 DOM,引发重绘重排;五是 Context 或 Redux 的全局更新影响无关组件。优化方法上,可以用 React.memo 阻止子组件渲染,useMemo 缓存计算结果,useCallback 稳定函数引用;用 react-window 实现虚拟列表减少 DOM 开销;通过拆分 Context 和用 reselect 优化 Redux Selector 来避免全局更新。此外,合理用 key 和懒加载也能提升性能
- “React Profiler 是性能调试利器, 提供 onRender 回调,记录渲染时间和阶段;DevTools 的火焰图能直观展示组件树耗时。我常用它验证优化,比如用 useMemo 后看 actualDuration 是否降低。相比浏览器工具,它更专注 React 层面的优化。”
react的渲染机制
React 渲染分两步:Render 阶段生成 Virtual DOM,通过 Fiber 遍历组件树,Diff 算法同层比较,标记更新;Commit 阶段同步改 DOM,执行useEffect和useLayoutEffect。Virtual DOM 减少 DOM 操作,Fiber 支持异步调度,React 18 还加了并发特性,像 startTransition 优化用户体验
nextjs的ssr和react的ssr
React SSR 在服务器渲染组件成 HTML,客户端 Hydration 激活,需要手动配置服务器和逻辑。Next.js 简化了这个过程,提供 getServerSideProps 自动处理,还支持 SSG、ISR 等多种模式,内置优化和路由,适合现代应用开发
维度 | React SSR(原生) | Next.js SSR |
---|---|---|
实现方式 | 手动搭建,需要自己写服务器代码和 Hydration 逻辑 | 内置支持,只需定义getServerSideProps |
复杂度 | 高,需要处理服务端和客户端一致性 | 低,框架封装好了细节 |
功能 | 仅提供渲染能力,其他功能需自行实现 | 提供路由、静态生成、API 等完整生态 |
灵活性 | 完全自由,可深度定制 | 有限,遵循 Next.js 约定 |
性能优化 | 需手动优化(如缓存、流式渲染) | 自动优化(如静态生成、代码分割) |
适用场景 | 自定义需求高的小型项目或实验 | 中大型项目,追求开发效率和 SEO |
学习成本 | 较高,需理解 SSR 原理和服务器配置 | 较低,文档友好,快速上手 |
react和vue对比
维度 | React | Vue |
---|---|---|
设计理念 | 函数式,单向数据流 | 渐进式,双向绑定 |
语法 | JSX | 模板语法 |
状态管理 | Hooks或Redux/MobX | 内置Vuex/Pinia |
渲染机制 | VirtualDOM+Fiber | VirtualDOM+响应式 |
学习曲线 | 较高 | 较低 |
生态 | 庞大,灵活 | 完整,专注 |
性能 | 需手动优化 | 自动优化,小型项目更高效 |
构建工具 | 手动或CRA | VueCLI/Vite |
fiber架构
“Fiber 架构是 React 16 引入的优化方案,旨在提升渲染过程的用户体验。React 15 使用的是 Stack Reconciler,渲染过程基于同步递归,无法中断。如果组件树过大或变更复杂,计算量大会阻塞主线程,导致长时间卡顿。Fiber 架构通过时间分片,将组件树拆分为 Fiber 节点,采用链表结构替代递归,逐步处理渲染任务。渲染过程中,Scheduler 会根据帧率动态检查是否有高优先级任务(如用户输入),若有则暂停渲染,优先处理;若无则继续渲染下一个 Fiber 节点。Fiber 使用双缓存机制,存储当前树(current,即用户看到的)和工作树(workInProgress),当某个节点处理完后,直接切换到工作树的下个节点,支持可中断和恢复。React 18 进一步引入并发渲染(Concurrent Rendering),通过时间切片和优先级调度(如 startTransition),确保紧急任务优先执行,提升复杂场景下的流畅性。”
react-window原理
react-window的虚拟列表原理是通过窗口化只渲染可视区域的元素,提升性能。核心是计算滚动偏移量和容器高度,确定startIndex和endIndex,只渲染这部分数据。实现上,用一个大容器撑开总高度,子项绝对定位到正确位置,滚动时动态更新索引。它减少了DOM节点数量,像10万条数据可能只渲染20个,比普通map快很多。我用它优化过长列表,首屏加载从几秒降到毫秒级。
原型和原型链
原型是函数的 prototype 属性,实例通过 proto 访问,用于共享方法。原型链是继承的链式结构,从实例到构造函数原型,再到 Object.prototype,直到 null。属性查找沿链向上,确保继承灵活。我会用原型共享方法,注意链长对性能的影响
react的diff算法
React 的 Diff 算法比较新旧 Virtual DOM,找出变化高效更新 DOM。核心是三个策略:同层比较,只比同一层级;类型一致性,相同类型更新属性,不同重建;用 key 优化列表,匹配复用节点。过程从根节点开始,递归比较,Fiber 记录更新,时间复杂度 O(n)。我会用唯一 key 和 React.memo 优化,确保性能
qiankun框架
Qiankun 是基于 single-spa 实现的微前端框架,继承了其路由分发和生命周期管理。JS 隔离方面,它通过沙箱给每个子应用虚拟了一个独立的 window,用 ProxySandbox 代理 window 对象,拦截读写操作,确保不污染全局,老浏览器用 SnapshotSandbox 快照恢复,切换时清理副作用。CSS 隔离通过动态类名实现,为子应用容器加唯一前缀,像 data-qiankun-appName,调整选择器隔离样式,还支持 Shadow DOM 完全隔离。通信上,主应用通过 props 在 mount 时传值给子应用,子应用间用 initGlobalState 创建全局状态,通过发布订阅模式通信,子应用用 setGlobalState 更新状态通知主应用,也可以用自定义事件处理复杂交互。实际项目中,我会用 prefetchApps 预加载优化性能,调试时检查沙箱和样式隔离,确保稳定。Qiankun 适合多团队协作或老项目改造,我会用它分模块开发,逐步迁移技术栈
利用lighthouse进行性能优化
我会用 Lighthouse 跑性能审计,关注 LCP 和 TBT,因为它们权重高。LCP 优化大图,压缩为 WebP,延迟非首屏加载;TBT 减少 JS,动态导入模块。CLS 确保图片有尺寸,SI 内联关键 CSS。我会定期测试,结合实地数据调整
- LCP:最大内容绘制,可视区域中最大的内容元素呈现
- TBT:从FCP到TTI的总时间
- FCP:用户第一次看到内容的时间
- TTI:用户完全可交互的时间
- CLS:累计布局偏移量,保证页面视觉稳定和用户体验
webpack
webpack的优化
Webpack 优化分构建速度和输出体积。速度上,我用 Webpack 5 缓存,配 thread-loader 多线程,HMR 提速开发。体积上,tree shaking 去未用代码,代码分割用 import() 懒加载,Terser 压缩 JS,CSS 提取到单独文件。React 项目中,我加 React.lazy 分包,图片用 CDN,首屏快了好几秒
vite和webpack的区别
Vite 和 Webpack 的核心区别在构建方式。Vite 用浏览器 ESM,开发时不打包,靠 ESBuild 预构建依赖,启动和 HMR 极快,生产用 Rollup 打包,配置简单。Webpack 全量打包,功能强但慢,生态成熟。我会选 Vite 做快速开发,Webpack 做复杂项目
为什么要使用webpapck?
- 模块化、资源处理、性能优化、开发体验
“前端开发需要 Webpack 因为现代项目模块化复杂,像 React 的 JSX、TS 浏览器不认识,Webpack 用 Loader 转译。文件多时手动管理麻烦,Webpack 合并优化减少请求,还能压缩代码、支持热更新提升开发效率。”
不用webpack可以吗?
“不用 Webpack 可以,像简单页面直接用 <script>
,新浏览器支持 ESM。但复杂项目不行,浏览器不认识 JSX、TS,CSS 和图片也需要处理。Webpack 提供兼容性和优化,替代方案像 Vite 更快但生态不如 Webpack 成熟。”
webpack做了什么?
- 解析 -> 转译 -> 打包 -> 优化。
“Webpack 从入口解析依赖,用 Loader 转译 JSX、CSS 等资源,生成依赖图后打包成 Bundle。它合并文件减少请求,压缩代码优化性能,用 Plugin 生成 HTML 或清理文件。我用它把 React 项目打包成单文件,开发时加热更新。”
webpack升级webpack5你都做了什么?
从webpack4升级到5,我们首先需要搞清楚webpack5带来了哪些优化?比如持久化缓存,tree shaking的增强,以及热更新hmr算法优化速度提升,并且全局使用了splitChunks代码分割,这样全方面提升了开发和构建效率,于是我们决定升级。首先第一步是更新package,然后将plugin也升级到相应版本,然后根据文档开启持久化缓存,移除废弃的api,最后分别在开发和生产环境下验证,热更新速度明显变快,打包体积由原来的接近3m缩小到2m多,明显得到优化。
-
持久化缓存:通过cache.type: 'filesystem'缓存编译结果到磁盘,只编译变更模块,构建速度从2分钟降到1.2分钟(提升40%)。
-
HMR算法优化:优化依赖分析和模块替换,HMR速度从500ms降到400ms(提升20%)。
-
Tree Shaking嵌套移除:支持移除嵌套未用代码(如函数内部未引用代码),打包体积从500KB降到400KB(减少20%)。
-
splitChunks改进:相比CommonsChunkPlugin,splitChunks支持异步模块,配置更智能(chunks: 'all'),减少40%冗余请求。
webpack的contenthash和chunkhash有什么区别?
chunkhash和contenthash是Webpack的两种hash策略,区别在于依赖范围。chunkhash基于整个chunk生成,适合代码分割的JS文件;contenthash基于文件内容,适合CSS或静态资源。
-
用chunkhash:代码分割时,比如用splitChunks分离vendor.js和app.js,我配置filename: '[name].[chunkhash].js',确保vendor不变时hash不变,优化缓存。
-
用contenthash:提取CSS时,比如用MiniCssExtractPlugin,我配置filename: '[name].[contenthash].css',CSS内容不变时hash不变,避免JS变化影响CSS缓存。