创见博客
爱学习集团前端面试(2.26 14:00)
七崽爱吃小饼干2026/03/02阅读 0专栏 前端面经
codeType
1. 自我介绍
2. 为什么考虑做前端
3. 计算机视觉在前端的应用
4. 如果让你设计一个组件,你会从哪些方面考虑
5. 做过哪些性能优化
6. 此外还做过哪些业务开发
7. 如果让我去定位性能问题,我会怎么做。具体要定位到是哪个组件该怎么定位 
8. 讲一下原型链和原型对象
9. position有哪些定位类型
10. flex布局怎么实现一个骰子的三
11. 对node有了解吗
12. 讲一下箭头函数

1.为什么考虑做前端

前几年前端处于发展期,我认为在前端领域还有很多可以研究的地方。很多人认为前端就是写页面,但是其实前度也包括工程化、性能优化、跨端、架构设计等多方面,既有技术深度也有技术广度。

2.如果让你设计一个组件,你会从哪些方面考虑

1. 先明确核心:组件的“定位与边界”(基础层)

首先会先对齐组件的核心目标:

  • 明确组件的「单一职责」:比如设计一个“按钮组件”,就聚焦“点击交互、样式适配、状态反馈”,不混入表单校验、数据请求等无关逻辑(比如我做埋点Hooks时,就严格区分“上报逻辑”和“业务逻辑”,组件设计也遵循这个原则);
  • 界定使用场景:是通用组件(如Button/Input)还是业务组件(如订单卡片/埋点按钮)?通用组件要适配全业务,业务组件可贴合具体场景,但也要预留扩展空间;
  • 明确输入输出:组件需要接收哪些props(必传/可选)、暴露哪些事件(如onClick/onChange)、返回哪些方法(如reset/refresh),避免“黑盒化”。

2. 功能与逻辑设计(功能层)

这是组件的核心,重点考虑「健壮性+灵活性」:

  • Props设计:
    • 区分必传/可选props,给可选props设置合理默认值;
    • 用TypeScript做严格类型约束(比如我埋点Hooks里对上报参数做的类型定义),避免传参错误;
    • 避免props过多(超过8个),可将同类配置合并为对象(如style={{ size: 'large', type: 'primary' }});
  • 状态管理:
    • 区分「内部状态」和「外部状态」:比如下拉组件的“展开/收起”是内部状态,选中值是外部受控状态,避免状态混乱;
    • 复杂组件用useReducer/Context管理内部状态,不暴露不必要的内部逻辑;
  • 事件与交互:
    • 兼容原生事件(如按钮的onClick透传原生event),支持自定义事件;
    • 考虑异常场景:比如表单组件的校验失败反馈、列表组件的空数据/加载中/报错状态。

3. 工程化与可维护性(工程层)

这是体现你工程化思维的关键,也是大厂看重的点:

  • 可复用性:
    • 通用组件抽离到组件库,业务组件封装成自定义Hook+UI结构,比如我把埋点逻辑抽离成Hooks,组件只负责调用;
    • 避免硬编码(如颜色、尺寸写死),用主题变量/样式变量统一管理;
  • 可扩展性:
    • 预留插槽(Slot)/自定义渲染函数(renderProps):比如表格组件支持自定义列渲染,满足不同业务的个性化需求;
    • 支持自定义className/style,方便业务覆盖样式;
  • 可测试性:
    • 组件逻辑拆分到纯函数,便于单元测试(如我埋点Hooks里的上报逻辑单独抽离,可单独测试);
    • 覆盖核心场景的测试用例(如props传错、交互触发、异常状态);
  • 性能优化:
    • 避免不必要的重渲染:React中用React.memo/useMemo/useCallback缓存组件/计算结果/函数;
    • 长列表组件用虚拟列表(react-virtualized),避免DOM过多导致卡顿;
    • 懒加载非核心逻辑(如组件的高级功能按需引入)。

4. 体验与兼容性(体验层)

组件最终要落地到用户,这层决定“用得爽不爽”:

  • 视觉体验:
    • 遵循设计规范(如间距、颜色、字体统一),支持响应式(适配PC/移动端);
    • 交互反馈:按钮点击有动效、加载状态有loading、操作失败有提示;
  • 无障碍(a11y):
    • 支持键盘操作(如Tab切换、Enter触发按钮);
    • 加合适的aria标签(如aria-label="提交按钮"),适配屏幕阅读器;
  • 兼容性:
    • 兼容主流浏览器(Chrome/Firefox/Safari,按需兼容IE11);
    • 考虑边界场景:比如组件在极端尺寸(超小/超大屏幕)、网络离线时的表现;
  • 性能体验:
    • 首屏渲染快:避免组件初始化时执行大量计算;
    • 交互响应快:事件处理函数避免长任务(超过50ms),耗时逻辑放requestIdleCallback/Web Worker。

