前端项目面试提问①:从技术栈到业务实现

4197 字
21 分钟
前端项目面试提问①:从技术栈到业务实现

整理完基础题以后,我又把手头的几个项目翻了一遍。Vue、Pinia、Axios 这些名字并不难记,真正需要准备的是:如果面试官继续问为什么这样写、换一种场景会不会出问题,我能不能顺着代码解释下去。

这篇就从无人机配送管理端开始,再聊凭证报表、小程序和自己的博客。我把项目里现有的处理方式与自己的理解放在一起,遇到还不完善的地方,也记下接下来会怎样改进。

一、先把项目技术栈讲清楚#

项目使用的技术与实现我重点复盘的问题
无人机配送管理端Vue 3.5.26、Vite 6.4.1、Element Plus、Pinia、Vue Router、Axios、ECharts权限、订单流程、请求封装、可视化
无人机配送小程序uni-app、Vue 3、Pinia、微信小程序运行环境登录、请求并发、平台适配、地图
凭证查询管理端Vue 3、Element Plus、Pinia、Axios、共享报表查询 composable单位/年度上下文、竞态、报表复用
健身小程序uni-app、Vue 3、TypeScript、Pinia、Vitest类型建模、本地与云端数据合并
个人博客Astro 7.0.7、Svelte 5、TypeScript、Tailwind CSS 4、Pagefind内容集合、交互边界、静态搜索

我先按当前 package.json 整理了这张表。涉及精确版本时,还需要结合锁文件确认范围依赖最终安装的版本。介绍项目时,我也会分别说明哪些地方使用 JavaScript、哪些地方使用 TypeScript,以及普通 uni-app 与 uni-app x 的区别。

二、无人机配送管理端#

1. 为什么选择 Vue 3 + Element Plus + Pinia?#

我会先从管理端每天处理的事情讲起:查列表、筛选订单、填写表单、打开弹窗,再根据权限进入不同页面。Element Plus 提供了这些常用组件,Vue 组合式 API 便于组织查询与编辑逻辑,Pinia 则承载用户、权限、设置等跨页面状态。

这个项目是在若依体系上继续开发的,所以我理解技术选择时,也会把已有代码和维护成本考虑进去。

追问:所有变量都放 Pinia 吗?

我会按状态的使用范围来分。弹窗开关、当前表单和局部 loading 通常留在组件内;多个页面共享且生命周期更长的数据才放 store。对于服务端数据,我还会单独考虑是否缓存、何时失效。

2. 动态路由是怎样加载的?隐藏按钮能保证安全吗?#

我会从 uav-ui/src/permission.js 的导航守卫讲起:先检查 token,当角色尚未加载时获取用户信息,生成可访问路由,通过 router.addRoute 注册,再重新导航到目标地址。沿着这条链路,还能看到登录白名单和锁屏路由的处理。

这里我会把页面权限和接口权限分开解释。动态路由控制可达页面,按钮权限控制操作入口;后端必须独立校验操作权限和资源归属。用户可以绕过页面直接请求接口,所以只隐藏按钮还不够。

追问:刷新页面后为什么要重新导航?

因为刷新后内存中的动态路由需要重新注册。注册完成再匹配目标地址,才能进入正确页面;同时要保证初始化条件收敛,避免反复加载形成重定向循环。退出或切换账号后的路由清理,也是需要单独检查的边界。

3. Axios 封装具体做了什么?#

复盘 uav-ui/src/utils/request.js 时,我按一次请求的过程来梳理:设置基础地址和 10 秒超时,注入 Bearer token,处理 GET 参数,再处理短时间重复提交、业务错误提示和二进制下载。登录失效时,还有 isRelogin.show 避免重复弹出提示。

我理解这几层的分工是:通用请求层负责传输和公共错误处理,业务 API 层表达订单、异常等业务动作,页面负责 loading、交互反馈和结果更新。排查错误时,我会分别看 HTTP 状态与响应体中的 code。

追问:这个封装还可以改进什么?

接下来如果继续完善这层封装,我会检查请求拦截器的失败分支是否正确返回拒绝态,以及业务 code 使用 || 200 是否会掩盖合法的零值。动手前,我会先确认接口约定,再决定具体改法。

4. 前端防重复提交等于接口幂等吗?#

我在请求层看到的处理,是对 POST/PUT 按 URL、数据和时间间隔判断重复,默认间隔为 1 秒,并只保存最近一次请求记录;较大的请求会跳过这项前端判断。

所以我会把它解释为减少误点的一道措施。多标签页、多设备、刷新或绕过前端的请求,仍可能重复到达服务端。对于创建订单、退款等操作,我还会考虑服务端的业务唯一约束、幂等键或条件状态更新。

追问:按钮禁用后还需要后端校验状态吗?

需要。用户打开页面之后,另一位操作员可能已经处理了订单。前端展示的是一个时间点的状态;真正提交时必须重新判断当前状态是否允许该动作。

