总结牛客前端工程师精选面经合集(四)

108 阅读8分钟

对vite的理解

下一代构建打包工具。

那么如何理解下一代呢?

随着项目越来越大,需要处理的JavaScript呈指数级增长,模块越来越多。构建工具需要很长的时间才能开启服务器,HMR也需要几秒钟才能在浏览器反应出来。所以vite的构建速度非常快。

  • 一个开发服务器,它基于原生ES模块提供了丰富的内建功能,HMR的速度非常快速
  • 一套构建指令,它使用rollup打开我们的代码,并且它是预配置的,可以输出生成环境的优化过的静态资源。

vite是默认支持css, ts的,以及css预处理语言的。只需要安装对应的包,而无需配置像webpack中的loader。

vite默认不支持vue,需要安装对应的插件。@vitejs/plugin-vue, @vue/compiler-sfc。并在vite.config.js文件进行配置。

vite实现原理

image.png

那么为什么vite会支持ts和css预处理语言呢?

  • vite会创建一个本地服务器,类似于webpack通过express, 他是通过connect来实现的。主要是为了请求转发。
  • vite内部将ts, less, sass等文件转化为es module 的js语法。并且依旧以原来的文件命名。然后通过connect将转化的文件返回给浏览器。

进制转换

  • parseInt(num, 基底)。指定num为几进制数,然后转换为10进制。
  • num.toString(基底)。将10进制的num转化为几进制的数。

判断数据类型的方法

以前写过一篇文章

object的tostring和构造器的tostring的区别

Number、String,Boolean,Array,RegExp、Date、Function等内置对象均重写了Object原型上的toString方法,作用是将当前数据类型转为字符串类型。 image.png

const num = 123;
num.toString();
// 该toString调用的是Number.prototype.toString方法。
// 但是Number.prototype.__proto__ = Object.prototype
// Object中也存在tostring函数。该函数返回的是当前对象的类型字符串。[object Number]
// 所以各个包装类和内置类都重写了toSring方法。

原型链的了解

每一个对象的__proto__属性都指向他的原型对象prototype。直到Object.prototype。并且对象中获取属性和方法都是通过这个指向来查找的。并且es5之前有些继承也是通过原型链。

说三种事件绑定的方式

developer.mozilla.org/zh-CN/docs/…

  • 行内事件处理器(不建议使用)
  • 事件处理器属性 (他还只能对同一元素同一事件添加一个,后添加的会覆盖前面的 )
  • addEventListener, removeEventListener。(支持到ie9, 可以对同一元素添加多个相同事件类型的事件不会相互覆盖。)

flex布局的意义

依靠它可以实现一些响应式操作,比如我们要一个容器固定在中间,即使我们的窗口大小发生变化。 同时也解决了元素的排列问题,让元素按行,按列排列。

元素隐藏的方式

  • opacity: 0。元素不可见,但是还会占据空间,而且,需要注意,此时给它绑定事件,它会触发
  • display: none。直接把元素隐藏了,不占据任何空间,也不响应任何事件。
  • visibility:hidden。visibility值为hidden,元素不可见,但是还会占据空间,虽然它占据空间,但是如果给它绑定事件,它也不会触发
  • transfrom: scale(0)。不可见,还会占据空间,但是绑定的事件不会触发。跟visibility:hidden表现一致。
  • width: 0, height: 0。如果它有子元素,还得设置overflow设置为hidden
  • overflow: hidden。隐藏溢出部分。
  • position定位偏移。
  • 绝对定位+z-index。让元素之间覆盖。

this指向问题

  • 默认绑定
  • 隐式绑定
  • 显示绑定
  • new 绑定

Vue2和Vue3的区别

vue官网总结的很详细

  • vue3新增suspense(实验中),teleport组件。

  • vue2使用options API, vue3使用composition API 实现逻辑模块化的复用。

  • vue2使用flow做类型检测,vue3全部使用ts重构。

  • vue2通过Object.definePrototype做数据响应式。vue3通过Proxy做数据响应式。

  • vue3对比vue2具有明显的性能提升

    • 打包大小减少41%
    • 初次渲染快55%,更新快133%
    • 内存使用减少54%
  • 生命周期的变化。有更好的语义化。

    • beforeCreate -> 请使用 setup()
    • created -> 请使用 setup()
    • beforeMount -> onBeforeMount
    • mounted -> onMounted
    • beforeUpdate -> onBeforeUpdate
    • updated -> onUpdated
    • beforeDestroy -> onBeforeUnmount
    • destroyed -> onUnmounted
    • errorCaptured -> onErrorCaptured
  • vue2只能有一个根节点,但是vue3可以有多个根节点,并且需要手动指定非props的属性绑定在那个dom上。

  • 单文件组件组合式 API 语法糖。 script setup

    • defineExpose,setup模板默认情况下是关闭的,那么通过ref, $parent获取组件实例,他是不会获取到setup模板中的属性的。所以我们可以通过defineExpose导出需要获取的东西。

    • 与script一起使用

      <script setup> 可以和普通的 <script> 一起使用。普通的 <script> 在有这些需要的情况下或许会被使用到: 无法在 <script setup> 声明的选项,例如 inheritAttrs 或通过插件启用的自定义的选项。声明命名导出。运行副作用或者创建只需要执行一次的对象。

  • 状态驱动的动态 CSS。v-bind函数支持传入一个js表达式,但是需要使用引号包裹起来。

    实际的值会被编译成 hash 的 CSS 自定义 property,CSS 本身仍然是静态的。自定义 property 会通过内联样式的方式应用到组件的根元素上,并且在源值变更的时候响应式更新。 image.png