5. 文档与易用性(收尾层)

好组件离不开好文档,降低协作成本:

  • 写清晰的文档:包括props说明、使用示例、注意事项(比如我埋点Hooks库会标注“卸载场景不支持requestIdleCallback”);
  • 提供demo示例:覆盖基础用法、高级用法、异常场景,让使用者快速上手。

二、简化版

设计组件我会聚焦5个核心点:

  1. 先明确组件的职责和使用场景,保证单一职责;
  2. 设计灵活的props和清晰的状态管理,兼顾通用性和定制化;
  3. 做性能优化(缓存、懒加载、虚拟列表)和工程化(TS类型、单元测试);
  4. 考虑用户体验(交互反馈、响应式、无障碍)和兼容性;
  5. 最后写清晰的文档和示例,降低使用成本。

3. 你做过哪些方面的性能优化

  1. 易代账表单组件从受控表单改为非受控表单

  2. 文心一言侧边栏对话历史列表,改为分页获取+虚拟列表

  3. 文心一言的埋点逻辑进行抽离封装,形成了一个埋点工具库。

    1. 在代码层面将埋点逻辑抽离成hooks,提升了代码复用性并且遵循了逻辑与UI分离的原则。
    2. 在性能优化方面,通过批量上报+空闲上报(requestIdleCallback)的方式减少了上报请求的数量并且充分利用了浏览器空闲时间,大大提升了上报的性能。
    3. 在可靠性方面,通过用localStorage缓存失败埋点请求,并且遵循指数退避算法进行失败重试,确保了失败的埋点数据不会丢失。
  4. 在react项目中会用到memo、useMemo以及useCallback来减少子组件不必要的重渲染。

4. 如果让我去定位性能问题,我会怎么做。具体要定位到是哪个组件该怎么定位

步骤 1:用 Chrome DevTools 做「全局性能扫描」(先找大方向)

这是定位性能问题的核心工具,操作步骤:

  1. 打开页面 → F12 打开 DevTools → 切换到「Performance」面板;
  2. 点击「录制按钮(圆形红点)」→ 复现卡顿操作(如滚动、点击、切换组件)→ 点击「停止」;
  3. 查看生成的性能图谱,重点关注:
    • FPS 面板:绿色条越低(低于 60),卡顿越严重;红色块表示「长任务」(阻塞主线程);
    • Main 线程面板:横向的长条形任务(超过 50ms)就是卡顿元凶,鼠标悬停可看任务详情;
    • Summary 面板:看耗时占比(Rendering/JS Execution/Layout 占比高就是问题点)。

步骤2:React 项目用 React DevTools Profiler

  1. 安装 React DevTools 插件 → 打开页面 → 切换到「Profiler」面板;
  2. 点击「录制」→ 复现卡顿操作 → 停止录制;
  3. 查看「火焰图 / 排名图」:
    • 火焰图:纵向是组件层级,横向是渲染耗时,长条形的组件就是耗时大户;
    • 排名图:按渲染耗时排序,直接显示「哪个组件渲染时间最长」「渲染次数是否异常(比如重复渲染)」;
    • 点击耗时组件,可查看「渲染原因」(如 props 变化、state 变化、父组件重渲染)。

常见组件级卡顿问题及解决方案(落地优化)

卡顿原因定位特征解决方案
组件重复渲染(React/Vue)Profiler 中组件渲染次数远高于预期React:用 React.memo/useMemo/useCallback;Vue:用 computed/v-once
组件渲染时执行大量计算Main 线程中组件 render 函数耗时>50ms把计算逻辑抽离到 useMemo(缓存结果),或放到 Web Worker 中执行
列表渲染无分页/虚拟列表长列表(1000+条)导致首次渲染卡顿用虚拟列表(react-virtualized/vue-virtual-scroller),只渲染可视区域组件
组件事件处理函数耗时高(如点击/滚动)Performance 中事件回调耗时>100ms节流/防抖(如滚动事件加 100ms 节流),或异步执行耗时逻辑
组件频繁操作DOMRendering 面板中 Paint 占比极高减少DOM操作(用React/Vue的虚拟DOM),批量修改样式,避免频繁重排

