鉴于篇幅我这篇只发两家,剩下两家发下一篇
我今年就是找工作
回顾
各位好,我目前还在找工作。我是今年 23 年 7 月中旬被裁,截止到这篇文章的发布日期呢,已经失业 5 个半月了,回想一下,这半年真长啊,但是过得也真快啊。这并不是一篇纯面经或者八股文之类的,也包含了我的一些个人经历。本次面的四家,大多数是问项目这种的主观题,大家可以做个参考,谢谢。客观题部分答案来自GPT
有不对的地方还请指正。
我是国庆节后开始投简历,到目前为止找工作已经两个半月近三个月。期间呢,BOSS 上打招呼无数,投简历近百,面试机会只有 10 个,这 10 家构成了我的三篇面经。
PS:第一篇面经文末可以交流
附上前两篇面经链接:
面经一:
?北京七年前端大专找工作竟如此坎坷?近N个月面试复盘(附总结答案),快来学习呀!系列更新中(一)
面经二:
系列(二)更新之——北京七年前端大专找工作竟如此坎坷¿?近N个月面试复盘(二)(附总结答案),进来坐坐学习下?持续更新中 - 掘金 (juejin.cn)
写本篇面经前呢,可以先谈谈我失业到现在这几个月都干了啥。也算是对这半年做个总结吧。
七月中旬刚被裁,一点也没有想立马找工作的想法,带着家里人来“大都市”玩了一周,之后我自己就又躺了两周,两周后就回家了一趟,待了一星期,在家期间考取了摩托车 D 证,这一个月就过去了。
国庆节前 8-9 月份的事我已经记不清楚了,可能是整整简历,每天做做饭,也看看面试题,八股文,躺一躺,一天天,一周周就这样过去了,那时候我每周末是不给自己压力的,还是按照之前工作时候的节奏来,周末放松,做家务。期间接触了业余无线电,也产生了兴趣,在 11 月份考取了业余无线电台操作证,12 月份拿到了呼号。
国庆节后投简历,继续看八股文,面试题,手写题,一些算法题之类的。那时候形势很不好,差不多 20 多天,一直没有面试,直到 10 月底那一天,我之前公司的同事给我内推了一家公司,才算是真正的有了第一家面试。很不幸,人家的要求变高了,我经历了好几轮的面试,线上线下的,最终还是没有通过,对方想招前端架构那种的,说按半年前的要求来说的话就通过了,但是现在门槛提高了。
11 月 9 号,接了一家设计公司的面试,简历时半个月前投的,这家公司工作时间 995,一面通过,最终二面栽在了手写 Promise.all 上,这家公司一面二面全是八股文,最终二面在前端专家的八股文攻势中败下阵下。
11 月 16 号,接了一家做公益平台技术支持的公司面试,规模不大,但是不加班,965,还挺好,面到最后我问了才知道是想招前端 Leader。在等了三个星期后,给到了不通过的答案。
在同一天,下午去面了一家外包公司的甲方,在国贸,做的是银行业务。令我最印象深刻的是那个面试官小姑娘一直揪着问我 VueX 怎么取值。最后还是没有通过。
11 月 23 号,接了一家做直播公司的面试,主要问的是我简历里的项目。最终是没过,可能觉着之前做的业务不符合吧。
同一天,接了美团外包的面试,一面过了,第二天二面栽在了一道算法题上,我算法很一般,做的时候还理解错了题意,没有通过。
这是截止 11 月份的面试,12 月份呢,我是在上半个月接到了 4 个面试,就是本篇要写的面经,我先把结果写出来。
12 月 11 号,那天北京下大雪,接了家做区块链数字藏品公司的面试,在昌平,头天晚上刚下的雪,公司还是在远离地铁站的村子的对面的鸟不拉屎的园区里。公司环境一般,面试问的简历项目。印象深刻的是,上来就问我前前公司的项目。面试完回家我就得感冒了,那一周过得很难受,但是这个月的面试就还集中在这一周。最后结果是,现在还没给结果,应该是没过。
同一天的下午,面了一家做海外电商数据的创业公司,1095,大小周, 公司不大,北京这边有 5-6 个人,目前一个前端。聊的挺好,最后是没有通过,人家说要求更严苛一些。
12 月 12 号,面了一家 K12 教育的公司,公司规模也不大,问的主要是 Vue api 这种,以及使用,我好久没写项目了,答的不是很好,关键我回答完,那个面试官一直问,是吗是吗是吗?最后的结果是没过。
12 月 13 号,面了一家做广告数据的公司,先是线上一面二面,没有问八股文,过了。12 月 19 号去线下三面,主要问我简历里的问题,还有听面试官吹业务,教我怎么聪明的选公司。这个是最有机会成功的,毕竟已经到最后面了,但是最后结果是没过,三面面试官 CTO 觉着我能力差点,这次的岗位要求能力高。。。这个,不知道是咋判断出这个结果的。我去三面之前看岗位关闭了,没想到我面完又看发现岗位又打开了,那时候我就知道,没过。
能看到这里的,给你们点个赞,棒棒的。原谅我写这么多的废话来舒缓我的情绪,谢谢大家。
最后是正题,这四家的面经我在此给读者们奉上。我把问答都尽量详细的列出来,像逐字稿,答的不好的地方大伙们见谅。
艺 XX(12 月 11 日,现场面)
一面
1. 上上家公司带了几个人,怎么分工合作的?怎么在期限内完成项目并且分工也合理?
对,我主要还是负责前端这块的部分,我上边有个架构师负责整体部分。举个例子来说吧,就按我那个项目,项目呢有几个关键节点的时间要保证,第一个节点呢是客户要拿这个系统去参加展会,那这样的话,倒排期的情况下时间就比较紧张了,首先我们选择增加人手,还有日常的加班。肯定啊,整体是做不完再的,只能是在这个节点内先保证基础功能的完成,项目可以跑通。分段考虑,制定阶段性的目标。
2. 好了好了我知道了,大的方面你就不用说了,你用的什么技术栈,Vue 是吧,就比方说你自己是怎么去评估,就比方说你自己有代码可以复用是吧,谁去做这块,还有我们要搭一个全局状态管理是吧,不能说你是个人都去写全局状态管理是吧,到时候你们都会冲突,我定一个变量是吧,我通知他们不要跟我去吧冲突。。。啦吧啦吧啦的,最后问:你们怎么分工的?
整体的架构搭建,还有技术上的选型,比如要定用什么框架 UI 库,基础的组件以及方法封装,比如权限管理系统的设计,还有重点难点业务的开发,这种都是我来负责,剩余具体业务方面就分配给其他前端来做
3. 难点是怎么划分的?
其实每个人对难点的理解是不同的,站在不同人的角度来看,可能我觉着难,您就觉着不难。具体到项目上,举个例子当时觉着有难点的地方一个是权限管理系统,自己就去找资料解决,再参考其他管理系统的设计,什么路由级,按钮级的。说着说着说跑题了我就问:您刚才是问?
4. 比如这个状态管理都是由你负责是吧?
这个 Vuex 这种把代码规范定义好,哪里需要就往哪里加,您是想听哪一部分的回答?我感觉我一直没有答到点上
5. 我想知道啊,前端这个技术不是所有人都有一样的技术
这个对,任务呢需要根据不同人的能力来划分任务,比如说难一点的就由我来负责
6. 比如说哪些难的模块是怎么去处理的,主要想看项目经历
这个项目是 19 年做的,离现在也有几年了,可能有些地方我记不太清楚了,我记着当时还有一个难点,就是有这么个需求,部署在客户内部系统的时候不需要本项目自身的登录系统,需要接客户内部系统登录,但是其他地方就还是走正常的项目登录,这就是当时的一个难点。
最后是想到通过打包时注入命令来区分是否需要登录等等等的(此处讲具体逻辑)。这个需求时后期加的,所以改动地方还是挺多的,需要对项目整体业务的了解还有代码的把控比较高。
7. 登录怎么区分游客登录或者真实登录?
您是指这个项目还是其他项目,我这个项目时没有游客登录的,如果有游客登录的话,游客点进去时试用呢,还是其他呢,是否需要给游客创建一个临时的 ID 这种,游客的权限有多大呢?我不太清楚您的具体的要求,您可以再讲一下?
8. 游客访问记录这种的,就给他就记录下来
就是不登录用户呗,他不能用系统里边的东西,咱们是后台管理系统啊,还是其他面向 C 端或者是 B 端的
9. 就是这个人进来就会有一些记录什么的
这个就是浏览量呗,我可以这么理解嘛,PV UV 这种的(应该是浏览轨迹)
【也算吧,他说】
我回答:这种一个是可以后端去统计,可以看到哪些请求是已经登录的用户的,哪个带登录 token 了哪个没带。还有前端就是可以使用第三方统计工具,比如神策,火山这种的,上报的时候登录用户把用户 ID 带上去,然后去后台可以筛出来哪些是登录的哪些是未登录的,以及页面停留时间等。
10. 你上一家公司是做啥的?
此处介绍老东家业务,以及我负责的部分,还有取得的成绩
11. 之前主要用的 Vue3 还是 2
2
12. 还写过抖音小程序是吧,微信小程序也做过是吧
介绍项目情况
13. 微信小程序和抖音小程序有啥区别?
没啥区别,抄来抄去的,顶多是用法还有 api 不一样,我三年前开发字节小程序的时候,有的东西开发文档上不清楚,我会去参考微信小程序的开发文档
14. 比如我现在有个微信小程序,怎么快速做一个抖音小程序?你应该知道里边的区别吧
真要做通用的话,Vue 有个框架 uni-app,做一份可以适配好多端,react 有 taro。
组件名字啊不一样啊,写法不一样啊,具体的得多看看各自的文档,应该会有开发好了开源的迁移的工具,具体没有了解过,也没有用过
15. 开发过浏览器插件?
介绍这个项目
16. 比如说这个插件开发完别人这么安装?
先走商店过审,然后把包下下来放到我们自己的落地页里,落地页会提供安装教程以及几种安装方式
17. 会报病毒嘛?
并不会,我们放出来的包都是经过商店审核的等等等等
18. 开发插件用的什么技术?
Vue,然后具体讲讲怎么开发浏览器插件,以及我升级插件版本的历程
19. 开发过小游戏?
是的,5 年前开发的,为 XXX 开发的一款,您看的是我的在线简历是吧,我的附件简历里没有写这部分的经历
【哦,我看的 BOSS 上的】(我把我简历送过去一份)
又讲了下游戏情况
20. 我们最近也有几个小程序还有一个小游戏项目
什么样类型的游戏?
【逃杀】
大逃杀?第一人称 FPS 设计游戏?这种的话?还能算小游戏嘛?
21. 用 Vue3 也比较多是吧,但是我在你简历上没有看出来,为什么
有啊,我在简历里写了,又一个小的作业批改系统是我用 Vue3 开发的,其他的因为篇幅原因,没有写过多
22. Vue2 转 Vue3 需要注意哪些细节?
我先问是指项目升级,还是我用法上的?
【项目升级】
我首先讲了我自己的项目是怎么做的,拆分需求出来做子系统。如果是老项目直接 2 升级 3,首先需要评下是否值得升级,是否需要升级,一般老项目都很稳定,没必要去做这个。好,如果真的需要升级,首先得去看官方文档里的升级指南,肯定会有一些破坏性的更新,然后再评估是否适合我们的项目。其实还有一种方法,引入 Vue3 点 composition api 的包,也可以在 2 项目里写 3 的新语法
23. 写过多语言是吧?
用 i18n
24. 用过 keep-alive,我记着他有问题啊?
(是我简历里写的)有什么问题,我用的时候没有遇到过问题,然后就讲我的使用场景
25. 缓存太多会卡,遇到过吗?
我是缓存了后台管理系统的 tab 页签,做了个数限制,没碰到过卡顿
26. 怎么解决 token 失效再请求的问题
(这也是我简历里写的)吧啦吧啦吧啦
附上我之前看过的文章
同时多个axios请求怎么实现无痛刷新token - 掘金 (juejin.cn)
27. 刷新 token 什么时候过期?登录 token 什么时候过期?
记不清楚了,后端定的
28. 怎么控制 token 失效后多个请求发起多个队列的问题?
记不清楚当时的解决方案了,应该是第一个失效后会存一个标识符,就不会再接收后续失效的队列了
29. 你遇到过 Vue2 变量提升的问题吗?比如说父组件的传参影响到了子组件
没有
30. 我看你封装了公共的购买方法是吧
然后我吧啦吧啦吧啦讲一堆
31. 上家离职原因
32. 上家给你多少钱啊?
33. 你期望多少钱
比上家高就行
34. 为什么你在上家不涨,现在裁员了还要求涨?
我上家涨了啊,我刚进来可不是这么多,我目前的薪资也保持了一两年了
35. 我看你 7 月份离职,现在都 12 月份了,歇了半年啊?
我就吧啦吧啦一顿输出
36. 找工作几个月
俩月
37. 为啥找这么久
没合适的
【是没合适的还是你不想去?】
面试机会不多现在
38. 有什么想问的
问业务,问项目,问开发人数,问面试岗位负责的业务,问上班时间,其余福利五险得问 hr(没有测试人员)
面了 50 多分钟,整体感觉驴唇不对马嘴,现在还没有给结果,应该是没过
和 XXX(12 月 11 日,线下)
一面
1. 个人介绍
2. 在上家公司主要用说什么技术栈
框架啊是吧,框架主要是 Vue,然后聊项目,引出来下一个问题
3. 权限动态路由实现是从后端返回路由,还是前端处理路由
讲一讲我之前的后台管理系统的权限设计。统计项目中所有需要权限的地方,与后端共同定义不同权限所代表的 ID,通过拿到权限 ID 列表递归路由,筛选出符合权限的路由,再用动态路由生成,实现路由级权限管理。按钮级就页面判断,也可以封装自定义指令,封装权限判断组件来解决。
4. 看你解决了 token 失效的问题
参见上一家的第 26 题
5. 看你之前项目使用 Echarts 也挺多的,问了一个 Echarts 的具体使用场景,这里就不展开说了
6. 简单描述一下深浅拷贝
深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是关于复制对象或数据结构时的两个不同概念。
a. 深拷贝(Deep Copy):
深拷贝是一种创建对象副本的方式,它会递归地复制对象的所有嵌套层级,包括嵌套对象、嵌套数组等。深拷贝生成的副本是完全独立于原始对象的,对副本的修改不会影响原始对象。
// 通过 JSON.stringify 和 JSON.parse 进行深拷贝
const originalObject = { a: 1, b: { c: 2 } };
const deepCopyObject = JSON.parse(JSON.stringify(originalObject));
console.log(deepCopyObject); // { a: 1, b: { c: 2 } }
这种方法的局限性是它无法处理包含函数、RegExp、Date 等特殊对象的深拷贝。
b. 浅拷贝(Shallow Copy):
浅拷贝是一种创建对象副本的方式,它只复制对象的一层结构,而不会递归复制嵌套对象。这意味着对于嵌套对象,浅拷贝生成的副本和原始对象共享同一嵌套对象的引用,因此对副本的修改会影响原始对象。
// 通过 Object.assign 进行浅拷贝
const originalObject = { a: 1, b: { c: 2 } };
const shallowCopyObject = Object.assign({}, originalObject);
console.log(shallowCopyObject); // { a: 1, b: { c: 2 } }
在浅拷贝中,常见的方法有 Object.assign
、Array.prototype.slice
(对于数组)、扩展运算符 ...
等。这些方法只复制对象或数组的一层结构,对于嵌套对象,仍然共享引用。
需要根据具体需求选择深拷贝或浅拷贝。深拷贝通常在需要完全独立副本的情况下使用,而浅拷贝则更适用于简单对象或在不需要修改嵌套层级的情况下。
7. 面对后台返回的一些复杂数据,你的思路有哪些,从哪儿下手?
在项目中呢,就直接从接口文件那里把数据格式化掉,处理掉,就可以直接在组件中使用,你说的思路,可以再具体讲一下吗
8. 像这种复杂数据,我们要优先知道我们想要得到什么
是,需要输出什么,后端接口返回过来是什么格式,毕竟也是自己后端开发的,想要什么样的数据可以跟后端沟通。。。等等等
9. 平时有没有研究什么设计模式?
我只答了一个工厂模式,附上 GPT 的回答
当涉及设计模式时,每个模式都有其独特的特征和应用场景。以下是几种设计模式的详细解释:
- 单例模式(Singleton Pattern):
- 描述: 保证一个类只有一个实例,并提供全局访问点。通过私有化构造函数和一个静态方法获取实例来实现。
- 应用场景: 用于管理全局配置、日志记录、数据库连接等唯一资源。
- 工厂模式(Factory Pattern):
- 描述: 定义一个创建对象的接口,但让子类决定实例化哪个类。客户端通过调用工厂方法而不是直接使用构造函数来创建对象。
- 应用场景: 当一个类无法预知它所需的对象的类时,或者需要将对象的创建延迟到子类时。
- 观察者模式(Observer Pattern):
- 描述: 一对多的依赖关系,当一个对象状态变化时,所有依赖它的对象都会得到通知并更新。主题维护一组观察者,通常通过添加、删除观察者来进行管理。
- 应用场景: 实现事件处理系统、UI组件更新、消息队列等。
- 策略模式(Strategy Pattern):
- 描述: 定义一系列算法,封装每个算法,并使它们可以相互替换。算法独立于客户端使用它们的代码。
- 应用场景: 当一个类有多个实现方式,或者需要动态地切换算法时。
- 装饰器模式(Decorator Pattern):
- 描述: 动态地给一个对象添加一些额外的职责,而不改变其结构。通过创建一系列的装饰类,每个装饰类包装原始类,可以递归地为对象添加功能。
- 应用场景: 扩展类的功能而不修改其代码,例如日志记录、验证等。
- 适配器模式(Adapter Pattern):
- 描述: 将一个类的接口转换成客户端所期望的另一个接口。通过创建一个适配器类,将新的类适配为原有代码期望的接口。
- 应用场景: 使用新的接口,但无法直接修改现有代码时,通过适配器进行接口转换。
- 模板方法模式(Template Method Pattern):
- 描述: 定义一个算法的框架,将一些步骤延迟到子类中。模板方法在基类中定义,但某些步骤的实现由子类提供。
- 应用场景: 算法的结构相对稳定,但某些步骤可能有不同的实现,让子类提供这些实现。
这些设计模式是常见的、通用的解决问题的方法,对于软件开发者来说,了解它们并在适当的场景使用它们可以提高代码的可读性、可维护性和可拓展性。
当涉及设计模式时,了解实际应用是很有帮助的。以下是几种常见设计模式的实际例子:
- 单例模式(Singleton Pattern):
- 例子: 一个日志管理器,负责在整个应用中记录日志。通过单例模式确保只有一个日志管理器实例,并全局提供日志记录服务。
class Logger {
constructor() {
this.logs = [];
}
log(message) {
this.logs.push(message);
console.log(message);
}
static getInstance() {
if (!this.instance) {
this.instance = new Logger();
}
return this.instance;
}
}
const logger = Logger.getInstance();
logger.log('Log entry 1');
logger.log('Log entry 2');
- 工厂模式(Factory Pattern):
- 例子: 一个页面控件工厂,负责创建不同类型的 UI 控件。客户端通过工厂方法获取所需的控件实例。
class Button {
render() {
console.log('Rendering a button');
}
}
class Input {
render() {
console.log('Rendering an input field');
}
}
class UIControlFactory {
createControl(type) {
switch (type) {
case 'button':
return new Button();
case 'input':
return new Input();
default:
throw new Error('Invalid control type');
}
}
}
const factory = new UIControlFactory();
const button = factory.createControl('button');
button.render();
const input = factory.createControl('input');
input.render();
- 观察者模式(Observer Pattern):
- 例子: 一个新闻订阅系统,新闻发布者是主题,订阅者是观察者。订阅者可以订阅多个主题,当主题发布新闻时,订阅者收到通知。
class NewsPublisher {
constructor() {
this.subscribers = [];
}
subscribe(subscriber) {
this.subscribers.push(subscriber);
}
unsubscribe(subscriber) {
this.subscribers = this.subscribers.filter((s) => s !== subscriber);
}
publishNews(news) {
this.subscribers.forEach((subscriber) => subscriber.notify(news));
}
}
class Subscriber {
constructor(name) {
this.name = name;
}
notify(news) {
console.log(`${this.name} received news: ${news}`);
}
}
const publisher = new NewsPublisher();
const subscriber1 = new Subscriber('Subscriber 1');
const subscriber2 = new Subscriber('Subscriber 2');
publisher.subscribe(subscriber1);
publisher.subscribe(subscriber2);
publisher.publishNews('Breaking news: Important event!');
这些例子演示了设计模式如何在实际应用中提供灵活性、可维护性和可拓展性。设计模式的目标是解决通用问题,因此可以在不同的场景中灵活应用。
10. 观察者模式
当然!让我们用新闻订阅系统深入研究观察者模式示例:
// 主题 - 新闻发布者
class NewsPublisher {
constructor() {
this.subscribers = [];
}
// 订阅
subscribe(subscriber) {
this.subscribers.push(subscriber);
}
// 取消订阅
unsubscribe(subscriber) {
this.subscribers = this.subscribers.filter((s) => s !== subscriber);
}
// 发布新闻
publishNews(news) {
this.subscribers.forEach((subscriber) => subscriber.notify(news));
}
}
// 观察者 - 订阅者
class Subscriber {
constructor(name) {
this.name = name;
}
// 接收通知
notify(news) {
console.log(`${this.name} received news: ${news}`);
}
}
// 创建新闻发布者实例
const publisher = new NewsPublisher();
// 创建两个订阅者实例
const subscriber1 = new Subscriber('Subscriber 1');
const subscriber2 = new Subscriber('Subscriber 2');
// 订阅者订阅新闻发布者
publisher.subscribe(subscriber1);
publisher.subscribe(subscriber2);
// 发布新闻,所有订阅者收到通知
publisher.publishNews('Breaking news: Important event!');
详细解释:
- NewsPublisher 类:
- 构造函数初始化一个空数组
subscribers
用于存储订阅者。 subscribe(subscriber)
方法将订阅者添加到subscribers
数组。unsubscribe(subscriber)
方法从subscribers
数组中移除指定的订阅者。publishNews(news)
方法遍历所有订阅者,并调用它们的notify
方法,通知它们有新闻发布。
- Subscriber 类:
- 构造函数接收订阅者的名称。
notify(news)
方法表示当订阅者收到通知时的操作,这里简单地打印收到的新闻。
- 使用实例:
- 创建一个新闻发布者实例
publisher
。 - 创建两个订阅者实例
subscriber1
和subscriber2
。 - 让订阅者订阅新闻发布者:
publisher.subscribe(subscriber1); publisher.subscribe(subscriber2);
- 当发布新闻时,所有订阅者都会收到通知并执行相应的操作。
这个例子演示了观察者模式的基本原理。发布者(主题)维护一个订阅者列表,并在状态发生变化时通知所有订阅者。这种模式支持松散耦合,允许发布者和订阅者之间独立变化,提高系统的灵活性和可维护性。
11. 我看你项目里都是 Vue,对 react 了解过吗?
会写,没做过项目。框架都相似,上手做起来很快的,之前公司一直是用 Vue,没机会做 react 项目,真做的话是没有问题的。
12. 除里 VueX 之外有没有用过其他简单的存状态的方法?
pinia
Pinia
和 Vuex
都是用于状态管理的库,但它们在设计和使用上有一些区别:
1. 响应式设计:
- Vuex: 使用 Vue 的响应式系统。State 的变化会触发视图的更新,但对于异步操作,需要使用
mapGetters
、mapMutations
等辅助函数。 - Pinia: 使用了 Vue 3 的 Composition API,利用
ref
和reactive
提供更直观的响应式状态管理。异步操作更加自然,不需要使用额外的辅助函数。
2. API 设计:
- Vuex: 采用对象风格的状态管理,包含
state
、mutations
、actions
和getters
。 - Pinia: 使用函数式 API,通过
defineStore
定义状态、getters
和actions
。
3. 模块化:
- Vuex: 提供模块化的状态管理,将应用的状态分割成多个模块。模块之间通过命名空间进行隔离。
- Pinia: 也支持模块化,但模块之间的隔离是通过独立的 store 实例实现的。
4. 性能:
- Vuex: 在大型应用中可能存在性能问题,特别是在频繁变更状态时。
- Pinia: 通过更直接的响应式 API 和更小的内部开销来提供更好的性能,特别是在大规模应用中。
5. 支持 Vue 版本:
- Vuex: 主要支持 Vue 2,Vue 3 的支持在 Vuex 4 中有限。
- Pinia: 主要是为 Vue 3 设计的状态管理库,Vue2 也可用。
6. TypeScript 支持:
- Vuex: 支持 TypeScript,但需要额外的类型定义文件。
- Pinia: 本身使用 TypeScript 编写,提供了更好的类型支持。
总结:
Pinia
更加面向 Vue 3,采用 Composition API,并提供更直观、模块化和性能更佳的状态管理。而 Vuex
适用于 Vue 2 和 Vue 3,更为成熟,但在某些场景下可能存在一些复杂性。选择使用哪个取决于你的项目需求、团队经验以及对 Vue 版本的要求。
13. 有没有用过那种直接挂在 Vue 实例上的?
Vue2
Vue Event Bus 是一种在 Vue 应用中进行组件通信的简单方式。它利用 Vue 实例作为中央事件总线,允许不同组件之间通过发布和订阅事件来进行通信。
下面是使用 Vue Event Bus 的基本步骤:
- 创建事件总线:
在一个单独的文件中创建一个新的 Vue 实例,充当事件总线。
// EventBus.js
import Vue from 'vue';
export const EventBus = new Vue();
- 在需要通信的组件中导入事件总线:
// ComponentA.vue
import { EventBus } from './EventBus';
export default {
methods: {
sendDataToComponentB(data) {
// 发布事件,传递数据
EventBus.$emit('data-event', data);
}
}
};
- 在接收数据的组件中订阅事件:
// ComponentB.vue
import { EventBus } from './EventBus';
export default {
data() {
return {
receivedData: ''
};
},
mounted() {
// 订阅事件
EventBus.$on('data-event', (data) => {
this.receivedData = data;
});
}
};
这样,当 ComponentA
中调用 sendDataToComponentB
方法时,会触发 data-event
事件,而 ComponentB
中通过订阅这个事件就能接收到数据并进行相应的操作。
虽然 Vue Event Bus 是一种简单而灵活的通信方式,但在大型应用中,可能会引起一些问题,例如事件名称的冲突,以及组件之间的关系不够清晰。在这些情况下,可能需要考虑使用 Vuex 或其他更复杂的状态管理方案。
Vue3
Vue 3 中使用 EventBus 的方法与 Vue 2 稍有不同,因为 Vue 3 引入了 Composition API。以下是在 Vue 3 中使用 EventBus 的基本步骤:
- 创建事件总线:
在一个单独的文件中创建一个provide/inject
对,充当事件总线。
// EventBus.js
import { ref, provide, inject } from 'vue';
const eventBusKey = Symbol();
export function useEventBus() {
const events = ref({});
function $on(eventName, callback) {
if (!events.value[eventName]) {
events.value[eventName] = [];
}
events.value[eventName].push(callback);
}
function $emit(eventName, ...args) {
if (events.value[eventName]) {
events.value[eventName].forEach((callback) => {
callback(...args);
});
}
}
provide(eventBusKey, {
$on,
$emit
});
return {
$on,
$emit
};
}
export function injectEventBus() {
return inject(eventBusKey);
}
- 在需要通信的组件中使用事件总线:
// ComponentA.vue
import { useEventBus } from './EventBus';
export default {
setup() {
const { $emit } = useEventBus();
function sendDataToComponentB(data) {
// 发布事件,传递数据
$emit('data-event', data);
}
return {
sendDataToComponentB
};
}
};
- 在接收数据的组件中使用事件总线:
// ComponentB.vue
import { useEventBus } from './EventBus';
export default {
setup() {
const { $on } = useEventBus();
const receivedData = ref('');
$on('data-event', (data) => {
// 处理接收到的数据
receivedData.value = data;
});
return {
receivedData
};
}
};
这个示例中,useEventBus
函数提供了 $on
和 $emit
方法,它们分别用于订阅事件和发布事件。在组件中,通过 setup
函数使用 useEventBus
来获取事件总线的实例,并调用相应的方法。
这样,当 ComponentA
中调用 sendDataToComponentB
方法时,会触发 data-event
事件,而 ComponentB
中通过订阅这个事件就能接收到数据并进行相应的操作。这是一种在 Vue 3 中使用 Composition API 的简单的 EventBus 实现方式。
14. vue 的 data 为什么要 return 一个函数
在 Vue 2 中,组件的 data
必须是一个返回对象的函数。这是为了确保每个组件实例都有自己的数据副本,而不是共享同一个对象。
例子:
Vue.component('my-component', {
data: function() {
return {
count: 0
};
},
// ...
});
这个函数每次被调用时,都会返回一个新的对象,因此每个组件实例都会得到一个独立的 data
对象,避免了组件之间数据共享引起的问题。
在 Vue 3 的 Composition API 中,data
的声明方式有所不同。使用 setup
函数返回一个对象,其中的变量和函数可以在组件中直接使用,不需要再用函数返回。这样,Vue 3 的组件不再需要用函数返回 data
。
例子:
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
return {
count
};
}
};
在 Vue 3 的 Composition API 中,ref
和其他响应式 API 可以用来声明组件的响应式数据,不再需要像 Vue 2 中那样使用函数返回一个对象。
15. 浏览器插件多了多久?自己做还是?发到 Chrome 商店了吗?
16. 插件的 Manifest V2 升 V3 是你做的吗?
因为这个公司也有自己的插件,他们也是 V2 版本的,可能是了解下我的升级方案。
是的,讲了下升级的必要性以及怎么升级的。
17. 升级中遇到的最大问题是什么?
有一个库打包后的 Manifest 文件不符合 V3 规范
18. 是哪个库?
我就讲了下是什么库。
19. 是 webpack 里的吗?等等等等
经过交流,我把我的解决方案告诉了他。
20. 正好我们也有这个 V2 版本的插件,还没升级,你帮看一下。
好家伙,我一看,他的项目引入的库和我引的一样,那后边升级遇到的问题应该也和我遇到的差不多。
经过交流,他是打包完之后有问题,我和他探讨了方法,并指出了问题所在。
然后把需要注意的点讲了下。最后我问了下,他目前遇到的问题。
21. 对,我现在还没空搞这个升级,想着等假期后有空了再搞。
问了下他插件是做什么的,以及业务形式。然后我给了我的升级建议。
22. 没有什么问题了,你有什么要问的吗?
问了岗位负责的业务还有公司业务以及规模、福利待遇等问题
二面
1. 自我介绍
2. 聊一个对你技术上提升比较大的项目
我就讲了下之前 19 年带领团队做的一款后台管理系统
3. 那你这个项目有遇到过什么问题
4. 你解决那个问题的方法,他的本质的原理是什么
5. 解决这个问题花了多久,是如何想到解决办法的
6. 还有其他的吗?
我讲了下开发插件以及升级版本所遇到的问题
7. 学习插件开发是通过什么方式学的
8. 以哪种方式为主,哪个是看的最多的
9. 插件从 V2 升级到 V3,怎么解决升级过程中遇到的问题
10. 怎么发现升级过程中的问这个问题,通过什么方式定位到的问题
11. 在上家公司主要做什么工作,有碰到过难一些的任务吗?
12. 我看你写了将 webpack 升级成 vite,启动项目就秒开,他的内部原理是什么
我讲解了升级的必要性以及 vite 的原理,为什么比 webpack 快
Vite 比 Webpack 快的主要原因在于其采用了一些优化策略和新的构建思路,使得开发和构建过程更加高效。以下是一些 Vite 相对于传统的 Webpack 构建工具的优势:
- 按需编译(Just-in-Time Compilation):
- Vite: 使用按需编译的方式,仅在需要时编译和提供模块。每个模块都有自己的构建上下文,这样在开发过程中只会编译正在编辑的模块,而不是整个应用。
- Webpack: 通常采用预编译的方式,对整个应用进行全量编译。这在大型项目中可能导致较长的构建时间。
- ESM(ES Module)导入:
- Vite: 利用现代浏览器对 ES Module 的原生支持,直接使用 ESM 语法进行导入,无需像 Webpack 那样对模块进行转译。
- Webpack: 需要将模块转译为 CommonJS 或 AMD 格式,导致一些性能损耗。
- 开发服务器:
- Vite: 使用原生 ES Module 运行时,配合 HMR(热模块替换)来实现快速的模块更新,避免了传统的构建刷新。
- Webpack: 使用 HMR 但在某些情况下仍需要重新构建。
- 无需 Bundle:
- Vite: 在开发阶段无需生成大的打包文件,使用原生 ES Module 运行时,减小了构建和传输的体积。
- Webpack: 通常需要生成较大的 bundle 文件。
- 依赖解析:
- Vite: 借助浏览器的原生模块解析能力,避免了复杂的依赖图谱分析。
- Webpack: 需要构建整个依赖图谱,可能导致构建时间增加。
总体而言,Vite 的设计思路更加符合现代前端开发的需求,尤其在快速的开发体验和构建性能方面具有显著的优势。然而,根据具体项目需求和复杂性,选择使用 Vite 还是 Webpack 可能会有所不同。
13. vite 打包出来的文件和 webpage 打包出来的文件有什么区别
vite 使用 rollup 打包,按我的理解来说,打包后的文件没有什么区别。
14. 我看你统一封装了购买逻辑,可以讲一下吗?
我把这个项目的情况,以及为什么封装,怎么封装,封装后有什么效果,都说了一下
15. 了解设计模式吗?
参见上边的回答
16. 介绍一下你这个作业批改系统
17. nginx 配置拦截主要起什么作用
拆分项目
18. 那分开之后主项目与子项目登录是如何做的
19. 了解 cookie 的安全机制吗?
Cookies 是一种在客户端和服务器之间传递信息的机制,但它们也涉及到一些安全性的考虑。以下是一些与 Cookie 相关的安全机制:
- HttpOnly 属性:
- 使用
HttpOnly
属性可以防止通过 JavaScript 脚本访问 Cookie。这有助于防止 XSS 攻击,其中攻击者尝试通过注入恶意脚本来获取用户的 Cookie。
Set-Cookie: sessionid=123; HttpOnly
- Secure 属性:
- 使用
Secure
属性可以确保 Cookie 只在通过 HTTPS 协议加密的连接中传输。这有助于防止在不安全的网络中拦截和窃听 Cookie。
Set-Cookie: sessionid=123; Secure
- SameSite 属性:
SameSite
属性定义了 Cookie 的跨站点请求行为。通过设置SameSite
,可以防止跨站点请求伪造(CSRF)攻击。
Set-Cookie: sessionid=123; SameSite=Strict
SameSite
的值可以是Strict
、Lax
或None
,分别表示在不同的请求情况下对 Cookie 的限制程度。
- Domain 和 Path 属性:
Domain
和Path
属性可以用来限制 Cookie 的范围,确保它只能被特定的域名或路径访问。这有助于降低 Cookie 被滥用的风险。
Set-Cookie: sessionid=123; Domain=.example.com; Path=/secure
- Cookie 的过期时间:
- 设置 Cookie 的过期时间可以限制其有效期。长期有效的 Cookie 可能被滥用,因此定期更新 Cookie 或设置较短的过期时间可以提高安全性。
Set-Cookie: sessionid=123; Expires=Wed, 21 Oct 2022 07:28:00 GMT
这些安全机制有助于提高 Cookie 的安全性,但开发者在使用 Cookies 时仍需要保持警惕,特别是在处理敏感信息时。使用安全的传输协议(如 HTTPS)、适当的 Cookie 配置和定期的安全审查都是确保 Cookie 安全性的重要步骤。
20. 除了用 cookie 保持登录,还有其他的吗?
LocalStorage 和 Session Storage
21. 用过这种方式的吗?并讲一下他们的区别
LocalStorage
、SessionStorage
和 Cookies
都是用于在客户端存储数据的机制,但它们有一些关键的区别:
- 生命周期:
- LocalStorage: 数据永久存储,除非通过程序或手动清除。
- SessionStorage: 数据仅在当前会话期间有效。当用户关闭浏览器标签页或窗口时,数据会被清除。
- Cookies: 具有可设置的过期时间,可以长期存储在客户端,即使用户关闭浏览器,也能保持一段时间。
- 作用域:
- LocalStorage: 存储的数据在同一域名下的所有页面间共享。
- SessionStorage: 存储的数据仅在同一会话期间的页面间共享。不同标签页或窗口之间的数据不共享。
- Cookies: 存储的数据在同一域名下的所有页面间共享。
- 存储大小限制:
- LocalStorage: 每个域名下通常可以存储 5MB 到 10MB 的数据(不同浏览器有差异)。
- SessionStorage: 存储容量也在 5MB 到 10MB 之间,但同样具体容量因浏览器而异。
- Cookies: 存储容量较小,通常限制在 4KB 左右。
- 传输:
- LocalStorage 和 SessionStorage: 存储在客户端,不会随着 HTTP 请求自动发送到服务器。
- Cookies: 存储在客户端,但会随着每个 HTTP 请求自动发送到服务器,这可能会影响性能。
- 使用场景:
- LocalStorage: 适用于需要永久存储、在多个标签页间共享数据的场景,例如用户的偏好设置。
- SessionStorage: 适用于需要在同一会话期间保留数据、但不需要长期保存的场景,例如表单数据暂存。
- Cookies: 适用于需要在客户端和服务器之间传递数据、保持用户登录状态等场景。
- 安全性:
- LocalStorage 和 SessionStorage: 受同源策略的限制,只能被同一域名下的页面访问。
- Cookies: 受同源策略的限制,但可以通过设置
HttpOnly
属性提高安全性,防止通过 JavaScript 脚本访问。
根据具体的需求,选择使用 LocalStorage
、SessionStorage
或 Cookies
会更加合适。
22. 如果遇到一个下载场景,通过 window.open 下载, 需要 token 鉴权,哪种存储方式是可以的?
我是这么说的,我之前的项目就是用 SessionStorag 存储的 token,下载就通过接口请求附带 token 拿到二进制流,然后生成 a 标签,并模拟点击下载解决。
这是 GPT 回答的
在下载场景中,如果需要通过 window.open
进行下载并且需要进行 token 鉴权,可以考虑使用 Cookies
或者 LocalStorage
(配合 HTTP 请求头)作为存储方式。这是因为这两种方式可以在 HTTP 请求中携带 token,而 SessionStorage
通常只在当前会话期间有效,不适合用于长期保存需要在不同页面或标签中使用的 token。
- Cookies:
- 使用 HTTP 请求头中的 Cookie 字段传递 token 是一种常见的做法。可以通过设置
document.cookie
来存储 token,并在window.open
的请求中自动携带该 Cookie。
// 存储 token
document.cookie = "token=your_token; path=/;";
// 使用 window.open 发起下载请求
window.open("download_url");
- 注意:确保设置了
path=/
,以确保在整个域名下都能访问这个 Cookie。
- LocalStorage:
- 可以将 token 存储在
LocalStorage
中,并在下载请求时手动添加到 HTTP 请求头中。
// 存储 token
localStorage.setItem("token", "your_token");
// 使用 window.open 发起下载请求
window.open("download_url", "_blank", "headers=Authorization: Bearer " + localStorage.getItem("token"));
- 这种方式需要在下载请求时手动添加 Authorization 头。
选择使用哪种方式取决于具体的需求和安全性考虑。注意在使用这些方式时要确保 token 的安全传输和存储,以防止信息泄露和滥用。
23. 如果下载的时候接口报错了怎么办?
有处理错误逻辑,toast 提醒用户等
24. 读过 Vue 的源码吗?
没有读过
25. 有读过其他插件的源码吗?平常有会看一些技术类的书籍吗?
26. 如果要用一款新技术,你平常都是怎么学习的?
27. Vue2 和 3 的区别
Vue 3 是 Vue.js 的下一代版本,相对于 Vue 2 带来了一些重大的改进和新特性。以下是 Vue 2 和 Vue 3 之间的一些主要区别:
- 性能提升:
- Vue 3: 引入了虚拟 DOM 的重写(Faster Virtual DOM),提高了渲染性能。新的编译器和优化的运行时带来更高的效率。
- Vue 2: 虽然性能已经很好,但 Vue 3 通过一系列优化进一步提升了性能。
- Composition API:
- Vue 3: 引入了 Composition API,提供了一种更灵活、更可组合的组件逻辑组织方式,使得代码更易维护和复用。
- Vue 2: 主要使用 Options API,逻辑分散在不同的选项中,有时难以维护较复杂的组件。
- Teleport:
- Vue 3: 引入了 Teleport(传送门)功能,可以方便地将组件的一部分渲染到 DOM 中的不同位置。
- Vue 2: 传送功能需要使用第三方库或手动处理。
- Fragment 和 Portal:
- Vue 3: 支持使用
<template>
标签作为 Fragment,以及使用teleport
实现 Portal 功能。 - Vue 2: 在模板中使用 Fragment 时通常需要使用特殊的语法,而 Portal 需要特殊的处理。
- 全局 API 的变化:
- Vue 3: 移除了 Vue 2 中一些不常用的全局 API,例如
filter
、directive
等,简化了 API。 - Vue 2: 拥有更多的全局 API 选项,但可能导致代码结构较为复杂。
- 响应性系统的改进:
- Vue 3: 使用 Proxy 实现响应式系统,相较于 Vue 2 的 Object.defineProperty 更加灵活和高效。
- Vue 2: 使用 Object.defineProperty 进行响应式数据的劫持。
- TypeScript 支持:
- Vue 3: 在设计上更好地支持 TypeScript,并提供了更好的类型推导。
- Vue 2: 也支持 TypeScript,但 Vue 3 对 TypeScript 有更好的集成。
这些是 Vue 2 和 Vue 3 之间的一些主要区别,Vue 3 引入了许多新特性和改进,提高了性能、可维护性和开发体验。如果要使用 Vue,尤其是在新项目上,考虑使用 Vue 3 以享受这些优势。
28. Vue2、3 响应式的区别
面试没问,我附上去
Vue 2 和 Vue 3 在响应式系统方面有一些显著的区别。以下是主要的差异:
- 实现方式:
- Vue 3: 使用了 Proxy 对象来实现响应式系统。Proxy 提供了更灵活的拦截器,能够捕获更多的操作,使得响应式系统更高效。
- Vue 2: 使用 Object.defineProperty 来实现响应式。这种方式相对于 Proxy 在某些场景下可能性能略低。
- 嵌套属性的响应:
-
- Vue 3: 对于嵌套属性的访问更加深入,可以更好地处理嵌套对象的响应。
- Vue 2: 在处理嵌套对象时,需要在内层对象的属性上使用
Vue.set
或者数组操作方法来确保响应。
- 性能提升:
- Vue 3: 由于使用了 Proxy,响应式系统的性能相对更高效,特别是在访问深层嵌套属性时。
- Vue 2: 使用 Object.defineProperty,性能在一些情况下可能相对较低。
- 数组操作的响应:
- Vue 3: 改进了数组的响应式处理,使得数组的操作更加准确和高效。
- Vue 2: 在使用数组的 push、pop、shift、unshift、splice 等方法时,需要手动触发更新或者使用
$set
。
- Componsition API 的影响:
- Vue 3: 响应式系统与 Composition API 密切结合,Composition API 提供了更灵活的组织组件逻辑的方式,更好地配合响应式系统的使用。
- Vue 2: 使用 Options API,组件逻辑相对分散在不同的选项中。
总体而言,Vue 3 的响应式系统更加灵活、高效,特别是在处理嵌套属性和数组操作时有显著的改进。这使得在使用 Vue 3 开发时,响应式系统能够更好地满足开发者的需求。
29. 现在面试多吗?
30. 你上一家也待好久了吧。。。
我给你讲讲我做的年终报告吧,现在也 12 月份了,今年的也该做了,不过今年该是其他人做了,之前我在的时候每年都是我做。然后吧啦吧啦吧啦一堆
31. 年终报告的动画是你自己做的还是?
32. 怎么处理手机适配问题的
vmin
vmin
是一种视窗相对单位,表示相对于视窗的高度和宽度中较小的那个的 1%。具体来说,1vmin
等于视窗宽度和高度中较小的那个的 1%。
这个单位通常用于创建在不同屏幕大小上都能够合适显示的元素。vmin
的使用场景通常是在需要根据视窗大小来动态调整元素大小的情况,以确保在不同设备上都有良好的可视性。
举个例子,如果视窗的宽度是 1000px,高度是 800px,那么 1vmin
就等于 8px(取 800px 中的 1%)或者 10px(取 1000px 中的 1%)。
33. 有碰到过前端性能问题吗?比如渲染时候卡顿
这就是前端性能优化方面的问题了,什么分包啊,CDN 啊,首屏加载什么的
我再补充一个关于 css 性能的属性
在 CSS3 中,可以通过使用 will-change
属性来提示浏览器一个元素会发生变化,以便进行提前优化渲染。这样可以让浏览器在元素实际发生变化之前进行一些优化准备工作,提高动画或变化的性能。
.element {
will-change: transform;
}
在上面的例子中,will-change: transform;
告诉浏览器这个元素的变换(transform)属性即将发生变化。这样浏览器可以提前分配资源以优化性能,但要注意滥用 will-change
可能会引起性能问题,应该谨慎使用。通常,仅在确实需要进行性能优化时才使用该属性。
34. 算法用的多吗?
算法是我的弱项【面试官回复:也是大多数人的弱项】
35. 问我上上家的经历
36. 上家公司离职的原因
37. 从离职到现在一直在休息吗?
38. 期望薪资多少?
咱们 1095,大小周的话,算时薪不比上家低,有一些涨幅就行
【上家工作时间是?】
1075,不加班
【那你这时薪可不低啊】
(我心想,JD 上写的 20-30K,还嫌我时薪高?)
39. 加班怎么看
钱给够,怎么都好说
40. 有啥要问的吗?
还是之前那一套,这个岗位负责的业务,公司的业务,技术规人员的规模,有没有测试人员,福利待遇这种
面了一个半小时,聊的挺好,最后是没有过
讨论交流
讨论交流请去我第一篇文末