简历配套面试题 — v1.5

基于 v1.5.mdx 简历中的具体技术栈与项目经历整理。 答案默认折叠,点击展开。建议面试前自测,能用自己的话说出来再展开核对。


一、Vue 深度

Q1: Vue 2 和 Vue 3 响应式实现的核心差异是什么?为什么 Vue 3 选择重写?

📖 参考答案 / 解析

Vue 2 使用 Object.defineProperty 劫持对象属性的 getter/setter:

  • 必须递归遍历对象所有属性进行劫持(初始化慢)
  • 无法监听新增 / 删除属性(需要 Vue.set / Vue.delete
  • 无法监听数组下标修改 / length 变化(要重写 7 个数组方法)

Vue 3 改用 Proxy

  • 拦截整个对象的访问,懒代理(访问到才递归),性能更好
  • 天然支持新增 / 删除属性、数组下标
  • 支持 Map / Set / WeakMap 等集合类型

为什么重写Object.defineProperty 的局限是语言层面的,无法绕过;而 Proxy 是 ES6 新特性,Vue 3 不再兼容 IE 11,可以放心用。

注意点Proxy 是浅层的,访问深层属性时才会再次代理 → Vue 3 通过这个实现了懒代理。

Q2: Composition API 解决了 Options API 哪些问题?什么时候不该用?

📖 参考答案 / 解析

Options API 的痛点

  1. 逻辑碎片化:一个功能的代码分散在 data / computed / methods / watch
  2. 复用差:Mixin 有命名冲突、来源不清的问题
  3. TypeScript 友好度差this 类型推导复杂

Composition API 的优势

  1. 功能组织代码(useXxx 一个 hook 集中相关逻辑)
  2. 复用通过普通函数实现,类型推导自然
  3. 更适合大型项目

什么时候不该用

  • 简单组件(< 50 行)用 Options API 反而更直观
  • 团队没 TS 基础时,Composition API 的优势不明显
  • 老项目大面积迁移成本高,不必为了用而用

陷阱ref vs reactive 的选择、toRefs 解构问题、watch vs watchEffect

Q3: 你在简历里提到「定位并解决 Vue 插件引起的重复渲染问题」,具体怎么排查的?

📖 参考答案 / 解析

排查路径

  1. 复现:Vue DevTools 打开"Highlight Updates",肉眼观察哪些组件在不该重渲染时高亮
  2. 定位触发源
    • 是 props 变化?用 watch 监听 props 看是不是引用变了
    • 是 state 变化?看响应式数据是不是被插件意外触发
    • 是父组件 re-render?用 React.memo 类比手法定位
  3. 常见原因
    • 插件在 setup 中创建了响应式对象,每次渲染都重新创建 → 引用不稳定
    • 插件提供的 provide 值是新对象引用 → 子组件 inject 后认为变了
    • 全局状态 mutate 而不是 replace
  4. 解决
    • 把对象提到 setup 外或用 shallowRef / markRaw
    • computed 缓存
    • 改用 PiniastoreToRefs 而不是解构

总结:核心是"引用稳定性"和"响应式追踪范围",DevTools 是最快的工具。

Q4: Pinia 比 Vuex 好在哪?项目里你是怎么组织 store 的?

📖 参考答案 / 解析

Pinia 优势

  1. 去除 mutation:直接改 state,更符合 Composition API 直觉
  2. TS 友好:自动推导 state / getters / actions 类型,零类型注解
  3. 模块化天然:每个 store 是独立函数,不需要 modules
  4. 轻量:~1KB
  5. DevTools 集成:time-travel、热重载

组织方式(个人实践):

stores/
  user.ts        // 用户身份、权限
  app.ts         // 全局配置、主题
  modules/
    chat.ts      // 实时聊天状态
    game.ts      // 游戏运行时状态

最佳实践

  • 小 store 优于大 store(单一职责)
  • Action 中 await 调用其他 store 是允许的
  • 跨 store 引用用 useOtherStore() 而不是直接 import 实例(避免循环依赖)
  • 持久化用 pinia-plugin-persistedstate

Q5: Vue 3 有哪些性能优化点?你用过哪些?

📖 参考答案 / 解析

框架层面

  • Patch Flag:编译时标记动态部分,运行时只 diff 动态节点
  • Block Tree:跳过静态子树
  • 静态提升 (hoistStatic):静态节点提到 render 外
  • 预字符串化:连续静态节点编译为字符串

应用层面(项目实践):

  • v-memo 缓存大列表项
  • shallowRef / shallowReactive 处理大对象
  • markRaw 标记不需要响应式的对象(如 Phaser 实例、Cesium 实例)
  • 组件懒加载 defineAsyncComponent
  • KeepAlive 缓存不常切换的页面
  • 长列表用虚拟滚动(vue-virtual-scroller

调试工具:Vue DevTools Performance 面板 + Chrome Performance。


二、React 生态

Q6: React Hooks 闭包陷阱怎么理解?有哪些典型场景?

📖 参考答案 / 解析

闭包陷阱本质:每次 render 都创建新的 props/state 快照,函数体内的变量绑定到当时那次 render 的值;如果在 setTimeout / setInterval / useEffect 中引用,可能用到过期数据。

典型场景

const [count, setCount] = useState(0)
useEffect(() => {
  const id = setInterval(() => {
    console.log(count) // 永远是 0
  }, 1000)
  return () => clearInterval(id)
}, []) // 空依赖 → count 被锁定在初始值

解决方案

  1. 加依赖useEffect(..., [count]) —— 但会反复创建定时器
  2. 函数式更新setCount(c => c + 1)
  3. useRef 持有最新值
    const countRef = useRef(count)
    useEffect(() => {
      countRef.current = count
    })
    // 在 setInterval 中读 countRef.current
  4. 自定义 useEvent / useEventCallback hook 包装

Q7: React Query 解决了什么问题?和 Redux / Zustand 是什么关系?

📖 参考答案 / 解析

React Query 定位服务端状态(Server State)管理库,不是通用状态管理。

它解决的问题

  • 接口请求的 loading / error / data 状态样板代码
  • 缓存(同一接口短时间内不重复请求)
  • 自动重试、轮询
  • 失效(mutation 后让相关 query 重新拉取)
  • 后台刷新(refetchOnWindowFocus)
  • 分页 / 无限滚动

与 Redux / Zustand 关系

  • Redux / Zustand 管的是客户端状态(UI 状态、表单、用户偏好)
  • React Query 管的是服务端状态(拉来的数据)
  • 两者互补不冲突,大型项目通常同时存在

项目实践(针对简历过敏性疾病随访项目):

  • 后台列表 / 详情用 Vue Query 自动管理缓存与失效
  • 全局用户身份用 Zustand
  • 即时通讯状态用 Zustand + 本地持久化

Q8: Zustand 的设计哲学是什么?为什么比 Redux 简洁那么多?

📖 参考答案 / 解析

核心哲学

  1. No Provider:不需要 <Provider> 包裹,全局单例
  2. Hook-first:通过 useStore 直接订阅
  3. 极简 API:一个 create 函数搞定 state + actions
  4. 选择性订阅:组件只订阅自己关心的 slice,避免无关 re-render

核心代码示意

const useBearStore = create((set) => ({
  bears: 0,
  increase: () => set((state) => ({ bears: state.bears + 1 })),
}))

// 组件中
const bears = useBearStore((s) => s.bears)

对比 Redux

  • Redux:action / reducer / dispatch / selector 四件套,模板代码多
  • Zustand:直接 set 更新,函数式或对象式都行

适用场景:中小型应用 / 局部状态(聊天室、游戏状态)。大型有复杂中间件需求(撤销重做、time-travel debug)的,Redux Toolkit 仍有优势。


三、TypeScript

Q9: 你在 uni-app 项目中怎么引入 TypeScript?踩过什么坑?

📖 参考答案 / 解析

接入步骤

  1. 升级 uni-app 到支持 TS 的版本(CLI 项目用 vite 模板)
  2. 添加 tsconfig.json,关键配置:
    {
      "compilerOptions": {
        "target": "ESNext",
        "module": "ESNext",
        "moduleResolution": "node",
        "strict": true,
        "jsx": "preserve",
        "types": ["@dcloudio/types", "@types/wechat-miniprogram"]
      }
    }
  3. 安装类型声明:@dcloudio/types(uni 全局 API)、@types/wechat-miniprogram(wx 全局 API)
  4. vue 文件添加 <script setup lang="ts">

踩过的坑

  • 多端条件编译类型不一致#ifdef MP-WEIXIN 在 H5 端类型缺失 → 用 // @ts-ignore 或自定义条件类型
  • 小程序组件 props 类型:自定义组件 defineProps 需要手动声明
  • uni.xxx API 在新版本类型不全:自己补充 .d.ts
  • Pinia + uni 持久化:小程序没 localStorage,需自定义 storage adapter,类型需对齐

收益:类型安全 + IDE 自动补全,大型项目几乎必备。

Q10: 写一个 TS 工具类型 DeepReadonly<T>,把对象所有嵌套属性变为只读。

📖 参考答案 / 解析
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? T[K] extends Function
      ? T[K]
      : DeepReadonly<T[K]>
    : T[K]
}

关键点

  • 用映射类型 [K in keyof T] 遍历
  • readonly 修饰
  • 用条件类型判断是否为 object,递归
  • 排除 Function(函数也是 object,但不需要递归)

进阶:还要排除 Date / RegExp / Map / Set

type Primitive = string | number | boolean | bigint | symbol | null | undefined
type Builtin =
  | Primitive
  | Function
  | Date
  | Error
  | RegExp
  | Map<any, any>
  | Set<any>

type DeepReadonly<T> = T extends Builtin
  ? T
  : { readonly [K in keyof T]: DeepReadonly<T[K]> }

实战用途:配置对象、状态快照防篡改。


四、工程化 / Monorepo

Q11: 简历提到「20+ 游戏包 / 5min → 2min」,构建优化具体怎么做的?

📖 参考答案 / 解析

优化路径(按收益排序):

  1. Monorepo 工具链选型:从 Lerna 改为 pnpm workspace + 增量构建工具(Turborepo / Nx)
    • pnpm 用硬链接,安装速度 + 磁盘占用都改善
  2. 构建缓存
    • Turborepo / Nx 的远端缓存:未改动的包直接命中缓存,跳过构建
    • Vite 的依赖预构建 + 缓存
  3. 并行构建
    • Turborepo 自动按依赖图并行
    • CI 中按 affected packages 只跑增量
  4. 抽取公共依赖
    • Vue3 / Pinia / 公共 UI 库放到 packages/shared
    • 避免每个游戏包重复打包同一份 vendor
  5. Source Map 策略
    • 开发用 cheap-module-source-map,生产用 hidden-source-map
  6. Tree-shaking
    • ESM-only 依赖、sideEffects: false
    • 按需引入 Vuetify / Element-Plus

衡量:用 vite-plugin-inspect / webpack-bundle-analyzer 看产物分布。

Q12: Babel 插件开发流程是怎样的?你做过哪些插件?

📖 参考答案 / 解析

流程

  1. AST 入门:用 AST Explorer 看代码对应的 AST 树
  2. Babel 插件结构:
    module.exports = function ({ types: t }) {
      return {
        name: 'my-plugin',
        visitor: {
          ImportDeclaration(path, state) {
            // 操作 path.node
          },
        },
      }
    }
  3. 常用 API:path.replaceWith / path.remove / t.callExpression...
  4. 测试:用 babel-plugin-tester

实战插件类型

  • 按需加载:Element-Plus / Vuetify 等组件库自动导入对应 css
  • 国际化:把硬编码字符串提取到 i18n 资源文件
  • 自定义指令转换:把 v-permission 编译为函数调用
  • 生产环境去 log:删除 console.log
  • autoImport:分析模板,自动 import 用到的组件

踩坑

  • Babel 7 vs 6 不兼容
  • path.traverse 容易死循环(要用 path.skip()state.processed = true
  • 遇到 import('xxx') 动态导入要特殊处理

Q13: Vite 比 Webpack 快在哪?有哪些场景 Vite 不适合?

📖 参考答案 / 解析

Vite 快的原因

  1. 开发模式用 ESM:浏览器原生支持 ESM,不需要打包,按需编译
  2. 依赖预构建用 esbuild:把 CommonJS 转 ESM、合并依赖,esbuild 是 Go 写的,比 babel/webpack 快 10-100 倍
  3. HMR 精准:模块图清晰,改一个文件只重编译这个文件

Webpack 快的地方

  • 生态成熟、配置灵活
  • 老项目兼容性好
  • 复杂 SSR / 多入口场景配置更完善

Vite 不适合的场景

  • 需要兼容老浏览器(IE 11 等):Vite 默认输出现代 ES,需要 @vitejs/plugin-legacy
  • 特别复杂的构建逻辑:Vite 的 Rollup 插件生态比 Webpack 弱
  • SSR 高度定制:Vite SSR 模板还在演进
  • 微前端 qiankun 等:Vite 5 + qiankun 仍有兼容问题,需要 vite-plugin-qiankun

实际选择

  • 新项目 → Vite
  • 老项目 + 复杂构建 → 保持 Webpack
  • 工具库 → Rollup(更纯净)

Q14: ESLint + Prettier + Husky + lint-staged + Commitlint 完整工具链怎么搭?

📖 参考答案 / 解析

分工

  • ESLint:代码质量(未使用变量、潜在 bug)
  • Prettier:代码格式(缩进、引号、分号)
  • Husky:Git Hooks 触发器
  • lint-staged:只检查 staged 文件,避免全量扫描
  • Commitlint:提交信息规范(Conventional Commits)

搭建步骤

# 1. 安装
pnpm add -D eslint prettier eslint-config-prettier eslint-plugin-prettier \
  husky lint-staged @commitlint/cli @commitlint/config-conventional
// package.json
{
  "scripts": {
    "prepare": "husky install"
  },
  "lint-staged": {
    "*.{ts,tsx,vue}": ["eslint --fix", "prettier --write"],
    "*.{json,md}": ["prettier --write"]
  }
}
# 2. 初始化 husky
npx husky install
npx husky add .husky/pre-commit "npx lint-staged"
npx husky add .husky/commit-msg "npx --no -- commitlint --edit ${1}"
// commitlint.config.cjs
module.exports = { extends: ['@commitlint/config-conventional'] }

冲突处理

  • ESLint 与 Prettier 规则冲突 → eslint-config-prettier 关闭冲突规则
  • 性能:大项目 lint 慢 → 用 --cache 选项

五、跨端开发

Q15: uni-app 和 Taro 怎么选?各自的局限是什么?

📖 参考答案 / 解析

uni-app

  • 基于 Vue
  • 编译目标多:H5 / 小程序(微信、支付宝、抖音、QQ、百度、京东等)/ App(基于 weex 或 Vue+原生混合)
  • 国内生态强,文档全,社区活跃
  • DCloud HBuilderX 体验好
  • 局限:App 端基于 weex 有性能瓶颈,复杂动画不流畅;TypeScript 支持后期才完善

Taro 3+

  • 基于 React(也支持 Vue 3)
  • 运行时方案:把 React 组件树转成小程序的视图层
  • TypeScript 支持原生友好
  • React 生态可以直接复用(react-query、antd-mobile 等)
  • 局限:性能略低于 uni-app(运行时方案有开销);国内社区不如 uni-app 火

选择标准

  • 团队 Vue 栈 + 主攻小程序 → uni-app
  • 团队 React 栈 / TS 重度依赖 → Taro
  • 需要原生体验的复杂 App → 都不太合适,建议 RN 或 Flutter

项目实战(针对简历):

  • 医科大学过敏性项目用 Taro + React(团队 React 栈)
  • 云上托育用 uni-app + Vue3(团队 Vue 栈)
  • 商米 ERP 用 uni-app + Vue3(商米设备兼容性好)

Q16: Electron 主进程 / 渲染进程怎么通信?安全风险有哪些?

📖 参考答案 / 解析

通信方式

  1. IPC(推荐)

    • 渲染 → 主:ipcRenderer.send / ipcRenderer.invoke
    • 主 → 渲染:ipcMain.on / webContents.send
    • invoke 配合 handle 返回 Promise,比 send 更现代
  2. contextBridge(安全做法):

    // preload.js
    contextBridge.exposeInMainWorld('electronAPI', {
      readFile: (path) => ipcRenderer.invoke('read-file', path),
    })
  3. remote 模块(已废弃,不要用)

安全风险

  • nodeIntegration: true + 加载远程 URL → XSS 直接拿到 Node 权限,可执行任意命令
  • contextIsolation: false → preload 与 web 共享上下文,攻击者可篡改 API
  • 未验证 IPC 参数 → 渲染进程构造恶意路径读取系统文件

最佳实践

  • nodeIntegration: false
  • contextIsolation: true
  • contextBridge 暴露白名单 API
  • IPC 参数严格校验
  • 加载的 URL 校验来源

实战(简历的称重系统):

  • 主进程持有 SerialPort 实例,处理底层串口通信
  • 渲染进程通过 invoke 请求称重数据
  • preload 桥接 electronAPI.weigh()

六、可视化 / GIS

Q17: 简历提到「100W+ 格点数据秒级渲染」,技术方案是怎样的?

📖 参考答案 / 解析

总体思路:CPU 计算 → WebWorker,渲染 → Canvas 分块 + 离屏 + 抽帧

具体方案

  1. 数据传输优化
    • 服务端用二进制(Float32Array / Protobuf)而非 JSON
    • pako gzip 解压前端做(节省带宽 70%+)
    • jszip 处理打包文件
  2. 计算分离
    • 主线程:DOM + 用户交互
    • WebWorker:插值、网格化、色斑映射计算
    • transferable 对象转移所有权,避免拷贝
  3. 渲染优化
    • Canvas 分块(tile):把大画布切成 256×256 的小块
    • 离屏 Canvas (OffscreenCanvas) 预渲染
    • requestAnimationFrame 调度
    • 只重绘视口可见区域
  4. 视觉欺骗
    • 大数据先渲染降采样版(10W 点)让用户看到东西
    • 后台加载完整精度,无感切换
  5. 抗锯齿与色彩
    • chroma-js 做颜色插值
    • 双线性 / 三次样条插值算法

性能指标(要能说清):

  • 100W 点首次渲染 < 3s
  • 平移 / 缩放保持 30+ FPS
  • 内存占用 < 200MB

陷阱

  • WebWorker 与主线程通信序列化有开销,频繁通信反而慢
  • Canvas 太大(> 16384×16384)部分浏览器崩溃

Q18: WebWorker 使用边界?什么时候不该用?

📖 参考答案 / 解析

适合用 WebWorker

  • CPU 密集型计算:加密、压缩、图像处理、地理网格化
  • 大数据处理:> 1W 条数据的解析、排序、过滤
  • 算法密集:路径规划、物理模拟、机器学习推理

不适合 / 用了反而慢的场景

  • DOM 操作:Worker 中没有 document / window
  • 小数据频繁通信:序列化 / 反序列化开销可能比直接计算还大
  • 简单同步任务:< 50ms 的任务不值得 Worker

通信成本

  • 默认是结构化克隆(深拷贝),大对象拷贝慢
  • Transferable 对象(ArrayBuffer / MessagePort / ImageBitmap)转移所有权,零拷贝

实战陷阱

  • Worker 文件路径在 Vite / Webpack 中需要特殊处理(new Worker(new URL('./worker.ts', import.meta.url))
  • 错误处理用 worker.onerror,错误信息可能被脱敏
  • 调试用 Chrome DevTools 的 Sources → Workers 面板

Q19: Cesium 三维地形渲染原理?大数据场景怎么优化?

📖 参考答案 / 解析

核心原理

  1. 瓦片金字塔:地球被切成多个 LOD(细节层级),近处用高精度瓦片,远处用低精度
  2. 四叉树调度:相机移动时,根据距离与角度计算需要加载哪些瓦片
  3. 请求合并:瓦片以 HTTP 请求异步加载,浏览器并发 6 个连接
  4. WebGL 渲染:地形 + 影像 + 矢量 + 模型层叠渲染

简历项目(航空气象)的优化点

  • GeoServer 提供 WMTS 高程瓦片:避免每次请求计算
  • 缓存 + 预加载:常用区域提前预热瓦片缓存
  • 降采样三维云图:远景用低分辨率纹理
  • 裁剪策略:屏幕外瓦片不渲染(视锥裁剪由 Cesium 内置)
  • 请求节流:相机快速移动时只请求最终位置的瓦片

踩坑

  • Cesium 包体积大(5MB+),需要按需引入或 CDN
  • 内存泄漏:自定义 Primitive 要手动 destroy()
  • iOS / 低端机性能堪忧

Q20: 风场流线动画的实现思路?

📖 参考答案 / 解析

算法核心

  1. 数据准备:风场数据是格点上的 U / V 分量(U=东西分量,V=南北分量),组成向量场
  2. 粒子初始化:在画布上随机散布 N 个粒子(典型 N = 5000~10000)
  3. 每帧更新
    • 对每个粒子,根据位置在风场中插值(双线性)出当前位置的风速 (u, v)
    • 粒子位置加上 (u * dt, v * dt)
    • 粒子寿命 -1,到 0 重生
  4. 渲染
    • 不要 clear 整个 canvas,而是用半透明矩形 globalAlpha=0.95 覆盖
    • 这样粒子轨迹有"拖尾"效果
    • 用粒子速度大小映射颜色(chroma-js)

性能优化

  • WebWorker 计算粒子位置,主线程只画
  • Canvas 用 GPU 加速:willReadFrequently: false
  • 粒子数量自适应:根据 FPS 动态调整

进阶:可以用 WebGL 着色器在 GPU 上跑,性能 10 倍以上提升(参考 Mapbox 的 windgl 实现)。


七、游戏开发

Q21: Phaser.js 游戏循环(Game Loop)原理?怎么和 Vue 3 集成?

📖 参考答案 / 解析

游戏循环原理

  • Phaser 内部用 requestAnimationFrame 维护一个主循环
  • 每帧调用 Scene.update(time, delta)
    • time:累计时间
    • delta:与上一帧的时间差(ms)
  • 物理引擎 / 动画 / 输入都基于 delta 计算,保证不同帧率下行为一致

与 Vue3 集成方式

// PhaserGame.vue
<template><div ref="container"></div></template>

<script setup lang="ts">
  import Phaser from 'phaser'
  import { ref, onMounted, onBeforeUnmount, markRaw } from 'vue'

  const container = ref<HTMLDivElement>()
  let game: Phaser.Game

  onMounted(() => {
    game = markRaw(
      new Phaser.Game({
        parent: container.value,
        type: Phaser.AUTO,
        scene: [BootScene, MainScene],
      }),
    )
  })

  onBeforeUnmount(() => game?.destroy(true))
</script>

关键点

  • markRaw:Phaser 内部状态非常多,不要被 Vue 响应式劫持,否则性能崩溃
  • Vue 与 Phaser 通信:通过 EventBus 或 Phaser 的 EventEmitter
  • HMR:开发时 destroy + recreate,否则会有多个实例

性能陷阱

  • 大量 sprite 用 Container 批量管理
  • 文本对象慢 → 用 BitmapText 代替
  • 帧率掉 → 用 Phaser DevTools 看哪个 system 占时间

Q22: 精灵图(Sprite Sheet)合并工具的实现思路?

📖 参考答案 / 解析

核心步骤(针对简历提到的自研工具):

  1. 扫描资源:读取目录下所有 PNG
  2. 打包算法:用 MaxRects / Skyline / Guillotine 算法把多个小图塞进大图
  3. 生成图集:把所有小图绘制到一张大 Canvas
  4. 生成元数据:JSON 描述每个 sprite 在大图中的 (x, y, w, h)
  5. 输出:PNG + JSON(Phaser / Pixi 都能直接消费)

优化点

  • 旋转:把竖图旋 90° 节省空间
  • Padding:小图之间留 1-2px,避免渲染时采样到邻居
  • 多张图集:超过 2048×2048 拆分(兼容老 GPU)
  • TinyPNG / pngquant 压缩

业务价值

  • 减少 HTTP 请求数(10 张图 → 1 张)
  • GPU 纹理切换次数减少(同图集的 sprite 不切换 batch)
  • 节省内存(合并后总体积小)

八、硬件集成

Q23: SerialPort 串口通信的关键流程?有哪些坑?

📖 参考答案 / 解析

流程

import { SerialPort } from 'serialport'
import { ReadlineParser } from '@serialport/parser-readline'

const port = new SerialPort({
  path: 'COM3',
  baudRate: 9600,
  dataBits: 8,
  parity: 'none',
  stopBits: 1,
})

const parser = port.pipe(new ReadlineParser({ delimiter: '\r\n' }))
parser.on('data', (data) => {
  console.log('Recv:', data)
})

port.write('READ\n', (err) => {
  if (err) console.error(err)
})

关键点

  • 波特率必须和设备一致(9600 / 19200 / 115200 是常见值)
  • 数据帧用 Parser:原始流是字节流,要按协议分帧(换行符 / 字节长度 / 自定义分隔符)
  • 设备路径:Windows 是 COM3,Mac/Linux 是 /dev/tty.usbserial-xxx

  • 设备不存在 / 占用:要 catch Error: Opening COM3: Access denied
  • 断线重连:监听 close 事件,定时器重试 open
  • 字符编码:电子磅秤常用 GB2312,需要 iconv-lite 转码
  • 数据丢包:Buffer 满了不及时读取
  • 多设备识别:用 USB VID/PID 而不是 COM 号(COM 号会变)

进阶:Web Serial API 在 Chrome 89+ 支持,无需 Node 也能调串口(但兼容性差,只能用 Chromium 内核)。

Q24: Koffi(C 动态库调用)你怎么用的?相比 ffi-napi 优势在哪?

📖 参考答案 / 解析

Koffi 简介:Node.js 调用 C/C++ 动态库(.dll / .so / .dylib)的工具,是 ffi-napi 的现代替代。

基本用法

import koffi from 'koffi'

const lib = koffi.load('myhardware.dll')

// 声明函数原型
const readCard = lib.func('int ReadCard(char *buffer, int len)')

const buffer = Buffer.alloc(256)
const result = readCard(buffer, buffer.length)
console.log(buffer.toString('utf8'))

相比 ffi-napi 优势

  1. Node.js 版本兼容性好:ffi-napi 在 Node 18+ 经常编译失败,Koffi 用纯 JS + N-API,无需编译
  2. Electron 友好:electron-rebuild 噩梦没了
  3. 性能更好:Koffi 自带内联汇编优化
  4. API 更现代:类型声明用 C 语言字符串,更直观
  5. 维护活跃:ffi-napi 已基本不更新

简历项目(屠宰场 ERP)应用

  • 调用厂商提供的 IC 卡读写 SDK(C dll)
  • 调用身份证识别 SDK
  • 二维码扫码模块 SDK

踩坑

  • 32 位 vs 64 位 dll 要匹配 Node 架构
  • 字符串编码(很多国产 dll 用 GBK)
  • 回调函数:Koffi 支持 callback 注册,但生命周期要小心

九、实时通信

Q25: WebSocket 心跳保活与断线重连怎么设计?

📖 参考答案 / 解析

心跳保活

class WSClient {
  private ws: WebSocket
  private heartbeatTimer: any
  private reconnectTimer: any
  private reconnectCount = 0

  connect() {
    this.ws = new WebSocket(this.url)
    this.ws.onopen = () => {
      this.reconnectCount = 0
      this.startHeartbeat()
    }
    this.ws.onmessage = (e) => this.handleMessage(e)
    this.ws.onclose = () => this.reconnect()
    this.ws.onerror = () => this.ws.close()
  }

  private startHeartbeat() {
    this.heartbeatTimer = setInterval(() => {
      if (this.ws.readyState === WebSocket.OPEN) {
        this.ws.send(JSON.stringify({ type: 'ping' }))
      }
    }, 30_000) // 30s
  }

  private reconnect() {
    clearInterval(this.heartbeatTimer)
    if (this.reconnectCount >= 5) return // 放弃
    const delay = Math.min(1000 * 2 ** this.reconnectCount, 30_000) // 指数退避
    this.reconnectTimer = setTimeout(() => {
      this.reconnectCount++
      this.connect()
    }, delay)
  }
}

关键设计点

  1. 心跳间隔:30s 是常见值,太短浪费带宽,太长可能被中间代理踢
  2. 指数退避1s → 2s → 4s → 8s → 16s,避免雪崩
  3. 重连上限:5-10 次后放弃,避免无限重试
  4. 应用层 ACK:服务端收到 ping 要回 pong,超时未收到说明链路坏了
  5. 状态机:connecting / connected / disconnected / reconnecting,避免重复连接
  6. 消息队列:断开期间的消息缓存,重连后批量发送

实战陷阱

  • 网络切换(WiFi → 4G)时 onclose 可能不触发,需要 setTimeout 主动检测
  • 浏览器后台标签页定时器被限流,心跳可能漏发
  • 移动端进入后台时主动关闭连接,避免被系统杀

Q26: MQTT 和 WebSocket 区别?什么时候用 MQTT?

📖 参考答案 / 解析

协议层面

  • WebSocket:基于 TCP,是一个通用的全双工通道,没有规定上层语义
  • MQTT:基于 TCP(或 WebSocket),是一个发布/订阅协议,专为 IoT 设计

核心差异

特性WebSocketMQTT
通信模式点对点发布订阅(Topic)
消息保证无内置QoS 0/1/2 三档
离线消息持久 Session
数据格式无规定二进制头部,紧凑
适用场景Web 实时交互IoT、传感器、物联网

MQTT 适合用的场景(针对简历的云上托育项目):

  • 多对多通信:温度传感器发布到 dorm/temperature,所有教师端订阅
  • 离线消息:传感器掉线后再上线,收到积压消息
  • 海量设备:MQTT broker 能扛百万级连接
  • 低带宽:协议头部小(最小 2 字节)

浏览器端:用 mqtt.js,底层走 WebSocket(因为浏览器不能开 raw TCP)

踩坑

  • Topic 设计:用 / 分层,支持通配符 +#
  • QoS 1 / 2 是端到端的,broker 不保证业务层处理成功
  • 公网 broker 要加 TLS(mqtts://)

Q27: Agora 实时音视频集成关键点?延迟怎么优化?

📖 参考答案 / 解析

集成关键点

  1. Token 鉴权:服务端用 App Certificate 签发临时 Token,前端不能硬编码 App ID 的明文
  2. 频道(Channel):所有进入同一频道的用户互通
  3. 角色:主播(host) vs 观众(audience),权限不同
  4. 轨道(Track)
    • LocalAudioTrack / LocalVideoTrack:本地采集
    • RemoteAudioTrack / RemoteVideoTrack:订阅远端

典型流程

const client = AgoraRTC.createClient({ mode: 'rtc', codec: 'vp8' })
await client.join(appId, channel, token, uid)
const localAudio = await AgoraRTC.createMicrophoneAudioTrack()
const localVideo = await AgoraRTC.createCameraVideoTrack()
await client.publish([localAudio, localVideo])

client.on('user-published', async (user, mediaType) => {
  await client.subscribe(user, mediaType)
  if (mediaType === 'video') user.videoTrack.play(`remote-${user.uid}`)
})

延迟优化

  • codec 选择:H.264 兼容性好,VP8/VP9 压缩率高,H.265 最优但兼容性差
  • 分辨率自适应:弱网降到 480p
  • 带宽探测:Agora SDK 内置,自动调整
  • 接入点就近:Agora 全球节点,主播 / 观众都连最近的边缘节点
  • 抗丢包:FEC 前向纠错 + ARQ 重传,移动端必开

针对简历游戏直播场景

  • 主播端用游戏画面采集(createScreenVideoTrack)+ 麦克风
  • 观众端只订阅,不发布
  • 互动用 RTM(实时消息)补充,做礼物 / 弹幕

十、浏览器 / 性能

Q28: 浏览器渲染流程?哪些 CSS 操作会触发 Reflow?

📖 参考答案 / 解析

渲染流程

HTML → DOM Tree
CSS → CSSOM Tree

Render Tree (合并)

Layout (布局/回流 Reflow)

Paint (绘制/重绘 Repaint)

Composite (合成图层)

触发 Reflow 的操作

  • 修改 width / height / padding / margin / border
  • 修改 position / top / left
  • 修改 font-size / font-weight
  • 读取 offsetWidth / scrollTop / getComputedStyle() 等(强制同步布局)
  • 增删 DOM 节点
  • 修改 class 影响布局

只触发 Repaint(不 Reflow)

  • 修改 color / background-color / visibility

只触发 Composite(不 Repaint)

  • transform / opacity(前提是元素已提升为合成层)

优化建议

  1. 批量修改document.createDocumentFragment()display: none 后修改
  2. 避免强制同步布局:读写分离(先读 → 一次性写)
  3. 用 transform 替代 left/top:动画专用
  4. will-change 提示:提前提升合成层(注意不要滥用)
  5. CSS Containmentcontain: layout/paint/strict

面试常考

  • 强制同步布局是什么?读 offsetWidth 时浏览器必须先完成 pending 的布局
  • 合成层提升的代价是什么?内存(每层都是独立位图)

Q29: JS 事件循环?宏任务 / 微任务有哪些?

📖 参考答案 / 解析

事件循环模型

┌──────────────────────────┐
│   执行同步代码(Call Stack) │
└──────────────────────────┘

┌──────────────────────────┐
│ 执行所有微任务(清空队列) │
└──────────────────────────┘

┌──────────────────────────┐
│   渲染(如果需要)         │
└──────────────────────────┘

┌──────────────────────────┐
│   执行一个宏任务           │
└──────────────────────────┘
            ↓ 循环

宏任务(每次循环执行一个):

  • setTimeout / setInterval
  • setImmediate(Node 独有)
  • I/O(文件、网络)
  • UI 事件(click、scroll)
  • MessageChannel

微任务(一次循环清空全部):

  • Promise.then / catch / finally
  • queueMicrotask
  • MutationObserver
  • process.nextTick(Node,优先级比 Promise 还高)

经典面试题

console.log('1')
setTimeout(() => console.log('2'))
Promise.resolve().then(() => console.log('3'))
console.log('4')
// 输出:1 4 3 2

进阶

  • Node.js 事件循环分 6 个阶段(timer / pending / idle / poll / check / close),与浏览器不完全一样
  • await 等价于 Promise.then,后续代码进微任务队列
  • 浏览器渲染时机:每次微任务清空后,但具体时机取决于浏览器(不一定每帧都渲)

Q30: 内存泄漏常见场景与排查工具?

📖 参考答案 / 解析

常见场景

  1. 未清理的定时器

    setInterval(() => {
      /* ... */
    }, 1000)
    // 组件卸载时未 clearInterval
  2. 未解绑的事件监听

    window.addEventListener('resize', handler)
    // 组件卸载时未 removeEventListener
  3. 闭包持有大对象

    function createHandler() {
      const bigData = new Array(1e6)
      return () => console.log(bigData.length) // bigData 永不释放
    }
  4. DOM 引用

    const cache = {}
    cache.el = document.getElementById('foo')
    document.body.removeChild(cache.el) // cache.el 仍持有 DOM
  5. WebSocket / WebWorker 没关

  6. Vue / React 组件中 ref 第三方实例(Phaser / Cesium)未 destroy

  7. 全局变量越来越大(缓存策略不当)

排查工具

  1. Chrome DevTools Memory 面板

    • Heap Snapshot:拍快照,对比两次操作前后的对象数量
    • Allocation timeline:录制内存分配过程,找出热点
    • Allocation sampling:低开销采样,长期监控
  2. Performance 面板:JS Heap 曲线,看是否单调递增(典型泄漏)

  3. Memory tab 中的 Retainers:看对象被谁引用了,找到泄漏源头

实战流程

  1. 复现操作(如反复打开关闭弹窗)
  2. 拍 baseline Heap Snapshot
  3. 操作 10 次
  4. 再拍一次 Heap Snapshot,Comparison 模式
  5. 找 Delta 异常增长的对象(如 Detached HTMLDivElement)
  6. 看 Retainers 链路,找到根因

Q31: 首屏性能优化你做过哪些?怎么衡量?

📖 参考答案 / 解析

关键指标(Core Web Vitals)

  • LCP(Largest Contentful Paint):最大内容绘制,反映首屏体感,目标 < 2.5s
  • FID / INP(Interaction to Next Paint):首次输入延迟 / 交互响应,目标 < 200ms
  • CLS(Cumulative Layout Shift):累计布局偏移,目标 < 0.1
  • FCP(First Contentful Paint):首次内容绘制
  • TTFB(Time to First Byte):服务器响应时间

优化手段(按收益排序):

  1. 资源加载
    • CDN 加速
    • HTTP/2 多路复用
    • 关键资源 preload,非关键 prefetch
    • 字体子集化(fonttools)
    • 图片:WebP / AVIF + 响应式 srcset
  2. 代码分割
    • 路由级懒加载
    • 第三方库按需引入
    • 抽离 vendor chunk
  3. 服务端
    • SSR / SSG
    • 边缘渲染(Vercel Edge / Cloudflare Workers)
    • 接口聚合,减少串行请求
  4. 运行时
    • 骨架屏代替白屏
    • 渐进式加载(先低质量图,再替换高清)
    • 关键 CSS 内联
    • defer / async 控制脚本执行时机
  5. 缓存
    • HTTP 强缓存(不变资源)+ 协商缓存(HTML)
    • Service Worker 离线缓存

衡量工具

  • Lighthouse(命令行 + Chrome 面板)
  • WebPageTest(多地区测试)
  • PerformanceObserver API(线上 RUM 上报)
  • Chrome DevTools Performance Insights

实战案例(简历的星光华人通项目):

  • 公众号网页首屏 LCP 优化:骨架屏 + 图片懒加载 + 接口缓存
  • 部署到 CDN
  • 关键 CSS 内联

十一、AI 编码助手

Q32: Claude Code / Copilot / Cursor 你都怎么用?哪些场景不该用?

📖 参考答案 / 解析

实际使用场景(按效果排序):

  1. 模板代码生成:CRUD 表单、表格、列表页 → 节省 60%+ 时间
  2. 类型声明:从 JSON 生成 TypeScript interface
  3. 测试用例:从函数签名生成 Vitest 用例
  4. 重构辅助:把 Options API 转 Composition API、提取 hook
  5. 文档生成:README、JSDoc、变更日志
  6. 复杂正则:自然语言描述 → 正则表达式
  7. 代码 Review:让 AI 先看一遍,找明显问题
  8. 算法实现:经典算法直接生成
  9. 配置文件:Webpack、Vite、ESLint 配置

不该用 / 慎用的场景

  1. 业务核心逻辑:AI 不懂上下文,容易写出"看起来对"的代码
  2. 安全敏感:鉴权、加密、SQL 拼接 → AI 可能引入漏洞
  3. 性能关键路径:AI 倾向于"通用解",性能不一定最优
  4. 数据库迁移 / 删除操作:风险太大,必须人工 review
  5. 不熟悉的领域:AI 可能编造(hallucination)API / 库
  6. 公司机密代码:要选择不上传训练的工具(Claude Code 是较好选择)

Claude Code 独特价值(vs Copilot / Cursor):

  • 多文件 agent 能力强,能跨文件理解项目
  • 长上下文,可以读完整文档
  • 工具调用(执行 bash、读写文件)— 适合自动化任务

心得:AI 是"加速器"不是"代驾"。让它写,然后逐行 review


十二、项目深挖

Q33: Spinman 平台 20+ 游戏怎么管理?新游戏怎么接入?

📖 参考答案 / 解析

架构示意

spinman-monorepo/
├── packages/
│   ├── shared/        # 共享 UI 组件、工具函数、类型
│   ├── core/          # 游戏引擎封装(Phaser / Pixi)
│   ├── auth/          # 鉴权 SDK
│   ├── socket/        # WebSocket / Agora 封装
│   └── live/          # 直播 UI 模块
├── games/
│   ├── game-slot/     # 游戏 1
│   ├── game-dice/     # 游戏 2
│   ├── game-poker/    # 游戏 3
│   └── ... (20+)
├── shell/             # 主应用壳子(路由、布局、鉴权入口)
└── tools/
    └── create-game/   # 新游戏脚手架

新游戏接入流程

  1. pnpm create-game game-newxxx → 脚手架生成模板
  2. 自动注入到 shell 的路由表(动态扫描 games/*
  3. 共享 UI / 鉴权 / 通信,开发者只关注游戏本身逻辑
  4. CI 自动按依赖图增量构建(Turborepo)
  5. 发布:每个游戏独立构建产物 + CDN 部署

核心收益

  • 新游戏从 0 到能跑:~30 分钟
  • 公共 bug 修一处所有游戏受益
  • 多游戏可同时迭代不冲突

踩坑

  • 共享库版本:用 workspace:* 内部依赖
  • Vite 多入口构建:要配置 optimizeDeps.entries
  • 类型共享:tsconfig 用 path 映射,编辑器支持好

Q34: 灵创你带 3 人团队,怎么排期 / 协作 / Review?

📖 参考答案 / 解析

排期方式

  1. 需求评估会:产品讲需求 → 技术分解为 task → 估时(1d / 3d / 5d 三档)
  2. 进 Jira / 飞书项目,每周冲刺 5d
  3. 每日站会 15min:昨天做了什么、今天做什么、有什么 blocker

协作机制

  • Git Flow:main / develop / feature/* / hotfix/*
  • 强制 PR Review:至少 1 人 approve 才能合并
  • PR 描述模板:变更原因、影响范围、测试方式、截图
  • Conventional Commits + 自动生成 CHANGELOG

Code Review 重点

  1. 命名是否清晰
  2. 是否有重复代码(参考已有 utils)
  3. 类型是否完整
  4. 边界 case(空数组、null、网络错误)
  5. 性能(循环里调函数、大对象 watch)
  6. 安全(XSS、SQL 注入、敏感信息硬编码)

培养机制

  • 每周技术分享(30min),轮流主讲
  • 推荐高质量文章 / 视频,建知识库
  • 复杂任务结对编程
  • 季度做技术总结,输出文档沉淀

实际产出(针对简历):

  • 工程化规范从 0 到 1(ESLint + CI/CD + 自动打包)
  • 线上 Bug 率降低(事故复盘 + 单测要求)
  • 团队能独立 take 跨行业项目

Q35: 100W+ 格点数据"秒级展示",秒级具体多少秒?瓶颈在哪?

📖 参考答案 / 解析

真实指标(要能讲清):

  • 数据传输:< 1s(gzip 压缩后 ~3MB)
  • WebWorker 计算插值 / 着色:~0.5s
  • Canvas 渲染:~0.3s
  • 总计:< 3s 首屏可见

主要瓶颈(按优化顺序):

  1. 网络传输(瓶颈 1):
    • 原始 JSON 30MB+ → 用 Float32Array 二进制 + pako gzip → 3MB
    • HTTP/2 多路复用
  2. JS 计算(瓶颈 2):
    • 主线程做计算会卡死 → WebWorker 异步
    • 算法选择:双线性插值 vs 三次样条 → 双线性够用且快
  3. Canvas 绘制(瓶颈 3):
    • 单个 fillRect 100W 次太慢 → 用 ImageData 一次性写入
    • ctx.putImageData() 是 GPU 加速的
  4. DOM 阻塞(瓶颈 4):
    • 大批操作放在 requestIdleCallback / requestAnimationFrame
    • 避免长任务(> 50ms)

剩余优化方向

  • WebGL Shader 直接渲染(10 倍性能,但开发复杂度高)
  • WASM 计算(适合更复杂的算法)
  • WebGPU(未来方向,浏览器支持中)

坦诚说明:3 秒不算极致,对气象决策场景已够用。如果是高频交互(如游戏),还需进一步优化到 < 1s。

Q36: 三家托育机构 50% 复用率怎么测算?怎么实现的?

📖 参考答案 / 解析

复用率测算(粗略口径):

  • 总代码量:约 30K 行(园长端 + 教师端 + 家长端)
  • 共享代码:约 15K 行(业务逻辑、UI 组件、工具函数)
  • 复用率 = 15K / 30K = 50%

实现方式

miniapp-monorepo/
├── packages/
│   ├── shared/
│   │   ├── components/   # 通用 UI(按钮、表单、列表)
│   │   ├── hooks/        # 通用 composables
│   │   ├── utils/        # 工具函数
│   │   ├── api/          # 接口封装
│   │   └── types/        # 类型定义
│   ├── biz-shared/
│   │   ├── auth/         # 鉴权(不同角色共用)
│   │   ├── upload/       # 文件上传
│   │   └── canvas-draw/  # 跨平台 Canvas 绘制(课程表、海报)
├── apps/
│   ├── principal/        # 园长端
│   ├── teacher/          # 教师端
│   └── parent/           # 家长端

关键决策

  • 角色相关逻辑放在各 app(不强行共享)
  • 跨角色一致的业务抽到 biz-shared(如鉴权、上传)
  • 纯 UI / 工具全放 shared
  • pnpm workspace 软链接,改一处所有端实时生效

带来的收益

  • 修 bug 一处生效
  • 三端 UI 风格统一
  • 新增功能(如海报分享)一次开发三端可用

踩坑

  • 小程序对 node_modules 引用方式不一样,要配 subPackages
  • 不同角色权限差异:用 provide / inject 注入角色信息,组件按角色裁剪显示

十三、软实力 / 团队管理

Q37: 你怎么做技术选型?举一个具体决策的例子。

📖 参考答案 / 解析

我的方法论

  1. 明确约束:业务场景、团队栈、时间预算、未来扩展
  2. 列候选:通常 2-4 个,避免只有一个"信仰选择"
  3. 多维评估
    • 学习曲线
    • 社区活跃度(npm 周下载、GitHub issue 响应)
    • 生态完整度
    • 性能 benchmark
    • 文档质量
    • 长期维护(避免选个人项目)
    • 公司投入(背靠大厂的更稳)
  4. POC 验证:用 1-2 天写小 demo,验证核心难点
  5. 决策记录:写 ADR(Architecture Decision Record),未来 review

具体例子:跨端框架选 uni-app vs Taro

约束

  • 团队 Vue 栈
  • 目标:微信 + 抖音 + 支付宝小程序 + H5
  • 时间:1.5 个月上线

评估

  • uni-app:Vue 生态、文档全、HBuilderX 友好
  • Taro:React 栈(团队不熟)、TS 友好但生态略弱

决策:选 uni-app,理由:

  • 团队上手快
  • 多端支持成熟
  • DCloud 商业支持靠谱

结果:1.5 个月按时上线,多端体验一致。

反思:如果是 5 年规划的项目 + React 团队,Taro + TS 可能更适合。

Q38: 跨部门 / 客户协作中遇到的最大挑战是什么?

📖 参考答案 / 解析

典型场景(针对简历"作为技术代表对接合作伙伴和客户"):

最大挑战:客户对技术不懂,但有非常具体的"业务直觉",两者经常冲突。

举例

  • 客户:"这个页面再快一点" → 实际首屏已经 1.5s
  • 客户:"数据要实时" → 实际场景 10s 延迟也可接受
  • 客户:"手机也要看" → 实际后台系统在 PC 端用得多

应对方式

  1. 翻译需求:把客户的"快"翻译为"FCP < 1s + 首屏 < 2s",给具体指标
  2. 数据说话:埋点统计实际场景,用数据反驳"直觉"(如:实时改 10s 轮询,看用户感知差异)
  3. AB 测试:争议时上线两版本对比
  4. 优先级管理:建 backlog,公开排期,避免临时插需求
  5. 教育客户:偶尔讲讲技术原理,提升对方理解

关键心态

  • 不要把客户当"对立面",他们的需求往往有真实业务痛点,只是表达不准
  • 技术人多问几个"为什么",往往能挖到本质需求
  • 不要用技术术语压人,用对方能理解的话沟通

反例(不该做的):直接说"这不可能" / "我们做不了" → 应该说"做这件事的成本是 X,如果调整成 Y 方案,成本能降到 Z"。

Q39: 你对自己未来 3-5 年的规划是什么?

📖 参考答案 / 解析

这题答案要"真诚"+"匹配岗位",给个框架:

短期(1 年)

  • 在新公司站稳,深度参与至少 1 个核心项目,沉淀有影响力的技术产出
  • 补齐当前的短板(如:大规模 C 端经验、性能监控体系、SSR 深度)
  • 持续输出博客 / npm 工具

中期(2-3 年)

  • 在某个细分方向(前端基建 / 可视化 / 跨端)成为团队公认的"专家"
  • 主导 1-2 个具有公司级影响力的技术项目(如基建升级、性能优化专项)
  • 参与开源社区,至少 1 个项目 1k+ star

长期(3-5 年)

  • 技术专家路线:前端架构师 / Principal Engineer,主导技术方向
  • 或:技术管理路线:带 10+ 人团队,负责业务线技术体系建设
  • 持续保持动手能力,不要变成"PPT 工程师"

核心目标:在 35 岁前,建立独特的技术影响力(开源 / 文章 / 演讲 / 公司内 case),而不是只有"工作年限"。

实诚版:现阶段最关注的是找一个能让我深度成长的平台。当前已经做过广度,下一步想专精深度。

避免的回答

  • "我想成为 CTO" → 太大,没法验证
  • "随便,看公司安排" → 缺乏主动性
  • "想做管理" / "想做技术" 二选一 → 过于绝对,应该是双向开放

学习建议

每天 1 题深挖

不要追求"题目数量",重点是每题都能用自己的话讲 5 分钟,能举例、能反驳、能扩展。

知识体系图

把所有题目按"技术栈 → 子领域 → 具体知识点"画成思维导图,找出自己的薄弱区

模拟面试

找朋友 / 同行模拟 1-2 次,重点训练:

  • 表达清晰度(不能磕巴)
  • 引导节奏(不能被追问到死胡同)
  • "我不知道" 的优雅回答

项目复盘准备

简历每个项目都准备 3 个深挖问题 + 1 个失败案例(HR 喜欢问挫折)。


祝面试顺利 🚀