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个核心点:
- 先明确组件的职责和使用场景,保证单一职责;
- 设计灵活的props和清晰的状态管理,兼顾通用性和定制化;
- 做性能优化(缓存、懒加载、虚拟列表)和工程化(TS类型、单元测试);
- 考虑用户体验(交互反馈、响应式、无障碍)和兼容性;
- 最后写清晰的文档和示例,降低使用成本。
3. 你做过哪些方面的性能优化
-
易代账表单组件从受控表单改为非受控表单
-
文心一言侧边栏对话历史列表,改为分页获取+虚拟列表
-
文心一言的埋点逻辑进行抽离封装,形成了一个埋点工具库。
- 在代码层面将埋点逻辑抽离成hooks,提升了代码复用性并且遵循了逻辑与UI分离的原则。
- 在性能优化方面,通过批量上报+空闲上报(requestIdleCallback)的方式减少了上报请求的数量并且充分利用了浏览器空闲时间,大大提升了上报的性能。
- 在可靠性方面,通过用localStorage缓存失败埋点请求,并且遵循指数退避算法进行失败重试,确保了失败的埋点数据不会丢失。
-
在react项目中会用到memo、useMemo以及useCallback来减少子组件不必要的重渲染。
4. 如果让我去定位性能问题,我会怎么做。具体要定位到是哪个组件该怎么定位
步骤 1:用 Chrome DevTools 做「全局性能扫描」(先找大方向)
这是定位性能问题的核心工具,操作步骤:
- 打开页面 → F12 打开 DevTools → 切换到「Performance」面板;
- 点击「录制按钮(圆形红点)」→ 复现卡顿操作(如滚动、点击、切换组件)→ 点击「停止」;
- 查看生成的性能图谱,重点关注:
- FPS 面板:绿色条越低(低于 60),卡顿越严重;红色块表示「长任务」(阻塞主线程);
- Main 线程面板:横向的长条形任务(超过 50ms)就是卡顿元凶,鼠标悬停可看任务详情;
- Summary 面板:看耗时占比(Rendering/JS Execution/Layout 占比高就是问题点)。
步骤2:React 项目用 React DevTools Profiler
- 安装 React DevTools 插件 → 打开页面 → 切换到「Profiler」面板;
- 点击「录制」→ 复现卡顿操作 → 停止录制;
- 查看「火焰图 / 排名图」:
- 火焰图:纵向是组件层级,横向是渲染耗时,长条形的组件就是耗时大户;
- 排名图:按渲染耗时排序,直接显示「哪个组件渲染时间最长」「渲染次数是否异常(比如重复渲染)」;
- 点击耗时组件,可查看「渲染原因」(如 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 节流),或异步执行耗时逻辑 |
| 组件频繁操作DOM | Rendering 面板中 Paint 占比极高 | 减少DOM操作(用React/Vue的虚拟DOM),批量修改样式,避免频繁重排 |
实操示例(React 项目定位卡顿组件)
- 打开 React DevTools Profiler,录制页面滚动卡顿的操作;
- 看到「UserList」组件渲染耗时 300ms,且每秒渲染10次(正常应只渲染1次);
- 点击「UserList」组件,查看渲染原因:「props.onUserClick 每次都是新函数」;
- 验证:给
onUserClick加useCallback缓存,重新录制,UserList 渲染耗时降到 20ms,卡顿消失; - 结论:UserList 组件因 props 频繁变化导致重复渲染,是卡顿元凶。
总结
定位页面卡顿到具体组件的核心步骤:
- 用 Chrome Performance 确定卡顿类型(JS/渲染/GC);
- React/Vue 项目用专属调试工具(Profiler/Vue DevTools)直接定位耗时组件;
- 通用项目用「Rendering 面板标红」+「Memory 面板查GC」定位问题组件;
- 禁用/优化目标组件,验证卡顿是否缓解。
5.原型链与原型对象
先讲三个概念:
obj.__proto__实例的原型对象Function.prototype函数的原型对象 所有函数的 proto 都指向它obj.__proto__.constructor实例的构造函数 原型对象上的属性,比如 tom.constructor === Person
原型对象是构造函数的 prototype 属性指向的对象,是实例的 “模板”,所有实例共享原型上的方法;原型链是对象查找属性的链路 —— 先找自身,找不到就顺着 __proto__ 往原型对象上找,直到 Object.prototype。比如用构造函数创建实例后,实例能调用原型上的方法,就是因为原型链的查找机制;ES6 的 class 本质也是基于原型链的语法糖。
原型对象的核心作用:
- 共享属性 / 方法:所有实例共用原型对象上的方法,节省内存;
- 实现继承:实例能访问原型对象上的属性 / 方法,这是 JS 继承的基础。
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)
<!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. 经典面试题(会讲题更加分)
// 题:输出结果是什么?
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的场景(如事件处理)。
总结
讲箭头函数的核心:
- 先抓「this 指向」这个核心差异(定义时 vs 调用时);
- 再讲语法简洁、无arguments/prototype等特性;
- 结合「适用/不适用场景」体现实战理解,比如箭头函数适合数组map,不适合对象方法。