前端面试百分之九十九过的技巧(一)

针对面试中出镜率比较高的重难点知识梳理。

相比于之前一篇 400 道前端工程师常考必考面试题+详细解答,本篇文章更贴合今年的面试实际。第一篇比较全面,也比较基础,建议先看一遍上一篇再看本篇会更容易理解。

一、ES6常见用法

关于 ES6(泛指 ECMAScript 2015 及以后的版本)几乎是面试必问的,一般的问法是:“平常会使用 ES6 吗?列举几个 ES6 的用法”。

回答出来三四个就差不多了,但回答的每一个都要弄清楚,有的面试官会延伸着追问。

如果时间充足,还是建议看看 阮一峰的 ES6 入门教程 。

1.1 let 和 const

  • let 在块级作用域内有效,不会污染全局变量
  • const 一经声明不能改变。注意保证的是它指向的内存地址不能改变,如果是对象或者数组里面的属性或元素可以改变的。
  • 存在暂时性死区,不能变量提升。
  • 只能先声明再使用,且不能重复声明

1.2 字符模板

用作字符串拼接:你好,${name}

1.3 变量的解构赋值

  • let [a,b,c] = [1,2,3]
  • 交换变量的值:[a,b] = [b,a]
  • 提取 JSON 数据,获取服务器返回数据时有用:let {data, code} = res
  • 输入模块的指定方法:
const { SourceMapConsumer, SourceNode } = require("source-map");
  • 从函数返回多个值: 返回个数组或对象

1.4 扩展运算符

扩展运算符(spread)是三个点(...)。

  • 复制数组:let arr2 = [...arr1]
  • 合并数组:[...arr1, ...arr2, ...arr3]

1.5 Promise

  • 成功调用 resolve,失败调用 reject
  • .then 获取结果,.catch 捕获异常。捕获异常还可通过 .then 的第二个参数
  • .finally 无论成功失败都一定会调用
  • 多个并发的请求,用 Promise.all()
    • 只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。
    • 只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
let p = Promise.all([p1,p2,p3])
p.then(([res1, res2,res3]) => {};

Promise 示例:

new Promise(){
    if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
}.then().catch().finally()

1.6 async await

async 函数是什么?一句话,它就是 Generator 函数的语法糖。 async 函数对 Generator 函数的改进:

  • 内置执行器,直接调用即可。Generator 还需要调用 next() 才能执行
  • 更好的语义。asyncawait*yield 更好理解
  • 返回值是 Promise

1.7 箭头函数

箭头函数 () => {}this 是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,函数在定义时,this 就继承了定义函数的对象。this 一旦确定以后不会改变。

普通函数的 this 指向的是调用它的对象。

二、移动端的 H5 兼容性和适配

常用的适配方案是 rem + flex 布局。

2.1 rem 适配

先在应用的入口设置基准字体大小,越早执行越好。如果设计图尺寸宽度是 750,就除以 7.5。具体的值根据实际情况来算。

document.documentElement.style.fontSize = document.documentElement.clientWidth / 7.5 + 'px';

设置完基准字体大小后,通过 SASS 的函数封装一个方法方便使用

@function px2rem($value){
  @return $value * 0.02rem
}

使用时直接调用封装好的函数即可,value 传 px 的值,方法会自动转换成 rem。

width: px2rem(100)

2.2 采用 flex 布局

flex 布局推荐 阮一峰的 Flex 布局教程:语法篇

三、Vue 相关问题

一般来说中小公司问框架会多一些,具体是 Vue 还是 React 根据公司所用技术栈而定。因为中小公司技术栈比较统一,很少出现同时用两种甚至三种框架的,且更注重干活的能力,所以框架的使用基本会占面试的一半时间。

大公司反而框架问的少,比较注重基础。他们的逻辑是考察你有没有这个能力,如果能力强用啥框架都能快速上手。

因为我对 React 不太熟,所以就只准备了 Vue 的一些问题。下面几个问题都是面试频率非常高的,一定要弄懂。

有一篇文章对 Vue 总结的比较全,这里也是摘录了几个答案。完整的内容看这里:最新的Vue面试题大全含源码级回答,吊打面试官系列

3.1 Vue双向绑定原理

如果问框架的话 Vue 肯定会问这个问题,把下面这一段话背下来就好。

Vue 主要通过以下 4 个步骤来实现数据双向绑定的:

  • 实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
  • 实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
  • 实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
  • 实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。

回答完这个问题有的面试官还会延伸问:Vue3 将会用 Proxy 替换 Object.defineProperty()proxy 有什么优点?

建议把 proxy 相关介绍 看一下,说出几个优点。

3.2 虚拟 Dom 实现原理

这个题的重要性仅次于 Vue 双向绑定原理,建议掌握

虚拟 DOM 的实现原理主要包括以下 3 部分:

  • 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
  • diff 算法 — 比较两棵虚拟 DOM 树的差异;
  • pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。

优点:

  • 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
  • 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
  • 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。

缺点:

  • 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。

3.3 路由缓存

使用 <keep-alive> 可缓存路由。

它有 includeexclude 两个属性,分别表示包含或排除某些路由,值可以是字符串、数组或者正则表达式。

独有的生命周期方法:activiteddeactivited

3.4 路由跳转,name 和 path 跳转的区别

路由跳转的几种方式

// 字符串
this.$router.push('home')

// 命名的路由
this.$router.push({
  name: 'user',
  params: {userId: '123'}
})
//接收参数
this.userId = this.$route.params.userId

// 带查询参数,变成 /user?userId=123
this.$router.push({
  path: '/user',
  query: {userId: '123'}
})
//接收
this.userId = this.$route.query.userId;

name 和 path 跳转的区别在于

  • name 传参用 params,path 传参用 query。
  • 用 name 跳转后参数不会携带到 url 上,用 query 传参参数会携带到 url 上。

3.5 路由守卫

全局前置守卫

注意一定要调用 next(); ,否则钩子就不会被 resolved。

const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
  // ...
  next();
})

