面试高频问题总结
CSS
- 不定宽高的div水平垂直居中
1、父元素添加 position: relativediv{position:absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}2、div {display: flex;justify-content:center; //子元素水平居中align-items:center; //子元素垂直居中}3、#box {width: 100px;height: 100px;position: relative;}#content {width: 50px;height: 50px;position: absolute;top: 0;right: 0;bottom: 0;left: 0;margin: auto;}
css选择器的优先级
!important>行内样式>id选择器>类/属性/伪类选择器>伪元素/标签选择器>通配符选择器*
什么是重绘和回流
答:回流: 当DOM元素的变化影响了元素的几何属性(例如宽和高),浏览器需要重新计算元素的几何属性,同样其它元素的几何属性也会和位置也会因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程称为“回流”。重绘: 完成回流后,浏览器会重新绘制受影响的部分到屏幕上中,该过程称为“重绘”。当我们改变DOM的大小,增加删除都会导致回流,当给DOM元素改变颜色的时候,会导致重绘,回流一定会重绘,重绘不会回流。回流会影响性能,所以我们尽快能的减少回流的操作
flex常用的容器属性
答:1. flex-direction: 设置容器中的主轴方向2. flex-wrap: 项目在主轴方向上是否换行显示3. justify-content: 设置容器中的项目在主轴上的对齐方式4. align-items: 单行项目在侧轴上的排列方式5. align-content: 多行项目侧轴上的对齐方式6. flex-flow: 是flex-direction和flex-wrap的合写, 默认值为row nowrap
JS & TS
- ES6提供哪些新特性
- 核心特性:
let/const
、箭头函数、解构赋值、Promise、Class、模块化(import/export
)。 - 高级特性:
Proxy
/Reflect
、Generator、Map
/Set
。
- 核心特性:
普通函数和箭头函数的区别?能不能用call/bind/apply?
- 箭头函数:
- 无自己的 this
(继承外层作用域),不可用 new
调用。
- 不能用 call
/apply
/bind
改变 this
。
- 无 arguments
对象,需用剩余参数(...args
)。
- 普通函数:
- 动态
this
(由调用方式决定),支持构造函数调用。
- 动态
let/const/var的区别
- var
:函数作用域,变量提升(可重复声明)。
- let/const
:块级作用域,存在 TDZ(暂时性死区),不可重复声明。
- const
:声明常量(内存地址不可变,但对象属性可修改)。
深拷贝和浅拷贝的区别是什么?
- 浅拷贝:仅复制对象的顶层属性(如Object.assign() 或扩展运算符
- 深拷贝:递归复制所有层级,生成完全独立的对象。
- 深拷贝实现:
// 方法1:JSON(不支持函数和循环引用) const deepCopy = JSON.parse(JSON.stringify(obj)); // 方法2:lodash.cloneDeep import { cloneDeep } from "lodash"; const deepCopy = cloneDeep(obj);
Object 的遍历对象有几种方式?
- for…in :遍历对象自身及原型链的可枚举属性;
- Object.keys(obj) :返回对象自身可枚举属性的数组;
- Object.getOwnPropertyNames(obj) :返回包括不可枚举属性的数组;
- Reflect.ownKeys(obj) :返回所有键(包括 Symbol)。
如何判断一个对象是空对象?
- Object.keys()方法
- JSON.stringify()方法
- for…in循环方法
- Object.getOwnPropertyNames()方法
- 推荐使用Object.keys(obj).length === 0方法,因为它简洁且性能良好。如果需要检测不可枚举属性,可以使用Object.getOwnPropertyNames()方法。
对this的理解
答: this是个关键字,它的指向和函数的调用方式有关
1. 函数调用模式, this指向window
2. 构造函数调用模式, this指向新创建的实例对象
3. 方法调用模式, this指向调用方法的对象
4. 上下文调用模式, call和apply方法中, this指向方法内的第一个参数bind方法中, bind创建的新函数的this绑定为bind方法中新的函数
5. 在事件处理函数中,this指向触发事件的当前元素
6. 定时器中,this指向window
7. 箭头函数中没有this指向问题,它的this和外层作用域的this保持一致
8. 匿名函数中的this总是指向window
new操作符做了什么
1. 创建一个新对象2. 函数内部的this指向这个对象3. 执行函数体4. 自动返回这个函数
对闭包的理解?并能举出闭包的例子
答: 闭包 函数和声明该函数的词法环境的组合(两个嵌套关系的函数,内部函数可以访问外部函数定义的变量)闭包的优点:1、形成私有空间,避免全局变量的污染2、持久化内存,保存数据闭包的缺点:1、持久化内存,导致内存泄露解决:1、尽快避免函数的嵌套,以及变量的引用2、执行完的变量,可以赋值null,让垃圾回收机制,进行回收释放内存(当不在引用的变量,垃圾回收机制就会回收)
例1: 点击li获取当前下标<ul><li>111</li><li>222</li><li>333</li><li>444</li><li>555</li></ul>var lis = document.querySelectorAll('li')for (var i = 0; i < lis.length; i++) {(function (j) {lis[j].onclick = function () {console.log(j)}})(i)}
2.防抖函数
什么是原型和原型链?
答: 原型: 函数都有prototype属性,这个属性的值是个对象,称之为原型原型链: 对象都有__proto__属性,这个属性指向它的原型对象,原型对象也是对象,也有__proto__属性,指向原型对象的原型对象,这样一层一层形成的链式结构称为原型链.
call、apply和bind的区别?
答: 1. call和apply方法都可以调用函数,方法内的第一个参数可以修改this的指向2. call方法可以有多个参数,除了第一个参数,其他参数作为实参传递给函数apply方法最多有2个参数,第二个参数是个数组或伪数组,数组里面的每一项作为实参传递给函数3. bind方法不能调用函数,它会创建一个副本函数,并且绑定新函数的this指向bind返回的新的函数
怎么理解函数的防抖和节流?
答:
1、定义:
防抖: 就是指触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间。例如:设定1000毫秒执行,当你触发事件了,他会1000毫秒后执行,但是在还剩500毫秒的时候你又触发了事件,那就会重新开始1000毫秒之后再执行示例:search远程搜索框:防止用户不断输入过程中,不断请求资源,n秒内只发送1次,用防抖来节约资源节流: 就是指连续触发事件但是在设定的一段时间内中只执行一次函数。例如:设定1000毫秒执行,那你在1000毫秒触发在多次,也只在1000毫秒后执行一次2、防抖和节流的实现:<body><input type="text" class="ipt" /><script>var timerId = nulldocument.querySelector('.ipt').onkeyup = function () {// 防抖if (timerId !== null) {clearTimeout(timerId)}timerId = setTimeout(() => {console.log('我是防抖')}, 1000)}document.querySelector('.ipt').onkeyup = function () {// 节流console.log(2)if (timerId !== null) {return}timerId = setTimeout(() => {console.log('我是节流')timerId = null}, 1000)}</script></body>
什么是事件流?
答: 事件流是指事件传播的顺序,由事件捕获 => 目标事件 => 事件冒泡
Vue & React & Angular
vue中的生命周期有哪些?常用的生命周期在哪些场景下使用?(⭐)
vue 中的父子组件如何通信?
- 父→子:通过props传递数据。
- 子→父:通过$emit触发事件,父组件监听。
- 其他方式:parent/parent/parent/children(不推荐)、Vuex/Pinia、Event Bus。
vue 的跨组件的数据传递方式有哪些?
- 父子组件:props(父传子) + $emit(子传父);
- 跨级组件:provide/inject(祖先提供数据,后代注入);
- 全局状态管理:Vuex/Pinia 存储共享状态;
- 事件总线:Vue3 使用mitt库实现跨组件通信;
- URL 参数:通过路由传参(如router.push({ query }))
普通的组件和函数组件有什么区别?
- 普通组件适用于需要管理状态、拥有复杂逻辑和生命周期处理的场景;而函数组件则更适合简单的、无状态的、对性能要求较高的场景。
vue 中 watch 和 computer 的区别
- computed :
- 依赖响应式数据生成新值,具有缓存(依赖不变时不会重新计算);
- 适用于同步计算(如 fullName = firstName + lastName)。
- watch :
- 监听数据变化执行异步或复杂操作;
- 无缓存,适合数据变化后需要副作用(如请求接口)。
vue 中的 v-if 和 v-show 的区别?分别说下它们的使用场景。
- 区别:
- v-if:动态添加/移除DOM元素,切换时触发组件生命周期钩子(如created/destroyed)。
- v-show:通过CSS的display: none控制显隐,DOM元素始终存在。
- 使用场景:
- v-if:条件很少改变(如权限控制),或需要频繁切换开销较大的组件(如复杂表单)。
- v-show:频繁切换显隐(如Tab栏),避免重复渲染的开销。
如何理解 vue 中的 diff 算法?
- 核心思想:通过对比新旧虚拟 DOM 树,找到最小更新路径;
- 优化策略:
- 同层比较,不跨层级;
- 通过key标识节点,减少不必要的重新渲染。
vue中的路由管理及路由的注入
- 组件内使用:useRoute()(当前路由)、useRouter()(路由实例)。
import { createRouter, createWebHistory } from 'vue-router'; const router = createRouter({ history: createWebHistory(), routes }); const app = createApp(App); app.use(router); // 注入路由
优化 & 打包 & 适配 & 配置项
浏览器的事件循环机制是什么?
- 宏任务:setTimeout、setInterval、DOM 事件;
- 微任务:Promise.then、MutationObserver;
- 执行顺序:
- 执行当前宏任务;
- 执行所有微任务;
- 渲染页面;
- 取下一个宏任务执行。
在地址栏输入网址,到数据返回的过程是什么?
答: 1. 输入url地址后,首先进行DNS解析,将相应的域名解析为IP地址。2. 根据IP地址去寻找相应的服务器。3. 与服务器进行TCP的三次握手,建立连接。4. 客户端发送请求,找到相应的资源库。5. 客户端拿到数据,进行相应的渲染。
什么叫跨域?常见的解决方案有哪些?
- CORS:后端设置响应头(如 Access-Control-Allow-Origin: * );
- 代理服务器:前端通过 Nginx 或 Webpack DevServer 代理请求;
- JSONP:通过 script 标签发送 GET 请求(仅支持 GET)*;
- WebSocket:不受同源策略限制。
重点讲解 项目中的Nginx的配置项
前端性能优化有哪些?例如在:配置层面,打包编译层面,有哪些优化?
- 构建层:
- Vite/Webpack:Tree-shaking、代码分割(splitChunks)、压缩资源。
- 图片优化:转 WebP、使用 SVG 图标、懒加载。
- 运行时:
- 组件懒加载:defineAsyncComponent(Vue)或 React.lazy。
- 虚拟滚动、防抖节流、CDN 加速静态资源。
- 配置层:
- HTTP/2、Gzip/Brotli 压缩、缓存策略(Cache-Control)。
打包编译的框架和流程有了解过么?
- 典型流程: 源代码-> 依赖分析 -> Loader转换资源 -> 插件优化/压缩 ->打包输出Bundle
- 关键工具:Webpack(基于 JS 模块)、Vite(基于原生 ES Modules)、Rollup(库打包)。
在项目中token过期,前后端的处理方式?
- 前端:
- 拦截响应(如 Axios 的
interceptors
),检测401
状态码 → 跳转登录页。 - 无感刷新:用 Refresh Token 换新 Access Token。
- 拦截响应(如 Axios 的
- 后端:返回标准的
401 Unauthorized
或自定义过期状态码。
微前端和低代码具体是怎么做的?有应用场景么?
- 微前端:
- 场景:大型应用拆分为独立子应用(如电商平台包含商品、订单模块);
- 技术:使用qiankun框架实现子应用隔离与通信。
- 低代码:
- 场景:快速生成表单、报表等标准化页面;
- 工具:阿里宜搭、腾讯微搭。
大屏有没有做过适配?不同的屏幕是如何实现兼容的?
- CSS3 缩放(Scale)配合视口单位(vw/vh)
- 首先设定一个基准的屏幕尺寸(例如 1920px * 1080px)作为设计稿尺寸。
- 然后通过 JavaScript 监听 window.resize 事件,计算当前窗口与基准窗口的缩放比例。
- 最后将整个页面的容器元素使用 transform: scale(scaleRatio) 进行缩放。
- 同时,页面内的所有尺寸(宽、高、字体大小)都使用 vw 和 vh 单位,这样可以确保所有元素都能随着视口大小进行自适应。
- Rem 布局:
- 类似于移动端的 Rem 方案,将 HTML 的 font-size 设置为基于屏幕宽度或高度的函数。
- 页面内所有元素的尺寸使用 rem 单位。当屏幕尺寸变化时,动态改变根元素的 font-size,所有元素就会等比例缩放。
- 使用成熟的库:@amov/ps-plugin 或 fit-screen 这类专门为大屏适配开发的库,它们封装了缩放逻辑,使用起来更加方便。
有没有碰到过不同的手机的适配问题?如何处理手机适配问题?
-
通常采用柔性布局和弹性布局相结合的策略
-
Viewport 设置
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
-
VW / VH(现代且推荐的方式)
- 直接使用 CSS 的视口单位 vw 和 vh。通过 PostCSS 插件(如 postcss-px-to-viewport)将设计稿中的 px 单位自动转换为 vw,非常高效。
-
Flexbox 和 Grid 布局
对于页面整体的模块排列,大量使用 Flex 弹性布局和 CSS Grid 网格布局,它们能很好地处理不同尺寸屏幕下的元素排列和对齐问题。 -
媒体查询(Media Queries):用于处理断点式的布局变化。当屏幕尺寸到达某个临界点时,改变布局结构(例如将多列布局变成单列布局)或隐藏/显示某些元素。
-
总结:适配策略是 “Rem/VW + Flex布局 + 媒体查询” 的组合拳。Rem/VW 解决尺寸缩放问题,Flex/Grid 解决布局问题,媒体查询解决特定场景下的特殊样式问题。
如何配合后端设计页面权限控制,动态路由,以及按钮的权限控制?
-
后端配合:
- 用户登录后,后端会返回一个用户角色(role) 或一个权限码列表(permission codes)。
- 后端提供的API接口本身也要做好权限验证。
-
前端实现
-
路由层面:
- 动态路由:前端先定义好所有可能的路由,但根据用户角色/权限,动态生成可访问的路由配置。使用 router.addRoute() 方法(在Vue Router中)动态添加到路由实例中。
- 路由守卫:在全局路由守卫(router.beforeEach)中,判断用户是否有权限进入目标路由。如果没有,则跳转到404或登录页。
-
页面/组件层面:
- 编写一个全局的自定义指令,例如 v-permission=“‘user:add’”。
- 指令的逻辑是:判断当前用户的权限列表是否包含指令所传的值。如果不包含,则直接 el.parentNode.removeChild(el) 将该DOM元素移除。
-
按钮层面:
- 同样使用上面的自定义指令 v-permission 来控制按钮的显示与隐藏。
- 或者封装一个权限判断的函数或组件,在按钮的父组件中调用,通过 v-if 来决定是否渲染按钮。
-
了解Promise么?Promise有哪些常用的方法? async await有什么作用?
-
Promise 是用于解决 JavaScript 异步编程回调地狱(Callback Hell) 的一种方案,它代表一个最终可能完成(成功)或失败(拒绝)的操作及其结果值。
-
常用的静态方法
- Promise.all(iterable):等待所有 promise 成功,或者任何一个失败。全部成功才成功,一个失败就立即失败。
- Promise.allSettled(iterable):等待所有 promise 都已敲定(无论是成功还是失败)。ES2020引入。
- Promise.race(iterable):等待任何一个 promise 敲定(成功或失败)。谁快就用谁的结果。
- Promise.any(iterable):等待任何一个 promise 成功。如果全部失败,则抛出一个聚合错误。ES2021引入。
- Promise.resolve(value) / Promise.reject(reason):创建一个立即成功或失败的 promise。
-
常用的实例方法
- .then():用于注册当 promise 成功时的回调函数。
- .catch():用于注册当 promise 失败时的回调函数。
- .finally():无论 promise 最终状态如何,都会执行的操作。
-
async/await 的作用
- async 关键字用于声明一个函数是异步的,这个函数会返回一个 Promise。
- await 关键字只能在 async 函数内部使用,用于“等待”一个 Promise 对象 resolved(成功),并取出它的结果值。如果等待的 Promise 被 rejected(失败),则会抛出异常,需要用 try…catch 来捕获处理。
对后端RESTful API了解么?有哪些命名规范?
- 广泛使用的 API 设计风格,核心思想是用 HTTP 动词(Method)表示操作,用 URL 表示资源
- 它的主要命名规范和实践包括
- URL 指向资源(名词),而不是动作(动词)。资源应该使用复数名词
- 使用 HTTP Method 表示操作类型(动词)
- GET:获取/查询资源(安全且幂等)。
- POST:创建新资源。
- PUT:完整更新/替换资源(幂等)。
- PATCH:部分更新资源。
- DELETE:删除资源(幂等)。
- URL 层级表示资源关系
- 例如,获取id为123的用户的所有订单:GET /api/users/123/orders
- 获取这个订单中的某个具体商品:GET /api/users/123/orders/456/products/789
- 使用合适的HTTP状态码
- 200 OK:请求成功。
- 201 Created:创建成功。
- 301 Moved Permanently,永久性重定向
- 302 Found(临时重定向)。
- 400 Bad Request:客户端请求错误。
- 401 Unauthorized:未认证。
- 403 Forbidden:无权限(已认证但权限不足)。
- 404 Not Found:资源不存在。
- 500 Internal Server Error:服务器内部错误。
组件的性能优化的方案有哪些?
- 减少不必要的渲染
- 使用 computed 属性:computed 属性具备缓存功能,仅在其依赖项改变时才会重新计算。这在需要多次计算同一值时,能避免不必要的重复计算,从而提升性能。
- 使用 watch 监听特定数据变化:借助 watch 可以监听特定数据的变动,仅在数据变化时执行相应操作,避免不必要的渲染。
- 使用 v-once 指令:v-once 指令能让元素和组件只渲染一次,后续数据变更时不会重新渲染。适用于那些数据不会改变的场景。
- 优化 DOM 操作
- 批量更新数据:Vue 的数据更新是异步的,频繁更新数据会触发多次 DOM 重绘和回流。可以利用 this.$nextTick 批量更新数据,减少 DOM 操作次数。
- 使用事件委托:把事件监听器添加到父元素上,而非每个子元素,这样能减少事件监听器的数量,提高性能。
- 懒加载组件
- 异步组件:Vue 支持异步组件,这能让组件在需要渲染时才加载,减少初始加载时间。
- 优化样式
- 减少 CSS 选择器的复杂度:复杂的 CSS 选择器会增加浏览器的计算时间,尽量使用简单的选择器。
- 避免内联样式:内联样式会增加 DOM 的大小,且难以维护和复用。尽量把样式写在 CSS 文件中。
- 优化图片资源
- 压缩图片:使用图片压缩工具对图片进行压缩,减小图片文件大小,提高加载速度。
- 使用响应式图片:依据设备的屏幕尺寸和分辨率,提供不同大小的图片,避免加载过大的图片。
- 使用函数式组件
- 函数式组件是无状态、无实例的纯函数,渲染开销小,适合用于简单的、频繁渲染的场景。