360的一、二、三面

285 阅读9分钟

写在前面

关于我:几年年前端工作经验(小于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路由原理

简易版 vue-router的 hash 模式实现

路由主要两种模式 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算法有啥不同

juejin.cn/post/715241…

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. 有什么指标评估性能,并针对其优化

juejin.cn/post/727374…

二面

总结:尽量不要说自己不擅长或者不喜欢的地方,突出自己的硬实力和擅长的地方

1. 自我介绍

1. 可以问下你前两家公司为啥离职嘛?

3. 介绍下模板库

4. 你们是怎么分工的?

5. 你们有没有大数据量,数据量大是怎么做的

6. BI还有没有什么项目是你做得比较好,想说的

7. 第二家公司项目呢?

8. 有没有拿到其他公司offer

9. 你为什么入职前面一家公司

10. 你是怎么学习的

  • 每日总结自己完成的工作,每天制定学习内容
  • 看别人写的代码
  • 刷leetcode,leetcode上的题简单的超过500
  • 看B站大佬的学习视频

三面

1. 自我介绍

2. 介绍下组件库

3. 说下对你成长/最有挑战的项目

4. 小程序如何做优化

5. 表格查看数据弹窗怎么做优化

6. 说下vue或者react项目中怎么做优化

写在后面

一二面试挺顺利的,面试官对我评分都挺高的(因为是内推的,所以知道)。三面是交叉面,具有一票否决权,没让我通过。
面试的部门做的东西是和我第三家公司都是做数据可视化的,当时还挺想去的,因为不想很快换工作方向,还是想继续在数据可视化方面发展。

关于面试,面试前需要准备,系统的复习,面试更多的是查漏补缺,一定要面试后总结(扩展/联想性的总结),前面的面试内容大概率是会在后面考察到的(毕竟常考的就那些)

最后祝大家面试顺利,有什么问题可评论/私聊,更多面试内容可以查看我的面试专栏。