最近疫情好转了很多,大家的心也开始躁动不安了,都有跳槽的心思。最近每天都会面试好几个小伙伴,以防不急之需。看了太多的简历,感觉可以分为三种,真假简历、假简历、真简历。那我是怎么问穿这些简历,下面给大家做个分享。
首先发几句牢骚,在刚开始带团队的时候,好不容易带会一个小伙伴,结果小伙伴呆了一年就要走了。最初会抱怨同事不够忠诚,现在就很坦然了,所谓忠诚就是背叛的筹码不够。最近感觉筹码够了,也动了跳槽的心思,恰逢公司每年例行的纳新,所以现在就处于一个不断被面试和面试的状态。面试的人多了,有诸多感触,想和大家分享一下。
下面主要分享一下,我面试过程中遇到假简历,想造假的伙伴们可以看一下,免得踩坑。
一、工作经验方面
很多求职者比较喜欢在这方面造假,主要有以下两种:
1、延长工作经验
去年年底时候,公司想找个经验丰富的前端,准备再设立一个研发部,当时面试一个据称有9年工作经验的大佬。简历非常好看,一家公司待了4年,另一家在职已5年了,一看就感觉很稳定,技术栈很全面,我当时很是激动,心想公司终于肯花钱招大佬了,本人才4年工作经验。
面试中,
- 我:您用Vue.js框架几年了?
- 他:8年了。
- 我当时就点懵,不相信得再问他:“那您是2012年就开始用Vue?”。
- 他:嗯,没错。
- 我仍不死心,再问,那你刚开始用的是Vue几呢?
- 他:当然是Vue2了。
此时心里一万头草泥马奔腾而过,还能不能靠谱点,2012年Vue还没问世吧。面试刚开始就结束了。
在这里我想说:
要造假麻烦先去了解一下前端每个技术栈的发展史吧!!
Vue的简史:
- 2013年7月28日,尤雨溪在GitHub上第一次为Vue.js提交代码,当时还不叫Vue.js。
- 2013年12月8日,尤雨溪在GitHub上发布0.6.0版本,并将项目正式改名为Vue.js。
- 2015年10月27日,Vue.js迎来1.0.0版本。
- 2016年10月1日,Vue.js迎来2.0.0版本,进入2的时代。
其他技术栈的发展史,都可以去git提交日志里面查到的。如Vue的
2、合并工作经验
合并工作经验的小伙伴,跳槽很频繁吧,怕让人觉得很不稳定。想法是好的,的确在同等条件下,我会考虑每份工作经验比较长的。
但是合并的时候,可不可牢记一下你的项目经历要跟工作经验的时间结点对的上。
在4月份刚面试了一位3年工作经验的小姐姐,人非常漂亮,第一印象非常好。
简历上的工作经验:
- 2016.05.10-2018.04.20 xx公司 web前端工程师
- 2018.04.24-2019.03.29 xx公司 web前端工程师
- 2019.4.8-至今 xx公司 web前端工程师
当时我对她早期的一个项目经历非常感兴趣,这个项目是用Vue开发,element做为组件库,主要功能是地图应用和数据大屏,跟公司的一个要启动的项目比较接近。简历上写是她独立完成的,问了她很多东西。感觉还不错,蛮靠谱的,最后想确认一下这个项目的工期。
- 我:这个项目什么时候开始的,花了多少时间完成这个项目
- 她:2018年3月份开始,花了4个多月。
- 2018年3月份,简历上写的是2018年5月份,我把她的话重复一遍,2018年3月份开始,花了4个多月。
- 她:嗯,这个项目功能很多,从开发到上线花了4个多月。接下来她给我描述这个项目的过程。
- 我:3月份开始的,7月份上线的,是不是很赶,中途是不是很经常加班。
- 她;是经常加班,以项目为主。
好吧,我已经跟她确认好几遍了,这个项目经历跟她的工作经验的时间节点对不上,虽然她非常适合,但是最终还是被我pass掉。我不敢打保票,她的工作经验是否真实,之前是否有频繁跳槽。
二、项目经历方面
很多小伙伴在写项目经历时,会习惯的把别人做过的东西据为己有,添加到自己的项目经历里面,自认为这样会使自己的项目经历非常丰富。
欲戴其冠,必承其重。 欲握玫瑰,必承其伤。
别人的东西,如果你没有把它理解透,就把它据为己有,到时候受伤的还是自己。下面用一个面试来说明。
4月中旬时候面试了一位有2年工作经验的小伙伴。项目经历很丰富,短短两年时间做了9个项目,而且很多项目都是负责人。
我就开始一一和他交流起来。
1、讲讲Vue路由是怎么配置
他想了一会儿才回答:“要先import Vue和Vue Router,然后再用Vue.use安装路由。”
我听了接着说那你先写一下吧。
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);
我看了一下,说到:“这个是安装Vue Router,那怎么配置。你继续写一下。” 这次他想的有点久,我提醒:“再给你3分钟思考。” 他磕磕绊绊写出来
const router = new Router({
mode: 'history',
routes:[
{
path: '/a',
component: () =>import('page/a'),
}
]
});
好吧跟挤牙膏似的。我安慰他别紧张,“为什么要用() =>import('page/a')
引入组件a。”
“这样写可以懒加载这个组件。”
“什么懒加载,有什么好处,在哪里体现?”
“懒加载就是在要展示这个组件时,才加载这个组件的资源,可以减少首屏加载时间。”
还可以,我本来还想继续问:“为什么这样写会实现,展示组件时才加载组件的资源?” 算了吧,这涉及到webpack了。
我继续问道,“如果我项目部署在xxx.com/test/下面。路由应该怎么配置?”
他支吾了半天,我提醒到:“base这个选项有没有用过,你平时怎么配置这个选项?”
“用Vue Cli搭建项目时自动生成的,一般都是默认的。”
好吧,我直接告诉他答案;“把base配置为/test/。” 反正下午也没事情,继续聊着吧。继续问:“那配置完路由,怎么引入到项目使用?”
他在纸上继续写
const router = new Router({
mode: 'history',
routes:[
{
path: '/a',
component: () =>import('page/a'),
}
]
});
const vm = new Vue({
el:"#app",
router:router,
render: h => h(App)
})
“你平时都在main.js里面配置路由吗?独立用一个文件配置路由,怎么写”
“把路由写在src/router文件夹下index.js文件中,再把它export导出”
export default router
然后再main.js中写
import router from 'src/router'
const vm = new Vue({
el:"#app",
router:router,
render: h => h(App)
})
“写在index.js文件,那么为啥是import router from 'src/router'
而不是import router from 'src/router/index'
”,我问道。
他又在支吾了,好吧我承认我有点刁难了。
我直接告诉他答案;“因为在webpack的resolve(解析)配置中有mainFiles这个选项,作用是配置解析目录时要使用的文件名,默认是 [“index”],所以才不要写index。”
题外话
这里说个题外话,那为什么import Vue from 'vue';import Router from 'vue-router';
这样可以引入Vue和vue Router,它们不是在node_modules里面吗?
其实还是webpack的resolve(解析)配置中的modules这个选项起作用,告诉webpack 解析模块时应该搜索的目录,默认是["node_modules"]
,所以才会直接去node_modules里面寻找。
而resolve.mainFields这个配置是告诉我们当从node_modules中导入模块时,此选项将决定在 package.json 中使用哪个字段导入模块,默认["module", "main"]
。
在Vue源码中package.json,有这一段配置
"main": "dist/vue.runtime.common.js",
"module": "dist/vue.runtime.esm.js",
所以import Vue from 'vue'
,实际上去加载dist/vue.runtime.esm.js这个文件,而这个文件最后有 export default Vue;
,这样就把Vue引入我们的项目。
回到主题
“没关系,要求把router选项的内容独立配置在一个文件里面,你要怎么做?”
这次他很快就写出来,看来还是会点嘛。在src/router文件夹下建立routes文件,在里面写。
const routes=[
{
path: '/a',
component: () =>import('page/a'),
}
]
export default routes
然后在index.js文件中
import routes from './routes'
const router = new Router({
mode: 'history',
routes: routes
});
看来这位兄台路由的基本配置还是会的,再问深入一点:“怎么通过路由配置地址白名单和黑名单?”
他想了一下,“在beforeEach钩子里面配置”,我点点头示意他继续,“写一下吧”
router.beforeEach((to,from,next) =>{
const passurl = [];
const nopassurl = [];
let ispass = true;
if(passurl.indexOf(to.path) > -1){
ispass = true;
}
if(nopassurl.indexOf(to.path) > -1){
ispass = true;
}
if(ispass){
next();
}
})
还可以,马马虎虎,“那嵌套路由会不会用?”
“有用过。”
“给你个场景,菜单a下面有子菜单a1,子菜单a2,你写一下路由配置和菜单页面文件目录的搭建及 <router-view/>
要写在哪里。”
- a
- layout.vue
- a1
-index.vue
-a2
-index.vue
//layout.vue
<template>
<router-view/>
</template>
const routes:[
{
path:'/a',
component:() =>import('page/a/layout'),
redirect: '/a/a1',
children:[
{
path:'/a/a1',
component:() =>import('page/a/a1')
},
{
path:'/a/a2',
component:() =>import('page/a/a2')
}
]
}
]
嗯,基本配置都会,最后问个问题:“路由404怎么配置?”
这位兄台又愣住了,问我什么是:“什么是路由404?”
好吧,“就是输入一个地址,但是这个地址没有在路由中配置,找不到对应的页面,这时候怎么处理?”
他想了想,“不知道,没用过。” 这次我没说答案。
{
path: '*',
redirect: '/home'
}
*
代表通配符,匹配任何路径,注意含有通配符的路由应该放在最后,不然任何地址都会跳到/home
这个地址。
2、关于首页中头部菜单和侧边栏菜单的功能
通常系统菜单会有多级菜单,而菜单的渲染数据一般来自系统的菜单权限数据,其一般是个树结构数据,当然有些后端比较懒,直接返回数组结构的数据,你还要自己构造树结构数据,怎么构造可以戳这里。
这样会涉及到两方面,一是树结构数据的处理,涉及到递归函数,二是递归组件的使用。
我开始问:“递归函数使用过吧,写一个处理树结构数据构造出一个以节点id为key,value为节点中url值的json”
写了5分钟后,他放弃,直接说不会。
function getUrl(data,result={}){
data.forEach(item =>{
result[item.id] = item.url;
if(item.children && Array.isArray(item.children) && item.children.length){
getUrl(item.children,result)
}
})
}
我再问:“那写一个方法,获取树结构数据的第一项的第一子节点的url,如果该节点还有子节点继续往下获取,直到该节点没有子节点为止。”
写了10分钟,还是放弃。
function getUrl(data) {
let item = data[0];
if (item && Array.isArray(item.children) && item.children.length) {
return getFirstUrl(item.children)
} else {
return item.url
}
};
这也不会,我直接放弃问他递归函数方面的东西。
他强笑解释:“这个会,只是现在有点紧张?”
这跟紧张有毛关系,我安慰他一下,接着问:“递归组件有没有用过,怎么用的,有什么地方要注意的?”
他想了好一会儿,我看他手在抖,说到:“别紧张,慢慢想”
“递归组件应该是组件调用自身?”
“那怎么调用自身?”
他吱唔了半天,“我忘记了”
好吧,服了。
递归组件是通过组件的name选项来调用自身,使用时要注意确保递归调用是条件性的 (例如使用一个最终会得到 false 的 v-if),不然会陷入死循环。
问道这里我就可以判断出来,他根本就没做过头部菜单,侧边栏菜单这些功能。这些项目经历应该有部分是假的。
下午也没什么事情,就继续往下问,看他这些项目经历有多假。
3、关于配置封装axios的提问
“axios是什么,和ajax有什么区别。”我直接问道。
“axios是一个基于promise的HTTP库,ajax不支持promise,如果多个请求之间有先后关系的话,就会出现回调地狱”
“怎么安装axios,怎么引入axios?”
“执行命令npm instal axios --save
安装,用import axios from 'axios'
引入”
“那你是怎么封装axios,大概写一下”
import axios from 'axios'
const service = axios.create({
timeout:3*60*1000,
baseURL:'xxx',
})
export default service
import service from './axios'
export function xxx(data){
return service.post('xxxx',data)
}
export function xxx(data){
retruan service.get('xxxx',{params:data})
}
我看了一下,这么简单。问道:”还有吗?”
他看了我一下。想了想,然后摇摇头。
“你平时登录后,是怎么告诉后端已经登录了”
“有用token和cookie”
“那用token时候,你怎么配置的”
他想了许久,“放在请求头上。”
“怎么放写一下。”
import axios from 'axios'
const service = axios.create({
timeout:3*60*1000,
baseURL:'xxx',
headers:{
token:xxx
}
})
export default service
“那登录接口不用在请求头上设置token,你怎么处理。”
等一会他摇头,那你“axios的拦截器用过吗?”
“有用过,request和response拦截器”
“讲一下这拦截器分别拦截了什么”
“request是在请求前拦截,response是在请求后拦截。”
“response是在请求后被then或catch之前拦截。”我补充了一下。“那用request拦截器实现给请求头加上token,写一下”
他写了几下,放下笔,说:“忘记怎么写了。”
好吧,看来他的项目经历是有多么水。我把答案告诉他。
service.interceptors.request.use(function(config){
const token = getToken();//去获取token,如果有设置在请求头上。
if (token) {
config.headers.token = token;
}
return config;
})
他看了,指了interceptors
这个单词,说:“我就是这个单词不会写,所以才写不出来。”
“噢,那你写一下,怎么对请求回来的数据预处理。”
service.interceptors.response.use(function(res){
const data = res.data;
if (res.status == 200) {
return data
}
})
难道真是那个单词忘记了,才不会写。我在问道:“那写一下怎么对请求出错进行拦截处理?”
service.interceptors.response.use(
function(res){
const data = res.data;
if (res.status == 200) {
return data
}
},
function(err){
this.$message({
message: err.data.msg,
type: 'error',
showClose: true,
})
return err
}
)
我看了他写的代码,已经很肯定他的项目经历是假的。“this指的是什么?”我问道。
“this是Vue实例。”
听到这句话,我已经不想继续问一下,客气跟他说:“回去等消息吧,2天内给你答复。”
关于axios配置的提问,真的只有这么多吗?当然不止,下面继续看我对另一位面试者的面试过程
这位面试者简历上写:熟悉axios,能进一步封装axios,比如请求前拦截处理请求头部,请求返回拦截处理返回数据,设置代理等。
关于之前的问题,我也一一对他进行提问了,答还不错,很快就到问他:“你是怎么对请求返回时做拦截处理的?写一下。” (ps:我公司面试时,都会要求上机面试)
const showErrorMessage = function(message){
vm.$message.close();
vm.$message({
message: message,
type: 'error',
showClose: true,
})
}
const response = function(res){
if(res.status == 200){
const data = res.data;
const message = data.msg ? data.msg : '未知错误';
if(data.code == 518 || data.code == 519){//登录失效
if(vm.$route.query.redirect){
vm.$router.push({
path:'/login',
query:{
redirect:vm.$route.query.redirect
}
})
}else{
vm.$router.push({
path:'/login',
query:{
redirect:encodeURIComponent(vm.$route.fullPath)
}
})
}
}else if(data.code && data.code != 200){
showErrorMessage(message)
}
return data
}
}
const response_err = function(){
if(err.response){
const response = err.response;
const status = response.status;
const message = response.data.msg ? response.data.msg : '未知错误';
switch (status) {
case 400:
showErrorMessage('请求出错');
break;
case 403:
showErrorMessage('拒绝访问');
break;
case 404:
showErrorMessage('资源不存在');
break;
case 405:
showErrorMessage('请求方法未允许');
break;
case 500:
showErrorMessage('服务器内部出错');
break;
case 503:
showErrorMessage('访问服务器失败');
break;
default:
showErrorMessage(message);
}
}
return Promise.reject(err);
}
service.interceptors.response.use(response,response_err);
我看了他的答案,拦截处理这方面应该都会。问问请求头配置方面。
“请求头除了配置token,之外还要配置那些东西,写一下?”
“还要配置Content-Type类型,告诉服务器我是用那种格式将参数传给你,这一般由后端接口定义需要那种类型。”
“那一般由哪几种类型?”
“在项目中常用的有三种类型。”
application/x-www-form-urlencoded
表单默认提交的格式,需要用new URLSearchParams
处理数据后传给后端。application/json
JSON数据格式,需要用new Object
处理数据后传给后端。multipart/form-data
需要在表单中进行文件上传时的格式,需要用new FormData
处理数据后传给后端。
“那么在配置Content-Type类型时,要求后端应该怎么配合”
他想了一会儿,说:“好像不要吧。”
我继续问道,“如果在头部配置token,后端需要做什么。”
“这个不太清楚,不知道。”,我就纳闷,如果是自己配置的,应该会遇到这些问题,结果会导致跨域。
我继续问道:“知道什么是CORS跨域吗?”
“这个不太清楚。”
“那你平时遇到跨域怎么处理的?”
“都是后端帮忙处理的。”
这后端真体贴。后端的Access-Control-Allow-Headers
要配置"content-type,token"
。
“那么如果后端要通过Cookie来验证,应该怎么配置。”
axios.defaults.withCredentials=true;
其实我挺想问他对应的后端要怎么配置,如果有实际配置过,有很大概率会遇到问题的。后端要配置两点
- 把
Access-Control-Allow-Credentials
配置为true,表明服务器同意接受Cookie。 Access-Control-Allow-Origin
不能设置为*
,必须指定明确的、与请求网页一致的域名。如Access-Control-Allow-Origin: "localhost:8037"
。
“讲一下baseURL的作用和合并规则?”
“baseURL是请求地址的基础路径,将自动加在url
前面,除非url
是一个绝对 URL,就是有带http://
或者https://
的。”
“如果后端返回二进制流类型的数据,你要怎么配置,才能接收。写一下”
service.post('xxx',data,{responseType:'blob'});
service.get('xxx',{params:data, responseType:'blob'})
“如果遇到后端返回的id是Number类型,出现精度失真的问题,怎么解决?”
“精度失真,这个后端应该会处理好吧,没遇到过?”
好吧?其实这个问题在项目功能模块越来越多之后,肯定会遇到过。不过这位面试者在这axios这方面比之前那位面试者要好点。
这个问题考核了很多个知识点。
- 什么是精度失真?js的Number类型有个最大值(安全值)。即2的53次方,为9007199254740992。如果超过这个值,那么js会出现不精确的问题。这个值为16位。
- 怎么产生精度失真的? 其实后端返回给前端的值是一个JSON字符串。axios会默认用JSON.parse将器转为json对象,当JSON.parse解析超过16位的数值就会产生精度失真。
- 那怎么解决呢,就要利用到axios这个transformResponse配置选项,这个选项的作用是在传递给 then/catch 前,允许修改响应数据。我们可以先利用正则和
replace()
来将返回数据中超过16位的数值全部替换成字符串,然后再用JSON.parse()转成JSON对象。
transformResponse: function (data) {
data = data.replace(/"\w+":\s*\d{16,}/g, function (longVal) {
let split = longVal.split(":");
return split[0] + ':' + '"' + split[1].trim() + '"';
});
return JSON.parse(data)
}
4、关于项目业务方面的提问
项目业务这方面内容很泛,我一般会按下面三种情况提问。
- 如果简历中的项目经历跟公司业务是契合的,我就会直接以项目经历中的业务为场景来考核
- 如果简历中的项目经历跟公司业务是不是很契合的,我会先问你在这些项目中遇到过什么困难,后面是怎么解决这些困难,然后根据你的回答,来找到突破点来提问考核,看你是不是符合公司的需求。
- 如果简历中的项目经历描述的很简单,不能从里面获取有效信息,我就会根据公司的业务模拟几个场景来考核。
我就第三种情况来举个例子。面试者自述已经独立用Vue全家桶做完了几个复杂的后台系统类的项目。
既然是复杂的后台系统项目,那肯定有有复杂表单。那么就考核一下他对数据来驱动页面展示的能力是咋样的。
基础套餐类型由套餐组合类型决定的。套餐组合类型为常规时,基础套餐类型为基础包。套餐组合类型为一次性时,基础套餐类型为可选包。套餐类型由续费包、加油包、可选包、附加包。
套餐名称的下拉选项是由运营商和套餐类型决定的。套餐类型和套餐名称可以添加多个。
后端接收的参数格式为
data:{
type:'',//套餐组合类型
operators:'',//运营商
packagelist:[
{
packageType:'',//套餐类型
packageName:'',//套餐名称
isMain:'',//是否是基础套餐,1是,0不是.
}
]
}
写一下这个怎么实现。这个业务场景是在表单中非常场景的,如果真是做过多个后台系统项目,这个应该非常简单。答案我就不写了,你们可以自己想一下怎么实现。
三、技能方面
我个人认为技能的熟练度可以分为4个等级。
- 了解:听过这个技能,知道这个技术的概念,是用来实现哪些功能。
- 熟悉:可以凭借技能文档来使用它。
- 熟练:可以脱离文档开发、可以进一步封装它。
- 精通:知道这个技能的原理,可以在去改造和优化它。
这个四个等级,很多文章都会介绍到,没什么好说的。
我这样要强调的是,
- 没有在大量得在项目中使用这个技能,不要去用
熟练
来描述你对这个技能的熟练度。 - 谨慎使用
精通
来描述你对这个技能的熟练度,一旦用,一定会问你,为什么这么使用(原理),而不是只问你怎么使用。
四、写在最后
虽然说简历是一块敲门砖,简历写的厉害一点,这块敲门砖就会重一点,就会砸开更多公司的大门。但是你有没有想过,如果这块敲门砖过重,你在面试过程中能举多久呢?到最后还是砸了自己的脚。
个人认为,简历除了是一块敲门砖,还是一份你和面试官之间交锋的计划书,在简历上面每一句话,都要对其负责,想明白自己能不能驾驭这个句话。驾驭不了,相当你自暴缺点给面试官看,得不偿失。
个人是不排斥假简历,只要有心去作假,对每个假项目经历、假技能都了如指掌,假得都会变成真的。
当然工作经历最好还是不要造假,因为在医社保系统的缴费记录可以看到你的工作经历。
今天的文章『面试的经历』—— 面试官是怎么问穿你的假简历分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22100.html