写在前面
关于我:几年年前端工作经验(小于5年),本科非计算机专业,前端算是自学的。个人觉得我技术水平一般(vue、react),就前端要求来说,擅长算法题(面试中的笔试题算都做出来了的),擅长工作中沟通、协调和把控整体的进度。
工作经历:北京,第一家公司小公司(人数小于500),第二家大公司(名字大家都知道那种),第三家公司中型公司(人数小于1000)。
为什么找工作:第三家公司裁员(非个人能力原因)
面试状况:已经拿到offer,入职。
关于本文:是我自己的面试经验,省略了一些开放性问题的答案
一面
1. 自我介绍
2. 介绍下做过的项目
3. 表格怎么做列上的优化
思路和行大体一致,对列做虚拟渲染
4. 虚拟渲染的原理(react-window)
核心思路:只渲染视图中的item
设计思路:
- 将每一个item的高度,偏移量维护在一个全局的map中,保证每行数据只计算一次。这个Map按需计算的,会根据用户的当前滚动位置,计算当前位置上item的数据。
- 通过scroll事件将滚动的条的偏移量,是否正在滚动等信息维护在state中,渲染时会根据state中的值以及Map中的信息,动态计算出可见区域内的item,将这些item渲染到界面上。
- 根据偏移量计算可见区域的item的过程使用二分查找和指数查找提速
- 如果虚拟列表无法确认每行的高度,那么需要计算一个预估高度用于显示滚动条,react-window预估高度的计算方法是,通过Map中的数据获得精准已经加载过的item的高度和,根据用户设置,或是默认的预估高度计算未加载的item的高度和,两者相加就是总的预估高度,预估高度在每次出发render的时候会更新。
- 将计算好的可视区域内的item的样式作为参数传递给子组件,尾提高性能,还将每个item的样式都缓存下来,减少重复计算
5. flex布局和grid布局的区别
grid是一个二维布局模型,它有行和列;flex是一个一维布局
如果一个布局需要多个行和列,那么Grid布局就是最合适的解决方案;
flex适用场景:网站导航,操作列表,搜索框与按,tab菜单,列表
grid适用场景:卡片网格
.wrapper {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 16px;
}
6. eventLoop
console.log('1')
async function async1() {
await async2()
console.log('2')
}
async function async2() {
console.log('3')
}
async1()
setTimeout(function() {
console.log('4')
}, 0)
new Promise(resolve => {
console.log('5')
resolve()
})
.then(function() {
console.log('6')
})
.then(function() {
console.log('7')
})
console.log('8')
7. vue路由原理
路由主要两种模式 hash模式和history模式
hash模式:
- 表现形式上: hash模式的url上一定带有#号
- 原理区别:
-
- hash模式基于锚点,以及onhashchange事件。
- histroy模式基于HTML5中的history模式,history.pushState、replaceState在IE10以后才支持,存在兼容性问题。push会像服务端发送请求,使用pushState不会发送请求,但是会在浏览器上产生历史记录,形成客户端路由
history模式使用:
- 该模式需要服务端的支持
- 单页应用中,服务端不存在某个路由的时候,地址会返回404,提示找不到页面
- history模式下前后端工作过程:刷新页面会像服务端进行网络请求,后端处理history模式,需要将默认的html文件返回给前端,前端获取到文件后在根据路由自行渲染。
8. vue3有哪些写法的改动
- 生命周期
- 3响应式主要使用Proxy,2使用Object.definerProperty
- 组合API(composition API)
- 3中的组件可以无根节点
- Teleport 将插槽中的元素或者组件传送到页面其他位置
- 3删除了filter,官方建议使用方法调用或者计算属性computed进行代替
- 3中data只接受function返回对象
- 3中使用.sync 替换2中的v-model命令
// vue2
<child-component v-model="msg"></child-component>
<!-- 相当于 -->
<child-component :value="msg" @input="msg=$event"></child-component>
// vue3
<child-component :msg1.sync="msg1" :msg2.sync="msg2"></child-component>
<!-- 相当于 -->
<child-component
:msg1="msg1"
@update:msg1="msg1=$event"
:msg2="msg2"
@update:msg2="msg2=$event">
</child-component>
- v-for和key, 3中的key放置在template标签上,不用给每个自己诶单设置一遍
<template v-for="item in list" :key="item.id">
<div>...</div>
<span>...</span>
</template>
- v-bind,参照了对象合并的规则,后者覆盖前者
- v-for和 v-if
9. vue3与vue2的diff算法有啥不同
diff算法是一种通过同层的书节点进行比较的高校算法,避免了对树进行逐层的搜索便利,所以时间复杂度只有O(n)
在diff比较过程中,循环从两边向中间收拢。
vue2的diff算法核心
- 首选进行新老节点头尾对比,头与头,尾与尾对比,头与尾, 尾与头,寻找未移动的节点
- 找出可复用的老节点,创建一个老节点keyToIndex的哈希表map记录key,然后继续遍历新老节点,通过key寻找可以复用的旧节点。
- 节点遍历完成后,通过新老索引,进行移除多余老节点或者增加新节点的操作(新节点 开始和结尾索引之间的及节点)
vue3diff算法核心
- 首选进行新老节点头与头,尾与尾对比,寻找为移动的几点
- 建立一个新节点在旧节点中的位置的映射表,这个映射表元素如果不为空,代表可服用
- 然后根据这个映射表计算出最长递增子序列,这个序列中的节点代表可以原地复用,之后移动剩余的心机诶单到正确的位置即递增序列的间隙中。
vue3和vue2中算法不同
- vue3只会头与头和尾与尾的做对比,对剩余节点的处理方式不同
-
- vue2是痛殴对旧节点列表建立一个{key: oldVnode}的映射表,然后遍历新节点列表中的剩余及诶单,根据newVnode.key在旧映射表中寻找可服用的及诶单,然后打补丁并且移动到正确的位置
- vue3则是建立一个存储剩余及诶单在旧节点上的索引的映射关系数组,建议完成这个数组后即找到了可复用的及诶单,然后年后通过这个数组计算得到最长递增子序列,这个序列中的节点保持不懂,然后将节点数组中的剩余及诶单移动到正确的位置上。
- vue3diff算法会跳过静态节点的diff
10. vue2中的data为啥要写成function
vue组件可能存在多个实例,如果对象形式定义为data,则会导致他们共用一个data对象,那么状态变更将会影响到所有组件实例,这是不合理的;
采用函数形式定义,在initData时,会将其作为工厂函数返回全新的data实例,有效规避多实例之间状态污染问题,而在Vue根实例创建过程中则不存在该限制,因为根实例只能有一个,不需要考虑这种情况
11. 算法- 合并两个已经排好序的数组
function mergeArr(arr1, arr2) {
const res = [];
let i1 = 0;
let i2 = 0;
// 以arr1为基准,加入结果
while(i1 < arr1.length) {
const item1 = arr1[i1];
const item2 = arr2[i2];
if(item1 <= item2) {
res.push(item1);
i1++;
} else if(item1 > item2) {
res.push(item2);
i2++;
}
}
// arr2剩余的加入结果
if(i2 < arr2.length) {
const rst = arr2.slice(i2);
res.push(...rst);
}
return res;
}
12. 算法升级-合并arr2到arr1中(就地合并)
function mergeArr2 (arr1, arr2) {
let preIndex = 0;
let tempArr = [...arr2];
let target = tempArr.shift();
for(let i = 0; i < arr1.length;) {
console.log(i, arr1);
const el = arr1[i];
if(target < el && i === 0) {
arr1.splice(0, 0, target);
target = tempArr.shift();
} else if(i === arr1.length - 1) {
arr1.splice(i, 0, target);
break;
} else if(target >= el && target < arr1[i + 1]){
arr1.splice(i + 1, 0, target);
target = tempArr.shift();
i++
} else {
i++;
}
}
if(tempArr.length) {
arr1.push(...tempArr);
}
return arr1;
}
13. 有什么指标评估性能,并针对其优化
二面
总结:尽量不要说自己不擅长或者不喜欢的地方,突出自己的硬实力和擅长的地方
1. 自我介绍
1. 可以问下你前两家公司为啥离职嘛?
3. 介绍下模板库
4. 你们是怎么分工的?
5. 你们有没有大数据量,数据量大是怎么做的
6. BI还有没有什么项目是你做得比较好,想说的
7. 第二家公司项目呢?
8. 有没有拿到其他公司offer
9. 你为什么入职前面一家公司
10. 你是怎么学习的
- 每日总结自己完成的工作,每天制定学习内容
- 看别人写的代码
- 刷leetcode,leetcode上的题简单的超过500
- 看B站大佬的学习视频
三面
1. 自我介绍
2. 介绍下组件库
3. 说下对你成长/最有挑战的项目
4. 小程序如何做优化
5. 表格查看数据弹窗怎么做优化
6. 说下vue或者react项目中怎么做优化
写在后面
一二面试挺顺利的,面试官对我评分都挺高的(因为是内推的,所以知道)。三面是交叉面,具有一票否决权,没让我通过。
面试的部门做的东西是和我第三家公司都是做数据可视化的,当时还挺想去的,因为不想很快换工作方向,还是想继续在数据可视化方面发展。
关于面试,面试前需要准备,系统的复习,面试更多的是查漏补缺,一定要面试后总结(扩展/联想性的总结),前面的面试内容大概率是会在后面考察到的(毕竟常考的就那些)
最后祝大家面试顺利,有什么问题可评论/私聊,更多面试内容可以查看我的面试专栏。