实操示例(React 项目定位卡顿组件)

  1. 打开 React DevTools Profiler,录制页面滚动卡顿的操作;
  2. 看到「UserList」组件渲染耗时 300ms,且每秒渲染10次(正常应只渲染1次);
  3. 点击「UserList」组件,查看渲染原因:「props.onUserClick 每次都是新函数」;
  4. 验证:给 onUserClick 加 useCallback 缓存,重新录制,UserList 渲染耗时降到 20ms,卡顿消失;
  5. 结论:UserList 组件因 props 频繁变化导致重复渲染,是卡顿元凶。

总结

定位页面卡顿到具体组件的核心步骤:

  1. 用 Chrome Performance 确定卡顿类型(JS/渲染/GC);
  2. React/Vue 项目用专属调试工具(Profiler/Vue DevTools)直接定位耗时组件;
  3. 通用项目用「Rendering 面板标红」+「Memory 面板查GC」定位问题组件;
  4. 禁用/优化目标组件,验证卡顿是否缓解。

5.原型链与原型对象

先讲三个概念:

  • obj.__proto__ 实例的原型对象
  • Function.prototype 函数的原型对象 所有函数的 proto 都指向它
  • obj.__proto__.constructor 实例的构造函数 原型对象上的属性,比如 tom.constructor === Person

原型对象是构造函数的 prototype 属性指向的对象,是实例的 “模板”,所有实例共享原型上的方法;原型链是对象查找属性的链路 —— 先找自身,找不到就顺着 __proto__ 往原型对象上找,直到 Object.prototype。比如用构造函数创建实例后,实例能调用原型上的方法,就是因为原型链的查找机制;ES6 的 class 本质也是基于原型链的语法糖。

原型对象的核心作用:

  • 共享属性 / 方法:所有实例共用原型对象上的方法,节省内存;
  • 实现继承:实例能访问原型对象上的属性 / 方法,这是 JS 继承的基础。
codeType
tom(实例)
  ├── 自身属性:name('Tom')
  └── __proto__ → Person.prototype
        ├── constructor: Person(原型对象的属性)
        ├── sayHi: 函数(自定义)
        └── __proto__ → Object.prototype
              ├── toString: 函数
              └── constructor: Object

6.position常见的属性

1. static(默认值:静态定位)

  • 核心特性:
    • 遵循正常文档流(元素按HTML结构从上到下、从左到右排列);
    • 不脱离文档流,top/right/bottom/left/z-index 等定位属性完全无效;
  • 使用场景:默认所有元素的定位方式,无需主动设置;
  • 示例:
    css
    div {
      position: static; /* 写不写都一样,是默认值 */
      top: 10px; /* 无效,不会生效 */
    }
    

2. relative(相对定位)

  • 核心特性:
    • 不脱离文档流(元素原本的位置会被保留,不会让后续元素填充);
    • 定位参考系:自身在文档流中的原始位置;
    • top/left 等属性生效,元素会相对于原始位置偏移;
  • 使用场景:
    • 微调元素位置(比如按钮图标偏移1px);
    • 作为 absolute 定位的「参考容器」(给子元素做绝对定位的父容器);
  • 示例:
    css
    .box {
      position: relative;
      top: 10px; /* 相对于原始位置向下偏移10px */
      left: 20px; /* 相对于原始位置向右偏移20px */
    }
    

3. absolute(绝对定位)

  • 核心特性:
    • 完全脱离文档流(元素原本的位置会被后续元素填充);
    • 定位参考系:最近的已定位祖先元素(即祖先元素的 position 不是 static,优先 relative/fixed/absolute/sticky);若没有已定位祖先,参考「视口根元素(html)」;
    • top/left 等属性生效,宽高默认由内容撑开;
  • 使用场景:
    • 弹窗、下拉菜单、悬浮提示(脱离文档流,不影响其他元素);
    • 元素精准定位(比如购物车角标、表单验证提示);
  • 关键注意:一定要给父元素设置 position: relative,否则会相对于整个页面定位,容易出问题;
  • 示例:
    css
    .parent {
      position: relative; /* 作为子元素的参考容器 */
    }
    .child {
      position: absolute;
      top: 0;
      right: 0; /* 相对于父元素的右上角定位 */
    }
    

4. fixed(固定定位)

  • 核心特性:
    • 完全脱离文档流;
    • 定位参考系:浏览器视口(viewport),不会随页面滚动而变化;
    • top/left 等属性生效;
  • 使用场景:
    • 页面固定导航栏、回到顶部按钮、悬浮客服窗;
    • 弹窗遮罩层(覆盖整个视口);
  • 注意:若祖先元素有 transform/filter/perspective 属性,fixed 会失效,改为相对于该祖先定位;
  • 示例:
    css
    .back-to-top {
      position: fixed;
      bottom: 20px;
      right: 20px; /* 固定在视口右下角 */
    }
    

