1)PC 端和移动端的自适应
我会先把移动端视口设置正确,然后以流式布局为主、断点为辅。核心是让布局天然适配,而不是靠堆媒体查询。
- 视口(移动端必写):
1 | <meta name="viewport" content="width=device-width, initial-scale=1" /> |
- 布局优先用
flex/grid+min/max-width,必要时再加断点(mobile-first):
1 | .list { |
- 常见补充点:图片
max-width: 100%防溢出;触控目标尺寸要足够;必要时处理安全区env(safe-area-inset-*)。
2)Tailwind
Tailwind 是 utility-first。优势是开发效率和一致性(设计 token 统一),缺点是类名变长、动态类名需要约束。
- 响应式:
p-3 md:p-6 lg:p-8 - 状态:
hover:* focus:* disabled:* - 复用:重复就抽组件;
@apply可以用但别把它当成回到“大坨 CSS”
高频坑:
- 生产样式丢失:
content扫描范围没覆盖模板/组件文件,构建裁剪把类删了。 - 动态拼类名不生效(构建期扫不到):
1 | const cls = `text-${color}-600`; |
解决思路:枚举映射(推荐)或 safelist(兜底)。
3)MD5:它是加密算法吗?现在安全吗?
MD5 不是加密,是哈希。MD5 不适合安全场景(尤其需要抗碰撞/防篡改的场景);密码存储更不能用 MD5。
- 加密:有密钥、可逆,目标是保密
- 哈希:无密钥、不可逆(理想目标),用于摘要/完整性
安全性结论(怎么用怎么答):
- 签名、证书、防篡改、对外校验:不要用 MD5(抗碰撞不足)
- 工程校验(非对抗):偶尔还见得到,但更推荐
SHA-256 - 密码存储:用
Argon2/bcrypt/scrypt+ 每用户随机 salt + 合理成本参数(必要时加 pepper)
4)MD5 的破解
大多数“破解 MD5 密码”是通过猜测输入(字典/规则/穷举/彩虹表),不是从哈希值反推原文。
- 字典/规则:针对弱密码最有效
- 暴力穷举:成本高但可行(GPU 会显著加速)
- 彩虹表:预计算映射,用空间换时间;salt 能让它失效
**补充区分:**碰撞攻击是“找两段不同内容 hash 相同”,不是找回密码;它会影响把 MD5 用在完整性证明/签名基础的场景。
5)HTTP/1.1、HTTP/2、HTTP/3
HTTP/2 主要解决应用层并发(多路复用、头压缩),但仍受 TCP 丢包队头阻塞影响;HTTP/3 基于 QUIC,在弱网下更有优势。
- HTTP/1.1:并发能力弱、header 冗余明显
- HTTP/2:二进制分帧 + 多路复用 + HPACK;仍跑在 TCP 上,丢包会阻塞后续
- HTTP/3:基于 QUIC(通常 UDP 承载),多路复用在传输层,弱网/切网体验更好
6)TCP 和 UDP 的区别 + 应用场景
TCP 提供可靠、有序与拥塞控制;UDP 更轻量但不保证可靠性。选型看业务要不要“可靠与顺序”,以及对时延的敏感度。
- TCP:面向连接;可靠/有序/去重;流控/拥塞控制
- UDP:无连接;不保证可靠/顺序;开销小、时延潜力更低
场景:
- TCP:HTTP/1.1、HTTP/2、数据库、文件传输
- UDP:DNS、音视频/游戏(更在乎实时性)、QUIC/HTTP/3(在 UDP 上实现可靠传输)
7)跨域:跨域恶意攻击的解决方案吗?
跨域本身是浏览器同源策略与 CORS 的问题;“跨站攻击”通常指 CSRF/XSS/点击劫持。要先分清:同源策略主要限制“读”,并不严格限制“发请求”。
-
CSRF:利用浏览器自动携带 Cookie 让请求产生副作用
防御:SameSite+ CSRF Token + 校验Origin/Referer+ 关键操作二次确认
要点:CORS 不是 CSRF 的防御手段(CSRF 不需要读响应)。 -
XSS:把脚本注入到你的站点上下文执行
防御:正确转义/过滤、避免滥用v-html、CSP、CookieHttpOnly(降低被读取风险)。 -
Clickjacking:iframe 套壳诱导点击
防御:X-Frame-Options或 CSPframe-ancestors。 -
CORS 自身的安全坑:错误的 Origin 回显、放开凭证等会把“读权限”送出去。做法是严格白名单匹配 Origin,能同域就同域。
8)数字签名
数字签名提供身份认证与防篡改(并支持责任归属),不是保密。通常是“先 hash,再签 hash,再验签”。
流程(抽象):
h = Hash(message)sig = Sign(privateKey, h)Verify(publicKey, Hash(message), sig)
9)CA 证书
CA 证书体系(PKI)解决“我如何相信对方公钥属于这个域名”。浏览器通过证书链校验,把信任根落在系统/浏览器内置的 Root CA 信任库上。
验什么:
- 域名匹配、有效期、用途(EKU)、链路是否可信
- 吊销信息(OCSP/CRL,实际常见 OCSP Stapling)
- 现实补充:HSTS、证书透明度(CT)
10)Vue 的 template 不是 HTML:Vue 做了什么?
浏览器不认识 v-if/v-for/@click 这类语法,Vue 会把 template 编译成 render 函数;render 执行得到 VNode;再把 VNode mount/patch 到真实 DOM。
典型链路:
- parse:template → AST
- transform:处理指令、静态提升、优化标记
- codegen:AST → render
- runtime:render → VNode → mount/patch → DOM
补充追问点:构建时编译(SFC)更常见;运行时编译需要带 compiler,体积更大。
11)Vue 虚拟 DOM 渲染成真实 DOM 的细节
首次渲染走 mount;更新时重新执行 render 得到新 VNode,然后 diff/patch,把差异转换成尽量少的 DOM 操作;调度层通常会做批量更新。
关键追问:
key:保证列表 diff 的稳定身份;缺失时容易状态错位、输入框值错乱- 为什么要 VNode:用 JS 结构描述 UI,便于 diff、跨平台与编译优化配合
12)AST 是什么东西?
AST 是抽象语法树,把代码/模板解析成语义结构。前端工程里 Babel/ESLint/Prettier/模板编译基本都基于 AST 做分析与变换。
典型流程:tokenize(词法)→ parse(语法)→ traverse/transform → codegen(可选)。
13)为什么 JavaScript 是单线程语言?
在浏览器里,JavaScript 之所以设计成“单线程执行”,核心是为了避免多线程同时操作 DOM/页面状态带来的竞态与一致性问题,同时也让编程模型更简单。
但“单线程”指的是 JS 执行栈/同一份上下文里的代码执行是串行的,并不代表“只能做一件事”。
可以分三层说明:
- 语言/引擎层:同一个 JS 上下文里,代码按调用栈顺序执行,是串行的(这就是我们说的单线程执行)。
- 宿主环境层:计时器、网络请求、DOM 事件、I/O 这些并不靠 JS 自己“并行执行”,而是由浏览器/Node 的底层能力异步完成,完成后把回调排进队列。
- 调度层:事件循环(event loop)决定“什么时候把队列里的回调拿出来执行”。因此你看到的效果是:执行仍是单线程,但任务来源可以是并发的。
常见追问:
- “既然单线程,怎么利用多核?”:浏览器可以用
Web Worker(与主线程隔离,不能直接操作 DOM);Node 里有worker_threads,以及底层线程池去处理部分 I/O/加密/压缩等任务。 - “那为什么不让 JS 多线程直接操作 DOM?”:需要大量锁/同步机制,复杂且容易出错,浏览器也更难保证渲染一致性与性能。
14)async/await 为什么像同步?事件循环怎么解释?
async 一定返回 Promise;await 会把后续逻辑切成 Promise 的 then 链(微任务)。语法让代码看起来顺序执行,但调度仍遵循事件循环:当前调用栈结束后先清空微任务,再进入下一个宏任务。
补充追问点:
await x等价于await Promise.resolve(x)try/catch捕获的是 await 的拒绝(rejection)
15)Vue 里写 setTimeout 一定要在同页清除吗?为什么要手动清?
不讲“同页”,讲生命周期:定时器的生命周期应与创建它的组件/逻辑作用域一致。组件卸载后如果定时器还在,会造成内存泄漏风险与“卸载后仍在执行”的副作用。
建议写法(Composition API):
1 | import { onBeforeUnmount } from "vue"; |
补充:setInterval 更必须清;异步请求类副作用可配合 AbortController 或在卸载时做状态保护,避免“卸载后更新状态”。
16)你用过哪些 CSS 工具?Tailwind 的黑白切换主题怎么弄?Less/SCSS 用过吗?
我一般不会上来就报菜名,而是先把问题翻译成工程目标:写起来顺、改起来稳、线上别出幺蛾子。
我会这样分层回答:
- 写法层:原生 CSS、Less/SCSS(变量、mixin、函数、拆文件)、Tailwind(utility-first,约束强、落地快)
- 组织层:BEM / CSS Modules / 组件作用域(避免样式污染)
- 工程层:PostCSS(autoprefixer)、stylelint、按需构建/裁剪、设计 token(最好能跨端复用)
Tailwind 的暗黑模式常用两种:
media:跟随系统class:用户手动切换(更常见,也更可控)
我更常用 class:根节点加/删 dark,组件里写 dark:*。
1 | <html class="dark"> |
如果面试官继续问“切换会闪一下怎么办”,我会补一句:首屏就要把主题类加对(最小 inline script 读 localStorage),或者走 CSS 变量 + token,Tailwind 管布局,颜色走变量,切换更丝滑。
17)为什么用 Vue3,不用 React?Vue2 和 Vue3 响应式差异是什么?性能为什么更好?
如果我选 Vue3,常见理由是:
- 团队已有 Vue 栈,迁移成本更低,交付更稳
- Composition API 更适合复杂业务逻辑的组织与复用
- SFC + 生态(路由、状态、工程)贴合项目形态
Vue2 vs Vue3 响应式差异:
- Vue2:
Object.defineProperty,对新增/删除属性、数组等边界多一些,需要约束写法 - Vue3:
Proxy,能拦截更多操作(新增、删除、迭代等),边界更少
性能为什么更好(说到点上就行):
- Vue3 很多收益来自 编译期优化(静态提升、patch flags、block tree),让运行时少 diff
- 运行时配合更细粒度更新与更好的 tree-shaking,整体更轻
最后一句我会这么收:React 也能很快,Vue3 也不是自动快。框架给的是“工具”,关键看你有没有用对它的优化抓手。
18)讲讲 pnpm:为什么用它?为什么别人 npm i 会报错?扁平化和软链接了解吗?lock 的版本符号懂吗?
我用 pnpm 的理由很直白:快、省、并且更“严格”。
- pnpm 有全局 store:同版本依赖只存一份
- 项目里通过链接引用:磁盘占用更小,装得更快
- 依赖边界更清晰:减少“没声明也能用”的隐式依赖
别人 npm i 报错,我不会先猜包有毒,我会先按顺序排查:
- Node 版本/包管理器是否一致(
.nvmrc/engines/ corepack) - lockfile 是否匹配(用 pnpm 的项目就别用 npm 装)
- peerDependencies 冲突(不同工具处理策略不同)
- OS/CPU 差异 + 二进制依赖 + postinstall + registry/网络
扁平化(hoist)你可以理解成“把依赖尽量提到顶层”,路径短但容易让你“偷吃”到没声明的包;pnpm 默认更严格,通过链接按依赖关系组织。
SemVer 符号我会这么解释:
1.2.3:固定版本^1.2.3:允许升级 minor/patch(不跨 major)~1.2.3:只允许升级 patch(不跨 minor)
19)怎么在一个页面上弹 20~30 个弹窗?
-
提示类:做 toast/notification 聚合(可折叠、可筛选、可一键已读),别用 30 个 modal 把人逼疯
-
确认类:做队列(一次只显示一个),或者“一个弹窗 + 列表/步骤”,把 30 次确认收敛到一个容器里
-
全局
ModalManager维护队列/栈 -
Vue 用
Teleport渲染到body,统一z-index、滚动锁定、ESC、focus trap -
内容量大就做虚拟列表,别把 DOM 撑爆
20)你提到会 MySQL:索引一般怎么加?为什么会快?有没有代价?
索引就是目录,它把“全表翻书”变成“按目录定位”。
怎么加(举例足够):
CREATE INDEX idx_x ON t(x);CREATE INDEX idx_abc ON t(a, b, c);(组合索引注意最左前缀)
为什么快:
- 少扫行:能更快定位范围
- 少排序/少回表:覆盖索引、按索引顺序返回能省很多成本
代价也要说清楚:
- 索引占空间
- 写入变慢(索引也要维护)
工程习惯:加完用 EXPLAIN 看执行计划,确认真的走索引、以及是否出现“走了但效果一般”的情况。
21)怎么在页面上显示组件?会几种方式?了解 h 函数吗?远程传组件源码/低代码思路了解吗?
以 Vue 为例,我会按“从常用到更底层”回答:
- template:最常用,协作成本最低
- 动态组件:
<component :is="CompOrName" />,适合配置化页面 - 渲染函数:
h(Component, props, slots)(或 JSX),适合强动态/组件库抽象 - 程序化挂载:
createApp(Component).mount(el),常见于全局弹窗/插件
远程组件这块我会先强调风险:不要把不可信代码直接在用户浏览器里执行。更常见的工程做法是加载“构建产物/模块”(动态 import()、CDN、Module Federation)并配套版本兼容与降级。
低代码我会用一句话概括:传 schema,不传任意 JS。核心是 schema + 组件注册表 + 可控的表达式/事件绑定(白名单/沙箱/权限)+ 版本与回滚。
22)MCP 是什么?你怎么跟面试官讲清楚它的价值?
这题别背定义,背了也没用。你就用一句“人话”开场:
MCP 可以理解成:给模型接外部工具的一套标准接口(能发现、能调用、能鉴权、能留痕)。
为什么它有价值?因为你一旦做过 Agent 或者 AI Coding,就会发现真正麻烦的不是“让模型说话”,而是“让模型做事”:
- 今天要接 Jira,明天要接 Git,后天还要接内部知识库……如果每次都手写一套胶水代码,最后会变成一坨很难维护的“连接器地狱”。
- 有了标准化的对接方式,工具接入更像“插上去就能用”,至少不会每次都从 0 开始造轮子。
面试里我一般会把它归成三句话:
- 省接入成本:统一工具描述与调用方式,少写很多重复适配。
- 好治理:权限、审计、限流、失败回退可以做成平台能力,不用每个工具各搞一套。
- 好扩展:新增工具是“加配置/加一个 server”,而不是重构整套系统。
最后我会主动补一句“刹车”:标准化 ≠ 放权。真正上线要最小权限、危险操作二次确认、以及完整的调用日志,不然就不是工程,是事故预备役。
23)Skills 是什么?你会怎么设计一个“好用的 Skill”?
Skills 你可以把它当成“团队 SOP 的可执行版本”。不是一句提示词,也不是“让模型更聪明”的咒语,而是把你们做事的套路写清楚,让它每次都按同一套路径走。
一个好用的 skill,我会盯住这四件事:
- 它吃什么:输入需要哪些信息?缺了怎么办?能不能自己补齐?(别让它瞎猜)
- 它怎么走:步骤要明确——先查哪里、再比对什么、失败怎么降级、什么时候停手。
- 它吐什么:输出要可验收——结构化、字段齐全、能被下游继续用,而不是一段“看起来很努力”的散文。
- 它不能做什么:禁区必须写(比如不能改线上配置、不能删库、不能把密钥写进代码)。
如果你想更“工程一点”,再加一条就够了:回归样例。给 5~10 条输入输出,后面你改 skill、加策略、换模型,都能回归跑一遍,避免越改越飘。
(顺便说个很实用的例子:你们图标库如果有 manifest,skill 就不用每次把一堆 svg 内容塞进上下文去比对,直接查目录就行——省 token 也省命。)
24)你怎么做 AI Coding?从需求到 PR 的流程是什么?
我做 AI Coding 的心法很简单:把 AI 当成效率高的实习生用,而不是当成“无敌架构师”供起来。
从需求到 PR,我会按这个顺序走:
- 先让它读规矩:仓库约定(
AGENTS.md)、代码风格、目录结构、已有组件怎么用。别一上来就让它写,不然它会“自创一套宇宙”。 - 改动拆小:一次只解决一个点,让 diff 可 review。你越贪,一次改一大片,越难收场。
- 先让它讲方案再写代码:我会要求它明确“改哪几个文件、为什么这样改、有哪些替代方案”。方案对了再动手,返工少一半。
- 跑验证:最少跑相关测试/构建;没有测试就用回归样例(手工也行,但要可复现)。
- PR 写清楚:改了什么、为什么改、怎么验证、有哪些风险点。
面试官很爱追问“你怎么防止它乱写”,我一般就说两招(真能救命):
- 给参考与优先级:比如“先找图标库/组件库,找不到才允许临时实现”,不然它会把 CSS 画图当默认答案。
- 让结果可验收:要么有测试,要么有回归样例,要么有日志/可观测链路。没有验收标准,AI 写得再漂亮也是玄学。
25)你做过哪些 AI 实践?怎么证明它真的有效,而不是“看起来很厉害”?
这题别吹“我用 AI 提效很多”,面试官听多了。你就用两个字镇场:证据。
我一般会把实践分成四类讲:
- AI Coding:从需求/设计稿到代码,但必须遵守仓库规范(组件/图标库优先复用)
- RAG 知识库:回答必须带引用、能追溯、能复盘
- Agent 工具调用:能做事,但有权限、审计、二次确认与降级
- Skills 工程化:把“查找/生成/校验”固化成稳定工作流
“怎么证明有效”,我会给出一套很朴素但很难反驳的说法:
- 我有回归集(哪怕 20 条也行),每次改动前后都跑一遍
- 我至少盯 2~3 个指标:正确率/引用正确率、P95 延迟、单次成本、节省的人力时间
- 我把风险写进方案里:提示词注入、防越权工具调用、敏感信息脱敏、失败降级
如果面试官愿意听细一点,我就讲一个具体改造:在 monorepo 里给图标库加 manifest,再用 AGENTS.md 写死“先复用再新造”的优先级,最后用 skills 固化“怎么找图标”。
效果不是一句“感觉更好”,而是:生成代码里重复造图标的比例下降、review 更快、上下游沟通成本也少了——这才叫实践。
26)解释下 RAG 的概念
你别一上来背全称,面试官更在意你是不是真懂它在解决什么问题。你就这么说:
RAG 的本质是:先检索资料,再让模型“拿着资料作答”,把“我凭空回答”变成“我有证据地回答”。
一套典型链路(讲到这一步基本就够用了):
- 用户提问 →(可选)改写 query
- 检索:向量检索/关键词检索/混合检索拿到候选文档
-(可选)重排:把更相关的放前面 - 组装上下文:把资料片段塞进 prompt
- 生成答案:要求带引用/可追溯
追问来了你怎么接?我一般会抓 3 个最容易翻车的点讲:
- 切分(chunking):切太大塞不进上下文,切太小又丢信息;要结合内容结构(标题/段落/代码块)切
- 召回与噪声:topK 不是越大越好,噪声上来模型就开始乱编;必要时要做重排或过滤
- 新鲜度与一致性:资料更新怎么同步?答案如何保证“引用能支撑结论”?(否则引用就是装饰品)
如果你想更“工程一点”,可以补一句:RAG 不是“上了向量库就完事”,上线后必须有回归集(20~100 条),盯引用正确率、命中率、P95 延迟和成本。
27)skills、tools、MCP 分别是什么?有没有开发/接入经验?
这题我会用一套很接地气的分法来讲,别把自己绕晕:
- tools:一把一把的小螺丝刀——能被模型调用的“原子动作”(比如查数据库、搜代码、拉 Jira、发请求)。重点是:参数明确、返回结构化、可鉴权、可审计。
- skills:把螺丝刀组装成“能干活的流程”——把步骤、边界、回退、输出格式写清楚,让模型每次都按同一套路走。
- MCP:把工具“接进来”的接口规范——让工具的发现/调用/权限/资源访问有统一方式,减少每个工具都手搓胶水代码。
面试官问“有没有接入经验”,你可以用“我做过什么改造”来回答,比说概念强:
- 我会先用
AGENTS.md把优先级写死(比如先找组件库/图标库,找不到才允许临时实现),避免模型自由发挥。 - 我会把“怎么找图标/怎么选图标”写成 skill(带关键词策略、风格过滤、找不到怎么兜底),让它稳定复用。
- 我会在图标库固化
manifest(id/别名/关键词/风格/路径),让检索走目录而不是肉眼比对 svg,省 token 也更稳。
最后补一句“面试官爱听的刹车”:工具接得越多越要收权限——最小权限、危险动作二次确认、全链路日志,不然“能调用工具”就会变成“能制造事故”。