前端面试复习|项目篇
涵盖:实时通信 / ECharts / 地图 SDK / 工程化 / 项目难点话术 / 简历项目 Q&A
复习时长建议:1 周
📑 目录
- 实时通信(WebSocket / WebRTC / MQTT)
- ECharts 数据可视化
- 地图 SDK 与电子围栏
- 前端工程化
- 低代码 / 中后台脚手架
- Node.js / BFF
- 项目难点话术(STAR)
- 简历项目延伸 Q&A
一、实时通信
1.1 WebSocket
为什么需要:HTTP 是请求-响应模式,服务器无法主动推送。WebSocket 是全双工长连接。
constws=newWebSocket('wss://api.com');ws.onopen=()=>ws.send('hello');ws.onmessage=(e)=>console.log(e.data);ws.onclose=()=>reconnect();ws.onerror=()=>{};生产级要点:
- 心跳:定时发 ping,30s 没收到 pong 就重连
- 断线重连:指数退避(1s、2s、4s、8s…)
- 消息队列:断线期间消息暂存,重连后补发
- 鉴权:URL 带 token 或第一条消息发 auth
完整封装:
classSocketClient{constructor(url){this.url=url;this.reconnectTimes=0;this.heartTimer=null;this.connect();}connect(){this.ws=newWebSocket(this.url);this.ws.onopen=()=>{this.reconnectTimes=0;this.startHeartbeat();};this.ws.onclose=()=>this.reconnect();}startHeartbeat(){this.heartTimer=setInterval(()=>{this.ws.send(JSON.stringify({type:'ping'}));},30000);}reconnect(){clearInterval(this.heartTimer);constdelay=Math.min(1000*2**this.reconnectTimes,30000);setTimeout(()=>{this.reconnectTimes++;this.connect();},delay);}}1.2 WebRTC
用途:浏览器间 P2P 音视频传输,1V1 通话、屏幕共享、实时直播。
三大核心 API:
| API | 作用 |
|---|---|
getUserMedia() | 获取摄像头/麦克风流 |
RTCPeerConnection | 建立 P2P 连接 |
RTCDataChannel | 任意数据通道 |
信令流程:
A → 创建 Offer → 信令服务器 → B B → 创建 Answer → 信令服务器 → A A、B 互换 ICE Candidate → P2P 通道建立多路视频卡顿优化:
- SFU 架构(服务器转发不合流)
- 自适应码率
- 关键帧请求(PLI)
- 硬件解码
1.3 MQTT
为什么用 MQTT 而不用 WebSocket:
- 极轻量(最小 2 字节头)
- 发布订阅模型,适合海量设备
- 支持 QoS(0/1/2 三级)
- 遗嘱消息
核心概念:
| 概念 | 解释 |
|---|---|
| Broker | 消息中转服务器(EMQX、Mosquitto) |
| Topic | 主题(如farm/cow1/temperature) |
| QoS 0 | 最多一次(可能丢) |
| QoS 1 | 至少一次(可能重复) |
| QoS 2 | 仅一次(最可靠最慢) |
importmqttfrom'mqtt';constclient=mqtt.connect('wss://broker.com');client.subscribe('farm/+/temperature');// + 单层通配client.on('message',(topic,payload)=>{});client.publish('farm/cow1/cmd','feed');二、ECharts 数据可视化
2.1 基础使用
import*asechartsfrom'echarts';constchart=echarts.init(document.getElementById('main'));chart.setOption({xAxis:{type:'category',data:['周一','周二']},yAxis:{type:'value'},series:[{type:'bar',data:[120,200]}],});2.2 性能优化
| 场景 | 方案 |
|---|---|
| 大数据(>1万) | appendData增量加载 |
| 多图表 | 共享主题 + 懒加载 |
| 体积大 | 按需引入 |
| resize 不灵 | 监听 resize + chart.resize() |
按需引入:
import*asechartsfrom'echarts/core';import{BarChart}from'echarts/charts';import{GridComponent,TooltipComponent}from'echarts/components';import{CanvasRenderer}from'echarts/renderers';echarts.use([BarChart,GridComponent,TooltipComponent,CanvasRenderer]);体积从 ~1MB 降到 ~200KB。
2.3 常踩坑
setOption默认合并,要清空用chart.clear()- 数据切换时 series 数量变化要
setOption(option, true) - v-if 销毁后要重新 init
- 容器宽高为 0 时初始化失败
2.4 Vue3 封装
<template> <div ref="chartRef" :style="{ width, height }"></div> </template> <script setup> import { ref, onMounted, onBeforeUnmount, watch } from 'vue'; import * as echarts from 'echarts'; const props = defineProps(['option', 'width', 'height']); const chartRef = ref(); let chart = null; onMounted(() => { chart = echarts.init(chartRef.value); chart.setOption(props.option); window.addEventListener('resize', () => chart.resize()); }); watch(() => props.option, (val) => chart?.setOption(val), { deep: true }); onBeforeUnmount(() => { chart?.dispose(); }); </script>2.5 大屏适配
.bigscreen{width:1920px;height:1080px;transform:scale(var(--scale));transform-origin:left top;}functionsetScale(){constscale=Math.min(window.innerWidth/1920,window.innerHeight/1080);document.documentElement.style.setProperty('--scale',scale);}window.addEventListener('resize',setScale);2.6 主题定制
echarts.registerTheme('myDark',{color:['#5470c6','#91cc75'],backgroundColor:'#1a1a2e',textStyle:{color:'#fff'},});echarts.init(dom,'myDark');三、地图 SDK 与电子围栏
3.1 高德 vs 腾讯
| 高德 | 腾讯 | |
|---|---|---|
| 国内数据 | ★★★★★ | ★★★★ |
| 海外 | ★★ | ★ |
| 文档 | 优秀 | 一般 |
3.2 核心能力
constmap=newAMap.Map('container',{zoom:11,center:[116.4,39.9]});// 标记newAMap.Marker({position:[lng,lat],map});// 路径规划newAMap.Driving().search([from,to],(status,result)=>{});// 地理编码newAMap.Geocoder().getLocation(address,callback);// 电子围栏判定AMap.GeometryUtil.isPointInRing([lng,lat],polygonPath);3.3 电子围栏
是什么:在地图上画一个虚拟"圈",判断设备/用户进入或离开 → 触发动作。
业务流程:
- 商家用
MouseTool.polygon()绘制多边形 - 后端持久化围栏数据
- 用户下单 → 取地址坐标 → 用
isPointInRing判断
完整代码:
// 1. 商家绘制constmouseTool=newAMap.MouseTool(map);mouseTool.polygon({fillColor:'#1791fc',strokeColor:'#1791fc',});mouseTool.on('draw',(event)=>{constpath=event.obj.getPath();constcoords=path.map(p=>[p.lng,p.lat]);saveToBackend(coords);});// 2. 用户下单判定constisInside=AMap.GeometryUtil.isPointInRing([userLng,userLat],polygonPath);if(!isInside)alert('超出服务范围');坐标系坑:
| 坐标系 | 用于 |
|---|---|
| WGS84 | GPS 原始 |
| GCJ02 | 高德、腾讯(火星坐标) |
| BD09 | 百度 |
后端拿到 GPS 坐标要先转 GCJ02 再画到高德地图,否则偏移几百米。
应用场景:
- 外卖配送范围
- 共享单车运营区/禁停区
- 门店服务范围(上门取件)
- 车辆调度
- 儿童老人定位手表
四、前端工程化
4.1 模块化历史
CommonJS(Node)→ AMD/CMD(已弃)→ ES Module
// CommonJSconstfs=require('fs');module.exports={};// ESMimportfsfrom'fs';exportdefault{};4.2 代码规范
# ESLint:代码错误# Prettier:代码格式# Husky:Git hooks# lint-staged:只检查暂存区# Commitlint:commit 规范.husky/pre-commit:
npx lint-staged4.3 Git 协作
| 命令 | 用途 |
|---|---|
git rebase -i | 合并/修改历史 commit |
git cherry-pick | 挑某个 commit |
git stash | 暂存当前修改 |
git reset --soft/--hard | 软/硬重置 |
git revert | 反向 commit |
Git Flow:
master ←─ release ←─ develop ←─ feature/xxx ←─ hotfix/xxx4.4 Monorepo
工具:pnpm workspace、Turborepo、Nx、Lerna
好处:多包共享依赖、统一版本、原子提交
4.5 CI/CD 流程
name:Deployon:push:branches:[main]jobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v3-uses:actions/setup-node@v3with:node-version:18-run:pnpm install-run:pnpm lint-run:pnpm test-run:pnpm build-name:Deployrun:rsync-avz dist/ user@server:/var/www/五、低代码 / 中后台脚手架
5.1 表单生成器(Schema 驱动)
constschema=[{type:'input',field:'username',label:'用户名',rules:[{required:true,message:'必填'}],},{type:'select',field:'role',label:'角色',options:[{label:'管理员',value:'admin'}],},];functionrenderForm(schema){returnschema.map(item=>{switch(item.type){case'input':returnh(ElInput,{...});case'select':returnh(ElSelect,{...});}});}5.2 RBAC 权限设计
用户 ─── 多对多 ─── 角色 ─── 多对多 ─── 权限前端三层权限:
- 路由级:动态注册可访问路由
- 菜单级:根据权限渲染菜单
- 按钮级:自定义指令
v-permission="'user:delete'"
constpermission={mounted(el,binding){const{value}=binding;constuserPerms=useUserStore().perms;if(!userPerms.includes(value)){el.parentNode?.removeChild(el);}},};5.3 多租户
| 模式 | 隔离 | 复杂度 |
|---|---|---|
| 独立数据库 | 完全 | 高 |
| 共享库独立 schema | 中 | 中 |
| 共享库共享表(tenant_id) | 低 | 低 |
前端要点:登录确定租户 + 请求带X-Tenant-Id头 + 子域名区分。
六、Node.js / BFF
6.1 事件循环(与浏览器差异)
Node.js Event Loop 6 个阶段: timers → pending → idle → poll → check → close与浏览器差异:
- Node 11.x+ 微任务穿插在每个阶段(和浏览器一致)
- 多了
setImmediate、process.nextTick process.nextTick优先级最高
6.2 Buffer / Stream
constbuf=Buffer.from('hello');console.log(buf.toString('hex'));fs.createReadStream('big.txt').pipe(fs.createWriteStream('copy.txt'));4 种流:Readable / Writable / Duplex / Transform
6.3 Express 中间件
constapp=express();app.use((req,res,next)=>{console.log('日志');next();});app.get('/users',(req,res)=>res.json([]));app.listen(3000);Koa 洋葱模型:请求 → mw1 前 → mw2 前 → 处理 → mw2 后 → mw1 后 → 响应
6.4 BFF(Backend For Frontend)
app.get('/api/dashboard',async(req,res)=>{const[user,orders,stats]=awaitPromise.all([fetch('/user-service/info').then(r=>r.json()),fetch('/order-service/recent').then(r=>r.json()),fetch('/stat-service/today').then(r=>r.json()),]);res.json({user,orders,stats});});6.5 npm 包发布
npminitnpmloginnpmpublishnpmversion patch# 1.0.0 → 1.0.1npmversion minor# 1.0.0 → 1.1.0npmversion major# 1.0.0 → 2.0.0七、项目难点话术(STAR)
7.1 STAR 法则
- SSituation:业务背景
- TTask:你要解决什么
- AAction:你做了什么(技术细节)
- RResult:结果(有数字)
7.2 难点 1:多端架构统一
场景:云护项目要同时支持 Web 后台 + 小程序 + 公众号 H5
问题:三端代码大量重复
方案:抽象业务层(API、Store、工具函数)成 npm 包,UI 层用 uni-app + 条件编译适配差异
结果:跨端复用率 70%+,新功能开发效率提升 40%
7.3 难点 2:长列表性能优化
场景:监控页面要展示 1000+ 设备实时状态
问题:直接渲染浏览器卡死
方案:虚拟滚动(el-table-v2)+ requestAnimationFrame 节流 + Web Worker 处理数据
结果:FPS 从 25 提升到稳定 60,内存占用降低 60%
7.4 难点 3:首屏加载优化
场景:长慈系统首屏 4.2s 用户投诉
排查:Lighthouse + Performance 面板定位,发现 ECharts 全量引入 + 图表组件同步加载
方案:路由懒加载 + ECharts 按需引入 + Gzip + 关键 CSS 内联
结果:首屏 4.2s → 1.3s,Lighthouse 58 → 92
7.5 难点 4:WebRTC 多路视频卡顿
场景:云护值守端要同屏看 16 路 1080P 视频
问题:客户端 CPU 跑满
方案:协商时降低非焦点画面分辨率(动态码率)+ 启用硬件解码 + 滚动可视区外暂停解码
结果:CPU 占用降低 50%,16 路同屏稳定运行
7.6 难点 5:MQTT 长连接稳定性
场景:牛轻松 App 在弱网环境下 MQTT 频繁断连
方案:心跳间隔动态调整(弱网延长)+ 指数退避重连 + 断线期间消息本地暂存 + QoS 1
结果:弱网下指令到达率从 78% 提升到 99.5%
八、简历项目延伸 Q&A
8.1 通用追问 10 个角度
每个项目准备这 10 个角度:
- 业务背景:to B 还是 to C?多少用户?
- 你的角色:独立做的?团队几个人?
- 技术选型理由:为什么用 Vue3 不用 React?
- 架构设计:项目目录结构?模块划分?
- 难点:最大技术挑战?
- 数据:提升数据怎么测的?工具?
- 协作:和后端/UI 怎么协作?
- 上线:怎么发布?灰度?回滚?
- 复盘:如果重做你会怎么改?
- 延伸:最自豪的一行代码/设计?
8.2 高频问题
Q:为什么用 Vue3 不用 React?
三个角度:
- 团队成本:团队 Vue 经验更多,React Hooks 学习曲线对部分成员陡峭
- 生态匹配:项目用了大量 Element-Plus 组件,Vue 生态更顺
- 维护成本:Vue 双向绑定和模板语法对业务开发者更直观
高交互复杂应用(Figma 这种),React + 类型系统可能更适合。
Q:为什么用 uni-app 不用 Taro?
团队是 Vue 技术栈,uni-app 0 学习成本。Taro 虽然 React 写法更现代,但需要培训。
项目主要面向小程序和 App,uni-app 微信生态适配更成熟。
Q:WebRTC 你做到什么深度?
- 业务层:完整对接信令、SDP 协商、ICE 流程
- 优化层:处理过多路视频卡顿
- 底层原理:理解 STUN/TURN/ICE
- 不擅长:底层协议修改、自研信令服务器(团队有专人)
诚实地标记自己的边界。
Q:项目的并发上限?
前端:1000+ 表格行 + 10 路视频流 + 实时数据推送时仍能保持 60FPS
后端:单接口 QPS 在 2000 左右(团队压测报告)
Q:项目的代码量?
- 我主导的项目代码量约 X 万行(cloc 统计)
- X 个核心模块,X 个通用组件
- 我个人贡献者占 70%+
本篇完。下一篇:04-软实力篇