面试复盘(二):希望可以获得简单直接的回答

1,210 阅读4分钟

面试复盘系列第二篇。最近一直感觉良好,迷之自信...

概况

  • 公司:坐标上海,游戏、社交类,前端团队三四个人「猜测数据 面试官原话:前端小而美」。
  • 面试官:一面 前端开发,二面 技术负责人。
  • 面试结果:未通过(不想去)。
  • 面试感受:没什么感受 o(╥﹏╥)o ,二面提出的几个问题得到的答复都好绕「😵头晕,我是不是问错了」。

面试题(一面)

  1. 为什么转行?8
  2. 重绘和回流?9 ✔
  3. 3D动画?5
  4. vue-router 路由守卫?全局、单个组件。8 ✔
  5. 闭包?7 ✔
  6. JS线程?宏任务、微任务。8 ✔
  7. v-for和v-if哪个优先级高?6 ✔
  8. 兄弟组件间通信,使用$emit最简单的方法?5 ✔
  9. 有没有自己实现一个简单的 vue? 6 ✔
  10. 是否有关注 vue 的最新动态?8

问题2 重绘和回流

回流(重排)

layout布局 > layer分层 > Paint(sheet) >> tiles >> raster >> draw quad >> display

更新元素的几何属性,如改变宽高、位置等,浏览器会触发重新计算布局,排列元素。

  • 改变元素位置、尺寸、字体大小
  • 改变浏览器窗口
  • DOM操作,DOM元素的添加、删除,DOM元素属性的获取和修改

大量、频繁的重绘,会对性能造成影响。应对措施如下:

  • 多个节点的添加,使用fragment包裹一次性添加
  • 多个样式的切换,使用切换class类名的方式
  • 避免频繁读取会引起重排的属性和方法,主要是宽高、位置等布局类的,如offsetWidth、offsetHeight、getComputedStyle()等
  • 将可能重绘的元素,使用absolute或fixed脱离文档流,较少重排影响的范围
  • 优化动画,canvas、CSS3 transform3D、WebGL,启用GPU硬件加速。

关于DOM操作这块,浏览器自身也做了很多优化,比如将多次回流重绘合到一起。

重绘

元素的外观发生变化,但是没有改变布局。如color、border-style、visibility、background等。

直接进入重新绘制阶段,执行效率比重排高。

Paint(sheet) >> tiles >> raster >> draw quad >> display

问题4 路由守卫

全局守卫

  1. 全局前置守卫。router.beforeEach
  2. 全局解析守卫。router.beforeResolve。调用时机(不同于beforeEach):导航被确认之前,所有组件内守卫和异步路由组件被解析之后。
  3. 全局后置守卫。router.afterEach。

路由守卫(独享)

在路由配置文件定义beforeEnter。

组件内守卫

  1. beforeRouteEnter。渲染该组件的对应路由被confirm前调用,不能获取组件示例this(还没创建)。
  2. beforeRouteUpdate。调用时机:foo/1与foo/2之间跳转时,复用当前组件实例,
  3. beforeRouteLeave。
import VueRouter from 'vue-router';
const router = new VueRouter({
	route: [
		{
			path: '/foo',
			component: Foo,
			beforeEnter: (to, from, next) => {}
		}
	]
});
router.beforeEach((to, from, next) => {});
router.beforeResolve((to, from, next) => {});
router.afterEach((to, from) => {});

new Vue({ router, render:h=>h(APP) }).$mount('#app');

// 组件内
const Foo = {
	template: '...',
	beforeRouteEnter(to, from, next) {
		next(vm => {}); // 可以传入回调,导航被确认时执行回调,通过vm访问组件示例
	},
	beforeRouteUpdate(to, from, next) {}, // next不支持传入回调
	beforeRouteLeave(to, from, next) {}
}

问题5 闭包

一个带了环境的函数。相比纯函数,多引用一些外部变量。

通常函数的作用域及其所有变量都会在函数执行结束后被销毁;而闭包,可以访问上级作用域,即使外层函数执行完,外层函数的作用域中能被闭包访问的,会一直保存在内存中,直到闭包不存在。

  • ES6 之前,利用立即执行函数(创建闭包后立即执行销毁)模仿块级作用域。
  • 节流、防抖、函数柯里化等,都用到了闭包。

问题6 JS线程

JS是单线程的,JS引擎内部会维护一个任务队列。

  1. 接受到任务后,首先判断是同步还是异步任务。
  2. 如果是同步任务,压入执行栈立即执行,执行完毕后出栈。
  3. 如果是异步任务,交给定时器触发线程或HTTP请求线程,在特定时机,由事件触发线程将对应的回调函数的任务加入到队列中。
  4. 当同步任务执行完毕,执行栈清空,会从任务队列头部读取、执行任务。

事件触发线程:用来控制浏览器事件循环,注意这不归 JavaScript 引擎线程管,当事件被触发时,该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。

主线程从任务队列读取事件,是循环不断的。

优先执行主线程上的任务,然后执行微任务,最后执行宏任务。

问题7 v-for、v-if优先级

v-for的优先级更高。也就是v-if将在每个循环项中重复执行。

vue风格指南以及eslint都告诉我们不要把v-if和v-for同时用在同一个元素上。原因:在每个循环项重复执行v-if的判断,对性能有一定的影响。

两种常见的场景,第一是需要使用循环项或其属性判断是否渲染,这个可以将循环列表替换为一个计算属性。第二是需要使用另一个变量作为渲染条件,这个v-if可以移动到容器元素,或者使用template元素。

HTML5的内容模板template标签元素,一个天然display为none的元素(设置display:block就会显示)。加载页面时不会显示,运行时可以进行JS操作。template元素的content属性,是只读的DocumentFragment。

问题8 兄弟组件使用$emit

vm.$root,当前组件的根Vue实例。

vm.$on(event, callback),监听当前实例上的自定义事件。

vm.$emit(event, data),触发当前实例上的自定义事件。

问题9:vue 简单实现

详见 理解&实现(一):Vue2 响应式原理理解&实现(二):Vue3 响应式原理

面试题(二面)

  1. 为什么转行?
  2. 目前在公司负责什么项目?在团队中扮演什么角色?
  3. 是否有做过 h5 小游戏?
  4. 你最看重团队的哪些方面?
  5. 你觉当刚才面试你人的水平如何?是否能对你的技术成长有帮助?
  6. 是否能接受加班?「我们不996,但是项目排期紧的时候,需要你每天都到公司。不忙的时候可以申请调休,但不能连续休好几天 /(ㄒoㄒ)/」
  7. 什么时候能入职?

系列文章

面试复盘(一):面试官「前阿里大佬」果然厉害

面试复盘(三):第一次电话面试「无力」