2024前端开发面试题实战整理(附答案)持续更新中~

以下会收集本人自己经历到的面试题情况和一些优秀的面试题整理,没有具体的分类归纳,也不会标注具体公司,只按顺序整理,只保证其面试题真实性。

因答案为后来自己整理,若对答案存在疑问,欢迎评论区留言谈论

下面,开整~~~

1 原型和原型链是什么? 

原型(Prototype)

原型是function对象的一个属性,它定义了构造函数创造出的对象的公共祖先。这意味着通过该构造函数产生的对象,可以继承该原型的属性和方法。原型的存在使得JavaScript中的对象可以共享方法和属性,从而节省内存并提高效率。

具体来说,每个函数(包括构造函数)都有一个prototype属性,这个属性是一个对象,包含了所有实例共享的方法和属性。当使用new关键字创建一个函数的实例时,这个实例内部会包含一个指向该函数原型的链接(可作补充在ES6及以前版本中,这个链接是通过__proto__属性实现的,但__proto__是一个非标准属性,ES6之后推荐使用Object.getPrototypeOf()来获取对象的原型)。

原型链(Prototype Chain)

原型链是JavaScript中的一个重要概念,它描述了对象之间的原型连接关系。当一个对象在查找一个属性或方法时,如果自身没有该属性或方法,则会沿着其原型链向上查找,直到找到该属性或方法或达到原型链的顶端(即Object.prototype,其__proto__属性为null)。

具体来说,每个对象都有一个__proto__属性(尽管它是非标准的,但用于说明原型链的概念很方便),这个属性指向该对象的原型对象。而原型对象本身也是一个对象,它也有自己的__proto__属性,指向它的原型对象,以此类推,形成了一个链式结构,这就是原型链。原型链的顶端是Object.prototype,它的__proto__属性为null,表示原型链的结束。

原型和原型链的作用

  1. 实现继承:通过原型和原型链,JavaScript实现了基于原型的继承机制。子类可以通过修改其原型的__proto__属性来继承父类的属性和方法,或者通过其他方式(如使用Object.create())来建立继承关系。
  2. 共享方法和属性:原型链允许对象之间共享方法和属性,从而节省内存。当多个对象需要访问相同的方法或属性时,它们可以共享存储在原型对象中的这些方法和属性,而不是在每个对象中单独存储。
  3. 动态扩展:由于JavaScript是动态类型语言,原型链允许在运行时动态地向对象添加新的属性和方法。这意味着开发者可以在不修改原有类定义的情况下,为对象添加新的功能。

东西有点多,重在理解,不然面试官稍微问一下,容易乱。

2 vue2中父子组件生命周期执行顺序是怎样的(各个阶段依次列举)

一、创建/挂载阶段

  1. 父组件
    • beforeCreate:在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
    • created:实例已经创建完成之后被调用。在这一步,实例已完成数据观测,属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
    • beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。
    • 子组件开始创建:此时父组件的模板开始编译,并遇到子组件标签时,会开始子组件的创建过程。
      • 子组件beforeCreatecreatedbeforeMount依次执行。
    • 子组件挂载完成:子组件的mounted被调用,表示子组件的模板已经编译并挂载到了页面上。
    • 父组件挂载完成:最后,父组件的mounted被调用,表示父组件的模板也已经编译并挂载到了页面上。此时,整个组件树都已经挂载完成。

二、更新阶段

  1. 父组件数据变化时
    • beforeUpdate:数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
    • 如果数据变化影响到子组件,则子组件也会依次执行beforeUpdateupdated
      • 子组件beforeUpdateupdated
    • updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用这个钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。但是要避免更改状态,因为这可能会导致无限循环的更新。
  2. 子组件数据变化时
    • 如果子组件内部的数据发生变化,则只影响子组件自身的beforeUpdateupdated

三、销毁阶段

  1. 父组件销毁时
    • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
    • 子组件开始销毁:父组件销毁前,会先让子组件开始销毁过程。
      • 子组件beforeDestroydestroyed依次执行。
    • destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

简单画个图帮助理解 

这一块理解这个图就ok

3 请简述SPA应用优缺点

优点

  1. 流畅体验:页面无需重新加载,提供快速、流畅的用户交互。
  2. 用户体验佳:类似原生应用的体验,包括平滑的过渡和即时反馈。
  3. 减少请求:通过Ajax等技术减少服务器请求,提升性能。
  4. 易于维护:前端代码模块化,便于维护和更新。
  5. SEO改善:现代技术如SSR、预渲染等已提升SPA的SEO表现。

缺点

  1. 首次加载慢:初始时需要加载整个应用,可能导致加载时间较长。
  2. 依赖JS:完全依赖JavaScript,若JS被禁用则应用无法工作。
  3. SEO挑战:尽管有改善,但相比MPA,SEO优化可能更复杂。
  4. 开发复杂:需要全面的前端技术栈知识,开发和调试更复杂。
  5. 历史管理:需要自行管理浏览器历史记录,增加开发难度。 

这里列举的比较多,回答时答前三条基本ok  

4 v-if和v-show的区别?

  • v-if:v-if是惰性渲染,即只有在条件为真时才会渲染对应的组件或元素,在条件为假时则不会渲染,且对应的组件或元素的所有事件监听器和子组件都会被销毁。这意味着在条件为假时,相关的组件或元素将不会出现在DOM中。
  • v-show:v-show则是通过CSS的display属性来控制元素的显示和隐藏。无论条件为真还是为假,元素都会被渲染到DOM中,只是通过改变display属性来控制其是否显示。因此,使用v-show时,元素始终存在于DOM中。

综上,如果需要减少初始渲染开销或避免不必要的资源占用,可以选择v-if;如果需要频繁切换元素的显示与隐藏且渲染开销不大,可以选择v-show。 

较为简单,所以回答时要思路清晰,用词准确

5 介绍一下盒模型? 

盒模型是CSS中用来描述HTML元素布局和尺寸的一个模型。每个HTML元素都被视为一个盒子,这个盒子由四个主要部分组成:

  1. 内容(Content):盒子里实际展示的内容,如文本或图片。
  2. 内边距(Padding):内容与其边框之间的空间。
  3. 边框(Border):围绕内边距和内容的线条。
  4. 外边距(Margin):边框外的空间,用于与其他元素分隔。

有两种主要的盒模型类型:

  • 标准盒模型(content-box):元素的宽度和高度仅包括内容区域,内边距、边框和外边距会增加到元素的总尺寸上。
  • IE盒模型(border-box):元素的宽度和高度包括内容、内边距和边框,但不包括外边距。

放两张图帮助理解 

6 css隐藏元素的方法有哪些?

1. display: none;

  • 效果:完全移除元素,不占据空间。
  • 适用场景:当不需要元素参与页面布局时。

2. visibility: hidden;

  • 效果:元素不可见,但占据空间。
  • 适用场景:当需要保留元素空间时。

3. opacity: 0;

  • 效果:元素完全透明。
  • 适用场景:当需要元素保持可交互但视觉上不可见时。

4. 绝对定位移出视口

  • 效果:通过定位将元素移出可视区域。
  • 适用场景:当需要隐藏元素但保留DOM结构时。

5. clip-path(高级)

  • 效果:裁剪元素形状使其不可见。
  • 适用场景:需要高级隐藏效果时。

6. height: 0; width: 0; 或 overflow: hidden;

  • 效果:通过改变尺寸或防止内容溢出隐藏内容。
  • 适用场景:当需要元素不占据额外空间时。

这些方法中,display: none; 是最常用的,因为它简单且彻底地移除了元素。其他方法则根据具体需求选择使用

7 es6新增了哪些新语法?

ES6(ECMAScript 2015)是JavaScript的一个重要版本,它引入了许多新特性和语法,使得JavaScript更加强大和易于使用。以下是ES6新增的一些主要语法特性:

