常见的前端考试面试题目【vue,react,css,six】

常见的vue,javascript,css前端面试题

1.Vue2和Vue3的区别至少说5点

  1. 性能优化:Vue3在性能方面进行了优化,使用了Proxy代理对象替代了Object.defineProperty,提高了响应式系统的性能,同时也优化了虚拟DOM的渲染性能。
  2. Tree-shaking支持:Vue3对Tree-shaking进行了支持,可以更好地优化打包后的代码,减少了不必要的代码量,提高了应用的性能。
  3. 更好的适配性:Vue3支持更多的平台和环境,包括Web、原生应用和桌面应用等,可以更好地适应不同的应用场景。
  4. 更小的体积:Vue3的体积比Vue2更小,可以更快地加载和解析。
  5. 更好的 TypeScript 支持:Vue3对TypeScript的支持更加完善,可以更好地进行类型检查和代码提示。
  6. 更好的组合 API:Vue3引入了Composition API,可以更好地组织和复用组件逻辑。
  7. 更好的响应式系统:Vue3的响应式系统比Vue2更加灵活和高效,可以更好地处理大规模数据的变化。
  8. 更好的虚拟 DOM:Vue3的虚拟 DOM 比Vue2更加高效,可以更快地进行渲染。
  9. 更好的插件机制:Vue3的插件机制更加灵活和易用,可以更好地扩展Vue的功能。
  10. 生命周期:对于生命周期来说,整体上变化不大,只是大部分生命周期钩子名称上 + “on”,功能上是类似的。不过有一点需要注意,Vue3 在组合式API(Composition API,)中使用生命周期钩子时需要先引入,而 Vue2 在选项API(Options API)中可以直接调用生命周期钩子

总之,Vue3在性能、体积、TypeScript支持、组合API、响应式系统、虚拟DOM和插件机制等方面都有很大的改进,是Vue2的升级版,可以更好地满足开发者的需求。

2.Vue3中组件通信的流程【父传子,子传父】

  1. 父组件向子组件传递数据的流程如下:

    1. 在父组件中定义需要传递给子组件的数据,并通过props将其传递给子组件。
    2. 在子组件中通过props接收父组件传递过来的数据。
    3. 子组件可以直接使用props中的数据。
  2. 子组件向父组件传递数据的流程如下:

    1. 在子组件中定义需要传递给父组件的数据,并通过emit方法触发一个自定义事件。

    2. 在父组件中通过v-on指令监听子组件触发的自定义事件,并在对应的方法中处理子组件传递过来的数据

    3. 父组件可以直接使用子组件传递过来的数据。

总的来说,父组件通过props向子组件传递数据,子组件通过emit触发自定义事件向父组件传递数据。这样就可以实现组件之间的通信

3.Apply/call/bind的原理是什么?

Apply、call和bind都是JavaScript中用于改变函数执行上下文(即this指向)的方法。

apply和call的原理类似,都是通过改变函数的this指向来实现。区别在于传入参数的方式不同,apply接收一个数组作为参数,而call接收一系列参数。

例如:

function sayHello() {
  console.log(`Hello, ${this.name}!`);
}
const person = { name: 'Alice' };
sayHello.apply(person); // 输出:Hello, Alice!
sayHello.call(person); // 输出:Hello, Alice!

bind的原理也是改变函数的this指向,但它不会立即执行函数,而是返回一个新的函数,需要手动调用才会执行。

例如:

function sayHello() {
  console.log(`Hello, ${this.name}!`);
}

const person = { name: 'Alice' };

const sayHelloToPerson = sayHello.bind(person);
sayHelloToPerson(); // 输出:Hello, Alice!

总的来说,apply、call和bind的原理都是通过改变函数的this指向来实现,只是传入参数和返回值的方式不同。

4.说说你对原型和原型链的理解?

原型是JavaScript中的一个重要概念,每个对象都有一个原型对象,它是一个指向另一个对象的引用。当我们访问一个对象的属性或方法时,如果该对象本身没有该属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法为止。

原型链是由原型对象组成的链式结构,每个对象都有一个原型对象,它的原型对象又有自己的原型对象,这样一直向上直到Object.prototype,这个原型对象是所有对象的顶层原型。在查找属性或方法时,如果当前对象没有该属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法为止。

原型和原型链是JavaScript中非常重要的概念,它们使得JavaScript可以实现面向对象编程的特性,如继承和多态。同时,原型和原型链也是JavaScript中的一些常见问题的关键,如this指向、作用域链等。因此,深入理解原型和原型链对于掌握JavaScript编程非常重要。

5.说说你对ES6中Generator的理解

ES6中的Generator是一种特殊的函数,它可以在执行过程中暂停并恢复。Generator函数使用yield关键字来暂停执行并返回一个值,使用next()方法来恢复执行并传入一个值。Generator函数可以用来实现异步编程、迭代器和状态机等功能。

Generator函数的特点包括:

  1. 使用function*关键字定义,函数体内部使用yield关键字来暂停执行并返回值。 *
  2. 调用Generator函数不会立即执行函数体,而是返回一个迭代器对象。
  3. 调用迭代器对象的next()方法会恢复执行Generator函数,并返回yield语句后面的值。
  4. Generator函数可以使用yield*语句来委托给另一个Generator函数执行。
  5. Generator函数可以使用return语句来结束执行,并返回一个指定的值。
  6. Generator函数可以使用throw语句来抛出一个异常,中断执行。

总的来说,Generator函数是一种强大的工具,可以用来实现复杂的异步编程和状态管理。它的特殊语法和执行方式使得代码更加简洁、易读、易维护。

6.说说你对Event Loop(事件循环)的理解

Event Loop是JavaScript中的一种机制,用于处理异步代码的执行顺序事件的处理。它是JavaScript运行时环境中的一个循环,不断地从任务队列中取出任务并执行,直到任务队列为空。

当JavaScript执行异步代码时,它会将这些代码放入任务队列中,等待Event Loop来执行。当主线程执行完同步代码后,Event Loop会检查任务队列中是否有任务需要执行,如果有,则会按照顺序取出任务并执行。如果任务队列为空,则Event Loop会一直等待,直到有新的任务加入队列。

Event Loop的机制保证了JavaScript的单线程执行模型,同时也保证了异步代码的正确执行顺序。在浏览器中,常见的异步任务包括定时器、网络请求、事件处理等。在Node.js中,异步任务还包括文件读写、数据库操作等。

7.说说Promise和async/await 的区别?

Promise和async/await都是用于处理异步操作的方式,但是它们之间有一些区别:

  1. 语法:Promise使用.then()和.catch()方法来处理异步操作的结果,而async/await使用async函数和await关键字来处理异步操作的结果。

  2. 错误处理:在Promise中,错误处理通常是通过.catch()方法来处理的,而在async/await中,错误处理通常是通过try/catch语句来处理的。

  3. 可读性:async/await通常比Promise更易于阅读和理解,因为它们使用类似于同步代码的语法。

  4. 性能:在某些情况下,Promise可能比async/await更快,因为async/await需要将代码转换为Promise链。

总的来说,Promise和async/await都是处理异步操作的有效方式,但是在不同的情况下,它们可能会有不同的优缺点。

8.说说浏览器事件循环和nodeJs的事件循环的区别?

虽然浏览器和Node.js都使用事件循环来处理异步任务,但它们的事件循环实现方式有所不同。

在浏览器中,事件循环包括主线程任务队列微任务队列

  • 当浏览器遇到异步任务时,它将其添加到任务队列中,等待下一次事件循环迭代时执行。
  • 主线程完成当前执行栈中的任务时,它会检查微任务队列是否有任务需要执行,如果有,则按顺序执行微任务队列中的任务。
  • 然后,它会从任务队列中取出一个任务,并将其添加到执行栈中执行。
  • 这个过程循环重复,直到任务队列和微任务队列都为空。

在Node.js中,事件循环包括主线程任务队列微任务队列观察者(Watchers)。

  • 观察者用于监听操作系统的事件,例如文件I/O、网络I/O等。
  • 当Node.js遇到异步任务时,它将其添加到任务队列中,等待下一次事件循环迭代时执行。
  • 当主线程完成当前执行栈中的任务时,它会检查微任务队列是否有任务需要执行,如果有,则按顺序执行微任务队列中的任务。
  • 然后,它会执行观察者中的回调函数,处理相应的事件。
  • 这个过程循环重复,直到任务队列和微任务队列都为空,并且没有活动的观察者为止。

因此,浏览器和Node.js的事件循环机制在微任务队列和观察者方面存在一些区别。

  • 在浏览器中,微任务队列只包括Promise和MutationObserver回调,而在Node.js中,微任务队列还包括process.nextTick回调
  • Node.js的事件循环还包括观察者,用于处理底层的操作系统事件。

9.说说你对浏览器缓存机制的理解

浏览器缓存机制是指浏览器在访问网页时,将网页的一些资源(如图片、CSS、JS等)缓存到本地,以便下次访问同一网页时可以直接从本地缓存中获取资源,从而提高网页加载速度和用户体验。

浏览器缓存机制分为两种:强缓存和协商缓存。

强缓存是指浏览器在第一次请求资源时,服务器返回的响应头中包含了缓存时间或者缓存标识,浏览器会根据这些信息判断是否使用缓存。如果缓存未过期,则直接从本地缓存中获取资源,不会向服务器发送请求,从而提高访问速度。

协商缓存是指浏览器在第一次请求资源时,服务器返回的响应头中包含了缓存标识,浏览器会将该标识存储在本地,并在下次请求时将该标识发送给服务器,服务器会根据该标识判断资源是否有更新,如果没有更新,则返回304状态码,告诉浏览器可以使用本地缓存,如果有更新,则返回新的资源。

浏览器缓存机制可以有效减少网络请求,提高网页加载速度和用户体验,但也可能导致缓存过期、缓存未更新等问题,需要开发者根据具体情况进行合理的缓存设置和更新策略。

10.说说你对浏览器内核的理解

主要分成两部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎。

  • 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。
  • JS引擎:解析和执行javascript来实现网页的动态效果。
    最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎。
  • 常见内核
    • Trident 内核:IE, MaxThon, TT, The World, 360, 搜狗浏览器等。[又称 MSHTML]
    • Gecko 内核:Netscape6 及以上版本,FF, MozillaSuite / SeaMonkey 等
    • Presto 内核:Opera7 及以上。 [Opera内核原为:Presto,现为:Blink;]
    • Webkit 内核:Safari, Chrome等。 [ Chrome的:Blink(WebKit 的分支)]

11.说说你对Vue的响应式原理的理解

Vue的响应式原理是指当Vue监测到数据变化时,它会自动更新与该数据相关联的视图,从而实现数据驱动视图的目的。

具体来说,当我们在Vue中定义一个数据对象时,Vue会将该对象转换成响应式对象。当我们改变响应式对象的属性时,Vue会自动更新与该属性相关联的视图。

实现这个功能的核心是Vue的侦听器和观察者机制。Vue使用侦听器来监测数据的变化,并将变化通知给相关的观察者。观察者会接收到通知后,再执行相应的更新操作。

这种响应式原理的实现,使得我们在使用Vue开发应用时可以更加专注于数据的处理,而不用关心视图的更新。同时,也让我们能够更方便地进行组件化开发,提高了代码的可复用性和可维护性。

12.Methods watch computed区别是什么

Methods 和 watch 都是 Vue.js 中的响应式数据处理方式,但它们的作用和使用方式有所不同。

Methods 是 Vue.js 中的一个选项,用于定义组件中的方法。这些方法可以在组件中被调用,通常用于处理用户交互或组件内部的逻辑。Methods 中的方法不会自动响应数据的变化,需要手动调用才能更新视图。

Watch 是 Vue.js 中的一个选项,用于监听数据的变化并执行相应的操作。当数据发生变化时,watch 中定义的函数会被自动调用,可以在函数中执行一些操作,例如更新视图或发送请求等。Watch 可以监听单个数据或多个数据的变化,也可以监听对象或数组的变化。

Computed 是 Vue.js 中的一个选项,用于定义计算属性。计算属性是根据其他数据计算得出的值,可以在模板中直接使用。Computed 中的属性会自动响应数据的变化,当计算属性依赖的数据发生变化时,计算属性会重新计算并更新视图。与 Methods 不同,Computed 中的属性不需要手动调用才能更新视图。

13.说说你对Virtual DOM的理解?

Virtual DOM是一种虚拟的DOM树,它是React中的一个重要概念。它是React用来提高性能的一种技术,通过在内存中维护一份虚拟DOM树,React可以在每次数据变化时,通过比较新旧两个虚拟DOM树的差异,只对需要更新的部分进行重新渲染,从而避免了全局重新渲染的开销。

Virtual DOM的优点在于:

  1. 提高性能:通过比较新旧两个虚拟DOM树的差异,只对需要更新的部分进行重新渲染,从而避免了全局重新渲染的开销。

  2. 简化代码:Virtual DOM可以让开发者专注于数据的处理,而不用关心DOM操作的细节,从而简化了代码的编写。

  3. 跨平台:由于Virtual DOM是在JavaScript层面实现的,因此可以跨平台使用,不仅可以在浏览器中使用,还可以在Node.js等环境中使用。

总之,Virtual DOM是React中的一个重要概念,它通过在内存中维护一份虚拟DOM树,提高了应用的性能,简化了代码的编写,同时也可以跨平台使用。

14.说说你对nextTick的理解和作用

nextTick是Vue.js中的一个异步方法,它的作用是将回调函数推迟到下一个DOM更新周期之后执行。在Vue.js中,当数据发生变化时,Vue.js会异步地更新DOM,而nextTick就是在DOM更新完成之后执行回调函数的。

nextTick的主要作用是在DOM更新之后执行一些操作,例如获取更新后的DOM元素的尺寸、位置等信息,或者在更新后执行一些需要在DOM渲染完成后才能执行的操作,例如使用某些插件或库。

另外,nextTick还可以用于在Vue.js的生命周期钩子函数中执行一些异步操作,例如在mounted钩子函数中使用nextTick来确保组件已经被挂载到DOM中后再执行一些操作。

总之,nextTick是Vue.js中非常重要的一个异步方法,它可以帮助我们在DOM更新完成后执行一些操作,从而避免一些潜在的问题。

15.说说你对webpack的理解

Webpack是一个现代化的JavaScript应用程序的静态模块打包器。它可以将多个JavaScript文件打包成一个或多个文件,以及处理其他类型的文件,如CSS、图片等。Webpack的主要功能包括:

  1. 模块化管理:Webpack支持模块化开发,可以将应用程序拆分成多个模块,每个模块都有自己的依赖关系,Webpack可以将这些模块打包成一个或多个文件。

  2. 代码转换:Webpack可以将ES6、TypeScript等高级语言转换成浏览器可以识别的JavaScript代码,还可以将CSS、LESS、SASS等样式文件转换成浏览器可以识别的CSS代码。

  3. 文件优化:Webpack可以对文件进行压缩、合并、混淆等优化操作,减小文件大小,提高应用程序的加载速度。

  4. 插件扩展:Webpack提供了丰富的插件,可以扩展Webpack的功能,如自动生成HTML文件、提取公共代码等。

总之,Webpack是一个非常强大的工具,可以帮助开发者更好地管理和打包应用程序,提高开发效率和应用程序性能。

16.谈谈GET和POST的区别

GET和POST是HTTP协议中常用的两种请求方法,它们的主要区别如下:

  1. GET请求会将请求参数放在URL的后面,而POST请求则将请求参数放在请求体中。因此,GET请求的参数可以被直接看到,而POST请求的参数则不会被直接看到。

  2. GET请求的参数长度有限制,一般为2048个字符,而POST请求的参数长度没有限制。

  3. GET请求是幂等的,即多次请求同一个URL的结果是相同的,不会对服务器产生影响。而POST请求不是幂等的,即多次请求同一个URL的结果可能不同,会对服务器产生影响。

  4. GET请求可以被缓存,而POST请求不可以被缓存。

  5. GET请求一般用于获取数据,而POST请求一般用于提交数据。

综上所述,GET请求适用于获取数据,而POST请求适用于提交数据。如果需要传输敏感信息,应该使用POST请求,因为POST请求的参数不会被直接看到。

17.说说HTTP和HTTPS的区别,HTTPS加密原理是?

HTTP和HTTPS都是用于传输数据的协议,但是它们之间有很大的区别。

  1. 安全性:HTTP是明文传输数据,数据容易被窃听和篡改,而HTTPS使用SSL/TLS加密传输数据,可以保证数据的安全性。
  2. 端口号:HTTP默认使用80端口,而HTTPS默认使用443端口。
  3. 证书:HTTPS需要使用数字证书来验证网站的身份,确保用户访问的是真实的网站,而HTTP没有这个机制。
  4. 性能:HTTPS的加密和解密过程会消耗更多的计算资源,因此比HTTP慢一些。
  5. 缓存:由于HTTPS的数据是加密的,所以不能像HTTP一样进行缓存,这也是HTTPS比HTTP慢的原因之一。

总之,HTTPS比HTTP更安全,但也更慢一些。在需要保护用户隐私和敏感信息的情况下,应该使用HTTPS。

HTTPS的加密原理是通过SSL/TLS协议来实现的。SSL/TLS协议使用了非对称加密和对称加密两种加密方式。在建立连接时,客户端和服务器会进行一次握手,客户端会向服务器发送一个随机数,服务器会用自己的私钥对这个随机数进行加密,然后发送给客户端。客户端收到加密后的随机数后,会用服务器的公钥进行解密,得到原始的随机数。接下来,客户端和服务器会使用这个随机数来生成一个对称密钥,用于后续的数据传输加密。这样,即使数据被窃听,也无法解密出原始数据。

18.TCP为什么要三次握手?

TCP采用三次握手的方式建立连接,主要是为了保证连接的可靠性和防止网络中的重复数据包。

第一次握手:客户端向服务器发送一个SYN(同步)包,表示客户端请求建立连接,并告诉服务器客户端的初始序列号。

第二次握手:服务器接收到客户端的SYN包后,向客户端发送一个SYN/ACK(同步/确认)包,表示服务器已经收到客户端的请求,并告诉客户端服务器的初始序列号。

第三次握手:客户端接收到服务器的SYN/ACK包后,向服务器发送一个ACK(确认)包,表示客户端已经收到服务器的确认,并告诉服务器客户端的下一个序列号。

通过三次握手,客户端和服务器都确认了对方的存在,并且都知道了对方的初始序列号,建立了可靠的连接。如果只有两次握手,可能会出现网络中的重复数据包,导致连接不可靠。

19.说说Proxy代理的原理

Proxy代理是一种网络通信方式,它充当了客户端和服务器之间的中间人,代替客户端向服务器发送请求,并将服务器的响应返回给客户端。其原理如下:

  1. 客户端向代理服务器发送请求,请求中包含目标服务器的地址和端口号。

  2. 代理服务器接收到请求后,会解析请求中的目标服务器地址和端口号,并向目标服务器发送请求。

  3. 目标服务器接收到代理服务器的请求后,会将响应发送给代理服务器。

  4. 代理服务器接收到响应后,会将响应返回给客户端。

通过这种方式,客户端和服务器之间的通信就被代理服务器所控制,客户端和服务器之间的真实IP地址和端口号也被隐藏。同时,代理服务器还可以对请求和响应进行过滤和修改,从而实现一些特定的功能,如访问控制、缓存、负载均衡等。

20.说说内存泄漏的理解?内存泄漏的情况有哪些?

内存泄漏指的是程序在运行过程中,分配的内存空间没有被及时释放,导致系统中的可用内存不断减少,最终可能导致系统崩溃或者变得非常缓慢。内存泄漏通常是由于程序中存在一些错误的编程习惯或者逻辑错误导致的。

内存泄漏的情况有很多种,以下是一些常见的情况:

  1. 没有正确释放动态分配的内存:程序中使用了动态分配的内存,但是在使用完毕后没有正确释放,导致内存泄漏。
  2. 循环引用:在程序中存在两个或多个对象之间的循环引用,导致这些对象无法被垃圾回收器回收,从而导致内存泄漏。
  3. 文件句柄未关闭:程序中打开了文件,但是在使用完毕后没有正确关闭,导致文件句柄一直占用内存。
  4. 缓存未清理:程序中使用了缓存,但是在使用完毕后没有及时清理,导致缓存占用了过多的内存。
  5. 事件监听器未移除:程序中注册了事件监听器,但是在使用完毕后没有移除,导致事件监听器一直占用内存。
  6. 线程未正确终止:程序中创建了线程,但是在使用完毕后没有正确终止,导致线程一直占用内存。
  7. 内存泄漏的DOM元素:在JavaScript中,DOM元素是非常常见的内存泄漏来源,因为DOM元素的创建和销毁是由浏览器控制的,如果程序中存在对DOM元素的引用,但是没有及时释放,就会导致内存泄漏。
  8. 定时器:如果程序中存在一些没有被正确清理的定时器,就会导致内存泄漏。例如,如果一个定时器在页面销毁之前没有被清除,就会一直占用内存空间,从而导致内存泄漏。
  9. 闭包:闭包是一种非常强大的编程技巧,但是如果不小心使用会导致内存泄漏。当一个函数返回一个内部函数时,内部函数会保留对外部函数的引用,如果这个内部函数被存储或者传递给其他对象,就会导致外部函数无法被垃圾回收机制回收,从而造成内存泄漏。
  10. 资源未释放:在程序中使用一些系统资源,如文件、网络连接、数据库连接等,如果这些资源在使用完毕后没有被正确释放,就会导致内存泄漏

以上是一些常见的内存泄漏情况,程序员在编写程序时应该注意这些问题,及时释放内存,避免内存泄漏。

21.说说你对Event Loop的理解?

Event Loop是JavaScript运行时环境中的一个重要组成部分,它是一种机制,用于处理异步事件和回调函数。在JavaScript中,所有的I/O操作、定时器和事件处理都是异步的,这意味着它们不会阻塞主线程的执行。Event Loop就是负责管理这些异步事件和回调函数的机制。

Event Loop的基本原理是,JavaScript引擎会维护一个消息队列,所有的异步事件和回调函数都会被放入这个队列中。当主线程执行完当前任务后,Event Loop会检查消息队列中是否有待处理的事件和回调函数,如果有,就将它们取出来执行,否则就等待新的事件加入队列。

在实际应用中,Event Loop的作用非常重要,它可以帮助我们实现高效的异步编程,避免阻塞主线程的执行,提高应用的性能和用户体验。

22.说说你对BOM浏览器对象模型的理解,常见的BOM对象你了解哪些?

BOM(Browser Object Model)浏览器对象模型是指浏览器提供的一组对象,用于访问和控制浏览器窗口的各个部分,如地址栏、导航栏、状态栏等。BOM对象与DOM对象不同,DOM对象用于访问和控制网页文档的内容和结构,而BOM对象用于访问和控制浏览器窗口的各个部分。

常见的BOM对象包括:

  1. window对象:表示浏览器窗口,是BOM对象的顶层对象,包含了所有其他BOM对象和全局JavaScript对象。

  2. location对象:表示当前文档的URL地址,可以用于获取和设置浏览器的URL地址。

  3. navigator对象:表示浏览器的信息,包括浏览器的名称、版本、操作系统等。

  4. screen对象:表示用户屏幕的信息,包括屏幕的宽度、高度、颜色深度等。

  5. history对象:表示浏览器的历史记录,可以用于访问和控制浏览器的后退和前进功能。

  6. document对象:虽然document对象属于DOM对象,但它也可以看作是BOM对象的一部分,因为它提供了访问和控制浏览器窗口中文档内容的方法和属性。

