前端知识点

54 阅读20分钟

React

  1. 有哪些常用的react hooks
  2. 由一延展:为何 React Hooks 不能放在条件或循环之内
  3. React的渲染机制
  4. 由3延展:说说什么是虚拟dom,diff算法?
  5. 由3延展:什么是fiber架构,它解决了什么问题?
  6. react性能存在哪些问题?如何优化?
  7. rudex对比context api,rudex对比mobx
  8. react对比vue
  9. nextjs是如何实现ssr的
  10. 什么是高阶组件、受控组件、非受控组件?
  11. useState的参数可以使用函数吗?
  12. 如何避免useEffect的闭包陷阱?
  13. react如何做错误边界(类组件)
  14. react如何实现vue的keep-alive
  15. 有没有自己实现过hook,hook和普通函数有什么区别
  16. 使用过rudex的哪些中间件,是分布式的吗?
  17. jest都是ai写的用例吗?能涵盖ui类的测试吗?比如点击按钮弹窗或者动画?

react-router

  1. 说说两种react-router的区别

Js

  1. 如何判断类型?intanceof确定能判断准吗?有没有一定准确的方法?
  2. 如何实现深拷贝?除了递归之外呢?如果这个元素里面又调用了它本身该怎么做?
  3. 闭包是什么?闭包会产生什么问题?
  4. 什么是垃圾回收机制?说说两种垃圾回收机制
  5. js的事件循环机制?介绍一下promise,如果.then()里面有一个Error该如何捕获?.catch()之后还能做链式调用吗?
  6. for in和for of的区别
  7. ajax或者axios如何取消请求?
  8. js的继承,除了extends还有什么办法?

浏览器和http相关

  1. 跨域问题,如何只在前端处理跨域?
  2. cookie,sessionStorage,localStorage和index DB的区别?除了大小,时限,会随请求发送给后端还有什么区别?后端能否调用本地存储?

webpack

  1. 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

特性ReduxMobXContext 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对比

维度ReactVue
设计理念函数式,单向数据流渐进式,双向绑定
语法JSX模板语法
状态管理Hooks或Redux/MobX内置Vuex/Pinia
渲染机制VirtualDOM+FiberVirtualDOM+响应式
学习曲线较高较低
生态庞大,灵活完整,专注
性能需手动优化自动优化,小型项目更高效
构建工具手动或CRAVueCLI/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缓存。