1. let和const

  • let:用于声明块级局部变量,与var相比,let声明的变量具有块级作用域,且不存在变量提升(hoisting)现象,即变量必须先声明后使用,否则会报错。
  • const:用于声明常量,一旦声明就必须被赋值,且其值在后续不可被重新赋值(如果是对象或数组,则对象或数组本身不可被重新指向另一个值,但其内部属性或元素的值可以修改)。

2. 箭头函数

  • 箭头函数提供了一种更简洁的函数声明语法,并且没有自己的thisargumentssupernew.target
  • 箭头函数的this指向是在定义时确定的,即指向其所在上下文的this值。

3. 解构赋值

  • 解构赋值允许从数组或对象中提取值,并赋值给不同的变量。这包括数组的解构赋值和对象的解构赋值。

4. 模板字符串

  • 使用反引号(``)来定义字符串,可以在其中嵌入表达式,并支持多行字符串。

5. 展开运算符(...)

  • 可以在函数调用、数组字面量和对象字面量中展开数组或对象。
  • 用于复制数组、合并数组、在函数调用时传递数组元素作为参数等。

6. Symbol类型

  • 新增的一种原始数据类型,用于创建唯一且不可变的标识符。

7. Map和Set数据结构

  • Map:是键值对的集合,键可以是任何类型。
  • Set:是值的集合,且值唯一。

8. Proxy和Reflect对象

  • Proxy:用于定义基本操作的自定义行为,如属性查找、赋值、枚举、函数调用等。
  • Reflect:提供与对象交互的方法,如定义、检查、修改对象属性等。

9. 模块化

  • 引入importexport关键字,支持ES6模块的导入和导出。

10. 类(class)

  • 基于原型的继承的语法糖,提供了更接近传统面向对象编程的语法。
  • 类的所有方法都定义在类的prototype上,且必须通过new来调用类。

11. Promise对象

  • 用于处理异步操作,提供了一种更简洁的链式调用方法。

12. async/await

  • 基于Promise的异步编程语法糖,使异步代码看起来更像同步代码。

13. 生成器(Generator)

  • 函数的一种特殊类型,可以暂停和恢复其执行。

14. 默认参数和剩余参数

  • 默认参数:允许在函数定义时设置参数的默认值。
  • 剩余参数:使用...语法表示一个函数可以接收任意数量的参数。

15. 扩展运算符(Spread Operator)与剩余参数(Rest Operator)

  • 扩展运算符:用于展开数组或对象的元素。
  • 剩余参数:用于收集函数参数。

16. 其他扩展方法

  • Array.from():将类数组对象或可迭代对象转换为真正的数组。
  • Object.assign():将所有可枚举属性的值从一个或多个源对象复制到目标对象。

基本上都在这里了,很多都是很重要而且很常用的,回答时尽量多答几个 

8 vue2和vue3的区别?

一、性能提升

  • Vue3:通过改进虚拟DOM的算法和底层架构,实现了更快的渲染速度和更低的内存使用率。Vue3使用了Proxy替代Object.defineProperty实现响应式,并且使用了静态提升技术来提高渲染性能。此外,Vue3的diff算法中增加了静态标记,采用最长递归子序列的算法来计算最小的修改偏移量,进一步优化了性能。
  • Vue2:虽然也使用了虚拟DOM来提高性能,但在响应式系统和diff算法上相对Vue3有一定的性能差距。Vue2使用Object.defineProperty来实现响应式,这种方式需要递归地对所有属性进行拦截,并且无法监听属性的删除和新增,需要额外的API(如set、delete)来处理这些情况。

二、API和编码方式

  • Vue3:引入了Composition API,这是一种更灵活的方式来组织组件的逻辑。通过Composition API,开发者可以将组件的功能拆分成更小的、可复用的函数(称为composables),这有助于构建大型应用并保持代码的可维护性。此外,Vue3对TypeScript的支持更加友好,提供了完整的类型定义。
  • Vue2:使用Options API,将数据和函数集中起来处理,将功能点切割了当逻辑复杂的时候不利于代码阅读。Vue2对TypeScript的支持相对较弱,需要额外的类型定义或使用第三方库。

三、生命周期钩子和全局API

  • Vue3:对生命周期钩子进行了调整,例如beforeCreate和created被setup()函数替代,其他钩子如beforeMount、mounted等也改为了onBeforeMount、onMounted等命名。此外,Vue3还新增了一些全局API,如createApp来创建Vue应用实例。
  • Vue2:使用传统的生命周期钩子命名,如beforeCreate、created、beforeMount、mounted等。全局API如Vue.extend、Vue.mixin等在Vue3中被废弃或替换。

四、响应式系统

  • Vue3:使用Proxy代理的方式实现响应式系统,这种方式可以拦截对象属性的读取和设置操作,从而实现响应式。Proxy的优势在于不需要像Object.defineProperty那样递归地遍历对象的所有属性,并且可以监听属性的删除和新增。
  • Vue2:使用Object.defineProperty来实现响应式系统,这种方式需要递归地遍历对象的所有属性,并为其设置getter和setter。但是,这种方式无法监听属性的删除和新增,需要额外的API来处理这些情况。

五、插槽和样式穿透

  • Vue3:在插槽方面,Vue3的具名插槽和作用域插槽的使用方式与Vue2有所不同。Vue3使用v-slot指令来定义插槽,并且作用域插槽的语法也有所变化。在样式穿透方面,Vue3支持:deep()和::v-deep()伪类来穿透组件的样式隔离。
  • Vue2:使用slot属性和slot-scope特性来定义插槽。在样式穿透方面,Vue2也支持/deep/和::v-deep伪类来穿透组件的样式隔离。

六、其他特性

  • Vue3:支持更多的特性,如片段(Fragment)、Teleport、Suspense等。片段允许组件有多个根节点,Teleport可以将组件的内容渲染到指定DOM节点的新特性,Suspense则提供了一种等待异步组件加载的方式。
  • Vue2:相对Vue3来说,在特性支持上较为有限,没有直接支持片段、Teleport和Suspense等特性。

总的来说,Vue3在性能、API设计、响应式系统、以及新特性等方面都进行了较大的改进和提升,使得它更加适合现代Web开发的需求。

半开放问题,但是最关键的几点必须要答出来

9 什么是闭包? 

闭包(Closure)是函数式编程中的一个重要概念,它出现在多种编程语言中,包括JavaScript、Python、Swift等。闭包指的是一个函数值,它引用了其外部作用域中的变量。即使该函数在其原始作用域之外执行,它依然可以访问那些变量。闭包通常由两部分组成:一个函数,以及创建该函数时的词法环境(即该函数的外部作用域)。

闭包的特性

  1. 函数嵌套:闭包通常涉及函数嵌套,即一个函数内部定义了另一个函数。
  2. 词法作用域:内部函数可以访问外部函数作用域中的变量,即使外部函数已经执行完毕。
  3. 持久化:闭包中的变量可以持久化,即使外部函数已经返回,闭包中的变量依然可以访问和修改。

闭包的作用

  1. 封装:闭包允许我们将数据和操作数据的函数捆绑在一起,形成一个独立的单元,从而隐藏了内部细节。
  2. 数据隐私:通过闭包,我们可以避免使用全局变量,从而减少命名冲突和提高模块间的独立性。
  3. 创建模块:闭包可以用于创建具有私有变量和公共API的模块。
  4. 回调函数和高阶函数:在JavaScript等语言中,闭包常用于实现回调函数和高阶函数,因为它们允许函数作为参数传递给其他函数,并在需要时执行。

 

闭包这块可能会涉及问到js的垃圾回收机制,下面一起讲。

10  垃圾回收机制是什么?

JavaScript的垃圾回收机制是一种自动的内存管理机制,旨在回收不再使用的对象所占用的内存。这个过程不需要开发者手动干预

核心概念
  • 垃圾:指那些不再被程序中的任何部分所引用的对象。
  • 回收:减少内存泄漏和提高程序的性能。内存泄漏是指程序中已分配的内存由于某种原因未释放或无法释放,导致内存的浪费和程序的低效运行(闭包就可能造成内存泄漏)。