总之,BOM对象是JavaScript与浏览器交互的重要手段,通过BOM对象,我们可以访问和控制浏览器窗口的各个部分,实现更加丰富和灵活的网页交互效果。

23.说说浏览器的渐进增强和优雅降级的区别

浏览器的渐进增强和优雅降级都是为了解决不同浏览器或设备之间的兼容性问题,但它们的思路和实现方式不同。

渐进增强是指在设计和开发网站或应用程序时,首先考虑基本功能的实现,然后再逐步添加更高级的功能和效果,以适应不同的浏览器和设备。这种方法强调的是向前兼容,即在较老的浏览器中也能正常运行,但在新的浏览器中能够提供更好的用户体验。渐进增强的优点是能够提高网站或应用程序的可访问性和可用性,同时也能够减少代码的复杂性和维护成本。

优雅降级则是指在设计和开发网站或应用程序时,首先考虑高级功能的实现,然后再逐步降低功能和效果,以适应较老的浏览器和设备。这种方法强调的是向后兼容,即在新的浏览器中能够提供更好的用户体验,但在较老的浏览器中也能正常运行。优雅降级的优点是能够提供更好的用户体验,但缺点是代码的复杂性和维护成本可能会增加。

总的来说,渐进增强和优雅降级都是为了解决兼容性问题,但渐进增强更注重向前兼容,而优雅降级更注重向后兼容。在实际开发中,应该根据具体情况选择合适的方法。

24.浏览器的内核都有哪些,什么区别?

常见的浏览器内核有以下几种:

  1. Trident内核:是微软开发的浏览器内核,主要用于Internet Explorer浏览器。

  2. Gecko内核:是Mozilla Firefox浏览器的内核,也被其他浏览器所采用。

  3. Webkit内核:是苹果公司开发的浏览器内核,主要用于Safari浏览器。

  4. Blink内核:是Google公司基于Webkit内核开发的浏览器内核,主要用于Chrome浏览器。

这些内核的区别主要在于其开发者、性能、兼容性、安全性等方面。例如,Trident内核在兼容性方面表现较好,但在性能和安全性方面相对较弱;而Gecko内核则在性能和安全性方面表现较好,但在兼容性方面可能存在一些问题。Webkit内核则在移动设备上表现较好,而Blink内核则在速度和稳定性方面表现较好。不同的浏览器厂商会根据自己的需求选择不同的内核来开发自己的浏览器。

25.网站性能优化的方案都有哪些?

网站性能优化的方案有以下几种:

  1. 压缩文件:压缩CSS、JavaScript、HTML等文件可以减少文件大小,从而加快页面加载速度。

  2. 图片优化:使用适当的图片格式、压缩图片大小、使用懒加载等方法可以减少图片对页面加载速度的影响。

  3. CDN加速:使用CDN(内容分发网络)可以将网站的静态资源分发到全球各地的服务器上,从而加快访问速度。

  4. 缓存优化:使用浏览器缓存、服务器缓存等方式可以减少重复请求,从而提高页面加载速度。

  5. 减少HTTP请求:减少页面中的HTTP请求可以减少页面加载时间。

  6. 代码优化:优化代码可以减少页面加载时间,如减少重复代码、使用异步加载等方法。

  7. 前端框架优化:使用轻量级的前端框架可以减少页面加载时间。

  8. 服务器优化:使用高性能的服务器、优化服务器配置等方法可以提高网站的响应速度。

  9. 数据库优化:优化数据库查询语句、使用缓存等方法可以提高网站的响应速度。

  10. 前端性能监控:使用前端性能监控工具可以及时发现并解决网站性能问题。

26.Link和@import之间有什么区别?

Link和@import都可以用来引入外部样式表,但是它们之间有以下区别:

  1. 加载顺序:Link标签会在页面加载时同时加载,而@import会在页面加载完毕后再加载。

  2. 兼容性:Link标签在所有浏览器中都被支持,而@import在一些旧版本的浏览器中可能不被支持。

  3. 作用域:Link标签可以在HTML文档的head和body中使用,而@import只能在CSS文件中使用。

  4. 权重:Link标签引入的样式表的权重高于@import引入的样式表。

  5. DOM操作:Link标签可以通过JavaScript动态地插入和删除,而@import不能。

综上所述,Link标签更加灵活和可靠,而@import则更适合在CSS文件中使用。

27.说说你对BFC的理解,触发条件有哪些?

BFC(块级格式化上下文)是CSS中的一种布局模式,它是一个独立的渲染区域,内部的元素布局不会影响到外部元素。BFC具有以下特性:

  1. 内部的元素会在垂直方向上一个接一个地放置,形成一个垂直的流。

  2. 内部的元素在水平方向上默认是不会互相影响的,除非设置了浮动或者定位属性。

  3. BFC区域不会与浮动元素重叠,而是会自动调整位置。

  4. BFC区域可以包含浮动元素,从而避免浮动元素对其他元素的影响。

  5. BFC区域的高度会被内部元素的高度撑开,即使内部元素是浮动元素也不例外。

触发BFC的条件有以下几种:

  1. 根元素或包含根元素的元素。
  2. 浮动元素(float不为none)。
  3. 绝对定位元素(position为absolute或fixed)。
  4. display属性值为inline-block、table-cell、table-caption、flex、inline-flex。
  5. overflow属性值不为visible的块级元素。

28.null,undefined 的区别

null 和 undefined 都是 JavaScript 中的特殊值,但它们有一些区别:

  1. null 表示一个空对象指针,即该变量被赋值为一个空对象,而 undefined 表示一个未定义的值,即该变量没有被赋值。

  2. null 是一个关键字,可以被赋值给任何变量,而 undefined 不是关键字,可以被重新定义。

  3. null 是一个对象类型,而 undefined 是一个原始类型。

  4. 在条件语句中,null 被视为 false,而 undefined 被视为 false。

  5. 在使用 typeof 运算符时,null 返回 “object”,而 undefined 返回 “undefined”。

总之,null 表示一个空对象指针,而 undefined 表示一个未定义的值。在实际开发中,我们应该根据具体情况选择使用哪个值。

29.说说css中元素脱离文档流的方式有哪些?定位的方式有哪些以及区别?

CSS中元素脱离文档流的方式有以下几种:

  1. 浮动(float):将元素从文档流中移出来,使其向左或向右浮动,可以让其他元素环绕在其周围。

  2. 绝对定位(position:absolute):将元素从文档流中移出来,并相对于其最近的已定位祖先元素进行定位。

  3. 固定定位(position:fixed):将元素从文档流中移出来,并相对于浏览器窗口进行定位,不随页面滚动而移动。

  4. 弹性布局(display:flex):可以通过flex布局的方式,将元素从文档流中移出来,并按照一定的规则进行排列。

定位的方式有以下几种:

  1. 相对定位(position:relative):相对于元素本身在文档流中的位置进行定位,不会脱离文档流。

  2. 绝对定位(position:absolute):相对于其最近的已定位祖先元素进行定位,如果没有已定位的祖先元素,则相对于文档进行定位,会脱离文档流。

  3. 固定定位(position:fixed):相对于浏览器窗口进行定位,不随页面滚动而移动,会脱离文档流。

  4. 粘性定位(position:sticky):在元素在容器中可见时,相对于容器进行定位,否则相对于文档进行定位,不会脱离文档流。

相对定位和绝对定位的区别在于,相对定位不会脱离文档流,而绝对定位会脱离文档流;固定定位和绝对定位的区别在于,固定定位相对于浏览器窗口进行定位,不随页面滚动而移动,而绝对定位相对于其最近的已定位祖先元素进行定位。

30.同步和异步的区别

同步和异步是指在进行数据传输或处理时,两个或多个任务之间的协调方式。

同步是指任务之间必须按照一定的顺序依次执行,每个任务必须等待前一个任务完成后才能开始执行。在同步模式下,任务之间的执行是有序的,一旦某个任务出现问题,整个任务流程都会被阻塞。

异步是指任务之间不需要按照一定的顺序依次执行,每个任务可以独立执行,不需要等待前一个任务完成。在异步模式下,任务之间的执行是无序的,每个任务都可以独立运行,不会因为某个任务出现问题而影响整个任务流程。

总的来说,同步和异步的区别在于任务之间的协调方式,同步需要按照一定的顺序依次执行,而异步则可以独立执行。

31.伪类和伪元素的区别有哪些? Css3新增了哪些选择器

伪类和伪元素的区别:

  1. 伪类用于描述元素的某种状态,如:hover、:active等,而伪元素用于创建一些不在文档树中的元素,如::before、::after等。

  2. 伪类在选择器中以单冒号(:)表示,伪元素在选择器中以双冒号(::)表示。

  3. 伪类可以用于任何元素,而伪元素只能用于某些特定的元素。

Css3新增的选择器:

  1. :not(selector):选择器匹配除了指定选择器以外的所有元素。

  2. :nth-child(n):选择器匹配其父元素下的第n个子元素。

  3. :nth-last-child(n):选择器匹配其父元素下的倒数第n个子元素。

  4. :first-of-type:选择器匹配其父元素下的第一个指定类型的子元素。

  5. :last-of-type:选择器匹配其父元素下的最后一个指定类型的子元素。

  6. :only-of-type:选择器匹配其父元素下唯一的指定类型的子元素。

  7. :empty:选择器匹配没有子元素的元素。

  8. ::selection:选择器匹配被用户选中的文本。

32.说说箭头函数和普通函数的区别?

箭头函数和普通函数的区别主要有以下几点:

  1. 箭头函数没有自己的this,它的this继承自父级作用域的this,而普通函数的this指向调用它的对象。

  2. 箭头函数不能使用arguments对象,而普通函数可以。

  3. 箭头函数不能作为构造函数使用,而普通函数可以。

  4. 箭头函数没有prototype属性,因此不能使用new关键字实例化,而普通函数可以。

  5. 箭头函数的语法更加简洁,可以省略function关键字和return关键字。

  6. 箭头函数不能使用yield关键字,因此不能用于生成器函数,而普通函数可以。

总的来说,箭头函数适用于简单的函数,而普通函数适用于复杂的函数,需要使用this、arguments、prototype等特性的函数。

33.SPA首屏加载速度慢怎么解决

SPA(单页应用程序)的首屏加载速度慢可能是由以下原因导致的:

  1. 大量的JavaScript代码:SPA通常需要大量的JavaScript代码来实现页面的交互和动态效果,这些代码可能会导致页面加载速度变慢。

解决方法:可以通过代码压缩、代码分割和懒加载等技术来减少JavaScript代码的大小和加载时间。

  1. 大量的网络请求:SPA通常需要从服务器获取数据,如果页面需要大量的数据,那么就会导致页面加载速度变慢。

解决方法:可以通过使用缓存、减少请求次数、使用CDN等技术来优化网络请求。

  1. 渲染性能问题:SPA通常需要在客户端进行页面渲染,如果页面结构复杂或者数据量大,那么就会导致页面渲染速度变慢。

解决方法:可以通过使用虚拟DOM、减少DOM操作、使用CSS动画等技术来优化页面渲染性能。

  1. 服务器性能问题:如果服务器性能不足,那么就会导致页面加载速度变慢。

解决方法:可以通过使用负载均衡、优化数据库查询、使用缓存等技术来优化服务器性能。

综上所述,优化SPA首屏加载速度需要综合考虑多个方面,包括JavaScript代码、网络请求、渲染性能和服务器性能等。

34.说说重排和重绘的区别?触发条件有哪些?

重排和重绘是浏览器渲染页面时的两个重要概念。

重排(reflow)指的是当页面中的元素发生布局变化时,浏览器需要重新计算元素的位置和大小,并重新排列页面中的元素。重排会导致页面重新布局,因此会比较耗费性能。

重绘(repaint)指的是当页面中的元素的样式发生变化时,浏览器需要重新绘制元素的外观,但不需要重新计算元素的位置和大小。重绘会导致页面重新绘制,但不会重新布局,因此比重排的性能开销要小。

触发重排的条件包括:

  1. 页面首次渲染时;
  2. 浏览器窗口大小发生变化时;
  3. 元素的位置、大小、内容发生变化时;
  4. 元素的样式发生变化时;
  5. 页面滚动时;
  6. 获取某些属性值时,如offsetWidth、offsetHeight等。

触发重绘的条件包括:

  1. 元素的样式发生变化时;
  2. 元素的背景色、边框颜色、文本颜色等发生变化时;
  3. 元素的透明度发生变化时;
  4. 元素的文本内容发生变化时;
  5. 元素的伪类发生变化时,如:hover、:active等。

需要注意的是,虽然重绘的性能开销比重排小,但频繁的重绘也会影响页面的性能。因此,在编写页面时,应尽量减少重排和重绘的次数,以提高页面的性能。

35.Javascript如何实现继承?

Javascript实现继承的方式有以下几种:

  1. 原型链继承

原型链继承是通过将子类的原型指向父类的实例来实现继承的。这种方式的缺点是父类的引用类型属性会被所有子类实例共享,容易造成数据污染。

  1. 构造函数继承

构造函数继承是通过在子类构造函数中调用父类构造函数来实现继承的。这种方式的缺点是无法继承父类原型上的方法和属性。

  1. 组合继承

组合继承是将原型链继承和构造函数继承结合起来使用的一种方式,既可以继承父类原型上的方法和属性,又可以避免引用类型属性共享的问题。

  1. 原型式继承

原型式继承是通过创建一个空对象作为中介来实现继承的,这个空对象的原型指向父类的实例。这种方式的缺点是无法传递参数给父类构造函数。

  1. 寄生式继承

寄生式继承是在原型式继承的基础上,增加了一个包装函数,用于封装继承过程中的一些操作。这种方式的缺点是增加了代码的复杂度。

  1. 寄生组合式继承

寄生组合式继承是在组合继承的基础上,使用寄生式继承来优化继承过程,避免了重复调用父类构造函数的问题。这种方式是目前最常用的继承方式。

36.说说什么是严格模式,限制都有哪些?

严格模式是 ECMAScript 5 引入的一种 JavaScript 执行模式,它强制执行一些更严格的语法和行为规则,以提高代码的安全性和可靠性。

严格模式的限制包括:

  1. 变量必须先声明再使用,否则会抛出错误。
  2. 禁止使用 with 语句。
  3. 禁止删除不可删除的属性。
  4. 函数的参数名不能重复。
  5. 禁止使用八进制数字。
  6. 对象字面量中属性名不能重复。
  7. 函数不能有重名的参数。
  8. 禁止使用 eval 函数。
  9. 禁止 this 关键字指向全局对象。
  10. 严格模式下,所有的变量都必须先声明再使用。

通过使用严格模式,可以帮助开发者避免一些常见的 JavaScript 错误,并提高代码的可读性和可维护性。

37.如何快速的让一个打乱一个数组的顺序,比如 var arr = [1,2,3,4,5,6,7,8,9,10]

可以使用 Fisher-Yates 洗牌算法来打乱数组的顺序,具体步骤如下:

  1. 从数组的最后一项开始,向前遍历数组,对于每一项,随机生成一个小于等于当前项的索引值。
  2. 将当前项与随机生成的索引值所对应的项进行交换。
  3. 重复步骤 1 和步骤 2,直到遍历到数组的第一项为止。

以下是使用 JavaScript 实现 Fisher-Yates 洗牌算法的代码:

