首先就是面试的常规步骤
- 自我介绍
- 提问自己在项目中有哪些优势,项目中的难点是如何解决的?
- 完整介绍一下项目
开胃小菜第一题:懒加载
- 组件懒加载 :使用import()动态导入组件。
- 图片懒加载 :可以通过第三⽅库(如vue lazyload)或原⽣API(如IntersectionObserver)来实现。
第二题:大文件上传以及应用场景的优化
- 分片上传:将大文件分割成许多小文件块,分批次上传,即使是上传的过程中出现中断,也可以从中断的地方重新开始上传,不用从头开始。
工作原理:
- 客户端分割文件:,客户端将文件分割成若干小块。
- 逐个分片上传:客户端将每个分片逐个上传到服务器,每次上传一个分片。上传过程中,客户端会与服务器进行通信,更新上传进度。
- 服务器接收并保存分片:服务器接收到每个分片后,会存储它们并等待所有分片上传完成。一些服务器会先存储分片,然后返回一个分片 ID 供客户端跟踪。
- 合并文件分片:服务器将所有分片合并成完整的的文件
- 返回上传结果:服务器将合并后的完整文件保存,并返回一个成功响应,告诉客户端上传完成。
- 断点续存:顾名思义就是 断点+续存
- 断点: 其分两种情况->第一种就是由于网络问题导致上传发生中断,还有一种就是我们手动点击暂停上传。
手动实现断点原理:
第一种:基于上传状态管理
通过标志变量
isPaused
控制上传的暂停与恢复,当客户端上传一个分片时,服务器会将该分片的状态记录到数据库中。所有我们可以通过手动修改状态来实现对文件上传的控制。第二种:基于
XMLHttpRequest.abort()
的手动断点续传在文件上传过程中,我们将每个分片的
XMLHttpRequest
对象保存下来。当用户点击暂停时,可以调用abort()
来取消当前上传的请求。这样,上传的请求就会被中止。