5. sticky(粘性定位)

  • 核心特性:
    • 「混合特性」:未滚动到阈值时是 relative(遵循文档流),滚动到阈值后变成 fixed(固定在视口);
    • 定位参考系:最近的滚动容器(通常是视口);
    • 必须配合 top/left/bottom/right 设定阈值,否则无效;
  • 使用场景:
    • 滚动时吸顶的导航栏、表格表头吸顶;
    • 侧边栏目录粘性定位;
  • 示例:
    css
    .nav {
      position: sticky;
      top: 0; /* 滚动到顶部时,固定在视口顶部 */
      background: #fff;
    }
    

三、核心对比表

定位类型是否脱离文档流参考系滚动表现关键属性
static❌ 不脱离文档流随页面滚动top/left 无效
relative❌ 不脱离自身原始位置随页面滚动top/left 生效
absolute✅ 完全脱离最近已定位祖先/视口随参考系滚动(无则随页面)top/left 生效
fixed✅ 完全脱离浏览器视口不随页面滚动top/left 生效
sticky❌ 未阈值/✅ 阈值后滚动容器阈值前随滚动,阈值后固定必须设 top/left 等

7.通过flex布局实现一个骰子的三

一、最终效果

+---------+
| ●       |
|   ●     |
|       ● |
+---------+

二、完整代码(HTML + CSS)

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Flex 实现骰子三点</title>
  <style>
    /* 骰子容器:正方形 + Flex 核心布局 */
    .dice {
      width: 100px;
      height: 100px;
      border: 2px solid #333;
      border-radius: 8px;
      padding: 8px;
      /* Flex 核心配置 */
      display: flex;
      flex-wrap: wrap; /* 允许子元素换行(3个点分布在3行) */
      justify-content: space-between; /* 水平两端对齐(控制左右点) */
      align-content: space-between; /* 垂直两端对齐(控制上下点) */
    }

    /* 骰子点:圆形样式 */
    .dot {
      width: 24px;
      height: 24px;
      border-radius: 50%;
      background-color: #333;
    }

    /* 中间点:单独水平居中 */
    .dot-center {
      margin: 0 auto; /* 让中间点在本行水平居中 */
    }
  </style>
</head>
<body>
  <div class="dice">
    <div class="dot"></div>         <!-- 左上点 -->
    <div class="dot dot-center"></div> <!-- 中间点 -->
    <div class="dot"></div>         <!-- 右下点 -->
  </div>
</body>
</html>

8.箭头函数

一、先定核心结论

箭头函数(=>)是 ES6 引入的函数简写语法,本质是「匿名函数」,核心特点是没有自己的 this/arguments/super/new.target,继承自外层作用域,同时语法更简洁——这是它和普通函数的核心差异。

四、面试高频延伸(加分项)

1. 箭头函数 vs 普通函数 核心对比表

特性箭头函数普通函数
this 指向定义时确定(继承外层)调用时确定(动态)
arguments无(用...args替代)有(类数组)
prototype无有
构造函数不能 new可以 new
yield 关键字不支持支持(生成器函数)
简写语法支持不支持

2. 经典面试题(会讲题更加分)

javascript
// 题:输出结果是什么?
var age = 100;
const obj = {
  age: 20,
  fn1: () => {
    console.log(this.age); // 100(this指向window)
  },
  fn2: function() {
    console.log(this.age); // 20(this指向obj)
    const fn3 = () => {
      console.log(this.age); // 20(继承fn2的this)
    };
    fn3();
  }
};
obj.fn1(); 
obj.fn2();

五、简化版回答(30秒快速讲清)

箭头函数是ES6的简写函数,核心特点:① 语法简洁,单行逻辑可省略括号/return;② 无自身this,继承外层作用域的this(解决回调函数this混乱问题);③ 没有arguments和prototype,不能作为构造函数。适用场景是回调函数(定时器、数组方法)和简洁工具函数,不适合对象方法、构造函数、需要动态this的场景(如事件处理)。

总结

讲箭头函数的核心:

  1. 先抓「this 指向」这个核心差异(定义时 vs 调用时);
  2. 再讲语法简洁、无arguments/prototype等特性;
  3. 结合「适用/不适用场景」体现实战理解,比如箭头函数适合数组map,不适合对象方法。
评论
0/100