携程前端秋招一面面经(45 分钟)
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 自我介绍。
- 讲一下数组去重有哪些方法?
- map 和 reduce 具体是怎么实现数组去重的?
- 常见的排序算法有哪些?复杂度分别是多少?适用场景有哪些?
- 堆排序有什么适用场景吗?可以讲一下吗?
- 在浏览器当中输入一个 URL 到最终的页面展示,网络层面经历了哪些过程?
- 怎么用 Flex 布局实现两侧固定、中间自适应的三栏布局?
- 你对弹性布局的理解是什么?你觉得弹性布局当中最重要的概念是什么?
- 主轴和交叉轴的对齐方式有哪些?
- 如果一个列容器下面有四个行,这四行有四个 item,这四个 item 都要居中对齐,如果四项都要居中怎么实现?如果第一项左对齐、剩下的三项居中,这个又怎么实现?
- 讲一下什么是闭包,举一个你实际项目当中用过闭包的例子。
- 讲一下事件冒泡和事件委托有什么关系。
- 平时用 AI 写代码对吧?假如 AI 给你的反馈不符合预期,你是怎么去解决这个问题的?
- 如果是你写的提示词描述不清楚,那要怎么去优化这个提示词?
- 你的实习项目提到了 Puck,能讲一下这个 Puck 主要是做什么的吗?用户是谁?
- 你能讲一下从用户拖拽到最终生成 HTML 页面,这里边的原理是什么吗?
- 这个 Puck 通过拖拽之后生成的这份数据主要保存了哪些信息?
- 为什么选择 zustand?
- 旅行内容社区和审核管理平台这部分涉及到移动端,你觉得 React Native 和 React 有什么区别?
- 项目开发的过程中有遇到过什么性能问题吗?
- 做一个数据量很大的长列表优化,你觉得可以用什么方案?
- 虚拟列表的原理是什么?
- 虚拟列表实现的技术手段有哪些?
- scroll 触发的频率会很高,怎么优化?
- 除监听滚动事件之外,你还能想到其他的技术手段吗?
反问
- 部门业务是什么?
- 对我有什么建议?
- 面试流程是怎样的?
《参考解析》
数组去重:从能用到工程化。[...new Set(arr)] 最常用,靠 SameValueZero 判等:对基本类型有效,对象只按引用去重,1 与 '1' 视为不同,NaN 会正确保留一个。filter 加 indexOf 是 O(n²) 的基础写法且对 NaN 失效;用对象或 Set 做哈希表一次遍历是 O(n),业务里真正需要的是按字段去重对象数组(比如按 id)。reduce 的写法是把哈希表塞进累加器:acc.seen.has(cur) ? acc : (acc.seen.add(cur), acc.list.push(cur), acc);map 的用法是先用它把元素映射成键,再借 Map 的键唯一性去重,如 new Map(arr.map(i => [i.id, i])) 最后取 values()。答题时讲清「什么类型、按什么键、是否保持原顺序、复杂度多少」,比背三种写法更得分。
排序算法与堆排序的适用场景。冒泡、插入、选择是 O(n²),其中插入排序在近乎有序的数据上接近 O(n);归并、快排、堆排平均 O(n log n)。归并稳定、最坏也是 O(n log n),但要 O(n) 额外空间;快排平均最快、原地,但最坏退化为 O(n²) 且不稳定(随机化或三数取中可规避);堆排原地、最坏情况可预测,但常数大、跳跃访问对缓存不友好。JS 的 Array.sort 在 V8 里对长数组走 TimSort(稳定,归并与插入的混合)。堆排序真正的价值在三处:只要最大的前 K 个而不全排序(维护大小为 K 的小顶堆,O(n log K),海量数据 Top K 的标准解法)、数据流持续取极值(优先队列、定时器)、以及内存受限且要求最坏情况可预测。被追问「为什么不用快排」时,落到稳定性、额外空间、最坏情况与是否流式这几个指标上。
输入 URL 到页面展示的网络过程。按步讲:URL 解析与本地缓存查找(强缓存命中直接返回);DNS 解析(浏览器缓存、hosts、递归查询,命中 CDN 返回就近节点 IP);TCP 三次握手,HTTPS 再做 TLS 握手协商密钥;经过网关、负载均衡或 CDN;发送 HTTP 请求(带 Cookie 与 If-None-Match、If-Modified-Since 这类协商头);服务端返回响应(协商命中返回不带体的 304);之后进入解析渲染,构建 DOM 与 CSSOM、合成渲染树、布局、绘制、栅格化上屏,期间并发加载 JS、CSS、图片与字体。优化点顺着这条链给:预解析与预连接、HTTP/2 多路复用与连接复用、压缩、CDN 就近、内容哈希加长缓存、关键 CSS 内联、JS 用 defer/async 不阻塞解析。
Flex 三栏布局与两轴对齐。两侧固定、中间自适应最简洁的写法是中间项 flex: 1(即 1 1 0%),两侧 flex: 0 0 200px;flex-basis: 0 的意义是按剩余空间分配而不是按内容起步,否则内容过长会挤压两侧。必须给中间项加 min-width: 0——Flex 项默认 min-width: auto,长文本或滚动容器会撑破布局,这是三栏最常见的坑。justify-content 管主轴(flex-start、center、space-between 等),align-items 管单行交叉轴(默认 stretch 会拉伸子项),align-self 覆盖单个子项,align-content 管换行后的行间分布;flex-direction: column 时两轴互换。四个行、每行四个 item 都居中:外层 display:flex; flex-direction:column; justify-content:center; align-items:center,每行再 justify-content:center;第一项左对齐、其余居中,用该项 margin-right: auto 把它推到左边,其余保持居中。弹性布局最重要的概念是「剩余空间的分配」,也就是 grow、shrink、basis 与最小尺寸的交互。
闭包与事件委托。闭包是函数与其定义时词法作用域的组合,内层函数引用外层变量,即使外层已返回这些变量仍存活在堆上。项目里的典型用途是防抖与节流(闭包保存定时器 id 与上次执行时间)、useDebounce 这类 Hook、事件处理保存上下文、以及模块模式隐藏实现细节;代价是变量不被回收,长期持有大对象或 DOM 引用会泄漏,所以要在卸载时清理定时器与监听。事件冒泡是事件从目标元素向上传到祖先,stopPropagation 可打断,preventDefault 只阻止默认行为不停冒泡;事件委托正是利用冒泡把监听统一挂在父容器,通过 event.target(配 closest 更健壮)判断来源,好处是监听器更少、动态新增的子元素自动生效。注意 focus、blur、mouseenter 不冒泡,不能直接委托。
长列表、虚拟列表与滚动优化。三条路:分页或懒加载累计、内容可见性渲染、虚拟列表。虚拟列表的核心是容器高度按总条数撑开、只渲染可视区加缓冲区的项,用 transform: translateY 或绝对定位偏移到正确位置,并维护「滚动位置 → 索引」映射(定高直接除,不定高要预测量或用 ResizeObserver 回填缓存)。技术手段包括缓冲区上下多渲染几条减少白屏、用 requestAnimationFrame 对齐渲染时机、只在索引区间变化时更新状态以避免高频重渲染、用 passive: true 让滚动不被监听器阻塞、把监听挂在容器而不是 window。除监听 scroll 外还有:IntersectionObserver 观察哨兵元素触发计算或加载、CSS content-visibility: auto 让浏览器跳过屏外渲染、contain: layout paint 限制重排范围;React 下也可直接用 react-window、react-virtuoso,把「自己实现要处理哪些边界」讲清比只说库名更有说服力。
Puck 拖拽、zustand 与 React Native。Puck 是开源的「可视化编辑器」框架,让非技术用户拖拽组件搭页面,用户是运营与内容编辑,价值是页面搭建不依赖前端发版。从拖拽到出 HTML 的原理是数据驱动:注册组件时给出「渲染组件加字段 schema」,拖入画布往数据模型插入节点(type、props、唯一 id),画布按数据递归渲染、编辑只改 JSON 不改 DOM;发布时把这份 JSON 落库并保留版本以便回滚,渲染端再用同一份数据渲染出真正的 HTML——保存的是组件树与 props,而不是 HTML 字符串。选 zustand 的理由是轻量、无 Provider 嵌套、按选择器订阅能精确控制重渲染、样板代码少,也容易在 React 之外读写状态;对比 Redux 的取舍是生态与心智负担。React Native 与 React 的区别:RN 渲染到原生视图、没有 DOM 与 CSS,样式是 JS 对象加 Flexbox 子集(默认 column),组件是 View、Text、Image 这类封装,导航、手势、权限、存储都走原生模块,所以跨端可复用的主要是业务逻辑与状态层。被问 AI 反馈不符合预期时,先分诊是理解偏差还是能力边界,再补上下文、给正反例、拆小任务,并要求它先给方案再动手、把报错原文回灌且一次只修一处。