数字马力面试题
面试题:
事件循环(event loop)
vue2/3的区别,与react的差异(会react的话会被比较)
闭包(js闭包)
原型与原型链
es6新特性
浏览器渲染过程
权限设置
响应式布局
组件间通信方式
工作中哪些比较困难的,怎么解决的,比较满意的哪些
有没有做过跨平台的项目
好的,面试官。非常感谢您给我这次机会。下面我将针对您提到的每一个知识点,结合我的理解和项目经验进行阐述。
1. 事件循环 (Event Loop)
事件循环是 JavaScript 处理异步操作的核心机制。它使得 JS 在单线程的情况下,能够通过非阻塞的方式处理 I/O 等异步操作。
核心概念:
- 调用栈 (Call Stack): 用于执行同步代码,后进先出。
- 任务队列 (Task Queue): 存放异步任务完成后的回调函数。主要分为两种:
- 宏任务 (Macro Task):
setTimeout,setInterval,setImmediate(Node.js), I/O 操作, UI rendering, 事件回调等。 - 微任务 (Micro Task):
Promise.then(),process.nextTick()(Node.js),MutationObserver等。
- 宏任务 (Macro Task):
- 事件循环过程:
- 执行全局的同步脚本,这些同步代码会形成执行上下文栈(调用栈)。
- 当调用栈为空时,事件循环会先去检查微任务队列。如果队列中有微任务,就按顺序全部执行完毕。
- 执行完所有微任务后,事件循环会从宏任务队列中取出第一个宏任务到调用栈中执行。
- 重复步骤 2 和 3。
简单记忆:一个宏任务 -> 所有微任务 -> 渲染(如有需要)-> 下一个宏任务...
例子:
console.log('1'); // 同步
setTimeout(() => {
console.log('2'); // 宏任务
Promise.resolve().then(() => {
console.log('3'); // 微任务 (在宏任务2内部)
});
}, 0);
Promise.resolve().then(() => {
console.log('4'); // 微任务
});
console.log('5'); // 同步
// 输出顺序: 1 -> 5 -> 4 -> 2 -> 3
2. Vue2 / Vue3 的区别,与 React 的差异
Vue2 vs Vue3
| 特性 | Vue 2 | Vue 3 |
|---|---|---|
| 响应式原理 | Object.defineProperty |
Proxy |
| 性能 | - | 更快,包体积更小 (Tree-shaking) |
| Composition API | 无 (Options API) | 有 (主打特性),更好的逻辑组织和复用 |
| TypeScript 支持 | 一般 | 原生支持极佳 |
| Fragment/Teleport/Suspense | 无 | 内置支持 |
| 生命周期 | beforeCreate, created 等 |
被 setup() 方法替代,并改名(如 beforeUnmount) |
| 根实例 | new Vue() |
createApp() |
核心区别:Composition API vs Options API
Vue3 的 Composition API 允许根据逻辑功能来组织代码,而不是像 Options API 那样强制按选项(data, methods, computed)分离。这使得代码更易于阅读、维护和复用,尤其是在复杂组件中。
Vue 与 React 的差异
| 方面 | Vue | React |
|---|---|---|
| 响应式原理 | 可变数据 + 自动依赖追踪 | 不可变数据 + 手动触发更新 |
| 直接修改数据,视图自动更新 | 需调用 setState 或 useState setter 函数 |
|
| 核心思想 | 声明式编程 + 响应式系统 | 函数式编程 + 虚拟DOM |
| API 风格 | 渐进式 & 易上手 | 灵活性高,学习曲线稍陡 |
提供了内置的指令(v-model, v-for) |
一切皆 JavaScript,JSX 表达能力更强 | |
| 语法 | 主要使用模板 (Template) | 主要使用 JSX |
| 状态管理 | Vuex/Pinia | Redux/MobX/Zustand |
| CSS 处理 | 单文件组件 (SFC) 中 <style scoped> |
CSS-in-JS, CSS Modules 等社区方案 |
总结: Vue 提供了更多“开箱即用”的规范和便利性,上手简单;React 则给予开发者更大的自由度和灵活性,但对团队的技术决策能力要求更高。
2. 后端一次性返回10万条数据给你,你如何处理?
问题考察点
性能优化意识(能否识别出“10 万条数据”会导致性能问题?是否第一反应是优化处理方式?)
浏览器渲染机制认知(是否理解 DOM 多、内存占用大、长任务对 UI 卡顿的影响?)
数据处理策略(是否会用分页、分片、懒加载、虚拟滚动等数据加载/渲染策略?)
项目实战经验(是否能结合实际业务讲解你曾用过的优化方案?)
前后端协同思维(是否考虑跟后端协商分页/接口设计?)
代码抽象能力(是否能设计合理的数据结构 / 缓存机制 / Worker / 节流方案?)
处理一次性返回的大量数据,可以考虑以下几种方法:
数据处理策略 1.数据分片(分页展示):将大型树结构分解成多个小块,按需加载各个部分。 2.虚拟列表:只渲染用户视口范围内的节点,减少DOM节点数量。 3.懒加载:初始只加载第一层或前几层数据,用户展开节点时再动态请求子节点数据
- 分页加载: 将数据分批次加载,每次只加载一部分数据,用户可以逐页查看。前端通过分页组件或手动翻页实现。
- 虚拟滚动: 仅渲染可视区域内的数据项,当用户滚动时动态加载和卸载数据。适用于数据量极大的情况,如长列表。
- 前端分页: 在前端对数据进行分页处理,将数据分成多个小数组,然后逐页展示。适用于数据量较大但不需要实时更新的场景。
- 服务器端分页: 在后端进行数据分页处理,前端只请求所需的数据。适用于需要即时加载大量数据的场景。
例子:
// 假设后端返回的数据格式为 { total: number, data: Array }
const data = await fetchData(); // 假设这是后端返回的数据
if (data.total > 10) {
// 分页处理,例如每页显示5条数据
for (let i = 0; i < Math.ceil(data.total / 5); i++) {
renderPage(data.data.slice(i * 5, (i + 1) * 5));
}
} else {
renderAll(data.data);
}
function renderPage(data) { /* 渲染当前页的数据 */ }
function renderAll(data) { /* 渲染所有数据 */ }
前端分页的实现:
// 假设每次只加载10条数据
const pageSize = 10;
for (let i = 0; i < Math.ceil(data.length / pageSize); i++) {
setTimeout(() => renderPage(data.slice(i * pageSize, (i + 1) * pageSize)), 0); // 使用 setTimeout 实现异步加载,避免阻塞主线程
}
function renderPage(data) { /* 渲染当前页的数据 */ }
服务器端分页的实现:
app.get('/data', (req, res) => {
const page = req.query.page || 1;
const limit = req.query.limit || 10;
// 从数据库中查询对应页的数据,并返回给前端
res.json(db.getDataPage(page, limit));
});
虚拟滚动的实现:
// 使用第三方库如 react-window 或 vue-virtual-scroller
const VirtualList = require('react-window');
function MyVirtualList() {
return (
<VirtualList
height={500}
itemCount={data.length}
itemSize={35} // 每个项的高度
width={300}
>
{({ index, style }) => <div style={style}>{data[index]}</div>}
</VirtualList>
);
}
分页加载的实现:
// 使用前端分页组件或手动翻页
function fetchDataPage(page) {
// 请求后端数据,并渲染当前页的数据
}
function handlePageChange(page) {
fetchDataPage(page);
}
总结: 根据数据量的大小和实时性需求选择合适的策略。对于非常大的数据集,通常推荐服务器端分页或虚拟滚动技术。
2.1 分页策略
分页策略是指根据数据量的大小和实时性需求选择合适的策略。
前端分页:
- 优点: 实时性强,用户可以随时看到最新的数据。
- 缺点: 数据量较大时,一次性加载所有数据会导致页面卡顿。
- 实现: 使用前端分页组件或手动翻页。
服务器端分页:
- 优点: 适用于数据量较大的情况,可以避免一次性加载过多数据导致的性能问题。
- 缺点: 每次翻页都需要向服务器请求数据,可能会增加服务器的压力。
- 实现: 在后端进行数据分页处理,前端只请求所需的数据。例如:
app.get('/data', (req, res) => {
const page = req.query.page || 1;
const limit = req.query.limit || 10;
// 从数据库中查询对应页的数据,并返回给前端
res.json(db.getDataPage(page, limit));
});
虚拟滚动:
- 优点: 只渲染可视区域内的数据项,适用于长列表。
- 缺点: 实现复杂度较高,需要第三方库支持。
- 实现: 使用如
react-window或vue-virtual-scroll-list等第三方库实现虚拟滚动。例如:
// 使用 vue-virtual-scroll-list
<template>
<virtual-scroll-list :items="data" item-height="35">
<div slot-scope="{ item }">{{ item }}</div>
</virtual-scroll-list>
</template>
分页加载:
- 优点: 用户体验好,用户可以逐步加载数据。
- 缺点: 实现复杂度较高,需要前端和后端的配合。
- 实现: 使用前端分页组件或手动翻页,每次只加载一部分数据。例如:
function fetchDataPage(page) {
// 请求后端数据,并渲染当前页的数据
}
function handlePageChange(page) {
fetchDataPage(page);
}
总结: 根据数据量的大小和实时性需求选择合适的策略。对于非常大的数据集,通常推荐服务器端分页或虚拟滚动技术。
2.2 分页策略
分页是一种常用的数据处理方式,特别是在处理大量数据时。以下是几种常见的分页策略及其优缺点:
-
前端分页(客户端分页):
- 优点:简单易实现,不需要后端的支持,可以快速展示数据。
- 缺点:如果数据量很大,前端需要加载所有数据,可能会导致性能问题。
- 实现:在前端对数据进行分页处理,每次只加载一部分数据。例如,使用
Array.prototype.slice方法进行分页。
-
服务器端分页(后端分页):
- 优点:可以控制数据加载量,避免一次性加载过多数据导致的性能问题。
- 缺点:需要后端支持,实现较为复杂。
- 实现:在后端进行数据分页处理,前端只请求所需的数据。例如,在 SQL 查询中使用
LIMIT和OFFSET子句。
app.get('/data', (req, res) => { const page = req.query.page || 1; const limit = req.query.limit || 10; // 从数据库中查询对应页的数据,并返回给前端 res.json(db.getDataPage(page, limit)); });
3. 闭包 (Closure)
闭包是指有权访问另一个函数作用域中变量的函数,即使另一个函数已经执行结束。
形成条件:
- 函数嵌套。
- 内部函数引用了外部函数的变量或参数。
- 内部函数被返回或在其他地方被使用,从而脱离了其原始的作用域。
作用:
- 创建私有变量: 模拟私有方法,避免全局污染。
- 实现数据持久化: 使函数外部能持续访问函数内部的变量。
例子:
function createCounter() {
let count = 0; // 私有变量
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
// 外部无法直接访问 count,但可以通过 counter 函数操作它
注意事项: improper use of closures can lead to memory leaks because the variables referenced by the closure won't be garbage collected.
4. 原型与原型链
- 原型 (Prototype): 在 JavaScript 中,每个函数(除箭头函数)都有一个
prototype属性,它指向一个对象。这个对象包含了由该函数创建的所有实例共享的属性和方法。 - 原型链 (Prototype Chain): 每个对象都有一个内部属性
[[Prototype]](可通过__proto__访问)。当访问一个对象的属性时,如果对象自身没有,就会沿着它的__proto__向上查找,直到找到Object.prototype为止,这条链就是原型链。JavaScript 正是通过原型链来实现继承的。
例子:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
const person1 = new Person('Alice');
person1.sayHello(); // 1. person1 自身没有 sayHello
// 2. 查找 person1.__proto__ (即 Person.prototype)
// 3. 找到了,调用
// person1 -> Person.prototype -> Object.prototype -> null
5. ES6+ 新特性 与 HTML5
ES6 (ECMAScript 2015) 是一个重大更新,之后每年都有新版本。
- 块级作用域:
let,const - 变量解构赋值:
const { a, b } = obj; - 模板字符串:
`Hello ${name}` - 箭头函数:
() => {}(无自己的this,arguments) - Promise: 处理异步操作,解决回调地狱
- 模块化:
import/export - Class 类: 语法糖,基于原型继承的更清晰的语法
- 默认参数、剩余参数、扩展运算符:
function(a=1, ...args){},[...arr] - ES2016+:
async/await,Array.prototype.includes(), 可选链?., 空值合并??,Object.values(),Object.entries(),Symbol,Map,Set,Proxy,Reflect,for...of,String.prototype.padStart(),Object.fromEntries()等。
HTML5 新特性:
- 语义化标签:
<header>,<footer>,<article>,<section>,<nav>,<main>,<aside>,<figure>,<figcaption>,<details>,<summary>,<dialog>,<time>,<mark>,<meter>,<progress>,<datalist>,<output>,<template>等。 - 表单控件:
<input type="date">,<input type="color">等。 - Canvas 绘图:
<canvas>。 - Web 存储:
localStorage,sessionStorage。 - Web 音频和视频:
<audio>,<video>。 - 拖放 API:
<div draggable="true">。 - Web Workers: 在后台线程中运行脚本,不阻塞 UI。
- WebRTC: 实时通信。
- Web Animations API: 动画。
- Web Components: 自定义元素。
- Geolocation API: 定位。
- IndexedDB: 客户端存储大量结构化数据。
- Service Workers: 离线体验,后台同步。
- Fetch API: 替代 XMLHttpRequest。
- WebSockets: 全双工通信。
- WebP 图片格式: 提供更好的压缩。
- SVG 图形: 可缩放矢量图形。
- Progressive Web Apps (PWA): 提供类似原生应用体验的 web 应用。
- WebVR 和 WebXR: 虚拟现实和增强现实。
- WebAssembly: 允许在 web 应用中使用编译后的语言代码。
- Web MIDI API: 音乐合成。
- Web NFC API: 近场通信。
- Web Share API: 分享内容。
- Web Payment Request API: 支付。
- Web Notifications API: 桌面通知。
- WebUSB API: USB 设备访问。
- Web Serial API: 串行设备访问。
5. 节流 (Throttle) 和防抖 (Debounce)
节流 (Throttle): 限制函数的执行频率,在指定时间内只执行一次。常用于滚动、窗口大小调整等频繁触发的事件。
防抖 (Debounce): 在事件触发后延迟执行,如果在这段时间内再次触发,则重新计时。常用于搜索框输入、按钮点击等。
// 节流
function throttle(func, delay) {
let timer = null;
return function() {
if (!timer) {
timer = setTimeout(() => {
func.apply(this, arguments);
timer = null;
}, delay);
}
};
}
// 防抖
function debounce(func, delay) {
let timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, arguments), delay);
};
}
使用场景:
- 节流:滚动事件、窗口大小调整。
- 防抖:搜索框输入、按钮点击。
6. 浏览器渲染过程
- 解析 HTML: 构建 DOM Tree。
- 解析 CSS: 构建 CSSOM Tree。
- 合并成渲染树 (Render Tree): 将 DOM 和 CSSOM 合并,排除
display: none等不可见元素。 - 布局 (Layout / Reflow): 计算每个节点在屏幕上的确切位置和大小。
- 绘制 (Painting): 将渲染树的节点像素化,填充到图层中。
- 合成 (Composition): 将多个图层(如由
transform属性创建)合成为一页,显示在屏幕上。
重排 (Reflow) 和 重绘 (Repaint):
- 重排: 改变了元素的几何属性(宽、高、位置),浏览器需要重新计算布局。
- 重绘: 改变了元素的样式但不影响布局(如颜色、背景色),浏览器只需重新绘制受影响区域。
优化: 应尽量减少重排和重绘,例如使用class集中改变样式,避免频繁操作 DOM。
7. 权限设置
前端权限通常分为 路由权限 和 功能权限。
-
路由权限 (菜单权限):
- 方案: 用户登录后,后端返回其可访问的路由列表或权限码。
- 实现: 在全局路由守卫(如 Vue Router 的
beforeEach)中,判断目标路由是否在用户权限列表中。若不在,则跳转到 401 或登录页。 - 动态路由: 根据权限列表,使用
router.addRoute()动态添加有权限的路由。
-
功能权限 (按钮权限):
- 方案: 后端同样返回权限点列表。
- 实现: 封装一个自定义指令(Vue)或高阶组件(React),例如
v-permission="'add'"。在指令或组件内部判断用户是否拥有该权限点,如果没有,则移除或禁用该 DOM 元素。
8. 响应式布局
目的是让网页在不同尺寸的设备上都能有良好的视觉体验。
- 核心技术:
- Viewport 元标签:
<meta name="viewport" content="width=device-width, initial-scale=1.0"> - 流体网格 (Fluid Grid): 使用百分比%、
fr、flex、grid等弹性单位布局,而非固定px。 - 弹性图片/媒体:
img { max-width: 100%; height: auto; } - CSS 媒体查询 (Media Queries):
@media (max-width: 768px) { ... },这是实现断点布局的关键。
- Viewport 元标签:
- CSS 方案:
- Flexbox: 一维布局,非常适合组件级的弹性布局。
- CSS Grid: 二维布局,非常适合整个页面的宏观布局。
- UI 框架: 如 Bootstrap、Element-Plus、Ant Design 等都内置了强大的栅格系统,能快速实现响应式。
9. 组件间通信方式
Vue:
- 父子组件:
- Props Down: 父向子传值。
- Events Up (
$emit): 子向父传值。
- 兄弟组件/跨级组件:
- Event Bus: 创建一个中央事件总线(Vue2 常用,Vue3 推荐用 mitt 库)。
- Vuex / Pinia (状态管理库): 全局状态管理,任何组件都可读写。
- Provide / Inject: 祖先组件向深层后代组件“注入”数据。
- 获取组件实例:
$parent/$children/$refs(不推荐,破坏组件封装性)
React:
- 父子组件:
- Props Down: 父向子传值。
- Function Props: 父传递一个函数给子,子调用该函数向父传值。
- 兄弟组件/跨级组件:
- 状态提升 (Lifting State Up): 将共享状态提升到最近的公共父组件中管理。
- Context API: 跨层级传递数据,避免“prop drilling”。
- Redux / Zustand / MobX (状态管理库): 全局状态管理。
- Ref 转发:
forwardRef用于获取子组件的 DOM 节点或实例。
10. 工作困难、解决方案与满意项目
(这是一个范例,请务必根据你自己的真实经历修改)
困难与解决:
- 困难: 在项目
[某电商后台]中,我们有一个非常复杂的动态表单生成器,它需要根据后端返回的 JSON schema 实时渲染出包含数十种字段类型且存在复杂联动规则的表单。初期版本性能很差,每次输入都会导致全量渲染,卡顿明显。 - 解决过程:
- 定位问题: 使用 React DevTools Profiler / Vue Devtools 性能标签页进行分析,发现不必要的组件重渲染是主因。
- 优化方案:
- 精细化控制渲染: 对每个表单项组件使用
React.memo/Vue component v-once或computed进行计算属性缓存,避免父组件状态变化导致的子组件全量更新。 - 数据结构优化: 与后端协商,将部分极深层次的 schema 拍平,减少前端递归渲染的深度。
- 防抖处理: 对频繁触发的事件(如搜索框输入)加入防抖。
- 精细化控制渲染: 对每个表单项组件使用
- 结果: 表单操作帧率显著提升,卡顿现象基本消失,用户体验得到极大改善。
满意项目:
我对 [某数据可视化大屏项目] 最为满意。
- 技术挑战: 需要在单个页面上高效渲染大量的动态图表和实时数据流,并保证在不同分辨率下完美展示。
- 我的贡献:
- 主导技术选型,使用
ECharts进行图表渲染,并对其进行了二次封装,统一了数据和主题配置,提升了开发效率。 - 利用
CSS Grid和rem单位实现了真正的自适应布局,完美适配了公司各种尺寸的大屏设备。 - 设计了前端数据更新机制,通过 WebSocket 和智能的数据差分更新,减少了不必要的 DOM 操作,保证了页面的流畅性。
- 主导技术选型,使用
- 成果: 项目得到了客户和领导的高度认可,成为了公司的标杆产品。
11. 跨平台项目经验
是的,我有过跨平台开发的经验。
- 技术栈: 我使用 React Native 开发过一款
[简单的企业内部办公应用],涉及消息通知、审批流程等功能。 - 核心体验:
- 学会了使用
Flexbox进行移动端布局,习惯了 RN 的样式写法。 - 熟悉了如何封装原生模块(Native Module)来调用 Android/iOS 的原生 API(如相机、相册)。
- 处理了导航(React Navigation)、状态管理(Redux Toolkit)、离线存储等常见需求。
- 深刻理解了“Learn once, write anywhere”的理念,以及它与“Write once, run anywhere”(如 Flutter)的区别。
- 学会了使用
- 其他了解: 我也了解 Electron 的原理,它通过集成 Chromium 和 Node.js 来用 Web 技术开发桌面应用。Taro / Uni-app 这类框架也是不错的多端解决方案。
12. 前端项目中的难点与解决方案
难点:
- 在
[某电商后台]项目中,我们遇到了一个复杂的表单生成器问题,每次输入都会导致全量渲染,卡顿明显。 - 在
[某数据可视化大屏项目]中,我们需要在单个页面上渲染大量的动态图表和实时数据流,同时保证在不同分辨率下完美展示。
解决方案:
- 表单生成器优化: 我们对表单操作进行了防抖处理,并使用
React.memo和useCallback来避免不必要的渲染,显著提升了表单操作的帧率,解决了卡顿问题。 - 数据可视化大屏: 我们使用了
ECharts进行图表渲染,并对其进行了二次封装。同时利用了 CSS Grid 和 rem 单位实现了自适应布局,并通过 WebSocket 智能更新数据,保证了页面的流畅性。 - 其他: 在
[某企业内部办公应用]中,我们使用了React Navigation进行导航管理,通过Redux Toolkit进行状态管理,并通过AsyncStorage进行离线存储,确保了应用的稳定性和性能。
13. 项目中的亮点
亮点:
- 在项目实现中为统一开发界面风格,简化开发流程,我参与搭建 前端工具库 ,参与以及主导封装了常用的组件和工具函数,提高了开发效率。并将此库上传到npm上,方便团队其他成员使用。该工具库已成为我们团队前端项目的标准依赖。
- 在
[某企业内部办公应用]中,我设计了一套前端数据更新机制,通过 WebSocket 和智能的数据差分更新,减少了不必要的 DOM 操作,保证了页面的流畅性。 - 在
[某数据可视化大屏项目]中,我主导了技术选型,使用 ECharts 进行图表渲染,并对其进行了二次封装,统一了数据和主题配置,提升了开发效率。同时利用 CSS Grid 和 rem 单位实现了真正的自适应布局,完美适配了各种分辨率。 - 在
[某电商后台]中,我主导了表单生成器的优化工作,通过精细化控制渲染和数据结构优化,显著提升了性能。 - 在
[某企业内部办公应用]中,我设计了一套前端数据更新机制,通过 WebSocket 和智能的数据差分更新,减少了不必要的 DOM 操作,保证了页面的流畅性。
14. 项目中的不足
不足:
- 在
[某企业内部办公应用]中,由于时间紧迫,部分组件的代码质量有待提高,需要进一步优化。 - 在
[某数据可视化大屏项目]中,由于项目规模较大,部分代码的模块化和复用性有待加强。
以上就是我对这些问题的理解与回答,谢谢面试官。

浙公网安备 33010602011771号