前言 兄弟也是好起来了,又又有大厂面试了。*
面试过程:
一、自我介绍
这个自我介绍我之前在面试文章中提到过,大家可以翻翻查看。
二、实习经历
面试官看到我目前在一家公司实习,于是让我聊了聊我的业务内容。
三、项目方面
1.你为什么选用 Tailwind CSS?能说说有什么好处吗?
原子化设计:Tailwind CSS 是一种原子化 CSS 框架,将样式拆分为最小的功能单元,每个类只负责一个特定的样式属性。
开发效率高:像写内联类一样快速编写样式,无需额外创建 CSS 文件。
响应式友好**:支持大量响应式类,例如 md:w-1/2、lg:w-1/4、lg:flex-row 等。
样式隔离性强:在 Vue 单文件组件中使用 Tailwind 类,避免传统 CSS 中的样式冲突问题,比如 TabBar、ShowProducts 等组件各自维护自己的样式。
技术广度体现:其实当时用这个是想展示一下对现代前端工具链的理解。
缺点: 学习曲线较陡,需要记忆大量类名和约定; 熟练后开发效率更高。
拓展思考: 便于 AI 辅助开发:原子类不依赖嵌套或继承,减少 AI 理解上下文的压力; 降低样式覆盖风险,减少了 CSS 的“层叠”问题和选择器冲突。
2.你的项目用到了组件懒加载,讲讲好处?
在路由懒加载中使用了组件懒加载,实现 按需加载,只有当用户导航到特定路由时,才会加载相应的组件。
如果不使用懒加载,打包时会把所有页面打包成一个文件,首页一次性加载全部资源,导致加载速度慢,用户体验差。
使用路由懒加载后,首页资源被拆分为多个 chunk 文件(如 app.js, home.js),CSS 同样被拆分。
面试官追问:你知道为什么会这样吗?
我当时没回答上来,但后来查资料得知:
import() 的调用处被视为代码分割点,被请求模块及其子模块会被分离为独立的 chunk。
Webpack 等构建工具识别 import(),并将动态导入的模块单独打包,从而减小初始加载体积。 总体积不变,但首屏加载资源减少,提升用户体验。
3.聊聊你项目中的动态组件
在实现一个礼物推荐助手时,我需要展示用户提问与 AI 回答。 为此我封装了两个组件:一个是用户消息组件,一个是 AI 回复组件。 每次对话内容存储在一个数组中,根据标志属性判断渲染哪个组件。 最终通过 Vue 内置的 标签结合 :is 属性实现了动态组件切换。
4.你实现 keep-alive
的目的,以及和 v-if / v-show 的区别?应用场景?
keep-alive 目的: 缓存组件状态(如表单输入、滚动位置); 避免组件频繁销毁重建; 减少 API 请求,提高性能和用户体验。 缓存控制: 使用 include="cachedComponents" 属性,只缓存设置了 meta.cache = true 的组件。
5.自定义图片懒加载怎么实现的?
scrollTop + offsetTop => getBoundingClientRect() => IntersectionObserver
从手动计算逐步过渡到现代浏览器 API,性能越来越好。
又问:你了解 HTML 中原生的 lazy 吗?能否讲讲?
原生 HTML 支持懒加载: 和
**优点:**简单易用,无需 JS,现代浏览器原生支持;
**缺点:**兼容性一般,IE 不支持,功能有限;
定制性不强,更高级的需求建议使用 IntersectionObserver 自定义实现。
6.响应式布局这方面,你是怎么做的?
我的项目中,有一个商品展示的功能,使用的是wc-waterfall ,动态的绑定gap和cols两个属性,通过生命周期挂载,添加事件监听,根据屏幕的大小,调整相应的值,来实现响应式布局。
通过声明在不同尺寸下微调样式细节
商城项目,经常用得到商品的展示,所以我会将它封装成一个组件,方便复用
四、场景题
1.setimeout
这个是一个面试经常问到的题目,但是他问的很细
for(var i=0;i<4;i++){ setTimeout(function(){ console.log(i); },1000) }
由于 var i 的声明具有函数作用域(在这里指全局作用域),所有的 setTimeout 回调函数实际上引用的是同一个 i 变量。当定时器触发时(即循环已经结束),i 的值已经是 4,因此所有回调打印的结果都是 4。在整个循环过程中只有一个 i,最后连着输出四个4.
又问大概在什么时候输出: 因为定时器不一定准,所以是大概的时间,可能就会回答在4秒后了,实际上,执行同步代码的循环后,定时器四个任务,相继执行,因为间隔时间很短,所以就很像四个定时器并发了一样,实际上还是一个又一个执行的。在大概一秒后。
如何输出 0 1 2 3呢? var => let
块级作用域:在每次 for 循环迭代中,都会创建一个新的 i 实例。这些 i 变量被限制在循环体的块级作用域内。 延迟执行的回调函数:每个 setTimeout 回调函数捕获的是它对应的那个特定的 i 实例。因此,当定时器触发并执行回调函数时,它们能够访问到正确的 i 值,而不是所有回调都指向同一个 i。 那我想要每隔大概一秒输出一个数字呢? 当时我想到了是:
for (let i = 0,time=1000; i < 4; i++,time+=1000) { setTimeout(function () { console.log(new Date()) console.log(i); }, time); } 2025-05-18T14:14:54.808Z 0 2025-05-18T14:14:55.806Z 1 2025-05-18T14:14:56.814Z 2 2025-05-18T14:14:57.800Z 3
- 还有就是使用闭包加立即执行函数了:
for (var i = 0; i < 4; i++) { (function (i) { setTimeout(function () { console.log(i); }, 1000*i); })(i); }
- 进阶:
function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } (async function () { for (var i = 0; i < 4; i++) { await delay(1000); console.log(i); } })();
2. 数组求和
const nestedObj = [1, [2, [3, [4, 5]]], 6, 7], 求和。前几天刚好看到了数组和对象的扁平化,刚好就能用上了,不过面试官好像想让我用更简单的方法,我没想出来。
let sum=0 function flattenObject(obj ) { for (const item of obj) { if (Array.isArray(item)) { flattenObject(item); } else { sum=sum+item; } } return sum; } console.log(flattenObject(nestedObj));// [ 1, 2, 3, 4, 5, 6, 7]
学习点其他简单的方法:
- 递归
function sum(arr) { return arr.reduce((total, item) => { return total + (Array.isArray(item) ? sum(item) : item); }, 0); } const nestedObj = [1, [2, [3, [4, 5]]], 6, 7]; console.log(sum(nestedObj)); // 输出: 28
结语
面了几家大厂后,也有一些心得:
我的分享结束期待下次再见有需要的朋友可以戳
erDiagram