大厂前端面试场景题整理
1. 用 Vue 或 React 写一个倒计时组件
解题思路:
倒计时本质上是一个定时器(setInterval 或 requestAnimationFrame),在组件挂载时启动,定时更新当前剩余时间,倒计时结束后清理定时器。
Vue 示例(Composition API)
<template>
<div>{{ time }}</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
const duration = 60 // 秒数
const time = ref(duration)
let timer: number | null = null
const startCountdown = () => {
timer = window.setInterval(() => {
if (time.value > 0) {
time.value--
} else {
clearInterval(timer!)
}
}, 1000)
}
onMounted(() => startCountdown())
onUnmounted(() => timer && clearInterval(timer))
</script>
2. 用 CSS 将正三角形变成倒三角形
核心原理: 利用 border 实现 CSS 三角形,改变边的颜色即可改变方向。
/* 正三角形(向上) */
.triangle-up {
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid black;
}
/* 倒三角形(向下) */
.triangle-down {
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid black;
}
3. 手写 Tooltip 组件
思路要点:
- 悬停时显示提示内容
- 支持自定义位置
- 使用 position: absolute + ref 定位提示内容
React 简写示例:
const Tooltip = ({ content, children }) => {
const [visible, setVisible] = useState(false)
return (
<div style={{ position: 'relative', display: 'inline-block' }}
onMouseEnter={() => setVisible(true)}
onMouseLeave={() => setVisible(false)}>
{children}
{visible && (
<div style={{
position: 'absolute',
top: '100%',
left: '0',
background: 'black',
color: 'white',
padding: '4px 8px',
borderRadius: '4px'
}}>
{content}
</div>
)}
</div>
)
}
4. 用 Vue 写一个 TodoList
实现点:
- 数据响应式管理
- 双向绑定
- 列表增删功能
<template>
<div>
<input v-model="newItem" @keyup.enter="addItem" />
<ul>
<li v-for="(item, index) in todos" :key="index">
{{ item }} <button @click="removeItem(index)">删除</button>
</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue'
const newItem = ref('')
const todos = ref<string[]>([])
function addItem() {
if (newItem.value) {
todos.value.push(newItem.value)
newItem.value = ''
}
}
function removeItem(index: number) {
todos.value.splice(index, 1)
}
</script>
5. 写一个表单校验(原生 + Element Plus UI)
原生校验:
<form onsubmit="return validate()">
<input id="email" />
<span id="error"></span>
<button type="submit">提交</button>
</form>
<script>
function validate() {
const value = document.getElementById('email').value
if (!value.includes('@')) {
document.getElementById('error').innerText = '请输入合法邮箱'
return false
}
return true
}
</script>
Element Plus + Vue 校验:
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email"></el-input>
</el-form-item>
</el-form>
<script setup>
import { reactive, ref } from 'vue'
const form = reactive({ email: '' })
const formRef = ref()
const rules = {
email: [{ required: true, message: '请输入邮箱', trigger: 'blur' },
{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }]
}
</script>
6. 写一个 Input 组件(可复用)
关键点:
- 绑定 value 与事件
- 支持 props 下发(placeholder、type、onInput)
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)"
:placeholder="placeholder" :type="type" />
</template>
<script setup>
defineProps({
modelValue: String,
placeholder: String,
type: { type: String, default: 'text' }
})
defineEmits(['update:modelValue'])
</script>
7. 长列表渲染(口述要点)
问题: 大量 DOM 节点(如上千项)会造成页面卡顿
解决思路:
- 虚拟列表技术:只渲染可视区域的数据
- 常见方法:
- 计算滚动区域可显示的元素数量(基于 scrollTop、容器高度、单项高度)
- 渲染 visible + buffer 区间的数据
- 设置固定高度撑开滚动条容器
- Vue 可用自定义 hook(如 useVirtualList)封装逻辑
- 推荐使用已有库如 vue-virtual-scroll-list 或 React 的 react-window
8. 手写 addEventListener(简化版)
这是一个模拟 DOM 事件监听器的实现(非浏览器 API 环境下自定义实现):
class EventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
}
off(event, callback) {
if (!this.events[event]) return;
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
emit(event, data) {
if (!this.events[event]) return;
this.events[event].forEach(cb => cb(data));
}
}
// 使用示例
const emitter = new EventEmitter();
function listener(data) {
console.log("收到事件数据:", data);
}
emitter.on("hello", listener);
emitter.emit("hello", { msg: "Hi" });
emitter.off("hello", listener);
emitter.emit("hello", { msg: "Bye" }); // 不会再触发
9. 异步每秒输出数组中的一个数字(长度为 100)
const arr = Array.from({ length: 100 }, (_, i) => i + 1);
async function printWithDelay(array) {
for (let i = 0; i < array.length; i++) {
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(array[i]);
}
}
printWithDelay(arr);
这些题目的共性与解题逻辑
这些前端题目看似各自独立,实际上它们背后有许多共性,主要体现在以下几个方面:基础能力、组件设计与复用性、性能优化、工程化思维等。
1. 基础能力考察:扎实的前端三大件掌握
这些题目大多数都考察了HTML、CSS 和 JavaScript 的基础能力,它们的本质是在验证你是否了解并能灵活使用浏览器的核心功能,包括:
- DOM 操作:如事件处理、表单验证、元素的增删改查。
- CSS 布局与样式:如边框、定位、响应式设计、样式重用等。
- JavaScript 基础知识:如定时器、事件处理、闭包、回调函数、生命周期、响应式数据等。
暂时无法在飞书文档外展示此内容
解题思维:从“原理 + 生命周期 + 样式结合”的角度分析,注重高内聚低耦合的设计原则,确保代码的可复用性与健壮性。
2. 组件开发与抽象能力
许多面试题的背后都在考察你是否具备构建通用、可复用组件的能力。这不仅仅是考察你能不能写出一个功能性组件,更重要的是:
- 是否能够根据业务需求进行适当的抽象,使组件具备更高的复用性和可维护性。
- 组件之间的数据流动是否清晰,能否做到易于扩展和维护。
暂时无法在飞书文档外展示此内容
解题思维:从高内聚低耦合的组件设计出发,关注如何解耦业务逻辑和 UI 展示,确保后续维护与扩展的简洁与清晰。
3. 性能优化意识与思维能力
这些题目特别关注性能优化,尤其是在面对大量数据渲染或频繁交互时,如何保证页面流畅度、减少资源消耗,提升用户体验。常见的优化方法包括:
- 懒加载和虚拟滚动:如长列表渲染和图片懒加载,避免一次性渲染大量 DOM 元素。
- 防抖与节流:如表单校验、搜索框输入等交互,减少不必要的重复计算。
- 代码拆分与懒加载:使用 Webpack、Parcel 等工具进行代码拆分,按需加载资源,减小页面初始加载时间。
暂时无法在飞书文档外展示此内容
解题思维:从用户体验出发,前端性能优化不仅要关注代码本身,更要考虑如何通过技术手段最大限度地降低页面负载,提高响应速度和交互流畅度。
4. 工程化与架构&项目整体思维
许多面试题不仅考察了实现一个功能的能力,还会涉及如何在复杂的工程中进行拆解、分层与管理。例如:
- 如何设计组件库,如何做好状态管理与事件处理。
- 如何利用工具(如 Webpack)进行项目的打包优化和模块管理。
- 如何通过代码规范和版本管理提升团队协作的效率。
暂时无法在飞书文档外展示此内容
解题思维:关注组件与模块的解耦与复用,考虑如何让组件不仅仅完成一个功能,而是能够更高效地在不同的场景中重复利用。
类似题型举例
以下是一些与已给定题目相似的常见面试题,帮助你拓展思维,提升解题能力。
暂时无法在飞书文档外展示此内容
总结
这些题目的共性不仅仅在于考察基础技能,还在于考察你的工程化思维、抽象能力、性能优化意识等多个方面。面试过程中,你不仅需要写出能跑的代码,更需要考虑代码的可维护性、扩展性和性能,这些思维能力的展现,才能使你在面试中脱颖而出。
解题时,可以遵循以下通用套路:
- 先明确需求:理解题目要求和功能细节,分清重点。
- 基础实现:先用最简单的方式实现,确保基本功能完成。
- 扩展优化:根据题目难度与深度,进行性能优化、代码解耦与结构设计。
- 工程化考量:考虑如何将解决方案提升为可复用的组件或模块。
#面试时最害怕被问到的问题##前端实习##前端#