前端综合技术面试记录
2025-01-10
某互联网公司
前端开发工程师
前端综合技术面试记录
面试日期:2025-01-10
面试公司:某互联网公司
应聘职位:前端开发工程师
面试关键词
JavaScript 基础 | Vue 框架 | TypeScript | Webpack 优化 | 路由权限 | 性能优化
📚 JavaScript 基础
1. let 和 const 的区别
要点回顾:
let:块级作用域,可重新赋值(mutable),不可重复声明(同一作用域)const:块级作用域,绑定不可变(binding immutable),必须初始化;对象/数组的内容可变(属性可改)
实战建议:
- 默认使用
const,需要重新赋值时再用let;尽量避免使用var
代码示例:
const a = { x: 1 }; a.x = 2; // 合法 a = {}; // 报错 let b = 1; b = 2; // 合法
2. for...in 和 for...of 的区别
要点回顾:
for...in:遍历对象的可枚举属性名(包括原型链上的可枚举属性),返回 key(字符串)。不适合数组元素遍历(顺序不保证)for...of:遍历可迭代对象(Array、Map、Set、String、arguments 等),返回 value
实战建议:
- 数组使用
for...of或for/forEach;对象遍历使用Object.keys()或for...in(注意 hasOwnProperty)
代码示例:
for (const key in obj) { if (!obj.hasOwnProperty(key)) continue; console.log(key, obj[key]); } for (const val of arr) { console.log(val); }
🌐 浏览器与性能优化
3. 从输入 URL 到浏览器渲染的全过程
关键阶段:
- 输入 URL → 浏览器检查缓存(HSTS、DNS cache)
- DNS 解析 → 得到 IP
- TCP 三次握手 → 若 HTTPS,多出 TLS 握手
- 发送 HTTP 请求(含 Headers、Cookie)
- 服务端处理 → Nginx → 应用 → DB / 缓存 → 生成响应
- 浏览器接收响应 → 解析 HTML(构建 DOM)
- 解析 CSS(CSSOM)、JS(执行) — JS 若无
defer/async会阻塞解析 - 生成 Render Tree → Layout(reflow) → Paint → Composite(合成 GPU 层)
- 浏览器渲染到屏幕
优化点:
- 资源并行加载、HTTP/2、资源压缩、缓存策略、critical CSS、懒加载、服务端渲染(SSR)
4. 首屏优化方案
网络层优化:
- 启用 HTTP/2 或 HTTP/3
- 使用 CDN 加速
- 资源压缩(gzip/brotli)
- 长缓存 + 版本控制
- 预连接/预取(
<link rel="preconnect">、prefetch)
解析层优化:
- 关键 CSS inline(critical CSS)
- JS 标记
defer或async - Code-splitting 减少 bundle 首包体积
渲染层优化:
- Skeleton / 骨架屏
- 图片懒加载(
srcset、sizes) - 使用
requestIdleCallback处理非关键任务
度量指标:
- 使用 Lighthouse、Web Vitals(FCP、LCP、TTI)持续监控
5. SEO 优化方案
技术实现:
- SSR/Prerender:保证搜索引擎抓取到完整 HTML
- Meta 信息:动态设置
title/meta description,使用 JSON-LD - URL 规范化:语义化路径、canonical 标签
- 站点地图:提供 sitemap.xml、robots.txt
- 性能优化:FCP/LCP 优化间接提升 SEO
- 社交预览:Open Graph/Twitter Card 标签
推荐框架: Next.js / Nuxt / SvelteKit
💡 JavaScript 进阶概念
6. 闭包(Closure)
要点回顾:
- 闭包是函数与其词法环境的组合
- 函数可以访问定义时的外部作用域,即使外部函数执行结束
常见用途:
- 数据私有化(模块化)
- 柯里化
- 函数工厂
- 保持状态
代码示例:
function makeCounter() { let count = 0; return function() { return ++count; } } const c = makeCounter(); // c() 每次调用会记住 count
注意事项:
- 注意内存泄漏(大量闭包持有大对象会影响 GC),必要时释放引用
7. 防抖(debounce)和节流(throttle)
概念区别:
- 防抖(debounce):事件停止一定时间后才触发(适合输入框搜索、窗口 resize)
- 节流(throttle):在固定时间间隔内只允许触发一次(适合滚动、鼠标移动)
代码实现:
// debounce function debounce(fn, wait) { let t; return function(...args) { clearTimeout(t); t = setTimeout(() => fn.apply(this, args), wait); } } // throttle function throttle(fn, interval) { let last = 0; return function(...args) { const now = Date.now(); if (now - last >= interval) { last = now; fn.apply(this, args); } } }
实战建议:
- 使用 lodash 的成熟实现,支持
leading/trailing参数
8. TypeScript 泛型
要点回顾:
- 泛型用于描述类型的参数化,增强复用性和类型安全
代码示例:
function identity<T>(arg: T): T { return arg; } const s = identity<string>('hello'); // s 类型是 string interface ApiResult<T> { code: number; data: T; } type User = { id: number; name: string }; const r: ApiResult<User> = { code: 0, data: { id:1, name:'A' } };
实战建议:
- 用泛型封装复用组件/工具(如接口返回类型、表单、容器组件)
- 合理约束
T extends {}避免any
⚡ Vue 框架
9. Vue2 和 Vue3 的主要区别
核心区别:
- 响应式实现:Vue2 用
Object.defineProperty,Vue3 用Proxy(支持新增属性监听、更高性能) - 性能:Vue3 更快,bundle 更小,Tree-shakable
- Composition API:Vue3 提供
setup/ref/reactive等,逻辑复用更优 - 新特性:Fragment / Teleport / Suspense 原生支持
- 渲染器:虚拟 DOM、renderer 重写,支持更灵活的自定义渲染
面试亮点:
- 强调 Proxy 带来的好处:拦截新增/删除属性、数组原生支持、性能优势
10. Object.defineProperty 的缺陷
主要问题:
- 无法监听属性新增/删除(需递归定义或
Vue.set) - 对数组变更需重写原型方法(复杂且易出错)
- 性能问题:深层对象需要递归劫持造成初始化开销
- 无法拦截 Map/Set 等数据结构操作
解决方案:
- Vue3 使用 Proxy 彻底解决这些问题
11. computed 和 watch 的区别
要点回顾:
computed:基于依赖缓存,用于声明式计算属性,只有依赖改变才重新计算,适合模板绑定watch:用于执行副作用,可监听数据变化并在变化时运行回调(适合异步或显式副作用)
代码示例:
const count = ref(1); const double = computed(() => count.value * 2); // 缓存 watch(count, (newV, oldV) => { // 执行副作用,比如请求、手动 DOM 操作 });
实战建议:
- 能用
computed的就别用watch;需要做异步或复杂副作用用watch
📦 Webpack 构建优化
12. Webpack 优化策略
构建体积优化:
- Tree-shaking
- Terser 压缩
- CSS 压缩
- 去掉无用 polyfill
- 按需加载
- 分包(splitChunks)
构建速度优化:
- 使用
cache-loader或硬盘缓存 thread-loader多线程构建esbuild/swc替代 Babel- HMR 优化
运行性能优化:
- Long-term caching(contenthash)
- preload/prefetch
- 按需加载(dynamic import)
- 第三方库 CDN 或 externals
资源优化:
- 图片压缩
- SVG sprite
- 字体子集化
分析工具:
webpack-bundle-analyzer分析体积,定位大依赖
实战建议:
- 先定位(bundle 分析),再拆包与异步加载
- 优先替换体积大但使用少的库
🛣️ Vue Router
13. vue-router 底层原理
两种模式:
- Hash 模式:
location.hash+hashchange事件 - History 模式:HTML5 History API(
pushState/replaceState+popstate事件)
核心机制:
- Vue Router 维护
currentRoute - 通过 matcher(基于
path-to-regexp)把 URL 映射成路由记录 RouterView响应式渲染组件
面试要点:
- 说出事件
hashchange与popstate - 提到动态路由匹配与
addRoute
14. vue-router 动态路由匹配
实现原理:
- 内部用
path-to-regexp将路径(含:param、*、正则)转成正则表达式并提取 params - 支持嵌套路由:子路由继承父路由 params
- 可在运行时动态注册路由
router.addRoute()(用于菜单权限懒加载)
实战建议:
- 权限路由先用路由白名单
- 登录后从后端获取菜单/权限生成 routes 并
addRoute - 然后
replace到第一个可访问页面
15. 菜单权限控制步骤
实施流程:
-
鉴权入口
- 登录后获取 token
- 后端返回用户权限/角色或权限码(建议最小粒度)
-
后端返回菜单树
- 后端按权限生成菜单(包含 route path、component 引用、action 权限)
-
前端动态生成路由
- 将菜单树映射成 route record →
router.addRoute(...) - 未命中的路由重定向到 403/登录页
- 将菜单树映射成 route record →
-
前端权限校验
- 在
router.beforeEach做路由守卫(检查 token、权限) - 在页面/按钮做能力级别判断(v-if 或指令)
- 在
-
按钮级权限
- 后端返回 action 列表(create/update/delete)
- 前端在组件中按权限开关 UI 按钮并控制接口调用
-
缓存 & 刷新
- 路由与权限需持久化(localStorage/sessionStorage)
- 刷新页面时优先从 token 调用权限接口恢复
安全注意:
- 后端必须二次校验(前端仅做体验层控制)
📎 大文件上传
16. 大文件上传实战方案
核心策略:
1. 切片上传(Multipart Upload)
- 前端把大文件分为 N 片(如 5MB)
- 每片并行上传到后端或直传到对象存储(OSS/S3)
- 后端或 OSS 支持合并分片
- 支持断点续传(记录已上传的块),校验 MD5/ETag
2. 直传 OSS(前端直传)
- 前端获取短期签名(STS / presigned URL)
- 直接上传至 CDN/OSS,减轻后端压力
3. 断点续传与重试机制
- 每片成功后记录状态,失败则重试
- UI 显示进度条和已上传片
4. 并发控制与速率限制
- 控制同时并发条数(例如 3~6 个并发)避免网络拥塞
5. 压缩/转码(可选)
- 在前端做图片压缩、视频转码尽可能减小体积
6. 安全校验
- 后端校验文件类型、大小、病毒扫描(可异步)
实现流程:
- 切片并计算每片 MD5/Hash
- 请求服务端获取上传授权与已上传片信息
- 并发上传未上传的切片到 presigned URL 或后端接口
- 上传完成后通知后端合并并返回 final URL
- 若中断,基于已上传记录重试
总结
本次面试主要涉及前端全方位的技术知识,涵盖:
- JavaScript 基础:变量声明、循环遍历、闭包、防抖节流
- 浏览器原理:渲染流程、首屏优化、SEO 方案
- Vue 框架:Vue2/3 区别、响应式原理、computed vs watch
- 工程化:Webpack 优化、构建策略
- 路由与权限:Vue Router 原理、动态路由、菜单权限控制
- 实战场景:大文件上传方案
通过这次面试,系统地梳理了前端开发中的核心技术点和实战经验。