function shuffleArray(arr) {
  for (let i = arr.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
  return arr;
}

var arr = [1,2,3,4,5,6,7,8,9,10];
shuffleArray(arr);
console.log(arr); // 打印打乱顺序后的数组

38.Vue的自定义指令钩子函数有哪些?你用自定义指令做过什么?

Vue的自定义指令钩子函数有以下几个:

  • bind:指令第一次绑定到元素时调用。
  • inserted:元素插入到父节点时调用。
  • update:元素更新时调用,但可能在其子元素更新之前。
  • componentUpdated:元素及其子元素更新后调用。
  • unbind:指令与元素解绑时调用。

我曾经使用自定义指令实现过以下功能:

  • 点击外部关闭弹窗:在bind钩子函数中,通过document.addEventListener()方法监听点击事件,然后在update钩子函数中判断点击的元素是否在当前指令绑定的元素内,如果不在则关闭弹窗。

    Vue.directive('click-outside', {
      bind: function (el, binding, vnode) {
        el.clickOutsideEvent = function (event) {
          if (!(el == event.target || el.contains(event.target))) {
            vnode.context[binding.expression](event);
          }
        };
        document.body.addEventListener('click', el.clickOutsideEvent);
      },
      unbind: function (el) {
        document.body.removeEventListener('click', el.clickOutsideEvent);
      },
    });
    
  • 防抖指令:在bind钩子函数中,通过setTimeout()方法实现防抖功能,然后在update钩子函数中判断是否需要执行防抖函数。这个指令可以用于输入框等需要防止频繁请求的场景。

    // 防抖指令
    const debounceDirective = {
      mounted(el, binding) {
        let timer = null;
        const delay = binding.value || 300;
        el.addEventListener('input', () => {
          clearTimeout(timer);
          timer = setTimeout(() => {
            binding.instance[binding.expression]();
          }, delay);
        });
      }
    };
    
    // 注册指令
    const app = createApp({});
    app.directive('debounce', debounceDirective);
    
    // 在组件中使用
    <template>
      <input v-model="searchText" v-debounce="search" />
    </template>
    
    <script>
    export default {
      data() {
        return {
          searchText: ''
        };
      },
      methods: {
        search() {
          // 搜索逻辑
        }
      }
    };
    

39.从A页面跳转到B页面,缓存A组件,从A组件跳转到C组件,取消缓存,如何实现?

当从A页面跳转到B页面时,A组件会被缓存。

但是,当从A组件跳转到C组件时,我们需要取消A组件的缓存,以便在返回A页面时重新加载A组件

这可以通过在A组件的生命周期钩子函数中使用$route的beforeRouteLeave函数来实现。

在该函数中,我们可以使用**$keepAlive**的exclude选项来排除A组件的缓存。这样,当从A组件跳转到C组件时,A组件将被销毁并从缓存中移除,以便在返回A页面时重新加载。

40.Vue2和Vue3中响应式原理及区别?

Vue2和Vue3中的响应式原理都是基于Object.defineProperty实现的,但是Vue3中对响应式系统进行了重构,提供了更高效的响应式更新机制。

在Vue2中,每个组件实例都有一个Watcher实例,Watcher实例会监听组件实例中所有响应式数据的变化,当数据变化时,Watcher实例会通知组件实例进行重新渲染。

在Vue3中,响应式系统被拆分成了两个部分:Reactive和Reactivity。Reactive是一个函数,接收一个对象作为参数,返回一个响应式的Proxy对象。当访问Proxy对象的属性时,会自动收集依赖,并在属性变化时触发更新。Reactivity则是一个API集合,提供了一些常用的响应式操作,如ref、computed等。

Vue3的响应式系统相比Vue2有以下优势:

  1. 更高效:Vue3使用Proxy代替了Object.defineProperty,Proxy可以直接监听整个对象,而不是像Object.defineProperty一样需要遍历对象的每个属性进行监听,因此在性能上更加高效。

  2. 更灵活:Vue3的响应式系统可以监听数组的变化,而Vue2需要通过hack数组的原型方法来实现监听。

  3. 更好的TypeScript支持:Vue3的响应式系统对TypeScript的支持更加友好,可以更好地进行类型推断和类型检查。

41.Vue是如何实现实现权限管理的,按钮级别权限如何实现?

Vue 可以通过路由守卫和自定义指令来实现权限管理。

  1. 路由守卫

Vue 的路由守卫可以在路由跳转前进行拦截,判断用户是否有权限访问该路由。可以在路由配置中添加 meta 属性,用来存储该路由需要的权限信息。然后在路由守卫中获取该路由的 meta 信息,判断用户是否有权限访问该路由。

  1. 自定义指令

Vue 的自定义指令可以用来控制按钮级别的权限。可以在指令中获取用户的权限信息,判断用户是否有权限操作该按钮。如果没有权限,可以禁用按钮或者隐藏按钮。

例如:

Vue.directive('permission', {
  inserted: function (el, binding) {
    // 获取用户权限信息
    const permissions = store.getters.permissions
    // 获取指令绑定的权限
    const permission = binding.value
    // 判断用户是否有权限
    if (!permissions.includes(permission)) {
      // 没有权限,禁用按钮
      el.disabled = true
      // 隐藏按钮
      el.style.display = 'none'
    }
  }
})

然后在模板中使用指令:

<button v-permission="'edit'">编辑</button>

这样就可以根据用户的权限信息来控制按钮的可用性和可见性了。

42.说说webpack中常见的Loader?解决了什么问题?

Webpack中常见的Loader有:

  1. babel-loader:将ES6/ES7/JSX等语法转换为ES5语法,解决了浏览器兼容性问题。

  2. css-loader:解析CSS文件,处理CSS中的依赖关系,例如@import和url()等,使得Webpack能够正确地打包CSS文件。

  3. style-loader:将CSS代码注入到HTML文件中的

这些Loader解决了Webpack打包过程中的各种问题,例如处理不同类型的文件、处理不同的语法、处理依赖关系等。通过使用这些Loader,Webpack能够将各种资源打包成一个或多个bundle文件,使得前端开发更加高效、便捷。

43.你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢

SPA(Single Page Application)单页面应用是指整个应用只有一个页面,通过异步加载数据和局部刷新页面来实现页面的交互和更新。SPA应用通常使用前端框架(如Angular、React、Vue等)来实现。

优点:

  1. 用户体验好:SPA应用可以实现无刷新加载页面,用户体验更加流畅。
  2. 前后端分离:SPA应用可以将前端和后端分离,前端只需要负责展示数据和交互,后端只需要提供API接口。
  3. 更快的加载速度:SPA应用只需要加载一次页面,之后只需要加载数据,因此加载速度更快。
  4. 更好的可维护性:SPA应用可以将不同的功能模块拆分成组件,便于维护和升级。

缺点:

  1. SEO不友好:由于SPA应用只有一个页面,搜索引擎难以抓取到页面的内容,因此SEO效果不如传统的多页面应用。
  2. 首次加载时间长:由于SPA应用需要加载所有的资源,因此首次加载时间较长。
  3. 浏览器兼容性问题:由于SPA应用使用了较新的Web技术,因此在一些老旧的浏览器上可能存在兼容性问题。

实现SPA应用的步骤:

  1. 选择合适的前端框架,如Angular、React、Vue等。

  2. 设计应用的路由结构,确定页面之间的跳转关系。

  3. 开发组件,将不同的功能模块拆分成组件。

  4. 使用AJAX或WebSocket等技术获取数据,并将数据渲染到页面上。

  5. 部署应用,将应用打包成静态文件,并上传到服务器上。

44.Vue中组件和插件有什么区别?

Vue中组件和插件的区别如下:

  1. 组件是Vue中的基本概念,是一种可复用的代码块,用于构建用户界面。组件可以包含模板、样式和逻辑代码,可以被其他组件或实例引用。而插件是一种扩展Vue功能的方式,通常用于添加全局功能或工具函数。

  2. 组件是Vue中的一个实例,具有生命周期、数据响应等特性,可以通过props、emit等方式与父组件通信。而插件是一个对象或函数,可以通过Vue.use()方法安装到Vue实例中,提供全局方法、指令、过滤器等功能。

  3. 组件通常是局部注册的,只在需要使用的组件中注册,可以避免全局污染。而插件是全局注册的,一旦安装到Vue实例中,所有组件都可以使用插件提供的功能。

  4. 组件可以被其他组件或实例引用,可以在组件中嵌套其他组件,形成组件树。而插件通常是独立的功能模块,不会被其他组件或实例引用。

总之,组件和插件都是Vue中重要的概念,但它们的作用和使用方式有所不同。组件用于构建用户界面,提供可复用的代码块;插件用于扩展Vue功能,提供全局方法、指令、过滤器等功能。

45.你了解vue的diff算法吗?说说看?

Vue的diff算法是一种高效的虚拟DOM比较算法,用于比较新旧虚拟DOM树的差异,并最小化DOM操作的数量,从而提高性能。

Vue的diff算法主要分为两个阶段:

  1. 首先是同层级比较,即对新旧虚拟DOM树的同一层级节点进行比较。这个过程中,Vue会先对新旧节点的key进行比较,如果key相同,则认为是同一个节点,直接进行更新操作;如果key不同,则认为是不同节点,需要进行创建或删除操作。

  2. 如果同层级比较无法完成比较,则进入子树比较阶段,即对新旧虚拟DOM树的子节点进行递归比较。这个过程中,Vue会先对新旧节点的第一个子节点进行比较,如果相同,则继续比较下一个子节点;如果不同,则进行创建或删除操作。

在比较过程中,Vue还会根据节点类型的不同,采用不同的比较策略。例如,对于文本节点,Vue会直接替换文本内容;对于组件节点,Vue会比较组件的props和子节点。

总的来说,Vue的diff算法通过巧妙地比较新旧虚拟DOM树的差异,最小化DOM操作的数量,从而提高性能。

46.Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?

Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有以下不同:

  1. 组织代码的方式不同:Options Api 是通过一个对象来组织代码,而 Composition Api 是通过函数来组织代码。

  2. 代码复用性更高:Composition Api 可以更好地实现代码复用,因为它可以将逻辑代码抽象成可复用的函数。

  3. 更好的类型推断:Composition Api 可以更好地支持 TypeScript,因为它可以更准确地推断类型。

  4. 更好的逻辑组织:Composition Api 可以更好地组织逻辑代码,因为它可以将相关的代码放在同一个函数中,而不是分散在不同的生命周期函数中。

  5. 更好的测试性:Composition Api 可以更好地进行单元测试,因为它可以更容易地将逻辑代码抽象成可测试的函数。

总的来说,Composition Api 可以更好地组织代码,提高代码复用性和可维护性,同时也可以更好地支持 TypeScript 和单元测试。

47.说一下Vue2数据响应式的原理和Vue3数据响应式原理的不同?

Vue2数据响应式的原理是通过Object.defineProperty()方法来实现的。在Vue2中,当一个对象被传入Vue实例中时,Vue会遍历这个对象的所有属性,并使用Object.defineProperty()方法将这些属性转换为getter和setter,从而实现数据的响应式。当数据发生变化时,setter会通知依赖于这个数据的视图进行更新。

Vue3数据响应式的原理是通过ES6的Proxy对象来实现的。在Vue3中,当一个对象被传入Vue实例中时,Vue会使用Proxy对象来代理这个对象,从而实现数据的响应式。当数据发生变化时,Proxy会通知依赖于这个数据的视图进行更新。相比于Vue2的Object.defineProperty()方法,Vue3的Proxy对象更加灵活,可以监听数组的变化,并且可以监听对象的新增和删除操作。

因此,Vue3的数据响应式原理相比于Vue2更加高效和灵活。但是,由于Proxy是ES6的新特性,目前还不支持所有的浏览器,因此在一些老旧的浏览器中可能无法使用Vue3的数据响应式。

48.说说 React中的setState执行机制?

在React中,setState是用来更新组件状态的方法。当调用setState时,React会将新的状态合并到当前状态中,并触发组件的重新渲染。

具体的执行机制如下:

  1. setState是异步的:当调用setState时,React并不会立即更新组件状态,而是将更新放入一个队列中,等待下一次更新时批量执行。这样做可以提高性能,避免频繁的重复渲染。

  2. setState的更新可能被合并:如果在同一次事件循环中多次调用setState,React会将这些更新合并成一个更新,只执行一次更新操作。这样可以避免不必要的重复渲染。

  3. setState的更新可能是异步的:在某些情况下,React会将setState的更新变成异步的,例如在事件处理函数中调用setState时,React会将更新变成异步的,以避免在处理事件时频繁地重复渲染。

  4. setState的更新可能是同步的:在某些情况下,React会将setState的更新变成同步的,例如在生命周期函数中调用setState时,React会将更新变成同步的,以确保更新能够立即生效。

总之,setState的执行机制是比较复杂的,需要开发者了解其内部实现,才能更好地使用它来更新组件状态。

49.说说对React中类组件和函数组件的理解?有什么区别?

React中的类组件和函数组件都是用来定义组件的方式,但是它们有一些区别。

类组件是使用ES6的class语法来定义的,它们可以有自己的状态和生命周期方法。类组件使用this关键字来访问它们的状态和props,它们也可以定义自己的方法来处理事件和其他逻辑。类组件通常用于复杂的组件,因为它们可以更好地管理组件的状态和生命周期。

函数组件是使用函数来定义的,它们通常比类组件更简单,因为它们没有自己的状态或生命周期方法。函数组件接收props作为参数,并返回一个React元素。函数组件通常用于简单的组件,因为它们更容易编写和理解。

在React 16.8之后,函数组件也可以使用Hooks来管理状态和生命周期,这使得函数组件可以处理更复杂的逻辑,使得函数组件在某些情况下比类组件更有优势。

50.说说对React Hooks的理解?解决了什么问题?

React Hooks 是 React 16.8 引入的新特性,它提供了一种新的方式来使用 React 的功能,主要解决了以下几个问题:

  1. 状态逻辑复用问题:在 React 之前,我们通常使用高阶组件(HOC)或 render props 来实现组件之间的状态逻辑复用。但是这些方式会导致组件层级嵌套过深,代码复杂度增加。React Hooks 提供了 useXXX 系列的 Hook,可以让我们在不增加组件层级的情况下,复用组件状态逻辑。

  2. 复杂组件变得难以维护:在 React 中,组件的状态和生命周期方法通常是在组件内部定义的,当组件变得复杂时,这些状态和生命周期方法会变得难以维护。React Hooks 提供了 useEffect 和 useLayoutEffect 等 Hook,可以让我们在组件外部定义副作用逻辑,使组件内部更加简洁。

  3. Class 组件的限制:在 React 中,Class 组件有一些限制,比如无法使用函数式组件的一些优化,比如无法使用 React DevTools 的一些功能。React Hooks 可以让我们使用函数式组件来实现 Class 组件的功能,从而避免这些限制。

总之,React Hooks 提供了一种更加简洁、灵活的方式来使用 React 的功能,可以让我们更好地组织代码,提高代码复用性和可维护性。

51.UseMemo和useCallback如何提升了性能,应用场景有哪些?

useMemo和useCallback都是React Hooks中用于优化性能的工具。

useMemo可以缓存计算结果,避免重复计算,从而提高组件的渲染性能。它的应用场景包括:

  1. 计算量较大的函数,如复杂的数学计算、字符串处理等。

  2. 需要进行大量数据处理的组件,如表格、图表等。

  3. 需要进行大量数据筛选、排序等操作的组件。

useCallback则是用于缓存函数,避免重复创建函数实例,从而提高组件的渲染性能。它的应用场景包括:

  1. 传递给子组件的回调函数,如onClick、onChange等。

  2. 在useEffect中使用的回调函数。

  3. 在useMemo中使用的回调函数。

总之,当组件需要进行大量计算或者频繁创建函数实例时,可以考虑使用useMemo和useCallback来提高性能。

52.Vue-router的实现原理是什么?

vueRouter作为一个插件 ,需要实现install方法,install方法中接收两个参数,一个是vue构造函数,另一个是使用vue.use() 的时候传递的参数。
然后我们去监听onHashchange事件的变化,当hash值发生变化时,匹配对应的组件。这个时候我们需要封装一个match方法,循环routes(routes当中存放组件和路径的对应关系),拿到所有组件和路径。
定义一个响应式对象,代表当前路径,判断当前路径和循环的路径是否相等,并且是否包含,如果满足条件就push到数组当中
所有的组件都存到数组中去,onHashchange发生变化时,把组件都清空,在继续调用match方法
这个时候进行router-view,对被routerView渲染的组件进行标记为true
在app组件中被routerView渲染机组件层级初始值标记为0
找到当前组件的父组件
对父组件进行循环,知道找到app组件,再循环中判断当前的子组件是否为routerView组件,如果是,层级**+1**
定义存储组件的变量,找到对应组件赋值

53.如何封装组件在不同的项目之间使用如何实现?

要封装组件以便在不同的项目中使用,可以按照以下步骤进行:

  1. 创建组件库:创建一个独立的项目,用于存放所有的组件代码和相关资源。可以使用现有的工具,如 Lerna 或 Yarn Workspaces,来管理多个包。

  2. 编写组件代码:编写组件的代码和相关资源,如样式、图片等。组件应该是可重用的,通用的,易于维护和扩展的。

  3. 打包组件:使用工具如 webpack 或 Rollup 将组件打包成一个或多个 JavaScript 文件。可以使用不同的打包方式,如 CommonJS、ES6 模块或 UMD。

  4. 发布组件:将打包后的组件发布到 npm 或其他包管理器中。确保组件的版本控制和文档都是正确的。

  5. 在项目中使用组件:在需要使用组件的项目中安装组件库,并按照文档说明使用组件。可以使用不同的方式引入组件,如 import、require 或 script 标签。

  6. 维护组件:定期更新组件库中的组件,修复 bug,添加新功能,确保组件的兼容性和稳定性。

总之,封装组件需要考虑到组件的可重用性、通用性、易用性和可维护性,以便在不同的项目中使用和维护。

54.vue、react、angular 区别?

Vue、React、Angular 都是现代化的前端框架,它们的主要区别如下:

  1. Vue 是一款轻量级的框架,易于学习和使用,适合小型项目和快速原型开发。React 是一个更加灵活的框架,适合大型项目和复杂应用。

  2. Vue 的模板语法更加简洁易懂,而 React 使用 JSX 语法,需要更多的学习成本。

  3. Angular 是一个完整的框架,包含了很多功能,如路由、表单验证等,但是学习成本较高。Vue 和 React 则更加灵活,可以根据需要选择使用相应的插件和库。

  4. Vue 和 React 都采用了虚拟 DOM 技术,可以提高性能和渲染速度。Angular 则采用了双向数据绑定,可以实现数据的自动更新。

  5. Vue 和 React 都有大量的社区支持和插件库,可以方便地扩展功能。Angular 的社区相对较小,但是官方文档和支持较为完善。

总的来说,Vue 更加简单易用,适合小型项目和快速原型开发;React 更加灵活,适合大型项目和复杂应用;Angular 则是一个完整的框架,适合需要大量功能和支持的项目。

55.说说你对Redux的理解?其工作原理?

Redux是一个JavaScript状态管理库,它可以帮助我们更好地管理应用程序的状态。Redux的核心概念是store、action和reducer。

  • Store:Redux中的store是一个JavaScript对象,它包含了整个应用程序的状态。我们可以通过store.getState()方法获取当前的状态,通过store.dispatch(action)方法来触发状态的变化,通过store.subscribe(listener)方法来监听状态的变化。

  • Action:Redux中的action是一个普通的JavaScript对象,它描述了发生了什么事情。每个action都必须包含一个type属性,它指定了这个action的类型。除了type属性,action还可以包含其他任意的属性。

  • Reducer:Redux中的reducer是一个纯函数,它接收当前的状态和一个action作为参数,然后返回一个新的状态。reducer必须是一个纯函数,它不能修改原来的状态,而是要返回一个新的状态。

Redux的工作原理可以简单地概括为:当我们触发一个action时,Redux会调用对应的reducer来更新状态。reducer会根据当前的状态和action来计算出一个新的状态,并返回给Redux。Redux会将新的状态存储在store中,并通知所有的监听器,告诉它们状态已经发生了变化。监听器可以根据新的状态来更新UI。

56.说说你对递归的理解?封装一个方法用递归实现树形结构封装

递归是一种算法或函数的实现方式,它通过在函数内部调用自身来解决问题。在递归过程中,每次函数调用都会将问题分解成更小的子问题,直到问题变得足够简单,可以直接解决。递归通常用于处理具有递归结构的数据,例如树形结构、链表等。

下面是一个用递归实现树形结构封装的方法:

public class TreeNode {
    private String name;
    private List<TreeNode> children;

    public TreeNode(String name) {
        this.name = name;
        this.children = new ArrayList<>();
    }

    public void addChild(TreeNode child) {
        this.children.add(child);
    }

    public void printTree() {
        printTree(this, 0);
    }

    private void printTree(TreeNode node, int level) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < level; i++) {
            sb.append("  ");
        }
        sb.append(node.name);
        System.out.println(sb.toString());
        for (TreeNode child : node.children) {
            printTree(child, level + 1);
        }
    }
}

这个方法封装了一个树形结构的节点,每个节点包含一个名称和一个子节点列表。printTree() 方法用递归实现了树形结构的遍历和打印,它接受一个节点和一个层级参数,递归地遍历节点的子节点,并在每个节点前面添加一定数量的空格,以表示节点的层级关系。最终输出整个树形结构。

56.什么是FOUC? 如何避免?

FOUC(Flash of Unstyled Content)是指在网页加载过程中,由于CSS文件加载较慢或未加载完成,导致页面出现短暂的未样式化内容的现象。

为了避免FOUC,可以采取以下措施:

  1. 将CSS文件放在HTML文件的头部,这样可以确保CSS文件在页面渲染之前加载完成。

  2. 使用内联CSS样式,将CSS代码直接写在HTML文件中,这样可以避免CSS文件加载的延迟。

  3. 使用CSS预处理器,如Sass、Less等,可以将CSS文件编译成一个文件,减少HTTP请求的次数,提高加载速度。

  4. 使用浏览器缓存,将CSS文件缓存到本地,下次访问时可以直接从缓存中读取,减少加载时间。

  5. 使用JavaScript控制CSS的加载,可以在CSS文件加载完成之前,先隐藏页面内容,等CSS文件加载完成后再显示页面内容。

57.说说你对预编译器的理解?

预编译器是一种程序,它在编译源代码之前对源代码进行处理。预编译器通常用于C、C++等编程语言中,它可以执行一些预处理操作,例如宏定义、条件编译、头文件包含等。

预编译器的主要作用是将源代码中的预处理指令(以#开头的指令)进行处理,生成一份新的源代码文件,然后再将这个新的源代码文件交给编译器进行编译。预编译器可以帮助程序员简化代码,提高代码的可读性和可维护性。

预编译器的工作流程通常包括以下几个步骤:

  1. 扫描源代码文件,找出所有的预处理指令。

  2. 根据预处理指令进行处理,例如宏定义、条件编译、头文件包含等。

  3. 生成一份新的源代码文件,将处理后的代码写入其中。

  4. 将新的源代码文件交给编译器进行编译。

总之,预编译器是编译器的一个重要组成部分,它可以帮助程序员简化代码,提高代码的可读性和可维护性。

58.shouldComponentUpdate 的作用

shouldComponentUpdate 是 React 生命周期中的一个方法,它的作用是在组件更新之前被调用,用于判断是否需要更新组件。如果 shouldComponentUpdate 返回 false,则组件不会被更新,如果返回 true,则组件会被更新。

shouldComponentUpdate 可以用来优化组件的性能,因为它可以避免不必要的组件更新。当组件的 props 或 state 发生变化时,React 会默认重新渲染组件,但是有些情况下,组件的 props 或 state 变化并不会影响组件的显示,此时就可以使用 shouldComponentUpdate 来判断是否需要更新组件。

在 shouldComponentUpdate 中,可以根据组件的 props 和 state 来判断是否需要更新组件。如果组件的 props 和 state 没有变化,则可以返回 false,避免不必要的组件更新。如果组件的 props 或 state 发生了变化,则可以返回 true,让组件更新。

需要注意的是,shouldComponentUpdate 只能用于 class 组件,函数组件可以使用 React.memo 来达到类似的效果。

59.概述下 React 中的事务处理逻辑

React 中的事务处理逻辑主要涉及到组件的更新和渲染过程。React 通过事务来管理组件的更新和渲染,保证了组件的一致性和可靠性。

在 React 中,每个组件都有一个事务队列,当组件状态发生变化时,React 会将更新操作添加到该组件的事务队列中。当事务队列中的所有更新操作都完成后,React 会触发一次批量更新,将所有更新操作一次性执行,最终更新组件的状态和视图。

React 中的事务处理逻辑还涉及到事务的嵌套和异常处理。当一个事务嵌套在另一个事务中时,React 会将其合并成一个事务,保证了事务的一致性。同时,React 还提供了异常处理机制,当事务执行过程中发生异常时,React 会回滚事务并抛出异常,保证了组件的可靠性。

总之,React 中的事务处理逻辑是保证组件更新和渲染的一致性和可靠性的重要机制。

60.react组件的划分业务组件技术组件?

React组件的划分可以分为业务组件和技术组件。

业务组件是指与业务逻辑相关的组件,例如登录组件、购物车组件、商品列表组件等。这些组件通常是由业务需求驱动的,具有明确的功能和目的,是应用程序的核心组成部分。

技术组件是指与技术实现相关的组件,例如表单组件、路由组件、状态管理组件等。这些组件通常是为了实现某些功能而存在的,例如表单组件用于收集用户输入数据,路由组件用于管理页面跳转,状态管理组件用于管理应用程序的状态。

在实际开发中,业务组件和技术组件通常是相互依赖的,业务组件需要技术组件的支持来实现其功能,而技术组件则需要业务组件来使用它们。因此,合理划分业务组件和技术组件,可以提高组件的复用性和可维护性,同时也可以提高开发效率。

61.react性能优化是哪个周期函数

React性能优化可以在多个生命周期函数中进行,但其中一个重要的周期函数是shouldComponentUpdate。这个函数在组件更新前被调用,可以让开发者手动控制组件是否需要更新,从而避免不必要的渲染。在这个函数中,可以比较新旧props和state的值,以及其他相关的数据,来判断组件是否需要更新。如果返回false,组件将不会进行更新,从而提高性能。

62.说说你对Fiber的理解和应用场景

Fiber是React 16中引入的一种新的协程调度器,它可以在React组件之间进行快速的切换,从而提高了React应用的性能和响应速度。

Fiber的主要作用是将React的渲染过程分解成多个小任务,然后通过调度器来决定哪些任务需要优先执行,哪些任务可以暂停或者取消。这样可以避免长时间的阻塞,提高了React应用的响应速度和用户体验。

Fiber的应用场景主要包括以下几个方面:

  1. 大型应用:Fiber可以帮助React应用更好地处理大量的组件和数据,提高应用的性能和稳定性。

  2. 动画效果:Fiber可以帮助React应用更好地处理动画效果,提高动画的流畅度和响应速度。

  3. 实时应用:Fiber可以帮助React应用更好地处理实时数据,提高应用的响应速度和实时性。

  4. 移动端应用:Fiber可以帮助React应用更好地适应移动端设备,提高应用的性能和用户体验。

总之,Fiber是React应用优化的重要手段之一,可以帮助开发者更好地处理复杂的应用场景,提高应用的性能和用户体验。

63.react性能优化方案

React性能优化方案包括以下几个方面:

  1. 使用React.memo()或PureComponent来避免不必要的渲染。React.memo()是一个高阶组件,可以将函数组件转换为具有记忆功能的组件,只有当组件的props发生变化时才会重新渲染。PureComponent是一个类组件,它会在shouldComponentUpdate()生命周期方法中自动比较props和state的变化,如果没有变化则不会重新渲染。

  2. 使用shouldComponentUpdate()生命周期方法来手动控制组件的渲染。在shouldComponentUpdate()方法中,可以比较新旧props和state的变化,如果没有变化则返回false,避免不必要的渲染。

  3. 使用React.lazy()和Suspense来实现按需加载。React.lazy()是一个高阶组件,可以将组件的加载延迟到需要时再进行,可以提高页面的加载速度。Suspense是一个组件,可以在组件加载时显示一个loading状态,等待组件加载完成后再显示组件内容。

  4. 使用React.useCallback()和React.useMemo()来避免不必要的函数调用和计算。React.useCallback()可以缓存函数,避免在每次渲染时都重新创建函数,React.useMemo()可以缓存计算结果,避免在每次渲染时都重新计算。

  5. 使用React.Fragment来减少不必要的DOM节点。React.Fragment是一个组件,可以将多个子节点包裹在一个父节点中,避免在DOM中创建多余的节点。

  6. 使用React.memo()和React.useCallback()来避免不必要的props传递。如果一个组件的props中包含了一个函数,而这个函数并不会在组件内部被修改,那么可以使用React.memo()和React.useCallback()来避免不必要的props传递。

64.简述flux 思想及Redux的应用

Flux 是一种前端架构思想,旨在解决传统 MVC 架构中数据流混乱、难以维护的问题。Flux 将应用程序分为四个部分:View、Action、Dispatcher 和 Store。View 层负责展示数据,Action 层负责用户操作,Dispatcher 层负责分发 Action,Store 层负责存储数据。这种单向数据流的架构使得数据流清晰可控,易于维护和扩展。

Redux 是一种基于 Flux 架构思想的状态管理库。Redux 将应用程序的状态存储在一个单一的 Store 中,通过 Action 和 Reducer 来修改和更新状态。Action 是一个普通的 JavaScript 对象,描述了发生了什么事件,Reducer 是一个纯函数,接收当前状态和 Action,返回新的状态。Redux 的状态管理使得应用程序的状态可预测、可控,易于调试和测试。

Redux 的应用场景包括但不限于:

  1. 大型应用程序,需要管理复杂的状态和数据流。

  2. 需要多个组件共享状态的应用程序。

  3. 需要记录和回放用户操作的应用程序。

  4. 需要实现时间旅行功能的应用程序。

65.说说html和css渲染的过程是什么

HTML和CSS渲染的过程如下:

  1. 解析HTML文档:浏览器会先解析HTML文档,构建DOM树,确定文档的结构和内容。

  2. 解析CSS样式:浏览器会解析CSS样式,构建CSSOM树,确定文档的样式和布局。

  3. 合并DOM树和CSSOM树:浏览器会将DOM树和CSSOM树合并,生成渲染树(Render Tree)。

  4. 布局:浏览器会根据渲染树的信息,计算出每个元素在屏幕上的位置和大小。

  5. 绘制:浏览器会将布局后的元素绘制到屏幕上。

  6. 重绘和回流:当元素的样式或内容发生变化时,浏览器会进行重绘和回流操作,重新计算布局和绘制。

总的来说,HTML和CSS渲染的过程就是将文档的结构、样式和布局信息转换成屏幕上的像素点,让用户能够看到网页的内容和样式。

66.说一下DOM0、DOM2、DOM3事件处理的区别是什么?

DOM0、DOM2、DOM3是JavaScript中事件处理的三个不同版本。

DOM0事件处理:通过给元素添加事件处理函数来处理事件。例如,使用element.onclick = function() {}来添加点击事件处理函数。DOM0事件处理只支持基本的事件类型,如click、mouseover等。

DOM2事件处理:通过使用addEventListener()removeEventListener()方法来添加和删除事件处理函数。DOM2事件处理支持更多的事件类型,如load、unload、scroll等,并且可以添加多个事件处理函数。

DOM3事件处理:在DOM2的基础上增加了更多的事件类型,如UI事件、键盘事件、鼠标滚轮事件等,并且支持更多的事件处理选项,如事件捕获和事件冒泡等。

总的来说,DOM0、DOM2、DOM3事件处理的主要区别在于支持的事件类型和添加事件处理函数的方式。

67.如何判断页面滚动到底部,如何判断页面中元素是否进入可视化区域?

判断页面滚动到底部可以通过以下代码实现:

if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
  // 到达底部
}