5. 订单按钮、状态标签和操作流程如何保持一致?#

结合前面整理过的订单列表,我会先统一状态含义和允许动作,让标签负责展示,按钮依据“状态允许 + 用户有权限”决定入口。如果接口拒绝了状态过期的操作,我会提示用户并刷新详情,让页面重新对齐服务端状态。

验证这部分时,我会沿业务路径考虑:待审核订单被另一人先处理、退款后重复点击、旧页面提交失效动作。这样才能检查按钮背后的流程是否一致。

6. 地图和 ECharts 页面如何避免越用越卡?#

如果要继续优化这类页面,我会先检查实例的创建和销毁:将地图或图表实例与业务数据分开管理,尽量复用实例更新数据;离开页面时解除监听、停止轮询并释放实例。对于高频位置更新,我会根据展示需求节流,并限制轨迹数据的累积范围。

追问:地图偏移和接口慢是同一种问题吗?

我会把这两个问题分开排查。地图偏移时先核对经纬度顺序、坐标系、时间戳和有效范围。配送小程序轨迹页调用定位时请求 gcj02,其他来源坐标仍需确认;对于实时数据,我还会检查是否存在乱序和过期点。

三、凭证查询与财务报表#

7. 用户快速切换单位和月份,旧请求覆盖新结果怎么办?#

这道题我会结合报表查询来讲。ruoyi-ui/src/views/report/composables/useFinancialReportQuery.js 使用递增的 requestSerial,每次请求保存当时序号,返回后只有仍匹配最新序号才更新 rows;catch 与 finally 也检查序号。

我理解这里最关键的一点,是请求返回顺序不一定等于发起顺序。比较序号不仅能挡住旧数据,也能避免旧错误提示和旧请求的 loading 清理影响当前查询。切换单位、年度或月份时,代码会让旧序号失效并重置行数据。我把核心过程简化成下面的例子:

// 简化示意:对应项目中的序号思路
let serial = 0
async function load(query) {
const current = ++serial
loading.value = true
try {
const result = await fetchRows(query)
if (current === serial) rows.value = result
} catch (error) {
if (current === serial) showError(error)
} finally {
if (current === serial) loading.value = false
}
}

追问:序号机制会取消网络请求吗?

不会,它只控制结果是否生效。取消请求可以减少客户端等待和资源占用,但请求可能已经到达服务端,仍应保留结果时序保护。验证时可让第一次请求延迟、第二次先返回,确认旧数据和旧错误都不会污染新查询。

8. 为什么把报表查询抽成 composable?#

回头看这个共享函数,我觉得比较清楚的地方是它把共同流程和报表差异分开了。函数接收 request、createEmptyRows、mapRows 和 errorMessage,集中处理单位/年度上下文、月份、loading、竞态和失败重置,各报表提供自己的数据转换。

如果面试官问我抽象的边界,我会回答:复用查询流程,保留各报表自己的列结构与金额映射。这样既减少重复逻辑,也不用让一个组件承担所有报表差异。

追问:缓存报表时 key 怎么设计?

如果后续增加报表缓存,我会让 key 至少包含单位、年度、月份和报表类型,并考虑用户身份或权限变化造成的隔离需求。只用月份做 key 会把不同单位的结果混在一起;退出登录时,我也需要处理敏感缓存。

四、无人机配送小程序#

9. uni-app 为什么还需要微信适配层?#

结合小程序的登录和授权流程,我理解 uni-app 统一的是部分开发写法,微信登录、手机号授权、订阅消息、隐私授权等能力仍受平台 API 和调用时机约束。把平台能力封装起来以后,我在页面里就能更集中地处理业务结果,把平台检测、错误转换和回调适配放到底层。

追问:H5 正常就说明小程序正常吗?

我不会只用 H5 的结果判断小程序。两者的运行环境、组件能力、网络配置与权限交互不同,我需要在目标小程序环境验证;涉及授权和设备能力的路径,还要真机确认。

10. 多个请求同时 loading,怎么避免一个请求先结束就把提示关掉?#

这部分我会拿 uav-mmp/utils/request.js 中的 loadingCount 举例:用计数记录正在展示加载提示的请求数量,第一次开始时显示,计数归零时隐藏。

我理解计数器的好处,是能表达多个请求同时进行的状态。检查这段逻辑时,我会特别留意成功、失败和取消路径是否成对清理;如果只在成功回调里减计数,遮罩就可能残留。

追问:取消旧请求与防重复提交一样吗?

不一样。该请求工具还有 pendingTasks 和取消旧任务的辅助逻辑;取消是控制请求生命周期,防重是减少相同提交,幂等是重复业务动作产生一致结果。三者要分别设计,不能互相替代。

11. 小程序上传图片成功后,为什么不能只保存临时路径?#

我会把“选到图片”和“保存业务凭证”看成两个阶段。临时路径用于当前设备上的文件访问,不能当成长期业务凭证。设计这条链路时,我会在上传完成后保存服务端返回的文件标识,再由订单接口关联;预览则按资源访问方式获取内容,避免把长期 token 拼进公开 URL。