<template>
  <div class="text">hello</div>
</template>

<script>
export default {
  data() {
    return {
      color: 'red'
    }
  }
}
</script>

<style>
.text {
  color: v-bind(color);
}
</style>
  • 配置全局属性的变化。vue2是创建多个根Vue实例来绑定全局配置。但是vue3是通过createApp来创建一个根Vue实例绑定全局配置。

  • v-model在自定义组件上的修改。

  • v-if 和 v-for两者作用于同一个元素上时,v-if 会拥有比 v-for 更高的优先级。

  • v-bind="object" 和一个相同的独立 attribute之间的覆盖规则。vue2总是独立的attribute覆盖object中同名的attribute。但是vue3中是后写的覆盖先写的。和object添加属性的行为一样。

  • 非emits配置的事件,自动添加到根dom上。废除v-on.native。v-on.native作用是将原生 DOM 监听器添加到子组件的根元素中。

proxy和defineProperty的优缺点

vue2对于新增的属性无能为力,比如无法监听属性的添加和删除、数组索引和长度的变更,vue2的解决方法是使用Vue.set(object, propertyName, value) 等方法向嵌套对象添加响应式。

defineProperty

  • 新添加的属性不是响应式。通过$set解决。
  • 需要深度遍历对象依次添加响应式,性能消耗大。解决方案Proxy
  • 通过数组下标修改无法更新视图。因为过于消耗性能。

proxy的优点

  • proxy劫持的是整个对象,对新增加的属性依旧是可以监听的。
  • proxy可以劫持对对象的13种操作。

设置style标签的scoped属性

他会出现穿透。所以我们一般都不要直接使用标签设置样式。都通过class属性设置。并且给每个组件的根标签设置一个class。

image.png

extends, mixins

mixins

复用代码逻辑。并且vue3中的data是浅层合并。即只要原组件中data中存在的属性,mixin都不会合并。

如果修改mixins中的数据,他只会影响当前组件,不会影响其他组件中引入的mixins数据。只要记住,通过mixins引入后,他就表示的是自己组件的一部分了和别的无关系了。

  • 如果是data函数的返回值对象

    • 返回值对象默认情况下会进行合并;
    • 如果data返回值对象的属性发生了冲突,那么会保留组件自身的数据;
  • 如何生命周期钩子函数

    • 生命周期的钩子函数会被合并到数组中,都会被调用。并且混入的生命周期先执行。
  • 值为对象的选项

    • 比如都有methods选项,并且都定义了方法,那么它们都会生效。将被合并为同一个对象。
    • 但是如果对象的key相同,那么会取组件对象的键值对。

extends

允许一个组件扩展到另一个组件,且继承该组件选项。不能继承template内容。 几乎和mixins相同。合并策略和mixins一样。

mixins 选项主要用来组合功能,而 extends 主要用来考虑继承性。

cdn是什么?

CDN,全称是“Content Delivery Network”,翻译过来就是内容分发网络。它应用了 HTTP 协议里的缓存和代理技术,代替源站响应客户端的请求。它可以缓存源站的数据。

它就是专门为解决“长距离”上网络访问速度慢而诞生的一种网络应用服务。

CDN 的最核心原则是就近访问,如果用户能够在本地几十公里的距离之内获取到数据,那么时延就基本上变成 0 了。

cdn将原站上的数据缓存到每个网络节点上。用户在上网的时候就不直接访问源站,而是访问离他“最近的”一个 CDN 节点,术语叫“边缘节点”(edge node),其实就是缓存了源站内容的代理服务器,这样一来就省去了“长途跋涉”的时间成本,实现了“网络加速”。

CDN 的负载均衡

我们再来看看 CDN 是具体怎么运行的,它有两个关键组成部分:全局负载均衡和缓存系统

全局负载均衡(Global Sever Load Balance)一般简称为 GSLB,它是 CDN 的“大脑”,主要的职责是当用户接入网络的时候在 CDN 专网中挑选出一个“最佳”节点提供服务,解决的是用户如何找到“最近的”边缘节点,对整个 CDN 网络进行“负载均衡”。

缓存系统是 CDN 的另一个关键组成部分,相当于 CDN 的“心脏”。如果缓存系统的服务能力不够,不能很好地满足用户的需求,那 GSLB 调度算法再优秀也没有用。缓存系统只能有选择地缓存那些最常用的那些资源。 如果将这些 DOM 变化做成异步的消息事件, 添加到消息队列的尾部,那么又会影响到监控的实时性,因为在添加到消息队列的过程中,可能前面就有很多任务在排队了。

这也就是说,如果 DOM 发生变化,采用同步通知的方式,会影响当前任务的执行效率

如果采用异步方式,又会影响到监控的实时性。那该如何权衡效率和实时性呢?

这就需要使用到微任务了。 image.png

为什么需要this

因为 JavaScript 语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。

如果想要访问上层作用域中的变量,那么我们就可以使用this。