其中,window.innerHeight表示浏览器窗口的高度,window.scrollY表示当前滚动条的位置,document.body.offsetHeight表示整个页面的高度。

判断页面中元素是否进入可视化区域可以通过以下代码实现:

function isElementInViewport(el) {
  var rect = el.getBoundingClientRect();
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
    rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  );
}

其中,el.getBoundingClientRect()可以获取元素相对于视口的位置和大小。如果元素的上下左右都在视口内,则认为元素进入了可视化区域。

68.说一下Vuex的实现原理,commit和dispatch方法如何实现的

Vuex是一个基于Vue.js的状态管理库,它的实现原理主要是通过一个全局的store对象来管理应用的状态。store对象包含了state、mutations、actions、getters等属性,分别用来存储应用的状态、修改状态的方法、异步操作的方法和计算状态的方法。

实现原理:

  • Store类表示一个Vuex的实例,它包含了状态、mutation和订阅者。
  • commit方法用于提交mutation,它会将mutation放入队列中,并遍历所有的订阅者,将mutation传递给订阅者。
  • subscribe方法用于添加订阅者。
  • install方法用于将Vuex安装到Vue中。
  • 在Vue的beforeCreate钩子中,会将$store属性添加到Vue实例中,这样就可以在组件中访问到Vuex的状态和方法了。

在Vuex中,commit和dispatch方法是用来触发mutations和actions的方法。commit方法用来同步地触发mutations中的方法,而dispatch方法用来异步地触发actions中的方法。

commit方法的实现原理比较简单,它只需要找到对应的mutation方法并调用即可。而dispatch方法的实现则需要先找到对应的action方法,然后再通过Promise来实现异步操作的效果。

具体实现过程如下:

  1. 在store对象中定义mutations和actions属性,分别存储修改状态的方法和异步操作的方法。

  2. 在commit方法中,通过传入的mutation名称和参数,找到对应的mutation方法并调用。

  3. 在dispatch方法中,通过传入的action名称和参数,找到对应的action方法并调用。同时,使用Promise来实现异步操作的效果。

  4. 在action方法中,可以进行异步操作,然后再通过commit方法来触发mutations中的方法,修改应用的状态。

总之,Vuex的实现原理主要是通过store对象来管理应用的状态,而commit和dispatch方法则是用来触发mutations和actions的方法,从而实现状态的修改和异步操作。

69.说说对盒子模型的理解?

盒子模型是指在网页中,每个元素都被看作是一个矩形的盒子,这个盒子由四个边框、内边距、外边距和内容区域组成。盒子模型是CSS布局的基础,通过盒子模型可以控制元素的大小、位置、边框、内边距和外边距等属性。

盒子模型分为两种:标准盒子模型和IE盒子模型。标准盒子模型的宽度和高度只包括内容区域,而IE盒子模型的宽度和高度包括了内容区域、内边距和边框。

在CSS中,可以通过box-sizing属性来指定盒子模型的类型。默认值为content-box,表示标准盒子模型;如果将box-sizing设置为border-box,则表示IE盒子模型。

盒子模型的大小可以通过width和height属性来设置,内边距可以通过padding属性来设置,外边距可以通过margin属性来设置,边框可以通过border属性来设置。在布局时,需要考虑盒子模型的大小、位置和边距等属性,以达到预期的效果。

70.Css的选择器有哪些?优先级?哪些可以继承

