前端面试常见问题
目录多个请求携带相同的 tokentoken 过期了怎么避免重复刷新 401/403输入url到页面显示过程浏览器渲染过程重排重绘垃圾回收机制闭包js事件循环机制nextTickEs6 的箭头函数和普通函数的区别bind、apply、call的区别原型链继承JS的存储方式cookie可以实现不同域共享吗强缓存和协商缓存Vuexpinia和vuex的区别Vue3 数据持久化存储如果数据需要同时存储在 pinia 和 localstorage,怎么保持数据的一致性vue 组件之间通信的方式Props/Emit与Provide/Inject的区别标题多个请求携带相同的 tokentoken 过期了怎么避免重复刷新 401/403当多个请求携带相同 token 且 token 过期时为了避免并发触发多个刷新请求造成重复刷新或 token 混乱可以使用请求队列 刷新锁机制。核心思路是请求拦截器中检测 401/403当发现 token 过期时先判断是否已有刷新请求在进行。若没有刷新请求设置一个全局isRefreshing true发起一次refreshToken请求。在刷新期间将其他失败的请求暂存到一个队列中如pendingQueue不立即重新发送。刷新成功后更新全局 token并遍历执行队列中被挂起的请求。刷新失败跳转登录页或清空状态。最后重置isRefreshing false清空队列。“我会通过请求拦截 刷新锁机制在 token 过期时只触发一次刷新请求其余请求挂起等待确保鉴权过程安全且高效。”输入url到页面显示过程浏览器渲染过程浏览器渲染的过程大致包括五个步骤首先解析 HTML构建 DOM 树同时解析 CSS构建 CSSOM 树接着将 DOM 树和 CSSOM 树合并生成渲染树Render Tree然后进行布局Layout / Reflow计算每个节点的几何信息最后执行绘制Paint将像素渲染到屏幕上当 DOM 或样式变化时会触发重排Reflow和重绘Repaint。整个过程涉及 JS 执行、样式计算和页面绘制是浏览器呈现页面的核心机制。重排重绘重排重绘详解重排也被称为回流是当页面的布局发生变化如元素的位置、尺寸或隐藏/显示状态改变等时浏览器重新计算页面的布局并且通常会伴随着重绘的过程。(常见添加、删除或修改可见的DOM元素几何属性改变浏览器窗口大小变化重绘是指当页面中某个元素的外观发生变化但不改变其几何属性如位置、尺寸等时浏览器重新绘制该元素的过程。常见元素的背景色、颜色、字体、阴影visibilityopacity垃圾回收机制闭包js事件循环机制JavaScript 是单线程执行的通过事件循环Event Loop机制实现异步操作。执行过程中首先执行同步代码遇到异步任务会将其放入宏任务队列如 setTimeout、setInterval、I/O或微任务队列如 Promise.then、process.nextTick。每次主线程执行完同步代码后会先清空微任务队列然后从宏任务队列取出下一个任务执行如此循环从而实现异步操作的有序执行和非阻塞效果。nextTickEs6 的箭头函数和普通函数的区别ES6 箭头函数与普通函数的主要区别在于第一this 指针箭头函数没有自己的 this它会继承外层上下文的 this而普通函数的 this 根据调用方式确定第二arguments 对象箭头函数没有 arguments需要用剩余参数而普通函数有自己的 arguments第三箭头函数不能作为构造函数使用也没有 prototype第四语法更简洁适合做回调或内联函数。bind、apply、call的区别原型链原型链是 JavaScript 对象属性查找的一种机制。当访问一个对象的属性时如果对象自身没有这个属性JavaScript 会沿着原型链对象的 [[Prototype]]也就是 __ proto__向上查找直到找到属性或者到达 null。对象的原型指向构造函数的原型对象继承JS的存储方式存储方式存储位置存储大小生命周期是否随请求发送特点Cookie浏览器/服务端~4KB可设置过期时间是可用于服务端识别自动带到 HTTP 请求LocalStorage浏览器5~10MB永久除非手动清除否简单 KV 存储刷新页面仍然存在SessionStorage浏览器5~10MB会话级关闭标签页清除否页面会话独立每个标签页不同IndexedDB浏览器几百 MB永久除非清除否面向对象数据库支持事务和大数据量cookie可以实现不同域共享吗默认情况Cookie 不同源不共享Cookie 共享的前提相同主域设置 Domain 属性Set-Cookie:tokenabc123;Domain.example.com;Path/;Domain.example.com 表示该 Cookie 对所有子域有效 a.example.com b.example.com www.example.com 都能访问到同一个 CookieCookie “共享” 与 “不同源” 的区别强缓存和协商缓存强缓存是浏览器直接使用本地资源不会向服务器发送请求依赖 Cache-Control 或 Expires。协商缓存则需要浏览器携带条件向服务器询问资源是否更新服务器返回 304 或 200从而保证资源是最新的。一般策略是先走强缓存过期后走协商缓存这样兼顾性能和实时性。Vuex在 Vuex 中state 用于存储全局状态getter 类似计算属性用来对 state 做派生处理或过滤mutation 是同步函数用于修改 state必须通过 commit 调用action 支持异步操作通过 dispatch 调用可以在 action 中执行异步请求或复杂逻辑然后再通过 commit 调用 mutation 修改 state从而保证状态变更可追踪。pinia和vuex的区别Pinia 是 Vue3 官方推荐的状态管理库相比 Vuex 更轻量、API 更简洁支持组合式 API 和直接在 action 中修改 state不需要像 Vuex 那样通过 mutation commitPinia 天然支持 TypeScript 类型推导模块化更灵活每个 store 独立无需手动注册模块而 Vuex API 较重状态修改必须通过 mutation类型推导不够友好模块化需要手动管理。总体来说Pinia 更符合 Vue3 组合式开发和高可维护性的理念。Vue3 数据持久化存储在 Vue3 中数据持久化主要有两种常用方式一是浏览器存储如 localStorage 或 sessionStorage适合保存简单状态或用户配置页面刷新或会话切换时仍能保留二是状态管理持久化如使用 Pinia 或 Vuex结合持久化插件将全局状态同步到本地存储实现刷新后自动恢复。对于复杂或大量数据也可以使用 IndexedDB做异步存储适合离线缓存或结构化数据场景。如果数据需要同时存储在 pinia 和 localstorage,怎么保持数据的一致性在 Pinia 的 action 中统一管理更新逻辑每次通过 action 修改 state 时同时将新数据写入 localStorage这样保证两者数据一致在初始化时再从 localStorage 读取数据恢复 state实现刷新后状态保持同时避免直接操作 state 导致同步问题。vue 组件之间通信的方式Vue 组件之间通信主要有几种方式父子组件通过props向下传递数据子组件通过$emit向上传递事件兄弟组件通常通过Event Bus或父组件中转跨层级或全局状态可使用 Vuex 或 Pinia 等状态管理库另外也可以通过provide / inject实现祖孙组件的数据传递。选择方式取决于组件关系和数据共享的范围。Props/Emit与Provide/Inject的区别props/emit 用于父子直接通信父组件通过 props 下发数据子组件通过 emit 通知父组件松耦合且易复用但不适合跨多层组件而 provide/inject 用于跨层级依赖注入父组件提供响应式对象或方法子组件注入使用天然保持响应式共享但耦合度高依赖父组件提供数据或接口。标题