- 续存:客户端会将已上传的片段信息保存到本地(如
localStorage
)或者服务器端。在上传过程中,客户端定期向服务器查询已上传的切片,或者服务器在每次上传请求中返回哪些切片已经上传。这可以通过接口实现,客户端根据服务器返回的结果决定从哪一个切片开始上传,跳过已经上传的部分。
第三题:继续提问断点续存---怎么确定上传完了?
首先当所有的切片上传完毕之后,客户端会向服务器端发送合并请求,然后服务器会检查所有切片是否上传完毕,通常会使用文件的哈希值来进行验证,在文件上传前计算文件的哈希值,并将哈希值随文件一起传输,服务器合并文件后,再次计算文件的哈希值并与客户端传输的哈希值进行对比,如果一致则表明文件上传成功。简单来说就是客户端和服务器端都需要记录每个切片的上传状态,确保没有遗漏。
第四题:如何客户端上传到一半,重新刷新页面,要不要重新上传
不需要!上面讲到的断点续存会将分片的上传状态储存到本地和服务器端,在客户刷新页面后可以根据已经上传的切片信息跳过已上传的切片,从剩余的切片开始继续上传。
第五个提问:文件分片的id储存在哪?
- 客户端:客户端通常会将文件切片的状态(包括分片的 ID、上传进度等)保存在本地存储中,这样可以在页面刷新后恢复上传进度。常用的本地存储方式有
localStorage
、sessionStorage
和IndexedDB
。 - 服务器端:服务器端通常会根据文件的名称和切片的索引生成一个唯一的 ID 来标识每个文件切片,并保存这些信息。服务器端会记录每个文件切片的状态,包括切片的 ID、上传进度等信息,以便在客户端发生刷新或断点续传时进行恢复。
阿里云OCR是做什么的,有什么用?
OCR
:Optical Character Recognition,光学字符识别。 阿里云 OCR是阿里云提供的一种基于深度学习和人工智能技术的服务,能够将图像中的文字信息提取出来并转换为机器可读的文本。
其主要功能包括:
- 通用文字识别:能够识别图片中的通用文本,无论是印刷文本还是手写文本(部分支持),可以从图像中提取出文字内容。
- 身份证识别:专门针对身份证等证件进行识别,提取出身份证上的信息,如姓名、出生日期、身份证号码等
- 银行卡识别:识别银行卡正面的卡号、有效期、持卡人姓名等信息。
- 车牌识别:识别汽车的车牌号码,通常结合视频流实时识别。
- 多语言文字识别:支持多语言的文字识别,不仅限于中文,还支持英文、日文、韩文等语言。
- 等等等......
追问:为什么要用阿里云OCR?
- 阿里云OCR拥有高精度的文字识别能力,利用深度学习,人工智能和大数据技术,提供了高准确度的文字识别能力。
- 提供多种专业场景支持,阿里云OCR提供了多种OCR模块可以满足不同行业和需求。
- 可拓展和易用性,阿里云OCR可以灵活拓展根据实际的需求和流量进行动态扩容。且用户无需深入理解复杂的人工智能技术,只需调用阿里云 OCR API就能快速利用 OCR 技术进行文字识别。
- 安全性和稳定性 ,阿里云 OCR 服务保证了数据的安全性和隐私保护。阿里云采用了严格的 数据加密 、身份认证 、访问控制 等安全措施,确保用户数据不会外泄。
- 节约成本,按需付费,阿里云 OCR 基于成熟的 AI 模型和算法,企业可以直接调用服务,而不需要进行复杂的模型训练。阿里云 OCR 支持按使用量计费,企业可以根据实际需求灵活支付,避免了高额的初期投资。
jwt解决了什么问题?为什么要用JWT
JWT :是一种用于在网络应用环境中安全地传输声明的开放标准,主要用途是实现 身份验证 和 信息交换 。 通俗来讲:JWT
就像一张"加密的电子门票",用来安全地在不同系统之间传递用户信息。就比如我们在去游乐园的时候
-
买票时:
- 你在窗口出示身份证(登录 ),工作人员验证后,给你一张带印章的门票(JWT)。
- 门票上印有你的姓名、有效期、允许玩的设施(用户信息+权限)。
-
玩项目时:
- 每个设施的工作人员只需检查门票印章 (验证JWT签名),不用再联系售票处查你的身份(无需查数据库)。
- 如果门票过期或被涂改(无效JWT),直接拒绝进入。
所以JWT解决了这些问题: 将所有的身份信息(如用户 ID、权限、过期时间等)编码 到一个独立的 Token 中,发送给客户端。客户端在后续的请求中,直接携带这个 JWT 作为 请求头的一部分,服务器根据 JWT 验证用户身份。
JWT的组成
一个JWT令牌通常是这样的字符串:
xxxxx.yyyyy.zzzzz
- header(头):说明令牌类型和签名算法
json
{ "alg": "HS256", "typ": "JWT" }
- Payload(数据):存放实际传递的信息(比如用户ID、过期时间)
json
{ "sub": "123", "name": "张三", "exp": 1735689600 }
- Signature(签名) :前两部分+密钥生成的防伪标记,防止数据被篡改。
为什么要使用JWT
-
无状态 & 减轻服务器压力 在传统解决认证时使用session:用户登录后,服务器要存Session,每次请求都得查一次数据库/缓存,验证用户是否有效。这就有一个问题了,当用户量大了,服务器存Session压力大,扩展麻烦。 而JWT的方式:服务器签发JWT后,不用存任何东西 ,客户端自己保管令牌。后续请求只需验证JWT签名和有效期,不用查数据库。
-
解决了传统 cookie + session 的跨域问题
因为浏览器的同源策略 Cookie 不能跨域 而Session需要在 Cookie 中存储
SessionID
所以传统的这种方式不能解决跨域问题,而JWT是通过HTTP请求头来传递的不存在跨域问题。 -
更灵活和可拓展性
用户的身份信息都存储在JWT,这使得JWT 在各种场景下都可以灵活应用。
-
简洁,安全
JWT 是轻量级的,便于操作和解析,同时通过签名确保信息的完整性和安全性
追问 JWT的双Token
首先为什么需要双token?
- 当Token被盗取后,攻击者可以在短期内一直冒充用户
- 用户需要频繁重新登录
- 服务器无法强制废除未过期的Token
双Token机制
使用 访问 Token(Access Token) 和 刷新 Token(Refresh Token) 这两种 Token 来实现双 Token。
- 访问 Token : 用于授权用户访问受保护的资源,一般有效期比较短,以降低令牌泄露后的风险,通常会存储在客户端的内存中或 HTTP 请求头
- 刷新Token :用于获取新的访问 Token,通常在访问 Token 过期后,使用刷新 Token 来获取新的访问 Token,而不需要重新登录,有效期通常为几天或几个月,存储在 HTTP-only cookie 中,确保它不容易被 JavaScript 脚本访问,增加安全性。
双 Token的工作流