过程
  1. 标记:垃圾回收器会遍历程序中的所有对象,标记出那些仍然被引用的对象(即“可达”对象)。
  2. 清除:未被标记的对象(即不再被引用的对象)被视为垃圾,垃圾回收器会清除它们,并回收它们所占用的内存
常见的垃圾回收算法
  • 标记清除(Mark-and-Sweep):这是现代JavaScript引擎(如V8)常用的算法。通过标记和清除两个步骤来回收内存。
  • 引用计数(Reference Counting):虽然简单,但由于无法解决循环引用问题,现代JavaScript引擎已不常使用。
V8引擎的垃圾回收策略

V8引擎采用了分代式垃圾回收,将对象分为新生代和老生代。新生代对象存活时间短,采用更快的回收策略;老生代对象存活时间长,采用更全面的回收策略。

开发者注意事项
  • 避免内存泄漏:确保不再使用的对象没有被无意中引用,导致内存无法释放。
  • 优化性能:虽然垃圾回收是自动的,但过多的垃圾回收可能会影响性能。开发者可以通过代码优化来减少垃圾的产生。

总的来说,JavaScript的垃圾回收机制让开发者可以专注于编写代码,而不用担心内存管理的问题。然而,了解垃圾回收的基本概念和策略仍然有助于编写更高效、更健壮的应用程序。

如果闭包造成了内存泄漏,垃圾回收机制是无法回收的,所以,下面接着讲一下,怎么识别内存泄漏及解决。 

  

11 你是怎么判断内存泄漏的,一般怎么解决? 

在JavaScript中,判断内存泄漏以及解决这些问题通常依赖于一系列的步骤和工具。以下是对这两个方面的详细解答:

一、如何判断内存泄漏

  1. 观察内存使用情况
    • 使用浏览器的开发者工具(如Chrome的开发者工具)来监视内存使用情况。在“Memory”选项卡中,可以记录内存分配和释放的情况,观察是否有持续增长的内存占用。
  2. 查找内存泄漏的迹象
    • 应用程序性能下降,响应时间变长。
    • 浏览器或Node.js进程占用的内存不断增加,即使在没有明显操作的情况下。
    • 使用“Heap Snapshots”功能捕捉内存快照,对比不同时间点的内存使用情况,查找异常增长的对象或数据结构。
  3. 使用性能分析工具
    • 在Chrome开发者工具中,可以使用“Timeline”选项卡记录应用程序的活动,并查看哪些函数或操作导致了内存的增加。
    • 也可以使用第三方工具如Leak Finder或Node.js的内存分析器来辅助检测内存泄漏。

二、如何解决内存泄漏

  1. 释放不再需要的引用
    • 确保在对象不再需要时,删除或清空对其的引用。这包括全局变量、DOM元素、事件监听器等。
    • 对于可能临时需要的对象,考虑使用弱引用(如果语言或框架支持),以便在不再需要时自动释放。
  2. 管理闭包
    • 闭包可以捕获并保存外部函数的变量,如果闭包被长期持有,可能会导致外部函数的变量无法被垃圾回收。
    • 仔细管理闭包的使用,确保在不再需要时解除对闭包的引用。
  3. 清理DOM元素
    • 当DOM元素从页面中移除时,确保同时删除与之相关的事件监听器和任何对该DOM元素的引用。
    • 使用removeChild移除DOM节点后,将对应的变量设置为null,以释放内存。
  4. 清除定时器
    • 对于setTimeoutsetInterval创建的定时器,确保在不再需要时清除它们,以避免定时器回调函数继续占用内存。
  5. 使用内存管理库
    • 考虑使用内存管理库(如Redux、MobX等)来管理应用程序的状态和数据流,这些库通常提供了更好的内存管理机制。
  6. 定期检查和优化
    • 定期检查应用程序的内存使用情况,并使用上述工具进行性能分析。
    • 根据分析结果对代码进行优化,减少内存泄漏的风险。

通过以上步骤和工具的使用,可以有效地判断和解决JavaScript中的内存泄漏问题,提高应用程序的性能和稳定性。

基本都在这里了,这三个问题,如果你比较有自信,可以一起答,互相之间是可以引出来的。闭包的弊端之一就是可能会造成内存泄漏,而垃圾回收机制又无法回收闭包变量,所以需要去识别判断内存泄漏,手动解决好内存泄漏问题。

12 相比于js,ts的新特性有哪些?

TypeScript(TS)是JavaScript(JS)的一个超集,它在JavaScript的基础上添加了许多新特性,主要包括以下几个方面:

1. 静态类型检查

  • 类型注解和类型推断:TypeScript允许开发者为变量、函数参数、函数返回值等添加类型注解,以明确类型信息。同时,TypeScript也可以根据上下文自动推断出变量的类型,减少了手动注解的工作量。这种静态类型检查可以在编译时捕获潜在的类型错误,使得代码更加健壮、可维护和可靠。

2. 面向对象编程的支持

  • 类和接口:TypeScript支持类和接口等面向对象编程的特性,使得代码更加结构化和可扩展。通过类和接口,开发者可以定义复杂的对象结构,实现代码的封装、继承和多态等特性。
  • 泛型:TypeScript支持泛型编程,允许开发者编写更加灵活和可重用的代码。泛型可以在编译时检查类型安全,避免了在运行时出现类型错误。

3. 更好的开发工具支持

  • IDE支持:由于TypeScript提供了类型信息,集成开发环境(IDE)可以更好地进行代码补全、代码导航和错误提示等功能。这提高了开发效率,减少了调试时间。
  • 构建工具支持:TypeScript可以与现有的构建工具(如Webpack、Gulp等)无缝集成,使得开发者可以更方便地进行项目构建和部署。

4. 模块化支持

  • ES6模块语法:TypeScript支持ES6模块语法,允许开发者以模块化的方式组织代码。这有助于减少全局变量的污染,提高代码的可维护性和可重用性。
  • 其他模块化方案:除了ES6模块语法外,TypeScript还支持其他模块化方案,如CommonJS、AMD等,以满足不同项目的需求。

5. 装饰器和命名空间

  • 装饰器:TypeScript支持装饰器(Decorators),这是一种特殊类型的声明,它能够被附加到类声明、方法、访问器、属性或参数上。装饰器使用 @expression 这种形式,expression 求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。
  • 命名空间:TypeScript支持命名空间(Namespaces),它主要用于组织代码,将相关的代码组织在一起,避免命名冲突。

6. 渐进式采用

  • TypeScript可以与现有的JavaScript项目无缝集成,开发者可以逐步将JavaScript代码迁移到TypeScript,而不需要一次性重写整个项目。这种渐进式采用的方式有助于降低迁移成本,提高项目的可维护性。

7. 社区和生态系统

  • TypeScript是由微软开发和维护的,拥有庞大的社区和活跃的生态系统。这意味着有大量的第三方库和工具可供使用,可以更好地支持开发需求。

综上所述,TypeScript相比于JavaScript具有静态类型检查、面向对象编程的支持、更好的开发工具支持、模块化支持、装饰器和命名空间等新特性。这些新特性使得TypeScript在大型项目和团队开发中更加受欢迎,可以提高开发效率和代码质量。

ts也是逐渐成为了面试必备,很有必要仔细了解一下 

13 什么是泛型? 

泛型是一种编程特性,它允许你编写不特定于任何数据类型的代码。换句话说,你可以定义函数、接口或类时,不指定具体的数据类型,而是在使用时才指定。这样做的好处是提高了代码的复用性灵活性类型安全性

主要特点:

  1. 类型参数化:泛型使用类型参数(如TU等)来表示未知的数据类型,这些类型参数在使用时会被具体的类型所替换。

  2. 提高复用性:你可以编写一个泛型函数、接口或类,它能够处理多种类型的数据,而无需为每种类型编写单独的代码。

  3. 增强类型安全:泛型在编译时进行类型检查,这有助于避免在运行时出现类型错误。