CSS选择器包括以下几种:

  1. 元素选择器:通过元素名称来选择元素,如p、div、a等。

  2. 类选择器:通过类名来选择元素,以点号(.)开头,如.class。

  3. ID选择器:通过ID来选择元素,以井号(#)开头,如#id。

  4. 属性选择器:通过元素的属性来选择元素,如[type=“text”]。

  5. 伪类选择器:通过元素的状态来选择元素,如:hover、:active、:visited等。

  6. 后代选择器:通过元素的后代元素来选择元素,如div p。

  7. 子元素选择器:通过元素的子元素来选择元素,如div > p。

  8. 相邻兄弟选择器:通过元素的相邻兄弟元素来选择元素,如h1 + p。

  9. 通用选择器:选择所有元素,用*表示。

CSS选择器的优先级从高到低依次为:!important > 行内样式 > ID选择器 > 类选择器、属性选择器、伪类选择器 > 元素选择器、伪元素选择器 > 通配符选择器 > 继承。

可以继承的CSS属性包括:font、color、line-height、text-align、visibility、cursor等。

71.元素水平垂直居中的方法有哪些?如果元素不定宽高呢?

元素水平垂直居中的方法有以下几种:

  1. 使用 flexbox 布局,将父元素设置为 display: flex;,并设置 justify-content: center; 和 align-items: center;。

  2. 使用绝对定位,将元素的 left 和 top 属性设置为 50%,并将 transform 属性设置为 translate(-50%, -50%)。

  3. 使用 table 布局,将父元素设置为 display: table;,并将子元素设置为 display: table-cell; 和 vertical-align: middle;。

如果元素不定宽高,可以使用以下方法:

  1. 使用绝对定位,将元素的 left 和 top 属性设置为 50%,并将 transform 属性设置为 translate(-50%, -50%)。这种方法可以使元素在水平和垂直方向上都居中,无论元素的宽高是多少。

  2. 使用 flexbox 布局,将父元素设置为 display: flex;,并设置 justify-content: center; 和 align-items: center;。这种方法可以使元素在水平和垂直方向上都居中,但是需要设置元素的宽高为 100%。

  3. 使用 JavaScript 计算元素的宽高,然后使用绝对定位或 flexbox 布局进行居中。这种方法可以使元素在水平和垂直方向上都居中,但是需要使用 JavaScript 计算元素的宽高,可能会影响性能。

72.怎么理解回流跟重绘?什么场景下会触发

回流(reflow)和重绘(repaint)是浏览器渲染页面时的两个重要概念。

回流指的是当页面中的元素发生位置、大小等属性的变化时,浏览器需要重新计算元素的位置和大小,并重新布局整个页面,这个过程称为回流。回流是一种比较耗费性能的操作,因为它会涉及到整个页面的重新布局。

重绘指的是当页面中的元素的样式发生变化时,浏览器只需要重新绘制这些元素的样式,而不需要重新计算元素的位置和大小,这个过程称为重绘。相比于回流,重绘的性能开销较小。

在实际开发中,以下场景可能会触发回流和重绘:

  1. 改变窗口大小
  2. 改变字体大小
  3. 添加或删除样式表
  4. 改变元素的位置、大小、内容等属性
  5. 改变元素的样式,如颜色、背景等
  6. 操作 DOM,如添加、删除、移动元素等

为了减少回流和重绘的次数,可以采取以下措施:

  1. 尽量避免频繁操作 DOM,可以先将需要操作的元素保存在变量中,最后一次性操作。
  2. 使用 CSS 动画代替 JavaScript 动画,因为 CSS 动画可以利用硬件加速,性能更好。
  3. 使用 transform 和 opacity 属性代替 top、left 等属性,因为前者不会触发回流。
  4. 使用事件委托,减少事件绑定的次数。
  5. 将需要频繁操作的元素设置为绝对定位或固定定位,避免影响其他元素的布局。

73.什么是响应式设计?响应式设计的基本原理是什么?如何做?

响应式设计是一种网页设计方法,可以使网站在不同设备上(如桌面电脑、平板电脑、手机等)呈现出最佳的用户体验。响应式设计的基本原理是根据不同设备的屏幕大小和分辨率,自动调整网页的布局、字体大小、图片大小等元素,以适应不同设备的显示效果。

响应式设计的实现需要遵循以下原则:

  1. 弹性网格布局:使用弹性网格布局可以使网页在不同设备上自适应调整布局。

  2. 自适应图片和媒体:使用自适应图片和媒体可以使网页在不同设备上自适应调整图片和媒体的大小。

  3. 媒体查询:使用媒体查询可以根据不同设备的屏幕大小和分辨率,自动调整网页的样式。

  4. 流式布局:使用流式布局可以使网页在不同设备上自适应调整宽度。

要实现响应式设计,可以采取以下步骤:

  1. 设计弹性网格布局,使网页在不同设备上自适应调整布局。

  2. 使用自适应图片和媒体,使网页在不同设备上自适应调整图片和媒体的大小。

  3. 使用媒体查询,根据不同设备的屏幕大小和分辨率,自动调整网页的样式。

  4. 使用流式布局,使网页在不同设备上自适应调整宽度。

  5. 进行测试和优化,确保网页在不同设备上呈现出最佳的用户体验。

74.如果要做优化,CSS提高性能的方法有哪些

以下是CSS提高性能的方法:

  1. 减少CSS文件的大小:通过压缩CSS文件、删除不必要的空格、注释和代码,可以减少CSS文件的大小,从而提高页面加载速度。

  2. 避免使用@import:@import会导致浏览器多次请求CSS文件,从而降低页面加载速度。可以使用link标签将CSS文件直接引入页面中。

  3. 避免使用过多的选择器:选择器的数量越多,浏览器需要处理的工作量就越大,从而降低页面加载速度。可以通过简化选择器、避免使用通配符等方式来减少选择器的数量。

  4. 避免使用过多的CSS样式:CSS样式的数量越多,浏览器需要处理的工作量就越大,从而降低页面加载速度。可以通过合并相同的样式、删除不必要的样式等方式来减少CSS样式的数量。

  5. 使用CSS Sprites:CSS Sprites可以将多个小图片合并成一张大图片,从而减少HTTP请求的数量,提高页面加载速度。

  6. 使用缩写属性:缩写属性可以减少CSS代码的长度,从而提高页面加载速度。

  7. 避免使用!important:使用!important会覆盖其他样式,从而增加浏览器的工作量,降低页面加载速度。可以通过优先级、选择器等方式来避免使用!important。

  8. 避免使用CSS表达式:CSS表达式会在每次页面重绘时重新计算,从而降低页面加载速度。可以使用JavaScript等方式来代替CSS表达式。

75.对前端工程师这个职位是怎么样理解的?它的前景会怎么样

前端工程师是负责开发网站和应用程序前端界面的专业人员。他们使用HTML、CSS和JavaScript等技术来创建用户界面,使用户能够与网站或应用程序进行交互。前端工程师需要具备良好的设计和编程技能,能够将设计师提供的设计转化为可交互的用户界面。

随着互联网的不断发展,前端工程师的前景非常广阔。越来越多的公司和组织需要拥有一个现代化、易于使用的网站或应用程序,这就需要有专业的前端工程师来开发和维护。同时,随着移动设备的普及,对于响应式设计和移动优化的需求也越来越高,这也为前端工程师提供了更多的机会。

总的来说,前端工程师是一个非常有前途的职业,随着技术的不断发展和应用范围的扩大,前端工程师的需求将会越来越大。

76.说说JavaScript中的数据类型?存储上的差别?

JavaScript中的数据类型可以分为两类:基本数据类型和引用数据类型。

基本数据类型包括:数字(Number)、字符串(String)、布尔值(Boolean)、空值(Null)、未定义(Undefined)。

引用数据类型包括:对象(Object)、数组(Array)、函数(Function)。

基本数据类型的值存储在栈内存中,而引用数据类型的值存储在堆内存中,栈内存中存储的是引用数据类型的地址。因为栈内存的访问速度比堆内存快,所以基本数据类型的操作速度比引用数据类型快。

另外,基本数据类型是按值访问的,即每个变量都有自己的值,而引用数据类型是按引用访问的,即多个变量可以引用同一个对象,修改其中一个变量的值会影响到其他变量的值。

77.请简单叙述js数据类型判断的方法有哪些?

JavaScript数据类型判断的方法有以下几种:

  1. typeof运算符:可以判断数据类型为undefined、boolean、number、string、function、object和symbol。

  2. instanceof运算符:可以判断对象是否为某个构造函数的实例。

  3. Object.prototype.toString方法:可以判断数据类型为Object、Array、Date、RegExp、Function等。

  4. isNaN函数:可以判断一个值是否为NaN。

  5. isFinite函数:可以判断一个值是否为有限数。

  6. Array.isArray方法:可以判断一个值是否为数组。

  7. typeof和instanceof结合使用:可以判断一个值是否为对象、数组、函数等。

需要注意的是,以上方法都有其局限性,需要根据具体情况选择合适的方法进行判断。

78.说说你对闭包的理解?闭包使用场景

闭包是指在一个函数内部定义另一个函数,并且内部函数可以访问外部函数的变量和参数,即使外部函数已经执行完毕,内部函数仍然可以访问这些变量和参数。闭包可以用来创建私有变量和方法,以及实现函数式编程中的柯里化和高阶函数等。

闭包的使用场景包括:

  1. 创建私有变量和方法:通过在函数内部定义变量和方法,并返回一个函数,可以实现对这些变量和方法的封装,从而创建私有变量和方法。

  2. 实现柯里化:柯里化是指将一个多参数函数转换为一系列单参数函数的过程。通过使用闭包,可以将一个多参数函数转换为一个接受第一个参数后返回一个接受第二个参数的函数,以此类推,从而实现柯里化。

  3. 实现高阶函数:高阶函数是指接受一个或多个函数作为参数,并返回一个新函数的函数。通过使用闭包,可以实现高阶函数,从而实现函数的组合和复用。

  4. 缓存函数:通过使用闭包,可以将函数的计算结果缓存起来,以便下次调用时直接返回缓存的结果,从而提高函数的性能。

  5. 实现回调函数:回调函数是指将一个函数作为参数传递给另一个函数,并在另一个函数执行完毕后调用该函数。通过使用闭包,可以实现回调函数,从而实现异步编程。

79.bind、call、apply 区别?如何实现一个bind

bind、call、apply 都是 JavaScript 中用于改变函数执行上下文的方法。

  • call 和 apply 都是立即执行函数,而 bind 返回一个新函数,需要手动调用。
  • call 和 apply 的第一个参数都是要绑定的 this 值,而 bind 的第一个参数是要绑定的 this 值,后面的参数是要传入函数的参数。
  • call 和 apply 的区别在于传入参数的方式不同,call 是一个一个传入,apply 是以数组的形式传入。

实现一个 bind 函数:

Function.prototype.bind = function (context) {
  var self = this;
  var args = Array.prototype.slice.call(arguments, 1);
  return function () {
    var bindArgs = Array.prototype.slice.call(arguments);
    return self.apply(context, args.concat(bindArgs));
  };
};

该实现中,我们将要绑定的函数保存在 self 中,将要绑定的参数保存在 args 中,返回一个新函数,新函数中将绑定的参数和调用时传入的参数合并后调用原函数。

80.Javascript本地存储的方式有哪些?区别及应用场景

Javascript本地存储的方式有以下几种:

  1. Cookie:Cookie是一种小型文本文件,可以存储在用户的计算机上。Cookie可以通过JavaScript进行读取和写入,但是Cookie的大小有限制,一般只能存储4KB左右的数据,而且每次请求都会携带Cookie,会增加网络负担。

  2. Web Storage:Web Storage包括localStorage和sessionStorage,它们可以在浏览器中存储键值对。localStorage存储的数据没有过期时间,而sessionStorage存储的数据在会话结束时会被清除。Web Storage可以存储更多的数据,但是只能存储字符串类型的数据。

  3. IndexedDB:IndexedDB是一种高级的本地存储技术,可以存储大量的结构化数据,支持事务和索引,可以通过JavaScript进行读取和写入。IndexedDB适合存储大量的数据,比如离线应用的数据缓存。

  4. File API:File API可以让JavaScript读取和写入本地文件,可以用于上传和下载文件,也可以用于本地存储数据。但是File API需要用户授权才能访问本地文件系统,而且不同浏览器的实现方式有所不同。

不同的本地存储方式适用于不同的应用场景。Cookie适合存储少量的数据,比如用户的登录状态;Web Storage适合存储中等大小的数据,比如用户的个人设置;IndexedDB适合存储大量的数据,比如离线应用的数据缓存;File API适合存储文件和大量的数据,比如上传和下载文件。

81.请叙述Vue2和Vue3的diff算法的区别

Vue2和Vue3的diff算法的主要区别在于Vue3采用了全新的diff算法,称为“静态提升”(Static Tree Optimization,简称STO)。

Vue2的diff算法是采用双指针的方式进行比较,通过递归遍历新旧节点树,找出差异节点并进行更新。这种算法的缺点是在比较大的节点树时,性能会受到影响。

Vue3的STO算法则是在编译阶段对模板进行静态分析,将静态节点提升为常量,减少了运行时的比较和计算。这种算法的优点是在渲染大型组件树时,性能更高。

另外,Vue3还引入了虚拟DOM的概念,将组件树转换为虚拟节点树,进一步提高了性能。虚拟DOM是一种轻量级的数据结构,可以在内存中快速创建和更新,减少了对浏览器DOM的操作,从而提高了渲染性能。

总的来说,Vue3的diff算法采用了更加高效的静态提升算法和虚拟DOM技术,使得在大型组件树的渲染性能上有了显著的提升。

82.请简单叙述你对作用域链得理解?

作用域是指变量和函数的可访问范围,它决定了代码中的标识符在何处可以被访问。在JavaScript中,作用域分为全局作用域和局部作用域。

全局作用域是指在整个JavaScript程序中都可以访问的变量和函数。在浏览器中,全局作用域是指window对象。

局部作用域是指在函数内部定义的变量和函数,只能在函数内部访问。当函数执行完毕后,局部作用域也就结束了。

作用域链是指在JavaScript中,每个函数都有一个作用域链,它是由当前函数的变量对象和所有外层函数的变量对象组成的。当在函数内部访问一个变量时,JavaScript引擎会先在当前函数的变量对象中查找,如果找不到,就会沿着作用域链向上查找,直到找到该变量或者到达全局作用域为止。

作用域链的作用是保证变量的访问顺序,使得内部函数可以访问外部函数的变量。但是,如果内部函数修改了外部函数的变量,可能会导致意想不到的结果,因此需要注意作用域链的使用。

83.Vue3中的生命周期函数的变化以及含义

Vue3中的生命周期函数相比Vue2有一些变化,主要是为了更好地支持Composition API。

  1. beforeCreate -> use setup()
    在Vue2中,beforeCreate是组件实例创建前的第一个生命周期函数,可以在这里进行一些初始化操作。而在Vue3中,可以使用新的Composition API中的setup()函数来代替beforeCreate。setup()函数会在组件实例创建前执行,可以在这里进行一些初始化操作。

  2. created -> use setup()
    在Vue2中,created是组件实例创建后的第一个生命周期函数,可以在这里进行一些异步操作。而在Vue3中,可以使用新的Composition API中的setup()函数来代替created。setup()函数中可以使用异步操作,例如使用async/await。

  3. beforeMount -> onBeforeMount
    在Vue2中,beforeMount是组件挂载前的生命周期函数,可以在这里进行一些DOM操作。而在Vue3中,beforeMount被重命名为onBeforeMount。

  4. mounted -> onMounted
    在Vue2中,mounted是组件挂载后的生命周期函数,可以在这里进行一些DOM操作。而在Vue3中,mounted被重命名为onMounted。

  5. beforeUpdate -> onBeforeUpdate
    在Vue2中,beforeUpdate是组件更新前的生命周期函数,可以在这里进行一些更新前的操作。而在Vue3中,beforeUpdate被重命名为onBeforeUpdate。

  6. updated -> onUpdated
    在Vue2中,updated是组件更新后的生命周期函数,可以在这里进行一些DOM操作。而在Vue3中,updated被重命名为onUpdated。

  7. beforeUnmount -> onBeforeUnmount
    在Vue2中,beforeUnmount是组件卸载前的生命周期函数,可以在这里进行一些清理操作。而在Vue3中,beforeUnmount被重命名为onBeforeUnmount。

  8. unmounted -> onUnmounted
    在Vue2中,unmounted是组件卸载后的生命周期函数,可以在这里进行一些清理操作。而在Vue3中,unmounted被重命名为onUnmounted。

  9. errorCaptured -> onErrorCaptured
    在Vue2中,errorCaptured是捕获组件内部错误的生命周期函数。而在Vue3中,errorCaptured被重命名为onErrorCaptured。

总的来说,Vue3中的生命周期函数变化不是很大,主要是为了更好地支持Composition API。同时,Vue3中的生命周期函数名称更加直观,更容易理解。

83.Vue3中自定义指令生命周期的变化及含义

  1. bind -> beforeMount
    在Vue2中,自定义指令的bind钩子函数会在指令第一次绑定到元素上时调用。而在Vue3中,bind钩子函数被重命名为beforeMount,并且只会在指令第一次绑定到元素上并且在挂载之前调用。

  2. inserted -> mounted
    在Vue2中,自定义指令的inserted钩子函数会在指令所绑定的元素插入到父节点时调用。而在Vue3中,inserted钩子函数被重命名为mounted,并且只会在指令所绑定的元素插入到父节点并且挂载完成后调用。

  3. update -> beforeUpdate
    在Vue2中,自定义指令的update钩子函数会在指令所绑定的元素的值更新时调用。而在Vue3中,update钩子函数被重命名为beforeUpdate,并且只会在指令所绑定的元素的值更新之前调用。

  4. componentUpdated -> updated
    在Vue2中,自定义指令的componentUpdated钩子函数会在指令所绑定的元素的值更新并且组件重新渲染后调用。而在Vue3中,componentUpdated钩子函数被重命名为updated,并且只会在指令所绑定的元素的值更新并且更新后的DOM已经重新渲染完成后调用。

  5. unbind -> beforeUnmount
    在Vue2中,自定义指令的unbind钩子函数会在指令从元素上解绑时调用。而在Vue3中,unbind钩子函数被重命名为beforeUnmount,并且只会在指令从元素上解绑并且在卸载之前调用。

84.Vue3中的组合式Api有哪些? 和Vue2的Options Api又什么不同?

Vue3中的组合式Api包括:

  1. reactive:将一个普通对象转换为响应式对象
  2. ref:将一个基本类型值转换为响应式对象
  3. computed:创建一个计算属性
  4. watch:监听一个响应式对象的变化
  5. provide/inject:父子组件之间进行依赖注入
  6. onMounted/onUpdated/onUnmounted:组件生命周期钩子函数

Vue2的Options Api则是通过在组件选项中定义data、methods、computed等属性来实现组件逻辑的封装。与Vue3的组合式Api相比,Options Api的缺点是难以维护和测试,因为组件逻辑被分散在不同的选项中。

Vue3的组合式Api则将组件逻辑封装在一个函数中,使得组件逻辑更加清晰、可维护、可测试。此外,组合式Api还支持依赖注入、组合函数复用等功能,使得组件开发更加灵活和高效。

85.什么是跨域?如何解决跨域问题?

跨域是指在浏览器中,当前网页所在的域名与请求的资源所在的域名不同,即跨域请求。由于浏览器的同源策略限制,跨域请求会受到限制,例如无法读取跨域请求的响应数据。

解决跨域问题的方法有以下几种:

  1. JSONP:利用 script 标签的跨域特性,通过动态创建 script 标签,将需要请求的数据作为参数传递给服务器,服务器返回数据时将数据包裹在一个函数调用中返回,浏览器接收到响应后会自动执行该函数,从而实现跨域请求。

  2. CORS:跨域资源共享,是一种标准的跨域解决方案,通过在服务器端设置响应头,允许指定的域名访问该资源,从而实现跨域请求。

  3. 代理:通过在同域名下的服务器端设置代理,将跨域请求转发到目标服务器,从而实现跨域请求。

  4. WebSocket:WebSocket 是一种基于 TCP 协议的全双工通信协议,可以在浏览器和服务器之间建立持久连接,从而实现跨域请求。

  5. postMessage:在不同窗口之间传递消息,可以通过 postMessage 方法实现跨域通信。

86.什么是跨域?如何解决跨域问题?

跨域是指在浏览器中,一个网页的脚本试图访问另一个网页的内容时,由于安全策略的限制,导致访问失败的情况。跨域问题是因为浏览器的同源策略限制所导致的。

同源策略是指,浏览器只允许当前网页与同一域名、协议、端口的网页进行交互,否则就会出现跨域问题。

解决跨域问题的方法有以下几种:

  1. JSONP:利用 script 标签的跨域特性,通过动态创建 script 标签,将需要获取的数据作为参数传递给服务器,服务器返回一个函数调用,将数据作为参数传递给该函数。

  2. CORS:跨域资源共享,是一种浏览器技术,通过在服务器端设置响应头,允许跨域访问。

  3. 代理:通过在服务器端设置代理,将客户端的请求发送到目标服务器,再将目标服务器的响应返回给客户端,从而实现跨域访问。

  4. postMessage:通过在不同窗口之间传递消息,实现跨域通信。

  5. WebSocket:通过 WebSocket 协议实现跨域通信。

87.什么是浮动?如何清除浮动?

浮动是指元素脱离文档流,向左或向右移动,直到遇到父元素或者另一个浮动元素为止。常见的浮动元素包括图片、文字、导航菜单等。

清除浮动可以使用以下方法:

  1. 使用clear属性:在浮动元素下方添加一个空元素,并设置clear属性为both,即可清除浮动。
<div class="clearfix">
  <div class="float-left">浮动元素1</div>
  <div class="float-left">浮动元素2</div>
  <div class="clear"></div>
</div>
.clearfix:after {
  content: "";
  display: block;
  clear: both;
}
  1. 使用overflow属性:在浮动元素的父元素上设置overflow属性为auto或hidden,即可清除浮动。
<div class="parent">
  <div class="float-left">浮动元素1</div>
  <div class="float-left">浮动元素2</div>
</div>
.parent {
  overflow: hidden;
}
  1. 使用伪元素清除浮动:在浮动元素的父元素上使用伪元素:before或:after,并设置content属性为空,display属性为block,clear属性为both,即可清除浮动。
<div class="parent">
  <div class="float-left">浮动元素1</div>
  <div class="float-left">浮动元素2</div>
</div>
.parent:after {
  content: "";
  display: block;
  clear: both;
}

88. 请简述HTML5的新特性。

HTML5是HTML的第五个版本,引入了许多新的特性和改进,包括:

  1. 语义化标签:HTML5引入了一些新的语义化标签,如 <header><footer><nav><article>等,使得页面结构更加清晰明了。

  2. 视频和音频支持:HTML5支持直接在网页中嵌入视频和音频,不需要使用第三方插件。

  3. Canvas绘图:HTML5引入了Canvas元素,可以通过JavaScript在网页上绘制图形、动画等。

  4. Web存储:HTML5提供了两种新的Web存储方式,即localStorage和sessionStorage,可以在客户端存储数据,避免了每次请求都要从服务器获取数据的问题。

  5. Web Workers:HTML5引入了Web Workers,可优先级以在后台运行JavaScript代码,提高网页的性能和响应速度。

  6. 地理位置定位:HTML5支持通过浏览器获取用户的地理位置信息,可以用于定位、导航等应用。

  7. 表单控件:HTML5引入了一些新的表单控件,如日期选择器、颜色选择器、搜索框等,使得表单更加易用和美观。

  8. Web Socket:HTML5支持Web Socket,可以在客户端和服务器之间建立持久性的连接,实现实时通信。

  9. WebRTC:HTML5支持WebRTC,可以在浏览器中实现点对点的音视频通信。

总之,HTML5的新特性使得网页更加丰富、交互性更强、性能更好,为Web开发带来了更多的可能性。

89. 请简述CSS3的新特性。

CSS3是CSS的最新版本,包含了许多新特性,以下是其中一些:

  1. 弹性盒子布局(Flexbox):可以更轻松地实现响应式布局和自适应布局。

  2. 网格布局(Grid):可以更方便地实现复杂的网格布局,支持多列和多行。

  3. 自定义字体(@font-face):可以使用自定义字体,而不必依赖于用户计算机上已安装的字体。

  4. 渐变(Gradient):可以使用线性渐变和径向渐变来创建更复杂的背景。

  5. 动画(Animation):可以使用CSS3动画来创建更流畅的过渡效果。

  6. 过渡(Transition):可以使用CSS3过渡来创建更平滑的过渡效果。

  7. 选择器(Selector):新增了一些选择器,如属性选择器、伪类选择器和伪元素选择器,可以更精确地选择元素。

  8. 媒体查询(Media Query):可以根据设备的屏幕大小和方向来应用不同的样式。

  9. 边框(Border):可以更方便地实现圆角边框和阴影效果。

  10. 2D/3D转换(Transform):可以使用CSS3转换来实现元素的旋转、缩放、倾斜和移动等效果。

总之,CSS3的新特性使得开发者可以更轻松地实现复杂的布局和效果,提高了网站的可访问性和用户体验。

90.请描述盒子模型的四个属性:margin、padding、border和content

盒子模型是指网页中的每个元素都可以看做是一个矩形的盒子,这个盒子由四个属性组成:margin、padding、border和content。

  1. margin(外边距):指元素与周围元素之间的距离,是元素与外部元素之间的空白区域。margin可以设置上下左右四个方向的值,可以为负值。

  2. padding(内边距):指元素的内容与边框之间的距离,是元素内部的空白区域。padding可以设置上下左右四个方向的值,不能为负值。

  3. border(边框):指元素的边框,是元素的边缘线条。border可以设置边框的样式、宽度和颜色。

  4. content(内容):指元素的实际内容,包括文本、图片、视频等。content的大小由元素的宽度和高度决定,可以通过设置宽度和高度来控制。

91.如何处理移动端的适配问题?

移动端的适配问题主要涉及到屏幕尺寸、分辨率、像素密度等因素,以下是一些处理移动端适配问题的方法:

  1. 使用响应式设计:通过使用CSS媒体查询和弹性布局等技术,根据不同的屏幕尺寸和分辨率,自动调整页面布局和元素大小。

  2. 使用Viewport:Viewport是指浏览器窗口中用于显示网页的区域,通过设置Viewport的meta标签,可以控制网页在移动设备上的显示效果。

  3. 使用rem单位:rem是相对于根元素(html)字体大小的单位,通过设置根元素字体大小,可以实现页面元素的自适应缩放。

  4. 使用Flexbox布局:Flexbox是一种弹性盒子布局模型,可以实现灵活的布局和对齐方式,适用于移动设备上的页面布局。

  5. 使用图片适配技术:通过使用图片的srcset属性和sizes属性,可以根据不同的屏幕尺寸和像素密度,自动选择合适的图片大小和分辨率。

综上所述,处理移动端适配问题需要综合考虑多个因素,采用多种技术手段进行处理。

92.请描述常见的HTTP请求方法。

常见的HTTP请求方法有以下几种:

  1. GET:用于获取资源,请求的参数会附在URL后面,请求的数据会被放在响应体中。

  2. POST:用于提交数据,请求的数据会被放在请求体中,常用于提交表单数据、上传文件等。

  3. PUT:用于更新资源,请求的数据会被放在请求体中,常用于更新已有资源。

  4. DELETE:用于删除资源,请求的数据会被放在请求体中,常用于删除已有资源。

  5. HEAD:与GET方法类似,但只返回响应头,不返回响应体,常用于获取资源的元数据。

  6. OPTIONS:用于获取服务器支持的HTTP请求方法,常用于跨域请求时的预检请求。

  7. TRACE:用于追踪请求-响应的传输路径,但由于安全性问题,现在已经很少使用。

  8. CONNECT:用于建立与服务器的隧道连接,常用于HTTPS协议的代理服务器。

以上是常见的HTTP请求方法,不同的请求方法有不同的语义和使用场景,开发者需要根据实际情况选择合适的请求方法。

93.请描述CSS中的position属性及其取值。

CSS中的position属性用于定义元素的定位方式。它有以下取值:

  1. static:默认值,元素按照文档流进行排列,不进行定位。

  2. relative:相对定位,元素相对于其原来的位置进行定位,不会影响其他元素的位置。

  3. absolute:绝对定位,元素相对于其最近的已定位的祖先元素进行定位,如果没有已定位的祖先元素,则相对于文档进行定位。

  4. fixed:固定定位,元素相对于浏览器窗口进行定位,不随滚动条滚动而改变位置。

  5. sticky:粘性定位,元素在滚动到指定位置时会固定在该位置,直到滚动到另一个位置。

使用position属性可以实现元素的精确定位和布局,但需要注意避免出现重叠和覆盖的问题。

94.请描述CSS中的display属性及其取值。

CSS中的display属性用于设置元素的显示方式,它有多个取值,包括:

  1. block:将元素显示为块级元素,即元素会独占一行,可以设置宽度、高度、内边距和外边距等属性。

  2. inline:将元素显示为行内元素,即元素不会独占一行,只会占据自身内容的宽度和高度,不能设置宽度、高度、内边距和外边距等属性。

  3. inline-block:将元素显示为行内块级元素,即元素不会独占一行,但可以设置宽度、高度、内边距和外边距等属性。

  4. none:将元素隐藏,即元素不会在页面中显示,但仍然存在于文档中。

  5. table:将元素显示为表格,即元素会被渲染为表格元素。

  6. table-cell:将元素显示为表格单元格,即元素会被渲染为表格中的单元格。

  7. flex:将元素显示为弹性盒子,即元素会被渲染为弹性容器。

  8. grid:将元素显示为网格容器,即元素会被渲染为网格布局容器。

通过设置display属性的取值,可以控制元素的显示方式,从而实现不同的布局效果。

95.请描述CSS中的float属性及其取值。

CSS中的float属性用于指定一个元素在其父元素中的浮动位置。它可以取以下值:

  1. left:元素向左浮动,周围的元素会围绕它向右浮动。
  2. right:元素向右浮动,周围的元素会围绕它向左浮动。
  3. none:元素不浮动,它将按照正常的文档流排列。
  4. inherit:继承父元素的float属性。

使用float属性的元素会脱离文档流,因此需要注意它可能会影响其他元素的布局。同时,浮动元素的高度会自动调整为其内容的高度,而不是父元素的高度。为了解决这些问题,通常需要使用清除浮动(clear float)的技术。

96.什么是异步编程?请举例说明。

异步编程是一种编程模式,它允许程序在执行某个操作时,不必等待该操作完成,而是可以继续执行其他操作。这种编程模式可以提高程序的性能和响应速度。

举例来说,当我们在网页上点击一个按钮,需要向服务器发送请求并等待服务器返回数据。在传统的同步编程模式中,程序会一直等待服务器返回数据,直到数据返回后才能继续执行其他操作。而在异步编程模式中,程序会立即返回并继续执行其他操作,同时在后台等待服务器返回数据。当服务器返回数据后,程序会再次执行相应的回调函数来处理数据。

在JavaScript中,异步编程通常使用回调函数、Promise对象或async/await语法来实现。例如,使用回调函数实现异步编程的代码如下:

function fetchData(callback) {
  // 发送请求获取数据
  fetch('https://example.com/data')
    .then(response => response.json())
    .then(data => {
      // 数据获取成功后执行回调函数
      callback(data);
    });
}

// 调用fetchData函数,并传入回调函数处理数据
fetchData(data => {
  console.log(data);
});

在上面的例子中,fetchData函数发送请求获取数据,当数据获取成功后会执行传入的回调函数来处理数据。这种方式可以避免程序在等待数据返回时被阻塞,提高程序的性能和响应速度。

97.什么是事件委托?有何优缺点?

事件委托是一种JavaScript编程模式,它利用事件冒泡机制,将事件处理程序绑定到其父元素上,从而减少事件处理程序的数量。

具体来说,事件委托的实现方式是将事件处理程序绑定到父元素上,然后在事件冒泡阶段捕获事件并判断事件源是否是目标元素,如果是则执行相应的处理程序。这样做的好处是可以减少事件处理程序的数量,提高性能,同时也可以动态添加或删除子元素而不需要重新绑定事件处理程序。

优点:

  1. 减少事件处理程序的数量,提高性能。
  2. 可以动态添加或删除子元素而不需要重新绑定事件处理程序。
  3. 可以处理动态生成的元素。

缺点:

  1. 事件委托可能会导致事件处理程序的执行顺序不一致。
  2. 事件委托可能会导致事件处理程序的事件对象不一致。
  3. 事件委托可能会导致事件处理程序的事件类型不一致。

98.请描述ES6的箭头函数及其使用方法。

ES6的箭头函数是一种更简洁的函数声明方式,它使用箭头符号(=>)来代替传统的function关键字。箭头函数的语法如下:

(param1, param2, …, paramN) => { statements }

其中,参数列表可以为空,或者包含一个或多个参数,多个参数之间用逗号分隔。函数体可以是一个表达式,也可以是一组语句,如果是一组语句,则需要使用花括号将它们括起来。

箭头函数的使用方法如下:

  1. 作为函数表达式赋值给变量
const sum = (a, b) => a + b;
console.log(sum(2, 3)); // 输出 5
  1. 作为回调函数传递给其他函数
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // 输出 [2, 4, 6, 8, 10]
  1. 作为对象方法
const person = {
  name: 'John',
  sayHello: () => console.log(`Hello, ${this.name}!`)
};
person.sayHello(); // 输出 "Hello, undefined!"

需要注意的是,箭头函数有一些特殊的行为:

  1. 箭头函数没有自己的this关键字,它会继承外层作用域的this值。

  2. 箭头函数没有arguments关键字,可以使用rest参数(…args)来代替。

  3. 箭头函数不能用作构造函数,不能使用new关键字调用。

  4. 箭头函数没有prototype属性,不能定义原型方法。

因此,在使用箭头函数时需要注意这些特殊的行为。

99.请描述ES6的模板字符串及其使用方法。

ES6的模板字符串是一种新的字符串语法,它允许在字符串中嵌入表达式和变量,使得字符串的拼接更加方便和直观。

模板字符串使用反引号(`)来表示,其中可以使用${}来嵌入表达式和变量。例如:

const name = 'Tom';
const age = 18;
const message = `My name is ${name}, and I am ${age} years old.`;
console.log(message); // 输出:My name is Tom, and I am 18 years old.

在模板字符串中,可以使用任何JavaScript表达式,包括函数调用、三元运算符、甚至是多行代码块。例如:

const num1 = 10;
const num2 = 20;
const result = `The result is ${num1 + num2}. ${num1 > num2 ? 'Num1 is greater.' : 'Num2 is greater.'}`;
console.log(result); // 输出:The result is 30. Num2 is greater.

模板字符串还支持多行字符串的表示,不需要使用转义符号或者字符串拼接符号。例如:

const multiLine = `
  This is a multi-line string.
  It can contain multiple lines of text.
  And it can also include expressions and variables, like ${name}.
`;
console.log(multiLine);
// 输出:
// This is a multi-line string.
// It can contain multiple lines of text.
// And it can also include expressions and variables, like Tom.

总之,ES6的模板字符串提供了一种更加方便和直观的字符串拼接方式,使得代码更加简洁易读。

100.请描述ES6的let和const关键字及其作用。

ES6中引入了let和const关键字,它们用于声明变量和常量。

let关键字用于声明块级作用域的变量,它与var关键字不同的是,let声明的变量只在当前代码块内有效,不会污染全局作用域。同时,let声明的变量不允许重复声明,即同一作用域内不能重复定义同名变量。

const关键字用于声明常量,它与let关键字类似,也是块级作用域的,但是const声明的变量必须在声明时进行初始化,且初始化后不允许再次赋值。const声明的常量一旦被赋值,其值就不能再被修改。

使用let和const关键字可以有效地避免变量污染全局作用域,提高代码的可读性和可维护性。同时,使用const关键字可以保证常量的值不会被修改,增强代码的安全性。

101.请描述ES6的解构赋值及其使用方法。

ES6的解构赋值是一种方便的语法,可以将数组或对象中的值解构出来并赋值给变量。它可以使代码更加简洁、易读,并且可以减少重复的代码。

使用方法:

  1. 数组解构赋值
let [a, b, c] = [1, 2, 3];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
  1. 对象解构赋值
let {name, age} = {name: 'Tom', age: 18};
console.log(name); // Tom
console.log(age); // 18
  1. 嵌套解构赋值
let [a, [b, c]] = [1, [2, 3]];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
let {name, age, address: {city, street}} = {name: 'Tom', age: 18, address: {city: 'Beijing', street: 'Chaoyang Road'}};
console.log(name); // Tom
console.log(age); // 18
console.log(city); // Beijing
console.log(street); // Chaoyang Road
  1. 默认值
let [a, b, c = 3] = [1, 2];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
let {name, age, address: {city = 'Beijing', street}} = {name: 'Tom', age: 18, address: {street: 'Chaoyang Road'}};
console.log(name); // Tom
console.log(age); // 18
console.log(city); // Beijing
console.log(street); // Chaoyang Road
  1. 重命名
let {name: n, age: a} = {name: 'Tom', age: 18};
console.log(n); // Tom
console.log(a); // 18
let {address: {city: c}} = {name: 'Tom', age: 18, address: {city: 'Beijing', street: 'Chaoyang Road'}};
console.log(c); // Beijing
  1. 函数参数解构赋值
function foo([a, b, c]) {
  console.log(a, b, c);
}
foo([1, 2, 3]); // 1 2 3
function bar({name, age}) {
  console.log(name, age);
}
bar({name: 'Tom', age: 18}); // Tom 18

102.什么是webpack?如何使用webpack打包项目

Webpack是一个现代化的JavaScript应用程序静态模块打包器。它可以将多个JavaScript文件打包成一个或多个bundle文件,以及处理其他类型的文件,如CSS、图片等。Webpack还支持代码分割、懒加载、热模块替换等功能,使得开发者可以更加高效地开发和维护复杂的前端应用程序。

使用Webpack打包项目的步骤如下:

  1. 安装Webpack:在项目目录下执行npm install webpack --save-dev命令安装Webpack。

  2. 配置Webpack:在项目根目录下创建一个webpack.config.js文件,配置Webpack的入口文件、输出文件、加载器、插件等。

  3. 编写代码:在项目中编写JavaScript、CSS、HTML等文件。

  4. 执行打包命令:在命令行中执行webpack命令,Webpack会根据配置文件将代码打包成一个或多个bundle文件。

  5. 部署代码:将打包后的文件部署到服务器上,即可在浏览器中访问应用程序。

需要注意的是,Webpack的配置文件可以根据项目的需求进行调整,例如使用不同的加载器、插件等。同时,Webpack还支持多种打包模式,如开发模式、生产模式等,可以根据不同的环境进行打包。

103.请描述ES6的解构赋值及其使用方法。

ES6的解构赋值是一种方便的语法,可以将数组或对象中的值解构出来并赋值给变量。它可以使代码更加简洁、易读,并且可以减少重复的代码。

使用方法:

  1. 数组解构赋值
let [a, b, c] = [1, 2, 3];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
  1. 对象解构赋值
let {name, age} = {name: 'Tom', age: 18};
console.log(name); // Tom
console.log(age); // 18
  1. 嵌套解构赋值
let [a, [b, c]] = [1, [2, 3]];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
let {name, age, address: {city, street}} = {name: 'Tom', age: 18, address: {city: 'Beijing', street: 'Chaoyang Road'}};
console.log(name); // Tom
console.log(age); // 18
console.log(city); // Beijing
console.log(street); // Chaoyang Road
  1. 默认值
let [a, b, c = 3] = [1, 2];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
let {name, age, address: {city = 'Beijing', street}} = {name: 'Tom', age: 18, address: {street: 'Chaoyang Road'}};
console.log(name); // Tom
console.log(age); // 18
console.log(city); // Beijing
console.log(street); // Chaoyang Road
  1. 重命名
let {name: n, age: a} = {name: 'Tom', age: 18};
console.log(n); // Tom
console.log(a); // 18
let {address: {city: c}} = {name: 'Tom', age: 18, address: {city: 'Beijing', street: 'Chaoyang Road'}};
console.log(c); // Beijing
  1. 函数参数解构赋值
function foo([a, b, c]) {
  console.log(a, b, c);
}
foo([1, 2, 3]); // 1 2 3
function bar({name, age}) {
  console.log(name, age);
}
bar({name: 'Tom', age: 18}); // Tom 18

104.什么是webpack?如何使用webpack打包项目

Webpack是一个现代化的JavaScript应用程序静态模块打包器。它可以将多个JavaScript文件打包成一个或多个bundle文件,以及处理其他类型的文件,如CSS、图片等。Webpack还支持代码分割、懒加载、热模块替换等功能,使得开发者可以更加高效地开发和维护复杂的前端应用程序。

使用Webpack打包项目的步骤如下:

  1. 安装Webpack:在项目目录下执行npm install webpack --save-dev命令安装Webpack。

  2. 配置Webpack:在项目根目录下创建一个webpack.config.js文件,配置Webpack的入口文件、输出文件、加载器、插件等。

  3. 编写代码:在项目中编写JavaScript、CSS、HTML等文件。

  4. 执行打包命令:在命令行中执行webpack命令,Webpack会根据配置文件将代码打包成一个或多个bundle文件。

  5. 部署代码:将打包后的文件部署到服务器上,即可在浏览器中访问应用程序。

需要注意的是,Webpack的配置文件可以根据项目的需求进行调整,例如使用不同的加载器、插件等。同时,Webpack还支持多种打包模式,如开发模式、生产模式等,可以根据不同的环境进行打包。

105.简单说一下什么是事件代理?

事件代理是一种JavaScript编程技术,它利用事件冒泡机制,将事件处理程序绑定到一个父元素上,而不是绑定到每个子元素上。当子元素触发事件时,事件会冒泡到父元素,父元素就可以捕获到事件并执行相应的处理程序。
这种技术可以减少事件处理程序的数量,提高性能,同时也可以动态地添加或删除子元素而不需要重新绑定事件处理程序。

使用事件代理的场景包括:

  1. 动态添加元素:当页面上的元素是动态生成的,我们无法直接为它们绑定事件处理程序,此时可以使用事件代理。

  2. 性能优化:当页面上的元素数量较多时,为每个元素都绑定事件处理程序会导致性能问题,使用事件代理可以将事件处理程序绑定在父元素上,减少事件处理程序的数量,提高性能。

  3. 简化代码:使用事件代理可以减少代码量,避免重复的代码,提高代码的可维护性。

例如,我们可以在一个父元素上绑定click事件,然后通过判断事件源来执行相应的操作,如下所示:

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
</ul>
var list = document.getElementById('list');
list.addEventListener('click', function(event) {
  if (event.target.tagName === 'LI') {
    console.log(event.target.innerHTML);
  }
});

在上面的代码中,我们为ul元素绑定了click事件,然后通过判断事件源来执行相应的操作,当点击li元素时,会输出li元素的内容。这样,无论我们添加多少个li元素,都只需要绑定一次事件处理程序,大大简化了代码。

106.什么是防抖和节流,有什么区别?如何实现?

防抖:把触发非常频繁的事件合并成一次去执行 在指定时间内只执行一次回调函数,如果在指定的时间内又触发了该事件,则回调函数的执行时间会基于此刻重新开始计算

节流::指频繁触发事件时,只会在指定的时间段内执行事件回调,即触发事件间隔大于等于指定的时间才会执行回调函数。总结起来就是:事件,按照一段时间的间隔来进行触发。

区别:防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行

防抖的实现可以通过设置一个定时器,在事件触发后等待一段时间,如果在等待时间内再次触发事件,则清除之前的定时器,重新设置定时器。如果等待时间结束后没有再次触发事件,则执行事件。

节流的实现可以通过设置一个标志位,在事件触发时判断标志位是否为真,如果为真则执行事件并将标志位设置为假,同时设置一个定时器,在一定时间后将标志位设置为真。如果在定时器时间内再次触发事件,则不执行事件。

防抖适用场景:
文本输入的验证,连续输入文字后发送 AJAX 请求进行验证,验证一次就好
按钮提交场景:防止多次提交按钮,只执行最后提交的一次
服务端验证场景:表单验证需要服务端配合,只执行一段连续的输入事件的最后一次,还有搜索联想词功能类似

节流适用场景:
如窗口调整、页面滚动、抢购和疯狂点击等会用到节流。

107.Vue自定义指令的钩子函数有哪些?

Vue自定义指令的钩子函数有以下几个:

  1. bind:指令绑定到元素时调用,只调用一次。
  2. inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
  3. update:被绑定元素所在的模板更新时调用,而不论绑定值是否变化。但是首次绑定时不会调用,需要在bind中初始化。
  4. componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
  5. unbind:指令与元素解绑时调用,只调用一次。

其中,bind和unbind是必须实现的钩子函数,其他的钩子函数可以根据需要选择实现。

108.如何理解HTML语义化

HTML语义化是指在编写HTML代码时,使用合适的标签和属性来描述页面内容的结构和含义,使得页面的结构和内容更加清晰、易于理解和维护。具体来说,就是使用具有语义含义的标签来表示页面的各个部分,如使用<header>标签表示页面的头部,使用<nav>标签表示导航栏,使用<article>标签表示文章内容等。

通过使用语义化的HTML代码,可以提高页面的可读性和可访问性,使得搜索引擎更容易理解页面的内容,从而提高页面的排名和流量。同时,也可以使得开发者更容易理解和维护代码,减少代码的冗余和重复,提高代码的可维护性和可扩展性。

109.JS内置类型有哪些

JS内置类型包括:

  1. 基本类型:Number、String、Boolean、Null、Undefined、Symbol(ES6新增)

  2. 引用类型:Object(包括Array、Function、Date等)、RegExp、Math等

其中,基本类型是按值传递的,而引用类型是按引用传递的。

110.v-show和v-if有什么区别?使用场景分别是什么?

v-show和v-if都是Vue.js中的指令,但它们的作用不同。

v-show是用来控制元素的显示和隐藏的,它会在元素的style属性中添加display属性来实现显示和隐藏。如果v-show的值为false,元素会被隐藏,如果v-show的值为true,元素会被显示。v-show不会改变DOM结构,因此在切换时比较快。

v-if是用来控制元素的渲染的,它会根据条件来决定是否渲染元素。如果v-if的值为false,元素不会被渲染到DOM中,如果v-if的值为true,元素会被渲染到DOM中。v-if会改变DOM结构,因此在切换时比较慢。

使用场景:

v-show适用于需要频繁切换显示和隐藏的元素,比如菜单、弹窗等。

v-if适用于需要根据条件来动态渲染元素的情况,比如根据用户权限来显示不同的内容、根据数据是否存在来渲染不同的列表等。

111.Js模块化开发有哪些方案

Js模块化开发有以下几种方案:

  1. CommonJS:Node.js采用的模块化规范,通过require()和module.exports实现模块化。

  2. AMD(Asynchronous Module Definition):异步模块定义,主要用于浏览器端,通过define()和require()实现模块化。

  3. ES6模块化:ES6标准中新增的模块化规范,通过import和export实现模块化。

  4. UMD(Universal Module Definition):通用模块定义,可以同时支持CommonJS和AMD规范,适用于跨平台开发。

  5. SystemJS:一个通用的模块加载器,可以加载任何模块规范的模块。

  6. RequireJS:一个AMD规范的模块加载器,可以异步加载模块。

  7. Browserify:一个将Node.js模块打包成浏览器可用的模块的工具,支持CommonJS规范。

  8. webpack:一个模块打包工具,支持多种模块规范,可以将多个模块打包成一个文件,减少http请求次数。

112.说说你对promise的了解

Promise是一种异步编程的解决方案,它可以让我们更方便地处理异步操作。Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。当Promise对象的状态从pending变为fulfilled或rejected时,就会触发相应的回调函数。Promise对象可以链式调用,这样可以避免回调地狱的问题。在使用Promise时,我们可以使用then()方法来处理成功的回调函数,使用catch()方法来处理失败的回调函数。如果我们需要同时处理多个Promise对象,可以使用Promise.all()方法来等待所有Promise对象都完成后再执行后续操作。

113.前后端路由差别

前端路由和后端路由的主要区别在于它们处理请求的方式和位置。

前端路由是在浏览器中处理的,它使用 JavaScript 来处理 URL 的变化,而不是像传统的后端路由一样在服务器上处理。前端路由通常用于单页应用程序(SPA),其中所有页面都在同一个 HTML 页面中加载,而不是每次请求都加载新的页面。前端路由可以通过修改 URL 来更新视图,而不需要重新加载整个页面。

后端路由是在服务器端处理的,它使用服务器端代码来处理 URL 的变化。当用户请求一个 URL 时,服务器会根据该 URL 执行相应的代码并返回相应的 HTML 页面或数据。后端路由通常用于传统的多页应用程序,其中每个页面都是独立的 HTML 页面。

总的来说,前端路由和后端路由都是用于处理 URL 的,但它们的实现方式和位置不同。前端路由更适合单页应用程序,而后端路由更适合传统的多页应用程序。

114.路由原理 history 和 hash 两种路由方式的特点

history 路由和 hash 路由是两种常见的前端路由方式,它们的特点如下:

  1. history 路由:使用 HTML5 的 History API,通过修改浏览器地址栏中的 URL 来实现路由跳转。特点如下:
  • URL 更加美观,可以使用自然语言的方式来表示页面路径;
  • 可以使用浏览器的前进和后退按钮来进行页面跳转;
  • 需要服务器端的支持,因为浏览器发起的路由请求会被发送到服务器端,服务器需要返回对应的页面。
  1. hash 路由:使用 URL 中的 hash(#)来实现路由跳转。特点如下:
  • URL 中的 hash 不会被发送到服务器端,因此不需要服务器端的支持;
  • 可以使用浏览器的前进和后退按钮来进行页面跳转;
  • URL 不够美观,因为 hash 后面的内容通常是一些编码后的字符串。

总的来说,history 路由和 hash 路由各有优缺点,开发者可以根据项目需求来选择适合的路由方式。

115.HTTP 报文的组成部分

HTTP报文由两部分组成:请求报文和响应报文。

请求报文包括:

  1. 请求行:包括请求方法、请求URI和HTTP协议版本。
  2. 请求头部:包括请求的一些附加信息,如Accept、User-Agent、Host等。
  3. 空行:请求头部和请求体之间必须有一个空行。
  4. 请求体:请求的数据,例如表单数据、JSON数据等。

响应报文包括:

  1. 状态行:包括HTTP协议版本、状态码和状态描述。
  2. 响应头部:包括响应的一些附加信息,如Content-Type、Content-Length、Server等。
  3. 空行:响应头部和响应体之间必须有一个空行。
  4. 响应体:响应的数据,例如HTML页面、JSON数据等。

116.谈谈你对MVVM的理解

MVVM是一种软件架构模式,它将应用程序分为三个部分:模型(Model)、视图(View)和视图模型(ViewModel)。其中,模型表示应用程序的数据和业务逻辑,视图表示用户界面,视图模型则是连接模型和视图的桥梁。

在MVVM中,视图模型负责将模型中的数据转换为视图可以使用的格式,并将视图中的用户操作转换为模型可以理解的格式。视图模型还负责处理视图的状态和行为,以及与模型之间的通信。

MVVM的优点在于它可以将应用程序的逻辑和界面分离,使得代码更加清晰、易于维护和测试。同时,MVVM还可以提高开发效率,因为开发人员可以专注于各自的领域,而不必担心其他部分的实现细节。

总之,MVVM是一种优秀的软件架构模式,它可以帮助开发人员构建高质量、易于维护的应用程序。

117.React 中 keys 的作用是什么

在 React 中,keys 是用来帮助 React 识别哪些元素发生了变化,从而提高渲染性能的重要属性。当 React 渲染一个列表时,它会根据每个元素的 key 属性来判断该元素是否已经存在于 DOM 中,如果存在,则更新该元素的属性和状态,如果不存在,则创建新的元素并添加到 DOM 中。

如果没有为列表中的元素指定 key 属性,React 会使用默认的索引作为 key 值,这样可能会导致一些问题,例如当列表中的元素顺序发生变化时,React 会认为这些元素已经发生了变化,从而重新渲染整个列表,这会影响性能。

因此,为了避免这种情况,我们应该为列表中的每个元素指定一个唯一的 key 值,通常可以使用每个元素的 ID 或者其他唯一标识符作为 key 值。这样,当列表中的元素发生变化时,React 只会更新发生变化的元素,从而提高渲染性能。

118.为什么虚拟dom会提高性能

虚拟DOM(Virtual DOM)是一种将页面状态抽象为JavaScript对象的技术,它可以在内存中对页面进行操作,而不需要直接操作DOM。虚拟DOM可以提高性能的原因如下:

  1. 减少DOM操作:DOM操作是非常昂贵的,因为每次操作都会导致浏览器重新计算布局和绘制。使用虚拟DOM可以减少DOM操作的次数,从而提高性能。

  2. 批量更新:虚拟DOM可以将多个DOM操作合并为一个批量更新,从而减少浏览器的重绘和重排次数。

  3. 快速比较:虚拟DOM可以快速比较前后两个状态的差异,从而只更新需要更新的部分,减少不必要的操作。

  4. 跨平台:虚拟DOM可以在不同的平台上运行,例如浏览器、服务器、移动端等,从而提高代码的复用性和可维护性。

总之,虚拟DOM可以优化DOM操作,减少重绘和重排,提高性能和用户体验。

119.简述flux 思想

Flux 是一种前端架构思想,它的主要思想是将应用程序的状态(state)和逻辑(logic)从视图(view)中分离出来,使得应用程序的状态和逻辑更加清晰和易于维护。

Flux 架构包含四个主要的组件:Action、Dispatcher、Store 和 View。Action 是应用程序中的事件,它会触发 Dispatcher 的相应方法。Dispatcher 是一个中央调度器,它接收 Action 并将其分发给相应的 Store。Store 是应用程序中的状态管理器,它存储应用程序的状态,并且在接收到 Action 后更新状态。View 是应用程序的界面,它从 Store 中获取状态,并将状态渲染到界面上。

Flux 架构的主要优点是它可以使应用程序的状态和逻辑更加清晰和易于维护。通过将状态和逻辑从视图中分离出来,可以使得应用程序更加模块化和可测试。此外,Flux 架构还可以使得应用程序更加灵活和可扩展,因为它可以轻松地添加新的 Action、Store 和 View。

120.setState 是同步的还是异步的

异步的情况:
由React控制的事件处理函数,以及生命周期函数调用setState时表现为异步 。

大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等(合成事件中),这些事件处理函数中的setState都是异步处理的。

同步的情况:
React控制之外的事件中调用setState是同步更新的。

比如原生js绑定的事件setTimeout/setInterval,ajax,promise.then内等 React 无法掌控的 APIs情况下,setState是同步更新state的

121.vue路由的钩子函数有哪些

Vue路由的钩子函数包括:

  1. beforeEach(to, from, next):在路由跳转之前执行,可以用来进行路由拦截或者权限验证等操作。

  2. afterEach(to, from):在路由跳转完成之后执行,可以用来进行页面统计等操作。

  3. beforeRouteEnter(to, from, next):在进入路由之前执行,可以用来进行异步数据加载等操作。

  4. beforeRouteUpdate(to, from, next):在路由更新之前执行,可以用来进行组件数据更新等操作。

  5. beforeRouteLeave(to, from, next):在离开路由之前执行,可以用来进行数据保存或者提示用户等操作。

122.请详细说下你对vue生命周期的理解

Vue生命周期是指Vue实例在创建、运行和销毁过程中经历的一系列事件。Vue生命周期分为8个阶段,分别是:

  1. beforeCreate:在实例被创建之前,此时数据观测和事件机制都未初始化,无法访问data、computed、methods等属性和方法。

  2. created:在实例被创建之后,此时已经完成了数据观测和事件机制的初始化,可以访问data、computed、methods等属性和方法,但是DOM还未渲染。

  3. beforeMount:在实例挂载到DOM之前,此时模板已经编译完成,但是还未将数据渲染到页面上。

  4. mounted:在实例挂载到DOM之后,此时数据已经渲染到页面上,可以进行DOM操作。

  5. beforeUpdate:在数据更新之前,此时可以进行一些操作,如获取更新前的DOM状态。

  6. updated:在数据更新之后,此时DOM已经重新渲染,可以进行DOM操作。

  7. beforeDestroy:在实例销毁之前,此时实例仍然可以访问,可以进行一些清理工作。

  8. destroyed:在实例销毁之后,此时实例已经被销毁,无法再进行访问。

Vue生命周期的理解可以帮助我们在开发过程中更好地掌握Vue实例的状态和行为,从而更好地进行开发和调试。同时,也可以帮助我们优化性能,避免不必要的操作和资源浪费。

123. $route$router的区别

$router是Vue Router的实例,它负责管理应用程序的路由状态。

$route是当前路由的信息对象,它包含当前URL解析出的信息,如路由参数、查询参数、hash等。

简单来说, r o u t e r 是 V u e R o u t e r 的实例,负责管理路由,而 router是Vue Router的实例,负责管理路由,而 routerVueRouter的实例,负责管理路由,而route是当前路由的信息对象,包含当前URL解析出的信息。

124.keep-alive-keep-alive-的作用是什么

是Vue.js中的一个内置组件,它的作用是缓存组件实例,以便在组件之间切换时,保留组件的状态,避免重复渲染和数据丢失。

具体来说,当一个组件被包裹在标签中时,它不会被销毁,而是被缓存起来。当下次再次使用这个组件时,Vue.js会从缓存中取出组件实例,而不是重新创建一个新的实例。这样可以提高应用的性能和用户体验。

需要注意的是,只能缓存有状态的组件,即有数据和生命周期钩子的组件。对于无状态的组件,如纯展示型组件,使用没有任何效果。

125.babel是什么,有什么作用?

Babel是一个JavaScript编译器,它可以将新版本的JavaScript代码转换成向后兼容的代码,以便在旧版浏览器或环境中运行。Babel的主要作用是帮助开发者使用最新的JavaScript语言特性,而不必担心它们是否被所有浏览器或环境所支持。Babel可以将ES6、ES7、ES8等新版本的JavaScript代码转换成ES5或更早版本的代码,以便在所有浏览器或环境中运行。此外,Babel还支持插件和预设,可以根据需要进行自定义配置,以满足不同的项目需求。

126.Proxy 相比于 defineProperty 的优势

Proxy 相比于 defineProperty 有以下优势:

  1. 更加灵活:Proxy 可以代理整个对象,而 defineProperty 只能代理对象的某个属性。Proxy 可以拦截对象的多种操作,如读取、赋值、删除等,而 defineProperty 只能拦截属性的读取和赋值。

  2. 更加强大:Proxy 可以拦截更多的操作,如函数调用、构造函数调用等,而 defineProperty 只能拦截属性的读取和赋值。

  3. 更加易用:Proxy 的 API 更加简单易用,不需要像 defineProperty 那样需要手动设置 getter 和 setter。

  4. 更加安全:Proxy 可以防止对象被篡改,而 defineProperty 只能防止对象的某个属性被篡改。

  5. 更加高效:Proxy 的性能比 defineProperty 更好,因为 Proxy 可以在底层实现中进行优化,而 defineProperty 的实现需要更多的代码和计算。

127.举一些ES6对String字符串类型做的常用升级优化?

ES6对String字符串类型做了很多升级优化,以下是一些常用的:

  1. 模板字符串:使用反引号(`)来定义字符串,可以在其中插入变量或表达式,使用${}包裹。

  2. 多行字符串:使用反引号(`)来定义字符串,可以直接换行,不需要使用\n。

  3. 字符串方法:ES6新增了很多字符串方法,如startsWith()、endsWith()、includes()、repeat()等,方便字符串的操作。

  4. Unicode支持:ES6支持Unicode字符集,可以使用\u{XXXX}来表示一个Unicode字符。

  5. 解构赋值:可以使用解构赋值来获取字符串中的某些字符,如const [a, b, c] = “abc”。

  6. 扩展运算符:可以使用扩展运算符来将字符串转换为数组,如[…“hello”]。

  7. 正则表达式:ES6对正则表达式做了很多升级优化,如新增了u修饰符,支持Unicode字符集,新增了y修饰符,支持粘连匹配等。

128.ES5、ES6和ES2015有什么区别?

ES5是ECMAScript 5的简称,是JavaScript的第五个版本,于2009年发布。ES6是ECMAScript 6的简称,也被称为ES2015,是JavaScript的第六个版本,于2015年发布。

ES6相对于ES5来说,增加了很多新的语法和特性,包括箭头函数、let和const关键字、模板字符串、解构赋值、类和模块等。ES6的目标是让JavaScript更加现代化、简洁、易读、易维护。

ES2015是ES6的另一个名称,因为在发布之前,ECMAScript委员会已经决定将其命名为ES2015,以反映其发布年份。之后,每年都会发布一个新版本,如ES2016、ES2017等。

总的来说,ES6和ES2015是同一个版本,是JavaScript的一个重要的升级版本,引入了许多新的语言特性和API,使得JavaScript更加现代化和强大。

129.什么是Vue SSR

Vue SSR(Server-Side Rendering)是指在服务器端将Vue组件渲染成HTML字符串,然后将其发送到浏览器端进行展示。相比于传统的客户端渲染,SSR可以提高首屏加载速度、SEO友好、更好的用户体验等优点。Vue SSR可以使用Vue官方提供的服务器端渲染解决方案,也可以使用第三方库如Nuxt.js等。

130.传入 setState 函数的第二个参数的作用是什么

2.传入 setState 函数的第二个参数是一个回调函数,它会在 setState 更新完毕并且组件已经重新渲染后被调用。它的作用是在组件更新后执行一些操作,例如获取更新后的 DOM 元素或执行一些异步操作。

131.React 中 refs 的作用是什么

React 中的 refs 是用来获取组件或 DOM 元素的引用的一种方法。它可以让我们直接访问组件或 DOM 元素,而不需要通过 props 或 state 传递数据。refs 可以用于以下几种情况:

  1. 获取组件实例:当需要在父组件中调用子组件的方法或访问子组件的属性时,可以使用 refs 获取子组件的实例。

  2. 获取 DOM 元素:当需要直接操作 DOM 元素时,可以使用 refs 获取 DOM 元素的引用。

  3. 自动聚焦:当需要在组件加载后自动聚焦某个输入框或按钮时,可以使用 refs 来实现。

需要注意的是,使用 refs 应该尽量避免,因为它会破坏组件的封装性,使组件变得不可预测。只有在必要的情况下才应该使用 refs。

132.react性能优化是哪个周期函数

React性能优化是在生命周期函数中进行的,主要是在shouldComponentUpdate和componentDidUpdate这两个函数中进行优化。shouldComponentUpdate函数用于判断组件是否需要重新渲染,可以通过比较前后props和state的值来决定是否需要重新渲染。componentDidUpdate函数用于在组件更新后进行一些操作,例如更新DOM或者发送网络请求等。在这个函数中也可以进行一些性能优化,例如避免重复渲染或者避免不必要的操作。

133.在生命周期中的哪一步你应该发起 AJAX 请求

在生命周期的 componentDidMount() 方法中发起 AJAX 请求。这是因为 componentDidMount() 方法在组件挂载后立即调用,这意味着组件已经准备好渲染,并且可以安全地发起 AJAX 请求。此外,如果您在 componentDidMount() 方法中发起 AJAX 请求,您可以确保在组件挂载后立即获取数据,这样您就可以在渲染组件之前更新组件的状态。

134.概述下 React 中的事件处理逻辑

react中的事件它并没有绑定到dom节点上而是绑定到了document上面,然后

同时去处理程序,同时这个浏览器的事件冒泡所有的节点也是在document上触发。

浏览器的事件执行顺序是捕获阶段->目标阶段->冒泡阶段,节点上的原生事件是在目标

阶段执行,合成事件是在冒泡阶段执行

135.说说你对flex布局的了解,使用场景?常用属性?

Flex布局是一种CSS3的布局方式,它可以让容器中的子元素按照一定的规则排列,从而实现灵活的布局效果。Flex布局的使用场景包括但不限于以下几种:

  1. 响应式布局:Flex布局可以根据不同的屏幕尺寸自动调整子元素的排列方式,从而实现响应式布局。

  2. 列表布局:Flex布局可以实现列表布局,使得列表中的元素可以自动换行,而不需要手动设置宽度和高度。

  3. 等分布局:Flex布局可以实现等分布局,使得容器中的子元素可以等分容器的宽度或高度。

常用的Flex布局属性包括:

  1. display:设置容器为Flex布局。

  2. flex-direction:设置子元素的排列方向。

  3. justify-content:设置子元素在主轴上的对齐方式。

  4. align-items:设置子元素在交叉轴上的对齐方式。

  5. flex-wrap:设置子元素是否换行。

  6. flex-grow:设置子元素的放大比例。

  7. flex-shrink:设置子元素的缩小比例。

  8. flex-basis:设置子元素的基准大小。

  9. align-self:设置单个子元素在交叉轴上的对齐方式。

136.改变this指向的方法有哪些?区别是什么?

在 JavaScript 中,有多种方法可以改变 this 的指向,包括:

  1. 使用 call() 方法:call() 方法可以在调用函数时显式地指定函数内部的 this 指向。例如:func.call(thisArg, arg1, arg2, ...),其中 thisArg 是要绑定的 this 值,arg1, arg2, ... 是函数的参数。

  2. 使用 apply() 方法:apply() 方法与 call() 方法类似,也可以在调用函数时显式地指定函数内部的 this 指向。不同之处在于,apply() 方法接收的是一个数组作为参数,而 call() 方法接收的是一个参数列表。例如:func.apply(thisArg, [arg1, arg2, ...])

  3. 使用 bind() 方法:bind() 方法可以创建一个新函数,该函数的 this 值被绑定到指定的对象。例如:var newFunc = func.bind(thisArg),其中 newFunc 是一个新函数,其 this 值被绑定到 thisArg

这些方法的区别在于:

  • call()apply() 方法可以立即调用函数并指定 this 值和参数,而 bind() 方法只是创建了一个新函数,需要再次调用才能执行。
  • call() 方法接收的是一个参数列表,而 apply() 方法接收的是一个数组作为参数。
  • bind() 方法可以创建一个新函数,该函数的 this 值被绑定到指定的对象,而 call()apply() 方法只能在调用时指定 this 值。

137. vuex和pinia的区别

  • pinia是下一代的vuex
  • pinia的api更简洁,只有三个属性,state,actions,getter;vuex有五个属性
  • pinia对ts支持更加友好
  • pinia可以使用调试工具
  • pinia自动分模块
  • p的action既可以是同步,也可以是异步
  • pinia可以通过this访问state

138.vite和webpack的区别

  1. 底层原理vite通过go语言实现,性能好,打包速度快很多,是webpack的10-100倍
  2. vite采用预构建,预编译,会把所有的模块做预编译,打包构建时只更新有修改的模块,而webpack项目越大打包速度越慢
  3. vite有缓存,对于工具模块,包依赖,采用强缓存,直接缓存到浏览器,不会再重新加载;对可能发生变化的模块做协商缓存,只编译有变化的部分

139.ES6对Array数组类型做了很多升级优化,以下是一些常用的:

  1. 扩展运算符(Spread Operator):可以将数组展开为逗号分隔的参数序列,方便地将一个数组复制到另一个数组中。

  2. Array.from()方法:可以将类数组对象或可迭代对象转换为真正的数组。

  3. Array.of()方法:可以创建一个包含任意数量参数的新数组。

  4. find()和findIndex()方法:可以在数组中查找符合条件的元素,并返回该元素或其索引。

  5. includes()方法:可以判断数组中是否包含某个元素。

  6. fill()方法:可以用指定的值填充数组中的所有元素。

  7. flat()和flatMap()方法:可以将多维数组转换为一维数组,并且可以对数组中的每个元素执行一个函数。

  8. sort()方法的升级:可以通过传递一个比较函数来实现更复杂的排序。

  9. forEach()方法的升级:可以通过传递一个可选的上下文参数来指定回调函数中的this值。

  10. map()方法的升级:可以通过传递一个可选的上下文参数来指定回调函数中的this值。

140.Vue组件间的数据传递方式有哪些

Vue组件间的数据传递方式有以下几种:

  1. 父子组件传递:父组件通过props向子组件传递数据,子组件通过$emit触发事件向父组件传递数据。

  2. 兄弟组件传递:可以通过一个共同的父组件作为中转站,将数据传递给兄弟组件。

  3. 事件总线:可以通过Vue实例作为事件总线,通过 e m i t 和 emit和 emiton方法实现组件间的数据传递。

  4. Vuex:Vuex是Vue的状态管理工具,可以在全局共享数据,实现组件间的数据传递。

  5. a t t r s 和 attrs和 attrslisteners: a t t r s 和 attrs和 attrslisteners是Vue提供的两个特殊属性,可以在组件中传递父组件的属性和事件。

  6. provide和inject:provide和inject是Vue提供的一种高级组件通信方式,可以在祖先组件中提供数据,然后在后代组件中注入数据。

以上是Vue组件间数据传递的常用方式,根据实际情况选择合适的方式进行数据传递。

141.说说你对webpack的理解?plugin和loader有什么区别?

Webpack是一个现代化的JavaScript应用程序打包工具,它可以将多个模块打包成一个或多个JavaScript文件,以便在浏览器中加载。Webpack的主要功能是将各种资源(如JavaScript、CSS、图片等)打包成静态文件,以便于在浏览器中加载。

Plugin和Loader是Webpack中两个不同的概念。Plugin是用来扩展Webpack功能的,它可以在Webpack构建过程中执行一些额外的任务,例如压缩代码、生成HTML文件、提取CSS等。而Loader则是用来处理Webpack中的各种资源文件,例如将ES6转换为ES5、将CSS转换为JavaScript等。

Plugin和Loader的区别在于它们的作用范围不同。Plugin是对整个Webpack构建过程进行扩展,而Loader则是对Webpack中的每个模块进行转换。Plugin可以在Webpack的各个生命周期中执行任务,而Loader只能在Webpack加载模块时进行转换。

142.说说你对MVVM和渐进式的理解?

MVVM是一种软件架构模式,它将应用程序分为三个部分:模型(Model)、视图(View)和视图模型(ViewModel)。其中,模型表示应用程序的数据和业务逻辑,视图表示用户界面,视图模型则是连接模型和视图的桥梁,负责将模型中的数据转换为视图中的数据,并将视图中的用户操作转换为模型中的操作。MVVM的优点是能够将应用程序的不同部分分离开来,使得代码更加清晰、易于维护和测试。

渐进式是一种开发方式,它强调应用程序的逐步迭代和演进,而不是一次性完成所有功能。在渐进式开发中,应用程序的功能被分为多个阶段,每个阶段都有一个可用的版本,并且每个版本都包含一些新的功能或改进。这种方式可以帮助开发人员更好地控制开发进度和风险,同时也可以让用户更早地体验到应用程序的部分功能,从而提高用户满意度。

143.说说你对Object.observable的了解?

Object.observable 是 ECMAScript 提案中的一个新特性,它提供了一种机制,可以让对象变成可观察的,即当对象发生变化时,可以自动通知相关的观察者。 具体来说,使用 Object.observable 方法可以将一个普通的对象转换成一个可观察对象,然后可以使用 observable.subscribe() 方法来订阅该对象的变化事件,当对象发生变化时,订阅者会收到通知。 这个特性可以用于实现数据绑定、状态管理等功能,可以让开发者更方便地处理复杂的数据流动和状态变化。不过需要注意的是,该特性目前还处于提案阶段,尚未被正式纳入 ECMAScript 标准,因此在实际开发中需要谨慎使用。

144.说说你对Vue.observable的了解?

Vue.observable,让一个对象变成响应式数据。Vue 内部会用它来处理 data 函数返回的对象返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器

Vue 2.x 中,被传入的对象会直接被 Vue.observable 变更,它和被返回的对象是同一个对象

Vue 3.x 中,则会返回一个可响应的代理,而对源对象直接进行变更仍然是不可响应的

145.Object.observable和Vue.observable的区别

Object.observableVue.observable 都是用于创建可观察对象的函数,但是它们有以下区别:

  1. Object.observable 是 ECMAScript 标准中的一个新特性,而 Vue.observable 是 Vue.js 框架中的一个 API。

  2. Object.observable 可以用于创建任何类型的可观察对象,而 Vue.observable 只能用于创建 Vue 组件内部的可观察对象。

  3. Object.observable 返回的是一个普通的可观察对象,而 Vue.observable 返回的是一个响应式对象,即当对象的属性发生变化时,会自动触发视图更新。

  4. Object.observable 可以使用 Observable.from() 方法将现有对象转换为可观察对象,而 Vue.observable 不支持这种转换方式。

总的来说,Object.observable 更加通用,可以用于创建任何类型的可观察对象,而 Vue.observable 则更加专注于 Vue 组件内部的响应式数据管理。

146.什么是宏任务和微任务

宏任务和微任务是javascript中用于管理异步操作的概念

  • 宏任务是一组需要再javascript主线程中桉顺序执行的操作。常见的宏任务包括setTimeout,setInterval,I/0操作等,宏任务会被放入事件队列中,等待主线程空闲时执行
  • 微任务是在宏任务执行过程中产生的需要立即执行的任务。微任务的优先级比宏任务高,会在当前宏任务执行完毕后立即执行,常见的微任务有promise的回调函数,MutationObserver等
  • 在事件循环中,当一个宏任务执行完毕后,会检查是否有微任务队列,如果有,则依次执行所有的微任务,直到微任务队列清空,然后在执行下一个宏任务。这样循环进行,保证了异步操作的有序性和响应性

总结

  • 宏任务是一组按顺序执行的操作,如 setTimeout、setInterval、I/O 操作等。
  • 微任务是在宏任务执行过程中产生的需要立即执行的任务,如 Promise 的回调函-数、MutationObserver 等。
  • 微任务的优先级高于宏任务,会在当前宏任务执行完毕后立即执行。
  • 事件循环机制确保了宏任务和微任务的执行顺序和协调性。

147.原型的使用场景有哪些?

  1. 对象的继承:通过将对象的原型指向其他的对象,从而集成属性和方法
  2. 属性和方法的共享:对象上定义的属性和方法可以被所有的实例对象共享,减少了内存消耗,提高的代码的执行效率
  3. 原型链的访问和扩展:访问对象的属性或者调用方法时,如果本身没有改属性和方法,就会顺着原型链向上查找,直到找到对应的属性或方法或者查找到原型链的尽头。
  4. 原型的动态性:在运行时可以随时修改原型对象,从而为所有对象实例添加新的属性和方法。这种动态性使得我们可以根据需要灵活地修改和扩展对象的功能。
  5. 创建对象的基础:JavaScript 中的对象实际上就是通过原型来创建的。原型作为对象的蓝图,定义了对象的初始状态和行为,可以通过原型创建出多个相同结构的对象实例。

148.页面上拉加载的实现思路和方法?

  • 监听滚动事件:通过 JavaScript 监听页面的滚动事件,当滚动到特定位置(例如页面底部)时触发加载操作。

  • 计算滚动位置:在滚动事件的回调函数中,可以使用 DOM API 获取当前页面滚动的位置,比如文档总高度、窗口可视区域高度和滚动条的偏移量等。

  • 判断是否达到加载条件:根据计算得到的滚动位置,与页面加载的条件进行判断,例如判断滚动位置是否接近页面底部。

  • 执行加载操作:一旦判断条件成立,就执行加载操作,通常是向服务器发送异步请求获取新数据。可以使用 AJAX、Fetch API 或者其他网络请求库来进行异步请求。

  • 处理加载结果:当异步请求返回数据后,根据返回的数据进行相应的处理,例如将新内容添加到页面中的指定位置或更新页面内容。

  • 更新滚动位置:如果加载的内容影响了滚动位置,需要更新滚动位置以便用户继续上拉加载新内容。可以使用 DOM API 设置滚动条的偏移量或者滚动至特定元素的位置。

149.如何自己手动封装一个Promise?

创建一个构造函数:首先,创建一个名为 MyPromise 的构造函数,并接收一个执行器函数作为参数。执行器函数应该具有两个参数,resolve 和 reject,这些参数是用于在异步操作完成时将 Promise 标记为成功或失败的回调函数。

定义 Promise 的初始状态和结果:在构造函数中,定义 Promise 的初始状态为 pending,并创建两个变量 value 和 reason,用于存储 Promise 的最终结果。

实现 resolve 和 reject 函数:在构造函数中定义 resolve 和 reject 函数,分别负责将 Promise 标记为成功或失败,并保存结果到相应的变量中。

添加 then 方法:在构造函数的原型上添加一个名为 then 的方法,接收两个回调函数作为参数,分别表示在 Promise 成功和失败时要执行的操作。

处理异步操作:在执行器函数中执行异步操作,根据操作的结果调用 resolve 或 reject 函数。

实现链式调用:在 then 方法中,根据当前 Promise 的状态和结果,判断要执行成功回调还是失败回调。如果回调函数返回一个 Promise,可以通过返回新的 Promise 实现链式调用。

class MyPromise {
  constructor(executor) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.value = value;
      }
    };

    const reject = (reason) => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.reason = reason;
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    if (this.status === 'fulfilled') {
      onFulfilled(this.value);
    } else if (this.status === 'rejected') {
      onRejected(this.reason);
    }
  }
}

150.说说你对webpack的理解?webpack的构建流程?

Webpack是一个现代化的JavaScript应用程序的静态模块打包器。它可以将多个JavaScript文件打包成一个或多个文件,以及处理其他类型的文件,如CSS、图片等。
Webpack 的构建流程主要包括以下几个步骤:

  1. 解析配置文件:Webpack 会读取项目中的配置文件,如 webpack.config.js,根据配置文件中的设置来进行构建。

  2. 解析入口文件:Webpack 会根据配置文件中的 entry 配置项,找到项目的入口文件,然后从入口文件开始递归解析所有依赖的模块。

  3. 加载模块:Webpack 会根据模块的类型,使用相应的 loader 对模块进行加载和转换。例如,对于 JavaScript 模块,Webpack 会使用 babel-loader 进行转换,对于 CSS 模块,Webpack 会使用 css-loader 进行加载和转换。

  4. 解析依赖:Webpack 会递归解析模块的依赖关系,直到所有依赖的模块都被加载和转换完成。

  5. 打包输出:Webpack 会根据配置文件中的 output 配置项,将所有模块打包成一个或多个 bundle 文件,并输出到指定的目录中。

  6. 优化打包结果:Webpack 会对打包结果进行优化,例如,使用 Tree Shaking 技术去除未使用的代码,使用 Code Splitting 技术将代码分割成多个 chunk,以提高页面加载速度和性能。

  7. 执行插件:Webpack 会执行配置文件中的插件,例如,使用 UglifyJSPlugin 对代码进行压缩和混淆,使用 HtmlWebpackPlugin 生成 HTML 文件等。

以上就是 Webpack 的构建流程,通过这些步骤,Webpack 可以将项目中的所有模块打包成一个或多个 bundle 文件,并优化打包结果,以提高页面加载速度和性能。

151.webpack的热更新是如何做到的?原理是什么?

  • Webpack的热更新是通过webpack-dev-server实现的。
  • 原理是在webpack打包后的代码中注入一个客户端脚本,该脚本与webpack-dev-server建立WebSocket连接,当代码发生变化时,webpack-dev-server会将变化的模块打包成一个json文件,然后通过WebSocket发送给客户端,客户端再通过HMR(Hot Module Replacement)技术将变化的模块替换掉原来的模块,从而实现热更新。HMR技术是通过在运行时替换模块代码的方式实现的,它可以保留应用程序的状态,避免重新加载整个页面,提高开发效率。

152.说说你对MVVM和渐进式的理解?

MVVM是一种软件架构模式,它将应用程序分为三个部分:模型(Model)、视图(View)和视图模型(ViewModel)。其中,模型表示应用程序的数据和业务逻辑,视图表示用户界面,视图模型则是连接模型和视图的桥梁,负责将模型中的数据转换为视图中的数据,同时也负责将视图中的用户操作转换为模型中的操作。MVVM的优点是能够将应用程序的各个部分分离开来,使得代码更加清晰、易于维护和测试

渐进式是一种开发方式,它强调将应用程序的开发过程分为多个阶段,每个阶段都可以独立完成并且可以逐步完善。渐进式的优点是能够快速迭代,减少开发周期,同时也能够更好地适应需求变化和技术变革。在渐进式开发中,通常会采用敏捷开发、DevOps等技术和方法来支持开发过程。

MVVM和渐进式的结合可以使得应用程序的开发更加高效、灵活和可维护。在渐进式开发中,MVVM可以帮助开发人员更好地分离应用程序的各个部分,使得每个阶段的开发更加清晰和有序。同时,MVVM也可以帮助开发人员更好地实现应用程序的复杂业务逻辑和用户界面,提高开发效率和代码质量。

153.Vue2的响应式原理是什么?如何解决视图更新问题

Vue2的响应式原理是通过数据劫持和发布-订阅模式实现的。

具体来说,当Vue实例化时,会对数据对象进行递归遍历,将每个属性都转换为getter/setter,并且在内部建立一个依赖收集器(Dep)。当数据发生变化时,会触发setter方法,setter方法会通知依赖收集器,依赖收集器会通知所有依赖该数据的Watcher对象进行更新。

Watcher对象是一个观察者,它会在模板中对应一个表达式,当表达式的值发生变化时,Watcher对象会接收到通知,然后执行对应的更新操作,最终更新视图。

为了解决视图更新问题,Vue2采用了异步更新策略。当数据发生变化时,Vue并不会立即更新视图,而是将所有需要更新的Watcher对象放入一个队列中,等到下一个事件循环时再统一更新视图。这样做可以避免频繁的视图更新,提高性能。同时,Vue还提供了nextTick方法,可以在异步更新完成后执行回调函数,以便在更新后进行一些操作。

154.封装一个modal模块态框的思路?

Vue封装一个modal模块态框的思路如下:

  1. 创建一个Modal组件,包含模态框的HTML结构和样式。

  2. 在Modal组件中定义props,用于接收父组件传递的数据,例如:标题、内容、是否显示等。

  3. 在Modal组件中定义data,用于控制模态框的显示和隐藏。

  4. 在Modal组件中定义methods,用于处理模态框的显示和隐藏,例如:点击确认按钮时触发的事件。

  5. 在父组件中引入Modal组件,并传递需要显示的数据。

  6. 在父组件中定义一个方法,用于控制Modal组件的显示和隐藏。

  7. 在父组件中使用v-if或v-show指令,根据需要控制Modal组件的显示和隐藏。

  8. 在父组件中调用Modal组件的方法,实现模态框的显示和隐藏。

  9. 可以根据需要对Modal组件进行扩展,例如:添加动画效果、自定义样式等。

155.什么是高阶组件?如何实现高阶组件

高阶组件(Higher-Order Component,简称HOC)是React中的一种高级技术,它是一个函数,接收一个组件作为参数并返回一个新的组件。HOC可以用于增强组件的功能,例如添加状态、处理逻辑、封装组件等。

HOC的实现方式如下:

function withHOC(WrappedComponent) {
  return class extends React.Component {
    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

上述代码中,withHOC是一个HOC函数,它接收一个组件作为参数WrappedComponent,并返回一个新的组件。新的组件是一个匿名类组件,它继承自React.Component,并在render方法中渲染了WrappedComponent组件,并将所有的props传递给它。

使用HOC可以在不修改原组件的情况下,增强组件的功能。例如,可以使用HOC添加一个loading状态:

function withLoading(WrappedComponent) {
  return class extends React.Component {
    state = { isLoading: false };

    setLoading = (isLoading) => {
      this.setState({ isLoading });
    };

    render() {
      return (
        <WrappedComponent
          {...this.props}
          isLoading={this.state.isLoading}
          setLoading={this.setLoading}
        />
      );
    }
  };
}

上述代码中,withLoading是一个HOC函数,它添加了一个isLoading状态和一个setLoading方法。在渲染新的组件时,将isLoadingsetLoading作为props传递给WrappedComponent组件。这样,WrappedComponent就可以通过调用setLoading方法来设置isLoading状态了。

156.说说你对Vue和React中Hooks的理解?

Vue和React中的Hooks是一种用于函数组件中的特殊函数,它们可以让我们在不使用类组件的情况下,使用状态和其他React或Vue特性。Hooks的出现使得函数组件具有了更多的功能,使得React和Vue的开发更加灵活和高效。

在React中,Hooks是一种让你在函数组件中“钩入”React状态和生命周期方法的方式。最常用的Hooks是useStateuseEffect。useState允许你在函数组件中使用状态,而useEffect允许你在组件渲染时执行副作用操作,例如订阅数据或更新DOM。

在Vue中,Hooks的概念被称为Composition API。Composition API是Vue 3中的新特性,它允许我们在函数组件中使用Vue的响应式数据和生命周期方法。最常用的Composition API是ref和watchEffect。ref允许你在函数组件中使用响应式数据,而watchEffect允许你在响应式数据变化时执行副作用操作。

总的来说,Hooks是一种让我们在函数组件中使用状态和其他特性的方式,它们使得React和Vue的开发更加灵活和高效。

157.Vue项目部署404的问题产生原因是什么?如何解决?

Vue项目部署404的问题可能是由于路由配置不正确或者服务器没有正确地配置导致的。

解决方法如下:

  1. 确认路由配置是否正确,特别是在使用history模式时,需要在服务器上配置重定向规则,将所有的请求都重定向到index.html文件上。

  2. 确认服务器是否正确配置,例如在使用Nginx服务器时,需要在配置文件中添加以下代码:

location / {
  try_files $uri $uri/ /index.html;
}

这样可以将所有的请求都重定向到index.html文件上。

  1. 确认打包后的文件是否正确部署到服务器上,可以通过检查文件路径和文件名是否正确来确认。

  2. 如果以上方法都无法解决问题,可以尝试使用Hash模式来解决,即在路由配置中添加mode: ‘hash’,这样可以避免路由问题导致的404错误。

158.说说webpack中常见的Plugin?解决了什么问题?

  • HtmlWebpackPlugin:将打包后的JS、CSS等文件自动注入到HTML文件中,方便开发者使用。-

  • MiniCssExtractPlugin:将CSS文件从JS文件中分离出来,避免JS文件过大,提高页面加载速度。

  • CleanWebpackPlugin:每次打包前清空输出目录,避免旧文件对新文件的影响。

  • DefinePlugin:定义全局变量,方便在代码中使用。

  • HotModuleReplacementPlugin:热更新插件,可以在不刷新页面的情况下更新代码,提高开发效率。

  • UglifyJsPlugin:压缩JS代码,减小文件体积,提高页面加载速度。

  • CopyWebpackPlugin:将静态资源文件复制到输出目录中,方便开发者使用。

159.Vue实例挂载的过程中发生了什么?

  • 解析模板:Vue会解析模板(template),将其转换为虚拟DOM(Virtual DOM)的形式。解析过程包括编译模板、生成渲染函数等操作。

  • 创建虚拟DOM:根据解析得到的虚拟DOM结构,创建一颗以Vue实例根节点为根的虚拟DOM树。

  • 数据响应式处理:在Vue实例挂载之前,Vue会对数据进行响应式处理。这包括对data选项中的数据进行劫持和监听,建立起数据和视图之间的关联关系。

  • 挂载虚拟DOM:将虚拟DOM挂载到页面中的目标元素上。Vue会找到挂载点(el选项指定的元素),并将虚拟DOM渲染成真实的DOM,并替换挂载点元素。

  • 执行生命周期钩子函数:Vue提供了一组生命周期钩子函数,用于在不同阶段执行特定的逻辑。在挂载过程中,会触发beforeCreate、created、beforeMount和mounted等生命周期钩子函数。

  • 监听数据变化和依赖收集:当数据发生变化时,Vue会通过已建立的响应式机制检测到变化,并触发重新渲染。同时,Vue会进行依赖收集,记录哪些地方使用了该数据,以便在数据变化时更新相关的视图。

通过上述步骤,Vue实例就成功地完成了挂载过程,并将应用的数据和视图建立了关联。这使得当数据变化时,Vue能够自动侦测到变化并更新视图,以实现数据驱动的页面渲染。

160.如何实现一个两栏布局,右侧自适应?三栏布局中间自适应?

两栏布局,右侧自适应: 可以使用CSS中的浮动(float)和盒模型来实现两栏布局,其中右侧栏设置为自适应宽度。
HTML结构:

html

<div class="container">
  <div class="left"></div>
  <div class="right"></div>
</div>

CSS样式:

.container {
  overflow: hidden; /* 清除浮动 */
}

.left {
  float: left;
  width: 200px; /* 左侧固定宽度 */
}

.right {
  margin-left: 220px; /* 左侧宽度 + 左右间距 */
}

通过将左侧栏设置为浮动,并指定固定宽度,右侧栏设置一个左外边距等于左侧宽度加上左右间距,从而实现右侧栏自适应宽度。

三栏布局,中间自适应: 同样可以利用浮动和盒模型来实现三栏布局,其中中间列设置为自适应宽度。
HTML结构:

html

<div class="container">
  <div class="left"></div>
  <div class="center"></div>
  <div class="right"></div>
</div>

CSS样式:

.container {
  overflow: hidden; /* 清除浮动 */
}

.left, .center, .right {
  float: left;
}

.left {
  width: 200px; /* 左侧固定宽度 */
}

.right {
  width: 300px; /* 右侧固定宽度 */
}

.center {
  margin-left: 220px; /* 左侧宽度 + 左右间距 */
  margin-right: 320px; /* 右侧宽度 + 左右间距 */
}

通过将左侧列和右侧列设置为浮动,并分别指定固定宽度,中间列设置左外边距和右外边距,从而实现中间列自适应宽度。中间列的宽度计算方式为:容器宽度 - 左侧宽度 - 右侧宽度 - 左右间距。

这样,无论是两栏布局还是三栏布局,都可以实现一列固定宽度.

161.Css如何实现一个三角形,原理是什么,最少两种方法?

在 CSS 中,可以使用多种方式来实现一个三角形效果。以下是两种常见的方式:

  1. 方式一
  • 使用边框属性: 通过设置一个元素的宽度和高度为 0,然后利用边框属性来创建三角形的效果。可以调整边框的颜色、宽度以及边框的显示方式来改变三角形的样式。
  • 原理:通过设置元素的边框宽度和样式来创建三角形,利用边框的斜边长为 0 的特性。
.triangle {
  width: 0;
  height: 0;
  border-left: 50px solid transparent;
  border-right: 50px solid transparent;
  border-bottom: 100px solid red;
}

这段代码会创建一个红色底边为 100px 的等腰三角形。

  1. 方式二
  • 使用伪元素(::before 或 ::after): 通过在元素的 ::before 或 ::after 伪元素上应用旋转和背景属性,可以实现三角形效果。
  • 原理:通过伪元素在元素前面或后面创建一个无内容的矩形,并将其旋转成三角形的形状,再调整背景颜色和位置以呈现出真实的三角形效果。
.triangle {
  width: 0;
  height: 0;
  position: relative;
}

.triangle::before {
  content: "";
  width: 0;
  height: 0;
  position: absolute;
  top: 0;
  left: 0;
  border-left: 50px solid transparent;
  border-right: 50px solid transparent;
  border-bottom: 100px solid green;
}

这段代码会创建一个绿色底边为 100px 的等腰三角形。

162.说说你对Vue中diff算法的理解,和react的有什么区别?

Vue 和 React 都是流行的前端框架,它们在虚拟 DOM 的 diff 算法上有一些区别,我将简要介绍一下。

Vue 中的 diff 算法:

Vue 使用的是双向的 diff 算法,即会同时对新旧虚拟 DOM 进行正向和反向遍历。Vue 首先将新旧 VNode 树的根节点进行比较,然后根据节点类型的不同采取不同的策略:

如果新旧节点是相同的文本节点,直接更新文本内容。
如果新旧节点是相同的元素节点(标签名和 key 相同),则对比和更新该元素节点的属性,并递归对比和更新子节点。
如果新旧节点不同,且与旧节点具有相同的父节点,则根据新节点的类型创建一个新的 DOM 元素,并将其替换旧节点。
这个算法会尽可能地复用已存在的 DOM 元素,减少 DOM 操作,提高性能。

React 中的 diff 算法:

React 使用的是单向的 diff 算法,在进行虚拟 DOM 比较时只会从上到下依次遍历,不会进行反向遍历。React 通过分层比较的方式来提高效率,将树的比对过程拆分成三个阶段:

Diffing:对比新旧节点的类型及属性,并更新差异部分。
Reconciliation:递归对比和更新子节点。
Commit:将更新后的虚拟 DOM 更新到真实的 DOM 上。
React 会先对根节点进行比较,然后通过递归比较和更新子节点。当发现某个节点需要更新时,React 会生成一个描述该变更的操作集合,然后统一将这些操作应用到 DOM 中,以减少真实 DOM 操作的次数,提高性能。

区别总结:

Vue 和 React 的 diff 算法在实现上有一些区别。Vue 使用双向的 diff 算法,尽可能复用已存在的 DOM 元素;而 React 使用单向的 diff 算法,通过分层比较和批量更新来优化性能。两种算法都旨在降低真实 DOM 操作的频率,提升前端应用的性能和用户体验。

163.前端跨域解决方案及其实现方式?

前端跨域是指在浏览器中,当一个网页的 JavaScript 代码尝试访问不同源(协议、域名、端口)的服务器资源时,由于浏览器的同源策略(Same-Origin Policy),会限制这种跨域的行为。为了解决跨域问题,可以采用以下几种方案:

  1. JSONP(JSON with Padding):JSONP 是一种利用 <script> 标签没有跨域限制的特性来实现跨域通信的方法。通过在页面中动态创建<script>标签,并设置其 src 属性为目标接口地址,同时传递一个回调函数作为参数,使得服务端返回的数据作为回调函数的参数执行。但是 JSONP 只支持 GET 请求,且只能用于请求接口返回 JSON 数据。

  2. CORS(Cross-Origin Resource Sharing):CORS 是一种现代浏览器支持的跨域解决方案,通过在服务器端设置响应头来控制跨域访问。具体地,服务器通过响应头中的 Access-Control-Allow-Origin 字段来告知浏览器允许的跨域源,还可以设置其他相关字段如 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers 来进一步控制请求的方法和头信息。前端无需特殊处理,只需确保请求是异步的。

  3. 代理服务器:可以通过在同源的服务器上设置代理服务器来转发请求,此方式适用于前后端分离的情况。前端将请求发送给同源服务器,同源服务器再将请求转发到目标接口并将响应返回给前端。这样前端就绕过了浏览器的跨域限制。

  4. WebSocket:WebSocket 是一种基于 TCP 的全双工通信协议,它允许客户端与服务器建立长连接,并进行双向数据传输。WebSocket 不受同源策略的限制,可以跨域通信。

  5. 通过修改服务器配置:如果是自己搭建的服务器,可以在服务器端修改配置文件,增加相应的跨域配置,比如设置响应头,允许特定的跨域来源。

需要注意的是,以上方案都有各自的适用场景和局限性,在选择使用时需要根据具体情况进行判断。另外,还要注意确保跨域请求的安全性和合法性,避免被滥用或攻击。

164.JavaScript如何实现函数缓存?函数缓存有哪些应用场景?

在JavaScript中,可以通过函数缓存来提高函数的性能和效率。函数缓存指的是将函数的输入参数与对应的输出结果进行缓存,以避免重复计算相同的输入参数。

以下是一种简单的实现函数缓存的方法:

function memoize(func) {
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache[key]) {
      return cache[key];
    } else {
      const result = func.apply(this, args);
      cache[key] = result;
      return result;
    }
  };
}

此处的memoize函数接受一个函数作为参数,并返回一个新的函数。新的函数会检查是否已经有缓存的结果,如果有,则直接返回缓存的结果;如果没有,则调用原始函数计算结果,并将结果缓存起来。

函数缓存适用于以下场景:

  1. 频繁调用耗时的计算函数:当需要多次调用同一个耗时的计算函数时,可使用函数缓存来避免重复计算,提高性能。
  2. 频繁请求远程数据:当需要频繁地请求远程数据时,可使用函数缓存来避免重复的网络请求,减轻服务器负担和优化用户体验。
  3. 频繁操作非纯函数:对于具有副作用或状态变化的函数,通过函数缓存可以避免重复执行这些副作用或状态变化,确保结果的一致性。
  4. 需要缓存中间结果的递归函数:在递归函数中,如果存在重复的计算过程,可使用函数缓存来避免重复计算,提高效率。

函数缓存可以有效地减少重复计算和提升函数执行效率,但需要注意缓存结果的内存消耗和缓存清理的时机,以避免内存泄漏和不必要的缓存。

164.什么是单点登录?如何实现?

单点登录(Single Sign-On,简称SSO)是一种身份认证的机制,它允许用户使用一组凭据(例如用户名和密码)进行一次登录,即可在多个关联的应用系统中无需再次输入凭据而实现自动登录。

实现单点登录通常涉及以下主要组件:

  1. 身份提供者(Identity Provider,简称IdP):负责管理用户的身份认证信息,并向其他关联的应用系统提供认证服务。常见的IdP有OAuth 2.0、OpenID Connect、SAML等。

  2. 服务提供者(Service Provider,简称SP):为用户提供具体的应用服务,在单点登录系统中,SP将依赖于IdP来进行用户身份认证。

实现单点登录的步骤如下:

  1. 用户访问Service Provider(SP)中的某个应用,并尚未登录。
  2. SP检测到用户未登录,将用户重定向到统一登录页面或请求认证接口。
  3. 用户通过提供用户名和密码等凭证,提交给IdP进行身份认证。
  4. IdP验证用户的凭证信息,如果认证成功,生成一个身份令牌(Token)。
  5. IdP将生成的身份令牌返回给SP,通常通过重定向或直接的响应方式。
  6. SP接收到身份令牌后,验证其有效性,如果有效则表示用户已经登录。
  7. 用户再次请求其他关联的应用时,SP会携带身份令牌前往IdP验证。
  8. IdP验证身份令牌,并返回验证结果给SP,以便继续提供服务。

通过这种方式,用户只需要在第一次登录时进行认证,之后可以无需重复登录即可访问其他关联的应用系统。这样不仅提高了用户体验,也简化了身份管理和权限控制的复杂性。

需要注意的是,在实现单点登录时,要确保通信过程中的安全性,例如使用HTTPS协议进行数据传输,对敏感信息进行适当加密等措施,以保护用户的身份和数据安全。

165.如何借助webpack来优化前端性能?

  1. 代码压缩和混淆:通过Webpack的UglifyJsPlugin或TerserPlugin等插件,可以对JavaScript代码进行压缩和混淆,去除空格、注释以及无效的代码,减小文件体积,提高加载速度。

  2. 按需加载(Code Splitting):通过Webpack的SplitChunksPlugin或动态导入等功能,将代码拆分为多个小片段,按需加载,实现按页面或功能模块划分的资源加载,减少首次加载时间,提高性能。

  3. 懒加载和预加载:使用Webpack的import()函数或React.lazy等方法实现懒加载,即在需要时再加载特定组件或模块,延迟网页中不必要的加载。另外,也可以使用Webpack的Prefetch或Preload功能,在空闲时间预先加载将来可能需要的资源,提前获取关键资源,增加用户感知的加载速度。

  4. 文件缓存和指纹:通过Webpack的output属性中的chunkhash或contenthash等机制,为生成文件添加唯一的标识符,实现静态资源的长期缓存,并将更新后的代码部署到用户端的浏览器缓存中,减少重复请求和传输数据,提高性能。

  5. Tree Shaking:通过Webpack的Tree Shaking机制,可以自动消除项目中未使用的代码(例如未被引用的模块或导出的函数),减少打包后的文件体积。

  6. 图片优化:通过Webpack的url-loader或file-loader等插件,对图片进行压缩、转换为Base64编码、按需加载等操作,减小图片文件大小,提高加载速度。

  7. 缓存组件:对于经常变化的模块,可以使用Webpack的cache-loader或hard-source-webpack-plugin等插件,在开发环境或构建过程中缓存已加载的模块,加快二次构建速度。

166.说说地址栏输入URL敲下回车后发生了什么?

  1. 浏览器的地址栏输入URL并按下回车。
  2. 浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
  3. DNS解析URL对应的IP。
  4. 根据IP建立TCP连接(三次握手)。
  5. 发起HTTP请求。
  6. 服务器处理请求,浏览器接收HTTP响应。
  7. 渲染页面,构建DOM树。
  8. 关闭TCP连接(四次挥手)。

167.如何判断一个元素是否在可视区中?

要判断一个元素是否在可视区中,可以使用以下方法:

  1. 使用getBoundingClientRect()方法:这是一种常见的方式,通过该方法可以获取到元素相对于视口的位置信息。根据元素的top、right、bottom和left属性,可以判断元素是否在可视区中。
function isInViewport(element) {
  const rect = element.getBoundingClientRect();
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
    rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  );
}
  1. 使用Intersection Observer API:该API提供了一种异步观察目标元素与其祖先元素或顶级文档视窗交叉状态的方法。可以传入回调函数来处理目标元素进入或离开可视区的情况。
function isInViewport(element) {
  const options = {
    root: null,
    rootMargin: '0px',
    threshold: 0
  };
  
  const observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      if (entry.intersectionRatio > 0) {
        // 元素部分或完全在可视区中
        console.log('元素在可视区中');
      } else {
        // 元素完全不在可视区中
        console.log('元素不在可视区中');
      }
    });
  }, options);
  
  observer.observe(element);
}

这些方法可以判断一个元素是否在可视区中,并可以根据需要执行相应的操作。需要注意的是,第一种方法在某些特殊情况下可能不准确,例如元素被部分遮挡或位于固定定位的容器内。使用Intersection Observer API可以更加精确地判断元素是否在可视区中,并且对性能更友好。

168.React router有几种模式?实现原理?

React Router 有两种主要的路由模式:HashRouter 和 BrowserRouter。

  1. HashRouter:
  • 使用 URL 中的哈希(#)来模拟路由,将路由信息存储在 URL 的片段标识符(即哈希)中。
  • 哈希是 URL 的一部分,但不会被发送到服务器端,因此不会触发页面刷新。
  • 在用户访问应用程序时,浏览器会根据哈希变化来匹配对应的路由并渲染相应的组件。
  1. BrowserRouter:
  • 使用 HTML5 的 History API 来实现路由,修改浏览器的历史记录以实现页面跳转而无需刷新整个页面。
  • 通过监听浏览器的变化事件(如 popstate),可以捕获到 URL 的变化,并匹配对应的路由进行组件渲染。
  • BrowserRouter 在现代浏览器中提供了更友好的 URL 显示,没有哈希字符,看起来更加干净。

React Router 实现原理:
React Router 是通过 React 提供的上下文(Context)来传递路由信息和匹配状态。它利用 React 提供的生命周期方法和虚拟 DOM 操作,实现了路由的渲染和更新。

  1. Router 组件:BrowserRouter 和 HashRouter 组件作为顶层容器,通过 Context 提供路由信息和匹配状态给子组件。

  2. Route 组件:根据路由路径进行组件匹配和渲染,并通过 Context 将匹配状态提供给子组件进行条件渲染。

  3. Link/NavLink 组件:生成包含正确 URL 的链接,与 Router 配合使用,点击链接时触发路由跳转。

  4. Switch 组件:用于包裹多个 Route 组件,只渲染第一个匹配成功的 Route,并避免重复渲染。

  5. useHistory 和 useLocation 钩子:提供了在组件中访问历史记录和当前位置信息的功能。

总结:
React Router 提供了 HashRouter 和 BrowserRouter 两种主要的路由模式。HashRouter 使用 URL 的哈希来模拟路由,BrowserRouter 使用 HTML5 的 History API 来实现真实路由。React Router 利用 React 的上下文和生命周期等特性,通过组件间的嵌套和匹配规则来实现路由的渲染和更新。

169.说你对代理模式的理解?应用场景?

代理模式是一种结构型设计模式,它允许通过创建代理对象来控制对另一个对象的访问。代理对象与目标对象之间存在一个接口,代理对象可以在不改变目标对象的前提下,对其进行增强或附加额外的功能。

代理模式常见的应用场景包括:

  1. 远程代理:当目标对象位于远程服务器上时,可以使用代理模式在客户端和远程服务器之间建立代理对象来进行通信,隐藏底层的网络细节,提高通信的效率和安全性。

  2. 虚拟代理:当创建目标对象的开销较大时,可以使用代理模式延迟目标对象的实例化,通过代理对象提供一个占位符,当真正需要访问目标对象时才进行实例化,以节省系统资源。

  3. 安全代理:代理模式可以在访问目标对象之前进行权限验证和身份验证,确保只有具备特定权限的客户端可以访问目标对象。

  4. 缓存代理:代理模式可以在访问目标对象之前检查是否已经有缓存的结果,如果有,则直接返回缓存的结果,避免重复计算或请求。

  5. 日志记录代理:代理模式可以在访问目标对象时记录日志,用于调试和审计目的。

总之,代理模式可以在不改变目标对象的情况下对其进行控制和增强,并提供了更灵活、安全、高效的对象访问方式。

170.让chrome支持小于12px的文字方式有哪些?区别?

在 Chrome 浏览器中,默认情况下是不支持小于 12px 的字体大小的。这是因为浏览器引擎对于小于 12px 的字体大小应用了最小字体限制。

然而,可以通过一些技术手段来绕过这个限制,实现在 Chrome 中显示小于 12px 的文字。以下是两种常见的方式:

  1. 使用 CSS 属性 transform 缩放:可以将要显示的文本包含在一个容器元素中,然后通过设置容器元素的 transform: scale() 属性对字体进行缩放,从而使其看起来小于 12px。例如:
.container {
  transform: scale(0.8);
}
  1. 使用 Web 字体:可以选择一些特殊的 Web 字体,这些字体在小尺寸下仍能保持良好的可读性,例如像素字体(Pixel Fonts)。使用这些字体可以实现小于 12px 的文字效果。

需要注意的是,使用上述方法显示小于 12px 的文字可能导致一些问题,如可读性下降、锯齿边缘等。同时,这些方法可能依赖于特定的浏览器行为或技巧,不同浏览器的支持程度和效果可能会有差异。因此,在使用这些方法时应该进行充分测试并权衡相关的因素。

另外,也推荐在设计中尽量避免过小的字号,以确保较好的可读性和用户体验。

171.说说你对尾递归的理解,有哪些应用场景?

尾递归是指在函数的最后一步操作中调用自身的递归形式。与一般递归不同,尾递归的递归调用是整个函数体中的最后一条语句,不存在其他表达式需要在递归返回之后执行。

尾递归的特点是每次递归都只保留一个栈帧,不会导致栈的无限增长,从而避免了栈溢出的风险。由于只保留最后一次调用的栈帧,因此尾递归具有优化的潜力,可以转化为循环来降低空间复杂度。

尾递归的应用场景包括但不限于以下情况:

  1. 阶乘计算:计算阶乘的过程可以使用尾递归来实现。
  2. 斐波那契数列:计算斐波那契数列的过程可以使用尾递归进行优化。
  3. 树或图的遍历:深度优先搜索(DFS)和广度优先搜索(BFS)等在树或图的遍历过程中,可以使用尾递归对子节点进行递归调用。
  4. 动态规划:某些动态规划问题的状态转移方程可以使用尾递归来实现,减少递归调用的开销。

需要注意的是,在一些编程语言中,并不会对尾递归进行自动优化,可能仍然存在栈溢出的风险。在使用尾递归时,可以采取手动优化的方法,将递归转化为迭代循环,以降低空间复杂度。

172.说说JavaScript数字精度丢失的问题,如何解决?

在JavaScript中,存在数字精度丢失的问题。这是由于JavaScript使用IEEE 754标准的双精度浮点数表示数字,而不是采用精确的十进制表示。

数字精度丢失的问题主要体现在进行浮点数计算时,结果可能出现舍入误差或精度损失。例如:

0.1 + 0.2 // 返回 0.30000000000000004,而非期望的 0.3

为了解决 JavaScript 数字精度丢失的问题,可以采取以下方法:

  1. 使用整数进行计算:将浮点数转换为整数进行计算,最后再将结果转回浮点数。通过操作整数可以避免浮点数的舍入误差。

  2. 使用专门的数值计算库:一些第三方库(如 BigNumber.js)提供了高精度计算的支持,可以处理大数和小数的精确计算。

  3. 使用toFixed()方法:toFixed() 方法可以将浮点数四舍五入为指定小数位数的字符串表示。但需要注意,它返回的是字符串类型而非数字类型。

(0.1 + 0.2).toFixed(1) // 返回 "0.3"
  1. 注意比较操作符的使用:由于浮点数比较可能存在精度问题,应尽量避免直接使用等于(==)或不等于(!=)来比较浮点数,而是使用范围判断或误差允许的方式进行比较。
Math.abs(0.1 + 0.2 - 0.3) < 0.0001 // 使用误差允许的方式判断两个浮点数是否近似相等

通过采用以上方法,可以在一定程度上避免 JavaScript 数字精度丢失问题,提高计算结果的准确性和可靠性。

173.说说git中head、工作树和索引之间的区别?

在Git中,有三个关键概念:HEAD、工作树(Working Tree)和索引(Index)。

  1. HEAD:
  • HEAD是指向当前所在分支最新提交的指针。它始终指向最新的提交,也可以看作当前分支的别名。通过HEAD,可以知道当前工作目录所在的分支以及最新提交的状态。
  • HEAD还可以通过切换分支或进行提交来改变其指向。
  1. 工作树(Working Tree):
  • 工作树是指存放Git仓库文件副本的目录,在该目录下进行修改、编辑、添加等操作。它是我们直接操作的区域,也就是我们通常说的"Working Directory"。
  • 对工作树内的文件的修改可以体现在工作树的状态上,但并不会立即影响到版本库的内容。
  1. 索引(Index):
  • 索引也被称为暂存区(Staging Area),它是位于.git目录下的一个文件,用于记录工作树中的文件与仓库历史之间的差异。它相当于一个缓冲区,保存了将要提交的文件的快照。
  • 在执行git commit命令之前,需将修改后的文件添加到索引中,将其暂存。待确认无误后,通过提交生成新的版本。

简而言之,它们之间的区别如下:

  • HEAD指向当前分支或提交,表示当前所在的位置。
  • 工作树是我们直接操作的目录,用来修改文件内容。
  • 索引是暂存区,记录了即将提交的文件的快照。

通过HEAD来管理分支和版本切换,工作树用于进行代码的编辑和修改,而索引则起到了暂存和准备提交的作用。它们三者之间的关系构成了Git版本控制系统的核心机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值