1. 语义化标签的作用是什么?常用的语义化标签有哪些?
- 作用:提升代码可读性、SEO 友好、便于屏幕阅读器解析。
- 常用标签:
<header>(头部)、<nav>(导航)、<main>(主体)、<article>(文章)、<section>(区块)、<aside>(侧边栏)、<footer>(底部)等。
2. HTML5 新增了哪些语义化标签?
新增标签包括:<header>、<nav>、<main>、<article>、<section>、<aside>、<footer>、<figure>(图文)、<figcaption>(图文说明)、<time>(时间)、<mark>(标记)等。
3. 块级元素与行内元素的区别?
| 维度 | 块级元素 | 行内元素 |
|---|
| 布局 | 独占一行,宽度默认 100% | 同排显示,宽度由内容决定 |
| 样式属性 | 可设置宽高、margin/padding 全方向生效 | 宽高无效,margin/padding 仅水平生效 |
| 示例 | <div>、<p>、<h1> | <span>、<a>、<strong> |
4. 盒模型由哪些部分组成?标准盒模型与 IE 盒模型的区别?
- 组成:内容(content)、内边距(padding)、边框(border)、外边距(margin)。
- 区别:
- 标准盒模型:
width/height = 内容区域宽高。 - IE 盒模型:
width/height = 内容+内边距+边框(可通过box-sizing: border-box启用)。
5. CSS 选择器的优先级是怎样计算的?
优先级从高到低:
!important(最高,不建议滥用);- 行内样式(
style属性,权重 1000); - ID 选择器(
#id,权重 100); - 类 / 伪类 / 属性选择器(
.class/:hover/[type],权重 10); - 标签 / 伪元素选择器(
div/::after,权重 1); - 通配符 / 继承样式(权重 0)。
- 规则:权重叠加比较(如
#id .class权重为 110),权重相同则 “后写的覆盖先写的”。
6. BFC 是什么?有哪些触发条件?
- BFC(块级格式化上下文):是 CSS 的渲染隔离区域,内部元素的布局不受外部影响,外部也不影响内部。
- 触发条件:
- 根元素(
<html>); - 浮动元素(
float: 非none); - 绝对 / 固定定位(
position: absolute/fixed); - 溢出隐藏(
overflow: 非visible); - 弹性 / 网格容器(
display: flex/grid)。
7. 清除浮动的常见方式有哪些?
- 额外标签法:在浮动元素后加空标签(如
<div style="clear: both;"></div>); - 父元素触发 BFC:给父元素加
overflow: hidden/display: flex等; - 伪元素法(推荐):
.parent::after {
content: "";
display: block;
clear: both;
visibility: hidden;
height: 0;
}
.parent { zoom: 1; /* 兼容IE */ }
8. position 有哪些属性值?它们的区别是什么?
| 属性值 | 定位基准 | 是否脱离文档流 | 特点 |
|---|
static | 默认,无定位 | 否 | 忽略定位属性(top/left 等) |
relative | 自身初始位置 | 否 | 不影响其他元素布局 |
absolute | 最近的非 static 父元素 | 是 | 父元素无定位则相对于根元素 |
fixed | 视口(浏览器窗口) | 是 | 滚动时位置固定 |
sticky | 视口 + 自身初始位置 | 否(滚动时临时脱离) | 滚动到阈值时变为 fixed |
9. flex 布局常用属性有哪些?
- 容器属性:
display: flex(启用 flex);flex-direction(主轴方向:row/column);justify-content(主轴对齐:flex-start/center/space-between);align-items(交叉轴对齐:flex-start/center/stretch);flex-wrap(是否换行:nowrap/wrap)。
- 项目属性:
flex(缩写:flex-grow(放大)、flex-shrink(缩小)、flex-basis(基准宽高));align-self(单独设置交叉轴对齐);order(项目顺序,默认 0,值越小越靠前)。
10. rem、em、vw、vh、px 的区别及使用场景?
| 单位 | 基准 | 场景 |
|---|
px | 固定像素 | 精确控制(如边框、小图标) |
em | 父元素字体大小 | 局部响应式(如按钮内边距) |
rem | 根元素(html)字体大小 | 全局响应式(如页面布局) |
vw | 视口宽度的 1% | 全屏适配(如宽度占比) |
vh | 视口高度的 1% | 全屏适配(如高度占比) |
11. JavaScript 的基本数据类型有哪些?
- 基本类型(值类型):
Number(数字)、String(字符串)、Boolean(布尔)、Undefined(未定义)、Null(空)、Symbol(唯一标识)、BigInt(大整数)。 - 引用类型:
Object(对象)、Array(数组)、Function(函数)等。
12. == 和 === 的区别?
==:松散相等,会自动类型转换后比较值(如1 == "1"为true)。===:严格相等,不做类型转换,同时比较值和类型(如1 === "1"为false)。
13. null 和 undefined 的区别?
| 类型 | 含义 | 场景 |
|---|
undefined | 变量已声明但未赋值 | 未传参的函数形参、访问不存在的对象属性 |
null | 主动表示 “空值” | 主动清空对象引用(如let obj = null) |
- 共同点:
==比较为true(null == undefined),===为false。
14. 浅拷贝与深拷贝的区别?
- 浅拷贝:只复制对象的 “第一层” 属性,若属性是引用类型(如对象 / 数组),则复制的是引用地址(修改新对象会影响原对象)。
- 深拷贝:复制对象的所有层级属性,新对象与原对象完全独立(修改新对象不影响原对象)。
15. 常用的深拷贝方式有哪些?
- JSON 序列化(简单场景):
JSON.parse(JSON.stringify(obj))(缺点:不支持函数、Symbol、循环引用); - 递归实现(自定义):遍历对象,逐层复制引用类型;
- 第三方库:
lodash.cloneDeep(obj); - 结构化克隆:
structuredClone(obj)(ES2022,支持循环引用、Symbol 等)。
16. this 的指向规则是什么?
- 默认绑定:独立函数调用时,
this指向全局对象(浏览器:window;Node:global;严格模式下为undefined); - 隐式绑定:函数作为对象方法调用时,
this指向调用该方法的对象; - 显式绑定:通过
call/apply/bind指定this指向; - new 绑定:构造函数中,
this指向新创建的实例对象; - 箭头函数:无自身
this,继承外层作用域的this。
17. call、apply、bind 的区别?
- 相同点:都能显式绑定
this。 - 不同点:
call:参数是 “逗号分隔”(如fn.call(obj, a, b)),立即执行;apply:参数是 “数组 / 类数组”(如fn.apply(obj, [a, b])),立即执行;bind:返回一个绑定了 this 的新函数(需手动调用),参数可分多次传入。
18. 闭包是什么?有什么作用?
- 闭包:函数能访问其定义时的外层作用域变量(即使外层函数已执行完毕),形成 “函数 + 外层作用域” 的组合。
- 作用:
- 延长变量生命周期(实现数据缓存);
- 实现私有变量(避免全局污染)。
- 注意:滥用会导致内存泄漏,需及时释放引用。
19. 原型和原型链是什么?
- 原型:每个函数都有
prototype(原型对象),每个对象都有__proto__(指向其构造函数的prototype)。 - 原型链:对象访问属性时,若自身没有,则通过
__proto__向上查找其原型对象的属性,直到Object.prototype.__proto__(为null),形成的链式结构即为原型链。
20. new 操作符做了什么?
- 创建一个空对象;
- 将空对象的
__proto__指向构造函数的prototype; - 执行构造函数,将
this绑定到新对象; - 若构造函数返回 “引用类型”,则返回该引用;否则返回新对象。
21. JavaScript 的作用域链是如何形成的?
- 作用域链:函数执行时,会创建 “执行上下文”,其中包含当前作用域的变量。若当前作用域找不到变量,则向上查找外层作用域的变量,直到全局作用域,形成的链式结构即为作用域链。
- 形成时机:函数定义时(而非执行时)确定外层作用域。
22. Event Loop 机制是什么?
- Event Loop:JavaScript 的异步执行机制(因 JS 是单线程),分为浏览器 Event Loop和Node.js Event Loop。
- 浏览器 Event Loop流程:
- 执行同步代码(宏任务);
- 执行微任务队列中所有任务;
- 渲染页面(若有);
- 从宏任务队列取一个任务执行,重复步骤 2-4。
- 宏任务:
setTimeout、setInterval、DOM事件、AJAX等; - 微任务:
Promise.then/catch/finally、MutationObserver、process.nextTick(Node)等。
23. 宏任务和微任务有哪些?
- 宏任务:浏览器:
setTimeout、setInterval、setImmediate(IE)、DOM事件、AJAX、requestAnimationFrame;Node:setTimeout、setInterval、setImmediate、I/O操作。 - 微任务:浏览器:
Promise.then/catch/finally、MutationObserver;Node:Promise.then/catch/finally、process.nextTick、queueMicrotask。
24. Promise 的三种状态是什么?
pending(进行中):初始状态,未完成也未失败;fulfilled(已完成):操作成功,状态不可逆,触发then回调;rejected(已失败):操作失败,状态不可逆,触发catch回调。
25. async/await 是如何实现的?
async/await是Promise+Generator 函数的语法糖:
async函数返回一个Promise;await会暂停async函数执行,等待右侧Promise状态变更;- 底层通过 Generator 函数的 “暂停 / 恢复” 机制 + 自动执行器(如
co库)实现。
26. 防抖与节流的区别?
- 防抖(debounce):事件触发后,延迟一段时间执行函数;若期间再次触发,则重新计时(如搜索框输入后延迟查询)。
- 节流(throttle):事件触发后,在一段时间内只执行一次函数;若期间再次触发,不执行(如滚动监听、按钮防重复点击)。
27. 什么是函数柯里化?
28. 什么是高阶函数?
- 高阶函数:满足以下任一条件的函数:
- 接收函数作为参数;
- 返回函数作为结果。
- 示例:
Array.map、setTimeout、Promise.then。
29. 什么是函数式编程?
- 函数式编程:一种编程范式,核心是 “将运算视为函数运算”,特点:
- 函数是 “一等公民”(可作为参数 / 返回值);
- 纯函数(无副作用、相同输入必返回相同输出);
- 避免可变数据,优先使用不可变数据;
- 常用特性:柯里化、高阶函数、递归等。
30. let、const、var 的区别?
| 维度 | var | let | const |
|---|
| 作用域 | 函数 / 全局作用域 | 块级作用域 | 块级作用域 |
| 变量提升 | 存在(声明提升,值为 undefined) | 不存在(暂存死区) | 不存在(暂存死区) |
| 重复声明 | 允许 | 不允许 | 不允许 |
| 可修改 | 是 | 是 | 否(常量,引用类型内部可改) |
31. 箭头函数与普通函数的区别?
this指向:箭头函数无自身this,继承外层作用域的this;普通函数this指向调用者;- 构造函数:箭头函数不能作为构造函数(无
prototype,不能用new); arguments:箭头函数无arguments对象(需用剩余参数...args);- 简写:箭头函数支持 “单表达式自动返回”(如
x => x*2)。
32. 模板字符串的作用和优点
33. 解构赋值的使用方式
- 数组解构:按顺序匹配,支持默认值、剩余参数。
const [a, b, c = 3, ...rest] = [1, 2]; // a=1, b=2, c=3, rest=[]
- 对象解构:按属性名匹配,支持重命名、默认值。
const { name: userName, age = 18 } = { name: "Bob" }; // userName="Bob", age=18
- 其他场景:函数参数解构、嵌套解构等。
34. 展开运算符(...)的应用场景
- 数组 / 对象拷贝(浅拷贝):
const arr = [...[1,2,3]];
const obj = { ...{a:1} };
- 数组 / 对象合并:
const arr1 = [1,2, ...[3,4]]; // [1,2,3,4]
const obj1 = {a:1, ...{b:2}}; // {a:1, b:2}
- 函数参数展开:
const fn = (a,b) => a+b;
fn(...[1,2]); // 3
35. Map 与 Object 的区别?
| 维度 | Object | Map |
|---|
| 键类型 | 只能是字符串 / 符号 | 任意类型(对象 / 函数等) |
| 键顺序 | 无序(数字键会排序) | 插入顺序 |
| 长度获取 | 需手动遍历计算 | size属性直接获取 |
| 遍历方式 | for...in/Object.keys | for...of/forEach等 |
| 性能 | 频繁增删时性能一般 | 频繁增删时性能更优 |
36. Set 的使用场景是什么?
Set是 “无重复元素的集合”,场景:
- 数组去重(
[...new Set(arr)]); - 存储不重复的标识(如 ID);
- 快速判断元素是否存在(
set.has(val))。
37. Symbol 的作用是什么?
- 作用:创建 “唯一的标识”,避免属性名冲突。
- 场景:
- 定义对象的私有属性(无法被
for...in遍历); - 定义常量(确保唯一性);
- 作为对象属性名(避免重名)。
38. Proxy 与 Reflect 的作用
39. ES Module 的导入和导出方式有哪些?
- 导出(export):
- 命名导出:
export const a = 1; 或 export { a, b }; - 默认导出:
export default function() {}(一个模块只能有一个默认导出)。
- 导入(import):
- 命名导入:
import { a, b as B } from './mod.js'; - 默认导入:
import mod from './mod.js'; - 全部导入:
import * as mod from './mod.js';
40. 浏览器的渲染流程是怎样的?
- 解析 HTML:生成 DOM 树;
- 解析 CSS:生成 CSSOM 树;
- 构建渲染树:合并 DOM 树和 CSSOM 树,只包含可见元素;
- 布局(Layout):计算元素的位置和大小(回流);
- 绘制(Paint):将元素绘制到图层(重绘);
- 合成(Composite):将图层合并,显示到屏幕。
41. 重绘与回流的区别?
- 回流(重排):元素的布局属性(如宽高、位置)变化,导致浏览器重新计算布局并生成渲染树(代价高)。
- 重绘:元素的样式属性(如颜色、背景)变化,但布局未变,浏览器仅重新绘制元素(代价较低)。
- 关系:回流必然触发重绘,重绘不一定触发回流。
42. 输入 URL 到页面显示经历哪些过程?
- URL 解析:判断是否为合法 URL;
- DNS 解析:将域名解析为 IP 地址;
- 建立 TCP 连接:三次握手;
- 发送 HTTP 请求:携带请求头、请求体;
- 服务器处理请求:返回 HTTP 响应(状态码、响应头、响应体);
- 关闭 TCP 连接:四次挥手(或复用连接);
- 浏览器渲染页面:解析 HTML/CSS/JS,生成页面(见第 40 题)。
43. 跨域的产生原因是什么?
跨域是浏览器的同源策略限制:当页面的 “协议、域名、端口” 与请求的资源不一致时,即产生跨域。
- 同源:协议、域名、端口完全相同(如
http://a.com:80和https://a.com:80不同源)。
44. 常见的跨域解决方案有哪些?
- CORS(跨域资源共享):服务器端设置响应头(如
Access-Control-Allow-Origin: *); - JSONP:利用
<script>标签的跨域特性,仅支持 GET 请求; - 代理服务器:本地开发用 webpack-dev-server 代理,生产用 Nginx 反向代理;
- postMessage:页面间跨域通信(如 iframe);
- WebSocket:不受同源策略限制。
45. cookie、session、localStorage、sessionStorage 的区别?
| 维度 | cookie | session | localStorage | sessionStorage |
|---|
| 存储位置 | 客户端 | 服务器端(依赖 cookie) | 客户端 | 客户端 |
| 存储大小 | 约 4KB | 无限制(服务器内存) | 约 5MB | 约 5MB |
| 生命周期 | 可设置过期时间 | 会话结束(或超时) | 永久(除非手动删除) | 标签页关闭后删除 |
| 通信 | 随请求发送到服务器 | 服务器端维护 | 仅客户端,不发请求 | 仅客户端,不发请求 |
46. 浏览器缓存机制有哪些?
分为强缓存和协商缓存,优先级:强缓存 > 协商缓存。
47. 强缓存与协商缓存的区别?
| 维度 | 强缓存 | 协商缓存 |
|---|
| 判断方式 | 浏览器直接判断本地缓存 | 需向服务器发送请求判断 |
| 响应头 | Cache-Control(如max-age=3600)、Expires | Last-Modified/If-Modified-Since、ETag/If-None-Match |
| 状态码 | 200(from memory/disk cache) | 304(Not Modified) |
| 性能 | 无请求,性能高 | 有请求,性能一般 |
48. Service Worker 的作用是什么?
- Service Worker:运行在浏览器后台的 “脚本”,独立于页面,可实现:
- 离线缓存(PWA 核心,缓存资源后离线可访问);
- 消息推送;
- 后台同步;
- 拦截请求(类似代理)。
49. HTTP 和 HTTPS 的区别?
| 维度 | HTTP | HTTPS |
|---|
| 协议基础 | 基于 TCP | 基于 TCP+SSL/TLS 加密 |
| 端口 | 80 | 443 |
| 安全性 | 明文传输,不安全 | 加密传输,安全 |
| 证书 | 无需证书 | 需要 CA 证书(需购买 / 申请) |
| 性能 | 速度快 | 因加密解密,速度稍慢 |
50. HTTP1.1、HTTP2、HTTP3 的区别?
| 维度 | HTTP1.1 | HTTP2 | HTTP3 |
|---|
| 传输方式 | 文本传输 | 二进制帧传输 | 基于 QUIC(UDP)传输 |
| 并发请求 | 同域名最多 6-8 个连接 | 多路复用(单连接多请求) | 更高效的多路复用 |
| 头部压缩 | 无(重复头部冗余) | HPACK 压缩 | QPACK 压缩 |
| 其他特性 | 长连接(Keep-Alive) | 服务器推送、优先级 | 更低延迟(基于 UDP) |
51. 常见的 HTTP 状态码有哪些?
- 1xx(信息):100(继续);
- 2xx(成功):200(OK)、201(创建成功)、204(无内容);
- 3xx(重定向):301(永久重定向)、302(临时重定向)、304(未修改);
- 4xx(客户端错误):400(请求错误)、401(未授权)、403(禁止访问)、404(资源不存在);
- 5xx(服务器错误):500(服务器内部错误)、502(网关错误)、503(服务不可用)。
52. TCP 三次握手的过程是什么?
三次握手是为了建立可靠的 TCP 连接:
- 客户端→服务器:发送 SYN 包(同步序列编号),请求建立连接,客户端进入 SYN_SENT 状态;
- 服务器→客户端:回复 SYN+ACK 包(同步 + 确认),服务器进入 SYN_RCVD 状态;
- 客户端→服务器:回复 ACK 包(确认),双方进入 ESTABLISHED 状态,连接建立。
53. XSS 攻击是什么?如何防御?
- XSS(跨站脚本攻击):攻击者向页面注入恶意脚本,当用户访问时,脚本执行(如窃取 Cookie)。
- 防御方式:
- 输入过滤(转义特殊字符,如
<→<); - 输出编码(渲染时对用户输入进行编码);
- 设置 Cookie 的
HttpOnly属性(禁止 JS 读取 Cookie)。
54. CSRF 攻击是什么?如何防御?
- CSRF(跨站请求伪造):攻击者诱导用户在已登录的情况下,向目标网站发送恶意请求(如转账)。
- 防御方式:
- 验证请求来源(检查
Referer/Origin头); - 使用 CSRF Token(请求携带随机 Token,服务器验证);
- 二次验证(如验证码、密码确认)。
55. 中间人攻击是什么?
- 中间人攻击:攻击者在 “客户端与服务器” 的通信链路中,伪装成 “中间节点”,拦截、篡改双方的通信内容(如伪造证书窃取 HTTPS 数据)。
- 防御:使用 HTTPS(验证证书合法性)、数字签名等。
56. Token 和 Session 的区别?
| 维度 | Token | Session |
|---|
| 存储位置 | 客户端(Cookie/Storage) | 服务器端(内存 / 数据库) |
| 扩展性 | 支持分布式(无状态) | 需共享 Session(如 Redis) |
| 安全性 | 签名防篡改,可过期 | 依赖 Cookie,易受 CSRF 攻击 |
| 适用场景 | 前后端分离、移动端 | 传统 Web 应用 |
57. JWT 的结构组成是什么?
JWT(JSON Web Token)由三部分组成,用.分隔:
- Header(头部):Base64 编码,包含算法(如 HS256)和类型;
- Payload(载荷):Base64 编码,包含用户信息、过期时间等(不要存敏感信息,可被解码);
- Signature(签名):用 Header 的算法,对
Header.Payload+ 密钥签名,用于验证 Token 合法性。
58. webpack 的核心概念有哪些?
- 入口(entry):项目的入口文件(如
entry: './src/index.js'); - 出口(output):打包后的输出路径和文件名(如
output: { path: './dist', filename: 'bundle.js' }); - loader:处理非 JS 文件(如
css-loader处理 CSS,babel-loader转译 ES6); - plugin:扩展 webpack 功能(如
HtmlWebpackPlugin生成 HTML,MiniCssExtractPlugin提取 CSS); - 模式(mode):
development(开发,不压缩)/production(生产,压缩)。
59. loader 和 plugin 的区别?
- loader:用于转换文件内容(处理非 JS 模块),运行在打包过程的 “模块解析阶段”;
- plugin:用于扩展 webpack 功能(如优化、生成文件),运行在打包过程的 “整个生命周期”,可监听 webpack 事件。
60. Vite 和 webpack 的区别?
| 维度 | webpack | Vite |
|---|
| 打包方式 | 基于 “打包”(先打包再启动) | 基于 “原生 ES 模块”(按需编译) |
| 启动速度 | 慢(依赖越多越慢) | 快(按需加载) |
| 热更新 | 慢(需重新打包) | 快(仅更新变化的模块) |
| 适用场景 | 大型复杂项目 | 中小型项目、Vue/React 项目 |
61. Tree Shaking 是什么?
- Tree Shaking:webpack 的优化功能,用于删除代码中未使用的部分(“死代码”),只打包被引用的代码。
- 条件:代码需是 ES Module(静态导入 / 导出),且在
production模式下自动启用。
62. Code Splitting 的作用是什么?
- Code Splitting(代码分割):将打包后的代码拆分为多个小文件,作用:
- 减少首屏加载时间(只加载必要的代码);
- 实现按需加载(如路由懒加载);
- 利用浏览器缓存(公共代码单独打包)。
63. 前端模块化方案有哪些?
- CommonJS:Node.js 的模块化方案,同步加载(如
require/module.exports); - ES Module:ES6 的模块化方案,异步加载(如
import/export); - AMD:浏览器端异步加载(如
require.js); - CMD:浏览器端异步加载(如
sea.js,按需加载)。
64. CommonJS 与 ES Module 的区别?
| 维度 | CommonJS | ES Module |
|---|
| 加载方式 | 同步(Node)/ 动态 | 异步 / 静态 |
| 导出方式 | 值拷贝(修改不影响原模块) | 引用(修改影响原模块) |
| 执行时机 | 运行时加载 | 编译时加载 |
| 适用环境 | Node.js | 浏览器 + Node.js(需配置) |
65. npm、yarn、pnpm 的区别?
| 维度 | npm | yarn | pnpm |
|---|
| 速度 | 慢(早期) | 快(缓存) | 更快(硬链接 + 符号链接) |
| 安全性 | 中等(依赖扁平但可能重复) | 高(锁文件更严格) | 高(无重复依赖) |
| 依赖结构 | 扁平结构(可能重复) | 扁平结构 | 非扁平结构(节省空间) |
66. Vue2 与 Vue3 的核心区别?
- 响应式原理:Vue2 用
Object.defineProperty(无法监听新增属性);Vue3 用Proxy(支持新增属性、数组下标); - API 风格:Vue2 用 Options API(选项式);Vue3 支持 Composition API(组合式)+Options API;
- 性能:Vue3 编译优化(如静态提升)、体积更小;
- 其他:Vue3 支持多根节点、Teleport、Composition API 等新特性。
67. Vue 中组件通信方式有哪些?
- 父子通信:父→子(
props),子→父($emit/v-on); - 祖孙通信:
provide/inject; - 兄弟通信:
EventBus(Vue2)/mitt(Vue3); - 全局状态管理:Vuex/Pinia;
- 路由参数:通过
$route.query/$route.params传递。
68. v-if 和 v-show 的区别?
- v-if:条件渲染,条件不满足时,元素会被 “从 DOM 中移除”(切换开销大);
- v-show:条件显示,条件不满足时,元素会被 “设置为
display: none”(切换开销小); - 场景:频繁切换用
v-show,条件少变用v-if。
69. ref 和 reactive 的区别?
- ref:用于基本类型 / 引用类型,访问时需用
.value(模板中自动解包); - reactive:仅用于引用类型,返回响应式代理对象,访问时无需
.value; - 场景:基本类型用
ref,对象 / 数组用reactive。
70. watch 与 computed 的区别?
| 维度 | watch | computed |
|---|
| 作用 | 监听数据变化,执行异步 / 复杂逻辑 | 依赖数据,计算并缓存结果 |
| 触发时机 | 数据变化时触发 | 依赖变化且被使用时触发 |
| 返回值 | 无(回调函数) | 有(返回计算结果) |
| 缓存 | 无 | 有(依赖不变则复用结果) |
71. nextTick 的作用是什么?
nextTick用于等待 DOM 更新完成后执行回调(因为 Vue 的 DOM 更新是异步的)。
72. Vue 的生命周期有哪些?
- Vue2 生命周期:
- 创建阶段:
beforeCreate(实例创建前)→created(实例创建后,无 DOM); - 挂载阶段:
beforeMount(挂载前)→mounted(挂载后,可操作 DOM); - 更新阶段:
beforeUpdate(更新前)→updated(更新后); - 销毁阶段:
beforeDestroy(销毁前)→destroyed(销毁后)。
- Vue3 生命周期:组合式 API 中用
onBeforeMount/onMounted等钩子函数,与 Vue2 对应。
73. keep-alive 的作用是什么?
keep-alive是 Vue 的内置组件,用于缓存组件实例,避免组件频繁创建 / 销毁,提升性能。
- 场景:路由切换时缓存页面(如列表页→详情页,返回列表页不重新请求数据)。
- 常用属性:
include(缓存哪些组件)、exclude(不缓存哪些组件)。
74. diff 算法的基本原理是什么?
Vue 的 diff 算法是同级比较,核心是 “最小化 DOM 操作”:
- 同级比较:只比较同一层级的节点,不跨层级比较;
- key 的作用:通过
key标识节点唯一性,快速判断节点是 “复用” 还是 “新增 / 删除”; - 比较规则:
- 若节点类型不同,直接替换;
- 若节点类型相同,比较
key和属性,复用节点并更新属性; - 列表比较时,通过
key减少移动操作。
75. 虚拟 DOM 是什么?
- 虚拟 DOM:用JS 对象描述真实 DOM 的结构(如
{ tag: 'div', props: { id: 'app' }, children: [] })。 - 作用:
- 减少 DOM 操作(通过 diff 算法计算最小更新量);
- 跨平台(同一虚拟 DOM 可渲染到浏览器、小程序等)。
76. slot 插槽的类型有哪些?
- 默认插槽:无名称的插槽,用
<slot>表示; - 具名插槽:有名称的插槽,用
name属性区分(如<slot name="header"></slot>); - 作用域插槽:子组件向父组件传递数据的插槽(如
<slot :data="list"></slot>,父组件用v-slot="scope"接收)。
77. Teleport 的作用是什么?
Teleport(瞬移)是 Vue3 的特性,用于将组件的 DOM 节点 “移动” 到指定的 DOM 位置(如<body>),同时保持组件的逻辑归属不变。
78. provide 和 inject 的使用场景是什么?
provide/inject是 Vue 的祖孙组件通信方式:
- 祖先组件用
provide提供数据; - 后代组件用
inject注入数据; - 场景:深层嵌套的组件通信(避免 props 逐层传递)。
79. Composition API 与 Options API 的区别?
| 维度 | Options API | Composition API |
|---|
| 代码组织 | 按选项(data/methods)分类 | 按功能逻辑分类 |
| 复用性 | 依赖 mixins(易冲突) | 用组合函数(更灵活) |
| 类型支持 | 较差(需装饰器) | 友好(TS 支持更好) |
| 可读性 | 大项目中逻辑分散 | 逻辑聚合,更易维护 |
80. React 和 Vue 的核心区别?
| 维度 | React | Vue |
|---|
| 响应式原理 | 状态驱动(setState 触发重新渲染) | 数据劫持(Vue2:Object.defineProperty;Vue3:Proxy) |
| 模板语法 | JSX(HTML 嵌入 JS) | 模板语法(HTML+Vue 指令) |
| 生态 | 更丰富(适合大型项目) | 更轻量(适合中小型项目) |
| 灵活性 | 高(自由度大) | 中等(约定大于配置) |
81. 什么是 JSX?
JSX 是 React 的语法扩展,允许在 JS 中写 HTML 结构(如<div>Hello</div>),最终会被 Babel 转译为React.createElement调用。
- 特点:支持表达式嵌入(
{变量})、条件渲染、列表渲染等。
82. 什么是 Hooks?
Hooks 是 React 16.8 的特性,用于在函数组件中使用状态和生命周期等特性(替代类组件)。
- 常用 Hooks:
useState(状态)、useEffect(副作用)、useContext(上下文)、useRef(引用)等。
83. useEffect 与 useLayoutEffect 的区别?
- useEffect:在DOM 渲染完成后执行(异步,不阻塞渲染);
- useLayoutEffect:在DOM 渲染完成前执行(同步,阻塞渲染);
- 场景:操作 DOM 样式且需立即生效时用
useLayoutEffect,其他场景用useEffect。
84. 懒加载的实现方式有哪些?
- 路由懒加载:Vue 用
() => import('./Page.vue'),React 用React.lazy(() => import('./Page.jsx')); - 图片懒加载:
- 监听滚动事件,判断图片是否进入视口;
- 用
IntersectionObserver API(更高效); - 原生
loading="lazy"属性。
85. 预加载的方式有哪些?
86. 图片优化的策略有哪些?
- 格式选择:WebP/AVIF(更小体积)、PNG(透明图)、JPG(照片);
- 压缩体积:用工具(如 TinyPNG)压缩;
- 懒加载(见第 84 题);
- CDN 加速:用 CDN 加载图片;
- 响应式图片:
srcset/sizes(不同设备加载不同尺寸)。
87. 如何减少白屏时间?
- 优化资源加载:压缩 JS/CSS、图片懒加载、路由懒加载;
- CDN 加速:静态资源用 CDN;
- 服务端渲染(SSR):Vue 用 Nuxt,React 用 Next;
- 预加载关键资源:
preload关键 CSS/JS; - 骨架屏:先显示骨架屏,再渲染内容。
88. 长列表渲染如何优化?
- 虚拟列表:只渲染 “当前视口内的元素”,复用 DOM(如
vue-virtual-scroller、react-window); - 分页加载:分批次请求数据,每次加载部分内容;
- 懒渲染:只渲染可见区域的元素,滚动时加载其他元素。
89. 数组去重的方式有哪些?
- Set(最简单):
[...new Set(arr)]; - filter+indexOf:
arr.filter((item, index) => arr.indexOf(item) === index); - reduce+includes:
arr.reduce((prev, curr) => prev.includes(curr) ? prev : [...prev, curr], [])
- 对象键值对:利用对象属性唯一性去重。
90. 字符串反转如何实现?
- split+reverse+join:
str.split('').reverse().join(''); - for 循环:
let res = '';
for (let i = str.length - 1; i >= 0; i--) res += str[i];
- 递归:
str === '' ? '' : reverse(str.slice(1)) + str[0]。
91. 如何判断一个字符串是否为回文?
回文:正读和反读都相同(如 “abcba”)。
92. 实现一个深拷贝函数
function deepClone(obj, map = new WeakMap()) {
// 处理null/基本类型
if (obj === null || typeof obj !== 'object') return obj;
// 处理循环引用
if (map.has(obj)) return map.get(obj);
// 处理数组/对象
const clone = Array.isArray(obj) ? [] : {};
map.set(obj, clone);
// 遍历属性
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], map);
}
}
return clone;
}
93. 实现一个防抖函数
function debounce(fn, delay) {
let timer = null;
return function(...args) {
// 清除之前的定时器
if (timer) clearTimeout(timer);
// 重新计时
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
94. 实现一个节流函数
function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
// 时间差大于delay则执行
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}
95. 实现 Promise.all
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
const results = [];
let count = 0;
const len = promises.length;
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i])
.then(res => {
results[i] = res;
count++;
// 所有Promise完成后resolve
if (count === len) resolve(results);
})
.catch(err => reject(err)); // 任一失败则reject
}
});
};
96. 前缀和的原理和应用场景是什么?
- 原理:前缀和数组
preSum中,preSum[i]表示原数组前i个元素的和(preSum[0] = 0,preSum[i] = preSum[i-1] + arr[i-1])。 - 应用场景:快速计算 “数组某区间的和”(如求
[l, r]的和:preSum[r+1] - preSum[l])。
97. 递归和迭代的区别?
- 递归:函数调用自身,将大问题分解为小问题(代码简洁,但可能栈溢出);
- 迭代:用循环重复执行代码(无栈溢出风险,但代码较繁琐);
- 示例:计算斐波那契数列:
- 递归:
function fib(n) { return n <= 1 ? n : fib(n-1) + fib(n-2); } - 迭代:
function fib(n) { let a=0,b=1; for(let i=2;i<=n;i++) { [a,b] = [b,a+b]; } return b; }
98. 时间复杂度与空间复杂度的区别?
- 时间复杂度:描述算法执行时间随输入规模增长的趋势(如 O (1)、O (n)、O (logn));
- 空间复杂度:描述算法所需额外空间随输入规模增长的趋势(如 O (1)、O (n));
- 目标:设计 “时间复杂度低 + 空间复杂度低” 的算法。
99. 栈和队列的区别?
- 栈(Stack):“后进先出”(LIFO),仅允许在一端(栈顶)操作;
- 队列(Queue):“先进先出”(FIFO),允许在一端(队尾)入队,另一端(队头)出队;
- 应用:栈(函数调用栈、括号匹配);队列(任务队列、广度优先搜索)。
100. 常见的排序算法有哪些?
| 算法 | 时间复杂度(平均) | 空间复杂度 | 稳定性 |
|---|
| 冒泡排序 | O(n²) | O(1) | 稳定 |
| 选择排序 | O(n²) | O(1) | 不稳定 |
| 插入排序 | O(n²) | O(1) | 稳定 |
| 快速排序 | O(nlogn) | O(logn) | 不稳定 |
| 归并排序 | O(nlogn) | O(n) | 稳定 |
| 堆排序 | O(nlogn) | O(1) | 不稳定 |