继续追问 在代码逻辑上怎么去使用 Access Token和 Refresh Token这两个字段?
- 前端方面
*- 登录时保存 Token
-
- 请求时携带 Access Token
-
- Token 过期时自动刷新
-
- 退出时清除 Token
js
// src/utils/auth.js
import axios from 'axios';
// 1. 保存 Token
export const saveTokens = (accessToken, refreshToken) => {
localStorage.setItem('accessToken', accessToken);
// Refresh Token 由后端通过 HTTP-Only Cookie 存储,前端不处理
};
// 2. 获取当前 Access Token
export const getAccessToken = () => {
return localStorage.getItem('accessToken');
};
// 3. 清除 Token
export const clearTokens = () => {
localStorage.removeItem('accessToken');
// 调用后端接口清除 Refresh Token(见后端部分)
};
// 4. 配置 Axios 实例
const api = axios.create({
baseURL: 'https://api.yourdomain.com',
});
// 5. 请求拦截器(自动添加 Access Token)
api.interceptors.request.use((config) => {
const token = getAccessToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 6. 响应拦截器(自动刷新 Token)
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
// 如果是 401 错误且未重试过
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
// 调用刷新接口获取新 Access Token
const res = await axios.post(
'https://api.yourdomain.com/refresh-token',
{},
{ withCredentials: true } // 允许携带 Cookie(Refresh Token)
);
const newAccessToken = res.data.accessToken;
saveTokens(newAccessToken); // 保存新 Token
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
return api(originalRequest); // 重试原请求
} catch (refreshError) {
// 刷新失败,跳转到登录页
clearTokens();
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
export default api;
- 后端
*- 登录接口:签发双 Token
-
- 刷新接口:用 Refresh Token 换新 Access Token
-
- 受保护接口:验证 Access Token
-
- 退出接口:吊销 Refresh Token
js
// server.js
const express = require('express');
const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');
const app = express();
app.use(express.json());
app.use(cookieParser());
const ACCESS_TOKEN_SECRET = 'your_access_secret';
const REFRESH_TOKEN_SECRET = 'your_refresh_secret';
// 模拟数据库存储 Refresh Token
const refreshTokens = {};
// 1. 登录接口
app.post('/login', (req, res) => {
const { username, password } = req.body;
// 模拟用户验证
if (username === 'admin' && password === '123456') {
const userId = 1;
// 生成 Access Token(15分钟过期)
const accessToken = jwt.sign(
{ userId, role: 'admin' },
ACCESS_TOKEN_SECRET,
{ expiresIn: '15m' }
);
// 生成 Refresh Token(7天过期)
const refreshToken = jwt.sign(
{ userId },
REFRESH_TOKEN_SECRET,
{ expiresIn: '7d' }
);
// 存 Refresh Token 到数据库
refreshTokens[userId] = refreshToken;
// 返回 Access Token,Refresh Token 通过 Cookie 设置
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true, // 生产环境启用 HTTPS
maxAge: 7 * 24 * 60 * 60 * 1000, // 7天
});
res.json({ accessToken });
} else {
res.status(401).send('用户名或密码错误');
}
});
// 2. 刷新 Token 接口
app.post('/refresh-token', (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.sendStatus(401);
// 验证 Refresh Token
jwt.verify(refreshToken, REFRESH_TOKEN_SECRET, (err, decoded) => {
if (err || !refreshTokens[decoded.userId]) {
return res.sendStatus(403); // 无效或已吊销
}
// 生成新 Access Token
const newAccessToken = jwt.sign(
{ userId: decoded.userId, role: 'admin' },
ACCESS_TOKEN_SECRET,
{ expiresIn: '15m' }
);
res.json({ accessToken: newAccessToken });
});
});
// 3. 受保护接口(验证 Access Token)
app.get('/user/profile', (req, res) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403); // Token 无效或过期
res.json({ name: '张三', role: user.role });
});
});
// 4. 退出接口
app.post('/logout', (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.sendStatus(204);
// 从数据库删除 Refresh Token
jwt.verify(refreshToken, REFRESH_TOKEN_SECRET, (err, decoded) => {
if (!err && decoded.userId) {
delete refreshTokens[decoded.userId];
}
});
// 清除 Cookie
res.clearCookie('refreshToken');
res.sendStatus(204);
});
app.listen(3000, () => console.log('Server running on port 3000'));
最后 手写防抖
js
function debounce(func, delay) {
let timer = null; // 用于存储定时器ID
return function (...args) {
// 清除上次的定时器
if (timer) {
clearTimeout(timer);
}
// 设置新的定时器
timer = setTimeout(() => {
func.apply(this, args); // 调用目标函数
}, delay);
};
}
总结
通过以上提问,我们可以看出大厂面试基本不考察纯粹的八股了,更加注重八股下面的底层原理,同时对实际开发中需要面对的场景问题进行深入的提问!所以在平时的学习过程中我们要多去研究源码,理解底层执行原理!