全局解析守卫

在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

全局后置钩子

你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {
  // ...
})

路由独享的守卫

你可以在路由配置上直接定义 beforeEnter 守卫:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

这些守卫与全局前置守卫的方法参数是一样的。

组件内的守卫

最后,你可以在路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave

注意要调用 next()

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

这块内容比较多,更详细的介绍看 vue-router 官方文档

3.6 Vue 和 React 之间的区别

Vue 的表单可以使用 v-model 支持双向绑定,相比于 React 来说开发上更加方便,当然了 v-model 其实就是个语法糖,本质上和 React 写表单的方式没什么区别。

改变数据方式不同,Vue 修改状态相比来说要简单许多,React 需要使用 setState 来改变状态,并且使用这个 API 也有一些坑点。并且 Vue 的底层使用了依赖追踪,页面更新渲染已经是最优的了,但是 React 还是需要用户手动去优化这方面的问题。

React 16以后,有些钩子函数会执行多次,这是因为引入 Fiber 的原因,这在后续的章节中会讲到。

React 需要使用 JSX,有一定的上手成本,并且需要一整套的工具链支持,但是完全可以通过 JS 来控制页面,更加的灵活。Vue 使用了模板语法,相比于 JSX 来说没有那么灵活,但是完全可以脱离工具链,通过直接编写 render 函数就能在浏览器中运行。

在生态上来说,两者其实没多大的差距,当然 React 的用户是远远高于 Vue 的。

在上手成本上来说,Vue 一开始的定位就是尽可能的降低前端开发的门槛,然而 React 更多的是去改变用户去接受它的概念和思想,相较于 Vue 来说上手成本略高。

3.7 组件间传值

组件间传值又分为父子组件传值和非父子组件传值

父子组件间传值

  • 父组件给子组件传值,直接通过props传值
<custom content="hello world"></custom>
  • 子组件给父组件传值,通过 emit发送事件
this.$emit('chooseType', type)

父组件接收事件:

<custom content="hello world" @chooseType="handleType"></custom>

非父子组件传值

主要通过事件总线传值

在根节点给 Vue 挂载一个空的 Vue 对象

Vue.prototype.bus = new Vue();

需要发送事件的组件里

this.bus.$emit("change", params)

接收事件的组件

this.bus.$on("change", (msg) => {
    //do yourself work
})

除了以上这几个问题,还有 Vue 生命周期,v-show 和 v-if,vuex 等知识点经常会问。

�更多 Vue 面试相关:

前端学习-Vue内置组件
入门级Vue 面试题+详解答案
中等难度Vue 面试题+详解答案

好了,今天先分享这么多,更多内容下期分享。

最后

进大厂没有想象的那么难,关键还是技术。

机会是留给有准备的人,了解岗位要求后,早准备,做足准备,可以少走弯路,网上各种面经笔经,学习课程应有尽有。

下面分享给大家一份我整理的《前端开发工程师必备资料包》。

269页前端大厂面试宝典

前端面试题汇总

JavaScript

性能

linux

jQurey

小程序相关

前端资料汇总

需要完整版的朋友可以si信我获取。(或者点击我的主页-个人介绍处有方式,电脑端才可以看到)

结束语

无论做什么,不止前端,都应该要有自己的想法和思考,这样子才能把事情做好,做得更深。否则这就像一场梦,醒来还是很感动。希望各位读者,看上面的题目并不是背答案,而是理解它,并能活用,以后做类似的事情,有参考的思路。如果遇到和我同一个面试官,题目当然是不完全一样的,此时需要临场发挥自己的积累和灵活运用了。

最后再补充一点,如果你见过了普遍情况,了解到了普遍现象,那要是什么都和人家一样,最后不就是也成为普遍水平了吗?如果想脱离当前现状,实现突破,那么目标应该是成为有个性、有特色的、有区分度的人,成为一名不一样的前端,不一样的人。

推荐阅读更多精彩内容