追问:上传成功但订单提交失败呢?

这种情况下,我会继续考虑未关联文件的清理和重试策略。重新提交时,可以复用仍有效的已上传文件标识,同时由后端检查所有权,避免把其他用户的文件关联到当前订单。

五、健身小程序#

12. TypeScript 给这个项目带来了什么?#

我会从项目里的 src/types/domain.ts 讲起,再看数据合并函数使用的 WorkoutSession 和 CheckIn 类型。工程中还配置了 vue-tsc 与 Vitest,分别用于类型检查和测试。

对我来说,类型最直接的帮助是明确训练记录、打卡数据和函数输入输出,重构时更容易发现字段不一致。不过面对云端响应和本地旧缓存,我仍需要运行时解析、校验和必要的数据迁移,不能只依靠类型声明。

13. 本地训练记录和云端记录怎样合并?#

我会按“用什么识别同一条记录、冲突时保留哪条”来解释 src/utils/cloud-merge.ts。当前函数使用 Map 按 session.id 去重,以 completedAt 或 startedAt 比较时间,保留较新的记录并倒序排列。同一天的打卡按 date 合并,sessionIds 用 Set 去重。

追问:时间相同、用户删除记录或设备时间错误怎么办?

当前函数按本地数组、远端数组的顺序遍历,时间相等时后来的记录会覆盖先前记录。因此同 ID 同时间的远端记录会胜出。函数本身没有表达删除墓碑,也没有解决设备时钟偏差。

这也是我后续会继续完善的地方:根据同步协议引入版本号、服务端更新时间和删除标记,把冲突处理约定清楚。当前按时间覆盖的规则解决了一部分合并问题,删除和时钟偏差还需要另外设计。

追问:你会测什么?

我会优先补充同 ID 新旧记录、同时间冲突、空数组、同日期多个 sessionId,以及异常日期输入这些用例。介绍测试情况时,我会以实际编写和运行的结果为准,明确哪些规则已验证、哪些还需要补充。

六、博客与内容工程#

14. 为什么博客使用 Astro,而管理端使用 Vue?#

我对这两个项目的需求判断不同。自己的博客主要交付文章内容,管理端则有大量连续交互和业务状态。Astro 适合以页面内容为中心组织站点,并按需要给交互组件加载客户端能力;Vue 管理端便于维护复杂表单、路由和状态流。

我的博客使用 Astro 内容集合读取 Markdown/MDX,通过 schema 约束标题、发布日期、封面、标签等字段,也集成了 Svelte。分析客户端开销时,我还会继续看具体组件的使用方式,确认哪些交互需要发送客户端脚本。

15. 新增文章为什么不用手动改文章列表?#

我平时新增文章的入口就是 src/content/posts。src/content.config.ts 的 glob loader 会扫描这里的 Markdown/MDX 文件,所以按既有 frontmatter 格式写好文章,就能进入内容集合。

追问:Pagefind 的搜索数据什么时候更新?

我在 build 脚本里可以看到,站点生成后还会执行 pagefind --site dist。因此写完文章源文件后,我还需要通过后续构建和部署,让线上页面与搜索索引一起更新。

16. 如果面试官要求你证明优化有效,怎么回答?#

我会提供同一设备、网络、数据规模与操作路径下的对比,并说明测量工具和指标。请求竞态可以用可控延迟复现;首屏优化可以对比资源体积与 LCP;同步合并可以用固定输入验证规则。如果某项优化还没有测量,我会先说明目前的判断依据,再补上验证计划。

七、一段可复述的项目介绍#

我接触的前端项目覆盖管理端、小程序和内容站点。管理端使用 Vue 3、Element Plus、Pinia 和 Axios,重点处理权限、业务状态和查询交互;凭证报表中通过共享 composable 与请求序号处理重复逻辑和异步竞态;小程序需要处理平台能力和并发请求;健身项目还涉及类型建模与本地、云端记录合并;博客则通过 Astro 内容集合组织文章。介绍具体贡献时,我会进一步说明自己实际负责的模块、遇到的问题和验证证据。

整理到这里,我给自己留下的复习方式,就是选一条真实链路从头讲到尾:用户做了什么、状态如何变化、请求如何到达后端、失败如何恢复。讲不顺的地方,再回到代码里看一遍;还没有处理好的边界,就记到后续改进里。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
前端项目面试提问①:从技术栈到业务实现
https://firefly.cuteleaf.cn/posts/frontend-project-interview-1/
作者
Yore
发布于
2026-09-10
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Yore
Hello, I'm Yore.
公告
欢迎来到Yore的博客!本站基于原创作者夏叶的博客网站模版创作。
分类
标签
最新动态
站点统计
文章
51
分类
2
标签
124
总字数
94,115
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
EdgeOne Pages
博客版本
Firefly v6.15.0
文章许可
CC BY-NC-SA 4.0