示例:

假设你有一个函数,它接收一个参数并返回相同的参数。不使用泛型时,你可能需要为每种数据类型编写一个函数:

function identityString(arg: string): string {
return arg;
}
function identityNumber(arg: number): number {
return arg;
}

使用泛型,你可以编写一个通用的函数来处理所有类型:

function identity<T>(arg: T): T {
return arg;
}
let outputString = identity<string>("Hello World"); // 输出: "Hello World"
let outputNumber = identity<number>(123); // 输出: 123

在这个例子中,<T>是一个类型参数,它表示“任意类型”。在调用identity函数时,你可以通过<string><number>来指定T的具体类型,但你也可以不指定,让TypeScript通过参数自动推断出类型(这称为类型推断)。

这个问题被问两次,朋友们,好好记┗|`O′|┛ 嗷~~

14 讲一下你了解到的Promise

Promise是JavaScript中用于处理异步操作的一种重要机制,它代表了未来将要发生的事件,并允许我们以同步的方式书写异步代码。

一、Promise的定义

  • 基本概念:Promise对象用于异步计算。它代表了一个尚未完成但预期将来会完成的操作。这个结果可能是一个值(fulfilled),也可能是一个原因(rejected),分别表示异步操作成功或失败。
  • 状态:Promise有三种状态:Pending(进行中)、Fulfilled(已完成)和Rejected(已失败)。只有异步操作的结果才能决定Promise的状态,任何其它操作都不能改变这个状态。

二、Promise的特点

  1. 状态不受外界影响:Promise对象的状态一旦改变,就不会再变。这意味着Promise的状态只能由Pending变为Fulfilled或Rejected,并且这个过程是不可逆的。
  2. 提供统一的接口:Promise对象提供了一系列的方法(如.then()、.catch()和.finally())来统一处理异步操作的结果或错误,这使得控制异步操作变得更加容易。
  3. 链式调用:Promise支持链式调用,即可以在一个Promise对象后面接着调用另一个Promise对象,这样可以方便地处理多个异步操作的依赖关系。

三、Promise的基本用法

  1. 创建Promise:使用new Promise()构造函数来创建一个Promise对象。构造函数接受一个执行器函数作为参数,该执行器函数本身又接受两个参数:resolve和reject。这两个参数都是函数,分别用于将Promise的状态从Pending变为Fulfilled或Rejected。

    let promise = new Promise((resolve, reject) => {
    // 异步操作
    if (/* 异步操作成功 */) {
    resolve(value); // 将Promise的状态变为Fulfilled,并将value作为操作成功的结果
    } else {
    reject(reason); // 将Promise的状态变为Rejected,并将reason作为操作失败的原因
    }
    });
  2. 处理Promise:使用.then().catch().finally()等方法来处理Promise的结果或错误。

    • .then()方法接收两个可选的参数:第一个参数是Promise成功时(即状态变为Fulfilled)的回调函数,第二个参数是Promise失败时(即状态变为Rejected)的回调函数(但通常使用.catch()来处理错误)。.then()方法返回一个新的Promise对象,这使得可以链式调用多个.then()

    • .catch()方法用于捕获Promise链中发生的错误,并返回一个新的Promise对象。

    • .finally()方法无论Promise最终状态如何都会执行,它不接受任何参数,也不返回任何值,但会返回一个Promise对象。

四、Promise的优缺点

优点

  • 避免了层层嵌套的回调函数,使得异步代码更加清晰和易于维护。
  • 提供了统一的接口来处理异步操作的结果或错误。
  • 支持链式调用,方便处理多个异步操作的依赖关系。

缺点

  • 一旦新建就无法取消,会立即执行。
  • 如果不设置回调函数,Promise内部抛出的错误不会反应到外部。
  • 在Promise链中,错误可能会被吞没(即如果忘记添加.catch()来捕获错误,那么错误将不会被处理)。

五、总结

Promise是JavaScript中处理异步操作的一种强大机制,它提供了统一的方式来处理异步操作的结果或错误,并支持链式调用和错误传播。虽然Promise有一些缺点,但它仍然是现代JavaScript开发中不可或缺的一部分。

 很常用也很重要,提到这个点的话,也可以提一下,async await,下面介绍

15 async await是什么,你怎么看 ?

async/await是JavaScript中用于处理异步操作的一种语法糖,它建立在Promise的基础之上,提供了一种更加简洁和易于理解的方式来编写异步代码

一、基本概念

  • async:是异步的简写,用于声明一个函数为异步函数。当函数被async修饰后,该函数会隐式地返回一个Promise对象。即使函数内部没有显式地返回Promise,JavaScript也会自动将其返回值封装成一个已解决的Promise对象。
  • await:是等待的意思,用于等待一个异步操作完成。await只能用在async函数内部,它会暂停async函数的执行,等待Promise解决(fulfilled)或拒绝(rejected),然后恢复async函数的执行,并返回解决的值或抛出拒绝的错误。

二、特点

  1. 简化异步代码:async/await使得异步代码的书写更加简单和直观,避免了回调嵌套的问题(也称为回调地狱),让异步代码看起来像同步代码一样。
  2. 错误处理:可以使用try/catch语句来处理await后面Promise的拒绝情况,使得错误处理更加方便和直观。
  3. 非阻塞:虽然await会暂停async函数的执行,但它不会阻塞整个线程或事件循环。async函数本身会立即返回,内部由await关键字修饰的异步过程会等待异步任务的完成再返回。

三、使用场景

  • 适用于需要按照顺序执行多个异步操作的场景,如先登录后获取用户信息的场景。
  • 适用于需要处理异步操作中的错误,并使用try/catch语句进行捕获和处理的场景。
  • 适用于需要简化异步代码,提高代码可读性和可维护性的场景。

四、注意事项

  1. await后面必须跟Promise:await后面通常跟着一个返回Promise的表达式。如果表达式的结果不是Promise,它会被自动转换成一个已解决的Promise。
  2. 错误处理:使用async/await时,应该用try/catch语句来捕获和处理可能出现的错误。否则,Promise的拒绝(rejection)会导致异常冒泡至外层的async函数,如果没有被捕获,会导致整个脚本终止。
  3. 并发处理:async/await本身不能直接处理多个并发的异步操作。如果需要并发执行多个异步任务,可以使用Promise.all等方法来处理。

五、总结

async/await是JavaScript中处理异步操作的一种强大工具,它使得异步代码的编写更加简单、直观和易于维护。然而,在使用时也需要注意一些事项,如错误处理和并发处理等问题。通过合理利用async/await,可以显著提高JavaScript代码的质量和效率。

我很喜欢用这种语法,当然,面试的时候注意要点回答即可 

 16 怎么理解js是单线程的,js执行事件的顺序是什么,怎么执行的? 

一、JS是单线程的理解

JavaScript(JS)被设计为单线程语言,这意味着在同一时间内,JS引擎只能执行一个任务。这种设计主要是基于其用途——主要用于与用户交互和操作DOM,以确保这些操作的一致性和可预测性。具体来说,JS引擎中负责解释和执行JavaScript代码的线程只有一个,即一次只能完成一项任务,这个任务执行完后才能执行下一个。这种机制简化了事件处理、DOM操作等场景下的编程模型,避免了多线程可能带来的复杂性和同步问题。

然而,需要注意的是,虽然JavaScript是单线程的,但浏览器本身是多进程的,其中渲染进程负责页面的渲染和JS的执行。在渲染进程中,又包含了多个线程,如GUI渲染线程、JS引擎线程、事件触发线程、定时器触发线程等。这些线程之间通过消息传递的方式进行通信,以确保页面的正常渲染和JS的异步执行。

二、JS执行事件的顺序

JavaScript中的代码执行顺序主要遵循以下规则:

  1. 从上到下,逐行执行:JS代码通常按照编写顺序从上到下逐行执行。

  2. 同步任务优先:在执行过程中,如果遇到同步任务(如普通函数调用、计算等),则会立即执行该任务,直到任务完成后再继续执行后续代码。

  3. 异步任务排队:如果遇到异步任务(如网络请求、定时器、事件监听等),则这些任务不会立即执行,而是会被放入到任务队列(也称为事件队列)中等待。当主线程中的同步任务执行完毕后,主线程会从任务队列中取出异步任务执行。

  4. 宏任务与微任务:异步任务可以进一步细分为宏任务(macrotask)和微任务(microtask)。                                                                                                                      宏任务包括整体代码script、setTimeout、setInterval、I/O、UI渲染等;                                微任务包括Promise.then、MutationObserver、process.nextTick等。

  5. 在同一事件循环中,微任务总是先于宏任务执行,并且微任务队列中的所有任务都会被执行完毕后,才会执行下一个宏任务。

三、JS执行事件的执行过程

以下是JavaScript执行异步事件的简化过程:

  1. 执行同步代码:首先执行代码中的同步部分,直到遇到异步操作。

  2. 异步操作入队:将异步操作(如定时器、网络请求等)的回调函数放入对应的任务队列(宏任务队列或微任务队列)中等待。

  3. 执行异步任务:当主线程中的同步代码执行完毕后,会检查任务队列中是否有待执行的异步任务。如果有,则取出任务并执行其回调函数。

  4. 事件循环:上述过程会不断重复,形成事件循环。主线程不断从任务队列中取出任务并执行,直到任务队列为空。

需要注意的是,由于JavaScript是单线程的,且异步任务不会阻塞主线程的执行,因此JavaScript程序能够保持较高的响应性和并发性。同时,这也要求开发者在编写JavaScript代码时,要充分考虑异步编程的特性和限制,合理使用Promise、async/await等异步编程工具来简化代码和提高性能。

 关于js的执行,画了流程图帮助理解

17 讲一下路由模式

前端的路由模式主要是指在单页面应用(SPA)中,通过前端技术实现页面路由控制,而不是通过传统的页面跳转(即刷新页面)来实现。这种模式允许应用在不重新加载整个页面的情况下,通过更改URL来呈现不同的内容或视图。

一、前端路由模式的主要类型

  1. Hash模式
    • 特点:通过在URL中添加#号(也称为锚点)和锚点值(hash值)来实现路由。当hash值发生变化时,页面不会重新加载,而是会触发hashchange事件,从而允许前端JavaScript捕获到这个变化并据此更新页面内容。
    • 优点
      • 兼容性好:支持所有现代浏览器,包括不支持HTML5 History API的旧版浏览器。
      • 简单配置:在Vue等前端框架中,默认使用Hash模式,不需要额外的配置。
      • 易于部署:由于使用了hash,URL发生变化时不会触发页面刷新,hash模式不会向后端发送HTTP请求,所有的hash值操作都与后端无关,因此部署时只需将静态文件部署到服务器即可。
    • 缺点:URL中带有#号,不够美观,且在某些情况下可能会与服务器端的锚点功能产生冲突。
  2. History模式
    • 特点:利用HTML5的History API(特别是pushState和replaceState方法)来实现路由。它允许开发者在不刷新页面的情况下,改变浏览器的URL地址,并添加新的历史记录条目。与hash模式不同,History模式下的URL不会包含#号,看起来更加美观。
    • 优点
      • URL更加美观,符合传统URL结构。
      • 支持更多的URL操作,如设置状态对象等。
    • 缺点
      • 需要后端服务器的配合,以确保在URL直接访问时能够返回正确的页面。否则,当用户直接访问某个路由地址时,服务器可能会返回404错误。
      • 在某些不支持HTML5 History API的旧版浏览器中可能无法使用。

二、前端路由模式的实现原理

  1. Hash模式:通过监听hashchange事件来实现路由的跳转和匹配。当URL的hash值发生变化时,会触发hashchange事件,前端JavaScript可以捕获到这个事件并据此更新页面内容。

  2. History模式:利用HTML5的History API(pushState和replaceState方法)来修改浏览器的URL地址,并添加新的历史记录条目。同时,通过监听popstate事件来捕获用户的导航行为(如点击前进、后退按钮或使用history.back()等方法),并据此更新页面内容。

三、前端路由模式的配置与选择

在Vue等前端框架中,可以通过在路由配置中设置mode属性来切换路由模式。例如,在Vue Router中,可以通过以下方式设置路由模式:

const router = new VueRouter({
mode: 'history', // 或者 'hash'
routes: [...]
});

在选择路由模式时,需要考虑以下因素:

  • 兼容性:如果项目需要考虑兼容性问题,或者需要在旧版浏览器中支持路由功能,Hash模式可能是一个更好的选择。
  • URL美观性:如果希望URL更加美观、简洁,不希望在URL中出现#符号,可以选择History模式。
  • 服务器配置:使用History模式时,需要确保后端服务器能够正确处理前端路由的URL,以避免在直接访问路由地址时出现404错误。

四、解决History模式下页面刷新404问题

在History模式下,如果服务器没有配置正确的路由规则,当用户直接访问某个路由地址或刷新页面时,服务器可能会返回404错误。为了解决这个问题,可以在服务器上进行如下配置:

  • 对于所有前端路由的URL,都返回同一个HTML页面(通常是index.html),然后由前端路由接管后续的URL解析和页面渲染工作。
  • 在Nginx等Web服务器中,可以通过配置try_files指令来实现这一功能。例如:
server {
listen 80;
server_name yourdomain.com;
location / {
root /path/to/your/frontend/files;
try_files $uri $uri/ /index.html;
}
}

以上配置会尝试按请求的路径($uri)直接访问文件或目录,如果找不到对应的文件或目录,则回退到/index.html页面,由前端路由接管后续的URL处理。

关于路由的两种模式,如果比较精通,可以讲的深一些,如果不熟的话,讲清楚自己会的即可,这里面深讲有很多东西,但是平时用的不多,别给自己挖坑。 

18 讲一下前端的http缓存相关

前端的HTTP缓存是提升网页性能的重要手段之一,它通过减少网络请求次数和降低服务器负载来优化用户体验。

一、HTTP缓存概述

HTTP缓存是指当浏览器向服务器请求资源时,会首先检查本地缓存中是否已存在该资源的有效副本。如果存在且未过期,则直接从缓存中返回资源,避免了对服务器的重复请求。HTTP缓存主要针对GET请求的资源,因为POST等请求通常涉及数据提交,不适合缓存。

二、HTTP缓存的分类

根据是否需要重新向服务器发起请求,HTTP缓存可以分为两大类:强缓存协商缓存

  1. 强缓存
    • 定义:在缓存数据未失效的情况下(即Cache-Control的max-age没有过期或者Expires的缓存时间没有过期),直接使用浏览器的缓存数据,不会请求服务器。
    • 特点
      • 优点:页面加载速度快,性能好。
      • 缺点:如果服务器资源有更新,用户可能无法获取到最新数据。
    • 相关HTTP头:Cache-Control(如max-age)、Expires、Pragma(但Pragma已逐渐被Cache-Control取代)。
  2. 协商缓存
    • 定义:当强缓存未命中时,浏览器会向服务器发送请求,通过对比资源是否修改来判断是否可以使用缓存。
    • 特点
      • 优点:可以确保用户获取到最新的资源。
      • 缺点:每次请求都需要与服务器交互,性能略逊于强缓存。
    • 相关HTTP头
      • 请求头:If-None-Match(与ETag配合使用)、If-Modified-Since(与Last-Modified配合使用)。
      • 响应头:ETag(资源的唯一标识)、Last-Modified(资源最后修改时间)。

三、HTTP缓存的工作流程

  1. 第一次请求资源
    • 浏览器向服务器发起资源请求。
    • 服务器返回资源,并在响应头中包含缓存控制信息(如Cache-Control、Expires、ETag、Last-Modified)。
  2. 后续请求资源
    • 浏览器检查本地缓存中是否有该资源的有效副本。
    • 如果有且未过期(强缓存命中),则直接从缓存中返回资源。
    • 如果过期或不存在(强缓存未命中),则向服务器发送请求,并带上协商缓存相关的HTTP头(如If-None-Match、If-Modified-Since)。
    • 服务器根据请求头中的信息判断资源是否修改,如果未修改(协商缓存命中),则返回304状态码,告诉浏览器使用缓存中的资源;如果已修改,则返回200状态码和新的资源。

四、HTTP缓存的配置

HTTP缓存的配置通常通过服务器端的设置来实现,也可以在HTML页面中通过meta标签来设置。

  • 服务器端配置:在web服务器(如nginx、apache)中配置缓存策略,设置Cache-Control、Expires等HTTP头。
  • HTML页面配置:在HTML页面的<head>标签中嵌入<meta>字段来设置缓存策略,但这种方式只对页面本身有效,对页面上的资源无效。

五、注意事项

  • 缓存失效问题:当服务器资源更新后,用户可能由于缓存未失效而无法获取到最新数据。可以通过在资源URL后添加版本号或时间戳等方式来避免这个问题。
  • 兼容性问题:不同浏览器对缓存策略的支持可能存在差异,需要根据实际情况进行调整。
  • 安全性问题:对于敏感信息或需要频繁更新的数据,应谨慎使用缓存策略,以免泄露信息或导致数据不一致。

综上所述,前端的HTTP缓存是提升网页性能的重要手段之一,通过合理配置缓存策略可以显著提高用户体验。然而,在使用缓存时也需要注意缓存失效、兼容性和安全性等问题。

放一张图帮助理解,不喜欢文字的小伙伴就看图吧 

 19 如果遇到数据量特别大的时候,怎么处理?

在前端开发中,处理大量数据是一项挑战,因为浏览器端的环境(如内存限制、渲染性能等)可能会成为瓶颈。

  1. 分页(Pagination)或懒加载(Lazy Loading)
    • 分页:将大量数据分成多个页面,用户通过点击页码或滚动来加载新页面的数据。这有助于减少单次加载的数据量,提高加载速度和用户体验。
    • 懒加载:仅加载用户可见或即将可见的数据部分,当用户滚动到页面底部或某个特定位置时,再加载更多数据。
  2. 虚拟化(Virtualization)
    • 虚拟化技术可以应用于列表、表格或任何需要渲染大量DOM元素的场景。它只渲染可视区域内的元素,并动态地管理这些元素的渲染和销毁,以减少DOM的数量和计算量。
    • 常用的库有react-window(React)、react-virtualized(React)、vue-virtual-scroller(Vue)等。
  3. 数据压缩和传输优化
    • 在服务器端对数据进行压缩,减少数据传输的带宽需求。
    • 使用更高效的数据格式,如JSON、Protobuf等,并可能的话,进一步自定义数据格式以减小体积。
    • 利用HTTP/2等现代协议的优势,如服务器推送、头部压缩等。
  4. 使用Web Workers
    • Web Workers允许你在与主执行线程分开的后台线程中运行脚本,这样可以避免阻塞用户界面。你可以将耗时的数据处理任务交给Web Worker执行,处理完成后将结果传回主线程。
  5. 无限滚动(Infinite Scrolling)
    • 类似于懒加载,但用户滚动页面时自动加载新内容,而不是通过点击或分页来加载。这可以提供更流畅的滚动体验,但需要注意控制加载的数据量,避免内存溢出。
  6. 服务端渲染(Server-Side Rendering, SSR)或同构渲染(Isomorphic Rendering)
    • 对于首屏渲染需要大量数据的情况,可以考虑使用服务端渲染技术。服务端预先渲染好页面,然后发送给客户端,可以减少客户端的渲染时间和数据处理压力。
  7. 数据分批处理
    • 在客户端处理数据时,可以将数据分批处理,每批处理一部分数据,处理完后再进行下一批。这有助于减少单次处理的数据量,避免内存溢出。
  8. 优化数据结构
    • 设计和使用更加高效的数据结构来存储和处理数据,可以减少内存占用和提高处理速度。
  9. 使用性能分析工具
    • 使用浏览器的开发者工具或第三方性能分析工具来监控和分析应用的性能瓶颈,从而针对性地进行优化。

真实问过,可惜当时只想出了分页和无限滚动,这一环节,处理方法还挺多的,主要就是压缩,切割方向 

 20 跨域问题是如何产生的?解决跨域问题的方式有哪些?请列举

跨域问题的产生

跨域问题主要是由于浏览器的同源策略限制而产生的。同源策略是浏览器的一种安全机制,它要求如果两个页面的协议、域名(或IP地址)和端口号三者中有任何一个不同,那么这两个页面之间就存在跨域问题。这种限制主要是为了保护用户的信息安全,防止恶意网站窃取数据。

解决跨域问题的方式

  1. JSONP(JSON with Padding)
    • 原理:利用<script>标签不受同源策略限制的特点,通过动态创建<script>标签来请求数据。服务器将数据作为参数传递给一个指定的回调函数,从而实现跨域数据交换。
    • 优点:兼容性好,可以解决主流浏览器的跨域问题。
    • 缺点:仅支持GET请求,不安全,可能遭受XSS攻击。
  2. CORS(跨域资源共享)
    • 原理:CORS是一种基于HTTP头的机制,它允许服务器标示哪些源(域、协议和端口)可以访问加载自己的资源。通过在服务器端设置Access-Control-Allow-Origin等响应头,浏览器就可以允许跨域请求。
    • 优点:支持所有类型的HTTP请求,是目前官方推荐的跨域解决方案。
    • 缺点:需要服务器端的支持。
  3. 代理服务器
    • 原理:通过搭建一个代理服务器,将客户端的请求转发给目标服务器,并将目标服务器的响应返回给客户端。由于客户端和代理服务器同源,因此可以绕过浏览器的同源策略限制。
    • 优点:实现简单,不需要修改前端代码。
    • 缺点:增加了一个网络请求的环节,可能会影响性能。
  4. WebSocket
    • 原理:WebSocket是一种在单个TCP连接上进行全双工通讯的协议。它不受同源策略的限制,只要服务器支持WebSocket协议,就可以实现跨域通信。
    • 优点:支持实时通信,性能较好。
    • 缺点:需要服务器支持WebSocket协议。
  5. postMessage
    • 原理:HTML5引入的window.postMessage方法允许跨窗口通信,无论这两个窗口是否同源。它主要用于不同源页面之间的消息传递。
    • 优点:实现简单,安全性较高。
    • 缺点:需要双方页面都进行编码支持。
  6. document.domain + iframe
    • 原理:通过设置document.domain为相同的值,可以使得不同源的页面在iframe中能够相互访问。但这种方法只适用于主域相同,子域不同的跨域场景。
    • 优点:实现简单,不需要服务器端支持。
    • 缺点:限制较多,只能用于特定场景。
  7. location.hash + iframe
    • 原理:通过改变iframe的URL的hash值来传递消息,由于hash值的变化不会触发跨域问题,因此可以实现跨域通信。但这种方法需要中间页面进行中转。
    • 优点:实现简单,兼容性好。
    • 缺点:传输数据量有限,且需要中间页面进行中转。
  8. window.name + iframe
    • 原理:利用window.name属性的特性(即使在不同的页面加载后,window.name的值仍然保持不变),通过iframe来传递数据。
    • 优点:传输数据量大,兼容性好。
    • 缺点:实现相对复杂,需要设置多个页面。

总结来说,解决跨域问题的方法多种多样,可以根据具体的需求和场景选择合适的方法。在实际应用中,CORS是官方推荐的跨域解决方案,因为它支持所有类型的HTTP请求,并且实现起来相对简单。而JSONP则是一种较为传统的解决方案,主要用于解决GET请求的跨域问题。代理服务器、WebSocket、postMessage等方法则各有其特点和适用场景。 

跨域也是重点,前三种必会,后面的可做了解 

21 http和https有哪些区别?

1. 协议安全性

  • HTTPS:是HTTP的安全版,它通过SSL/TLS协议来加密客户端和服务器之间的数据传输,确保数据在传输过程中的安全性和完整性。这种加密机制可以有效防止数据被截获、篡改或窃取。
  • HTTP:则没有这样的加密功能,它是以明文方式发送内容,不提供任何方式的数据加密。因此,HTTP协议不适合传输敏感信息,如信用卡号、密码等。

2. 连接方式

  • HTTPS:使用加密的连接方式,通过SSL/TLS协议在客户端和服务器之间建立一个加密通道,确保数据在传输过程中的安全性。
  • HTTP:则使用明文的连接方式,数据在传输过程中可以被截获或篡改。

3. 证书管理

  • HTTPS:需要使用CA(证书颁发机构)颁发的证书来进行加密和解密操作。这些证书验证了服务器的身份,并确保了加密通信的安全性。
  • HTTP:则不需要证书,因为它不涉及加密通信。

4. 连接状态

  • HTTPS:连接在数据传输过程中始终保持加密状态,即使连接被截断,也不会影响数据的加密状态。
  • HTTP:连接是明文的,一旦连接被截断,数据就可能被窃取或篡改。

5. 端口号

  • HTTPS:默认使用443端口进行通信。
  • HTTP:则默认使用80端口。

6. 资源消耗

  • HTTPS:由于使用了加密和解密操作,因此在数据传输过程中需要消耗更多的计算资源。
  • HTTP:则相对资源消耗较小,因为它不涉及加密操作。

7. 兼容性

  • HTTPS:在某些情况下可能会出现兼容性问题,因为某些操作系统或浏览器可能不支持某些类型的证书或SSL/TLS协议。
  • HTTP:的兼容性较好,可以在各种设备和操作系统上使用。

8. 搜索引擎优化(SEO)

  • HTTPS:可以提高网站的SEO(搜索引擎优化),因为搜索引擎更倾向于将使用HTTPS的网站排在前面,这增加了网站的权威性和可信度。
  • HTTP:则无法获得这些SEO优势。

综上所述,HTTPS和HTTP在协议安全性、连接方式、证书管理、连接状态、端口号、资源消耗、兼容性和SEO等方面存在明显的区别。在需要保证数据传输安全的情况下,应优先使用HTTPS协议。

这一块的重点在是否更有安全性,和加密上,抓住关键词,应该ok 

22 vite为什么比 webpack的编译速度快?

1. 利用了现代浏览器对ES Modules(ESM)的原生支持

  • 直接运行源代码:Vite利用了现代浏览器对ES Modules的原生支持,使得在开发环境下可以直接运行源代码,而无需像Webpack那样进行打包。这种方式省去了打包过程,从而显著提升了启动速度。
  • 按需编译:Vite采用了按需编译的策略,即只有在实际请求某个模块时,才会对该模块进行编译。这种按需加载的方式进一步减少了编译时间。

2. 使用了Esbuild进行依赖预构建

  • 构建速度优势:Esbuild是一个使用Go语言编写的打包工具,其构建速度比以Node.js编写的打包器(如Webpack)要快得多。Vite使用Esbuild来预构建依赖,从而提升了整体的编译速度。

3. 内存中的模块处理

  • 减少磁盘读写:Vite将开发环境下的模块处理放在了内存中,而不是像Webpack那样将所有模块都写入磁盘再进行读取。这种内存中的模块处理方式消除了磁盘读写的开销,进一步提高了开发过程中的速度。

4. 高效的热模块替换(HMR)

  • 快速局部更新:Vite通过利用ES模块的特性和内存中的模块处理,实现了高效的HMR机制。它能够在开发过程中快速捕捉到代码的变化,并立即将更新的模块发送给浏览器,实现即时的局部刷新。相比之下,Webpack的HMR机制在配置和实现上相对较为复杂。

5. 轻量级的插件系统

  • 减少开销:Vite的插件系统相对于Webpack来说更为轻量化和高效。Vite利用了ES模块的特性,能够以更直接的方式与插件进行交互。这种简化的插件系统设计使得Vite在构建过程中减少了不必要的开销,进一步提升了构建速度。

6. 持续的性能优化

  • 持续优化:Vite的核心团队致力于不断优化和改进工具本身的性能。他们关注于提高开发者的开发体验和效率,积极参与生态建设。这种持续的性能优化使得Vite在保持其速度优势的同时,不断适应新的开发需求和技术变化。

综上所述,Vite通过利用现代浏览器特性、高效的构建工具、内存中的模块处理、高效的HMR机制、轻量级的插件系统以及持续的性能优化等手段,实现了比Webpack更快的编译速度。然而,需要注意的是,虽然Vite在开发环境下的速度很快,但在生产环境下,由于需要对代码进行压缩、优化等处理,Vite的构建速度可能会比Webpack慢一些。因此,在选择构建工具时,需要根据项目的具体需求和场景进行权衡。

 关于编译打包工具这一点可能都会问到,下面会细说 

23 关于webpack,你了解多少? 

Webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler),它允许开发者将项目中的资源(如JavaScript、CSS、图片等)视为模块,并通过分析和处理这些模块之间的依赖关系,将它们打包成一个或多个bundle(捆绑包),这些bundle可以在浏览器中加载和执行。Webpack极大地提高了前端开发的效率和项目的可维护性,是现代前端开发不可或缺的工具之一。以下是对Webpack的详细了解:

一、Webpack的基本概念和特点

  1. 静态模块打包工具:Webpack能够处理应用程序中的所有资源,包括JavaScript、CSS、图片等,并将它们打包成浏览器可以识别的格式。
  2. 模块化开发:Webpack支持ES6模块化语法以及CommonJS、AMD等模块化标准,使得开发者能够以模块化的方式组织代码,提高代码的可维护性、可重用性和可扩展性。
  3. 依赖管理:Webpack能够自动分析项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的扩展语言(如SCSS、TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
  4. 资源优化:Webpack提供了丰富的loader和plugin,可以对项目中的资源进行各种优化处理,如代码压缩、图片压缩等,以减少文件体积,加快加载速度。
  5. 开发便捷性:Webpack提供了开发服务器(webpack-dev-server)和热模块替换(HMR)等功能,使得开发者在开发过程中能够实时预览更改,而无需手动刷新浏览器。
  6. 易于集成:Webpack易于与其他工具和库集成,如React、Vue、Angular等前端框架,以及ESLint、Prettier等代码质量工具。

二、Webpack的工作原理

Webpack的工作原理可以概括为以下几个主要步骤:

  1. 初始化参数:Webpack从配置文件(如webpack.config.js)和命令行参数中读取和合并配置,确定最终的打包参数。
  2. 开始编译:使用上一步得到的参数初始化Compiler对象,并加载所有配置的插件。Compiler对象执行run方法,开始执行编译过程。
  3. 确定入口:根据配置文件中的entry字段找出所有的入口文件。这些入口文件是Webpack打包的起点。
  4. 编译模块:从入口文件开始,Webpack会递归地分析每个模块的依赖,并使用配置的Loader对这些模块进行翻译和转换。
  5. 完成模块编译:在所有的模块都被Loader转换后,Webpack会得到每个模块的最终内容和它们之间的依赖关系图。
  6. 输出资源:Webpack根据入口和模块之间的依赖关系,将模块组合成一个个包含多个模块的Chunk(代码块)。然后,Webpack将这些Chunk转换成单独的文件,并加入到输出列表中。
  7. 输出完成:最后,Webpack根据配置确定输出的路径和文件名,将文件内容写入到文件系统中。

三、Webpack的配置和使用

Webpack的配置主要通过webpack.config.js文件来实现,其中可以指定入口文件、输出文件、模块规则、插件等。以下是一些常见的配置选项:

  • entry:指定打包的入口文件,可以是一个或多个文件。
  • output:配置输出的文件名和路径。
  • module:用来配置不同类型模块的处理规则,比如解析JavaScript、CSS、图片等。
  • resolve:配置模块解析的方式,可以指定模块的搜索路径和扩展名。
  • plugins:用于扩展Webpack功能的插件,比如压缩代码、拷贝文件等。

Webpack的使用通常包括安装Webpack、配置webpack.config.js文件、编写项目代码、运行Webpack命令进行打包等步骤。

四、Webpack的插件和loader

Webpack的插件(Plugins)和loader(加载器)是Webpack的核心功能之一。

  • 插件(Plugins):主要是扩展Webpack的功能,其本质是监听整个打包的生命周期。Webpack基于事件流框架Tapable,在运行的生命周期中会广播出很多事件,插件可以监听这些事件,在合适的时机通过Webpack提供的API改变输出结果。
  • 加载器(Loader):主要是对资源进行加载/转译的预处理工作,其本质是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。某种类型的资源可以使用多个loader,执行顺序是从右到左,从下到上。

五、Webpack的应用场景

Webpack不仅可以用于构建浏览器端的应用,还可以通过适当的配置和插件,支持Node.js环境下的服务器端渲染(SSR)、Electron桌面应用等多种目标。这使得Webpack成为了一个非常灵活和强大的构建工具。

综上所述,Webpack是现代前端开发中的重要工具,它通过模块化开发、依赖管理、资源优化等功能,极大地提高了前端开发的效率和项目的可维护性。

 24 介绍一下vite?

Vite是一个面向现代浏览器的前端构建工具,由Vue.js的创始人尤雨溪开发。它以其轻量、快速的特点,在前端开发社区中受到了广泛的关注和采用。以下是对Vite的详细介绍:

一、基本概念与特点

  1. 轻量快速:Vite基于ES Modules(ECMAScript标准的原生模块系统)实现,利用现代浏览器对ES Modules的原生支持,实现按需编译,避免了传统构建工具如Webpack在开发阶段的全量打包过程,从而大大提高了开发效率和构建速度。
  2. 即时编译:Vite采用即时编译(JIT,Just-In-Time)模式,即只有在浏览器请求某个模块时,才会在服务端进行编译,并将编译结果返回给浏览器。这种方式减少了不必要的编译工作,进一步提升了开发体验。
  3. 支持多种框架:Vite不仅支持Vue.js,还支持React、Preact、LitElement等多种前端框架,可以满足不同框架的项目需求。
  4. 配置简单:Vite的配置非常简单,只需要一个配置文件vite.config.js,就可以完成大部分常用的配置,而且配置项也非常少。

二、工作原理

Vite的工作原理主要基于以下几点:

  1. 利用ES Modules:Vite利用现代浏览器对ES Modules的原生支持,通过<script type="module">标签加载入口文件,避免了传统构建工具中的打包过程。
  2. 按需编译:Vite采用按需编译的方式,只有在浏览器请求某个模块时,才会在服务端进行编译,并将编译结果返回给浏览器。这种方式减少了不必要的编译工作,提高了开发效率。
  3. 热更新(HMR):Vite的热更新原理和Webpack-dev-server类似,都是使用了WebSocket在本地服务端和客户端建立双向通信。当文件发生变化时,服务端通过WebSocket将更新推送给客户端,客户端接收到更新后,只更新发生变化的模块,而不是重新加载整个页面。

三、优势与不足

优势
  1. 启动速度快:由于Vite启动的时候不需要打包,也就无需分析模块依赖、编译,所以启动速度非常快。
  2. 热更新速度快:Vite在HRM方面,当某个模块内容改变时,让浏览器去重新请求该模块即可,而不是像Webpack那样重新编译该模块的所有依赖。
  3. 开发体验好:Vite的即时编译和按需加载特性,使得开发者在开发过程中可以实时看到修改效果,提高了开发效率。
不足
  1. 生产环境打包:虽然Vite在开发环境下表现优异,但在生产环境下,它仍然需要使用传统的打包工具(如Rollup)进行打包。对于某些特定的生产环境需求(如代码分割、懒加载等),可能需要额外的配置和优化。
  2. 浏览器兼容性:Vite依赖于现代浏览器对ES Modules的原生支持,因此可能无法在某些旧版浏览器上运行。对于需要兼容旧版浏览器的项目,可能需要使用其他构建工具或进行额外的兼容性处理。

四、使用场景

Vite非常适合用于快速开发和原型制作。它的轻量、快速和易用的特点,使得开发者可以更加专注于业务逻辑的实现,而不是花费大量时间在构建和配置工具上。同时,Vite也支持多种前端框架和丰富的插件生态,可以满足不同项目的需求。

五、总结

Vite是一个面向现代浏览器的前端构建工具,以其轻量、快速和易用的特点在前端开发社区中受到了广泛的关注和采用。它利用现代浏览器对ES Modules的原生支持,实现了按需编译和热更新等功能,大大提高了开发效率和构建速度。虽然Vite在生产环境下可能需要结合其他工具进行打包和优化,但在快速开发和原型制作方面表现优异。

以上就是webpack和vite了,也是面试重灾区,记住抓关键词,不熟悉的不要随便裸扩展 

 25 rem和em的有什么区别?

rem

  • 相对于根元素(html)的字体大小。
  • 全局性,适合用于统一设置页面的字体大小和布局,方便整体调整。
  • 在现代浏览器中广泛支持。

em

  • 相对于父元素的字体大小。
  • 局部性,适合在需要精确控制子元素相对于父元素字体大小的场景中使用。
  • 在所有浏览器中都被支持。

简而言之,rem是全局的,基于根元素;em是局部的,基于父元素。选择哪个取决于你的具体需求,是想要全局统一调整还是局部精细控制。

回答清楚即可,不用过多赘述 

 

26 get与post有什么区别? 

1. 请求目的

  • GET:主要用于从服务器上获取数据。它是HTTP协议中用于请求资源的一种无副作用的方法,即请求不会对服务器上的数据造成任何改变。
  • POST:主要用于向服务器提交数据,以创建或更新资源。它通常用于提交表单数据,或在服务器上进行其他数据更新操作。

2. 参数传递方式

  • GET:将表单中的数据按照variable=value的形式,添加到action所指向的URL后面,并使用“?”连接URL和参数,多个参数之间用“&”连接。这种方式使得数据直接暴露在URL中,容易被用户看到和记录。
  • POST:将表单中的数据放在HTTP请求的消息体中,按照变量和值相对应的方式,传递到action所指向的URL。这种方式对用户来说是不可见的,增加了数据传输的安全性。

3. 数据量限制

  • GET:由于URL长度有限制(一般为2KB到8KB之间,具体取决于浏览器和服务器),因此GET请求传输的数据量相对较小。
  • POST:理论上没有数据量限制,可以传输大量数据,特别适用于文件上传等场景。

4. 安全性

  • GET:由于数据暴露在URL中,因此安全性较低,不适合传输敏感信息,如密码、用户信息等。
  • POST:由于数据在HTTP请求的消息体中传输,对用户不可见,因此安全性较高,适合传输敏感信息。

5. 缓存和回退/刷新

  • GET:请求结果可以被缓存,并且可以直接进行回退和刷新操作,不会对用户产生额外影响(但会重新提交请求)。
  • POST:请求结果默认不被缓存,且如果直接进行回退和刷新操作,可能会再次提交数据,导致不必要的重复处理。

6. 能否保存为书签

  • GET:请求的URL可以被保存为书签,方便用户下次直接访问。
  • POST:由于POST请求包含大量的数据在请求体中,且这些数据不会显示在URL中,因此其请求的URL通常无法直接保存为书签。

7. 幂等性

  • GET:是幂等的,即多次执行相同的GET请求,结果应该相同(不考虑缓存和服务器内部状态变化)。
  • POST:通常不是幂等的,因为每次POST请求都可能会导致服务器上的数据发生变化。

在实际开发中,应根据具体需求选择合适的请求方法。

重点是在前几点之中 

  • 24
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值