2023年(js,css3,es6,Vue,react)面试常问面试题总结

1.vue和react的区别是什么?

  1. 模板语言:

Vue使用基于HTML的模板语言,允许您在HTML中直接编写Vue代码,并且可以轻松地与现有的HTML、CSS和JavaScript代码集成。React没有自己的模板语言,它使用JavaScript的JSX语法来描述UI组件。

  1. 数据绑定:

Vue使用双向数据绑定,这意味着当数据发生变化时,视图会自动更新。React则使用单向数据流,只能从父组件向子组件传递数据,不能在组件之间共享数据。

  1. 组件:

Vue和React都使用组件来构建复杂的UI。Vue组件的结构更加简单明了,因为它将JavaScript和CSS代码封装在一个文件中。React则将这些代码分开管理,需要在不同的文件中进行编写和维护。

  1. 性能:

Vue和React都是非常快速的,但Vue在性能方面更加强大,因为它使用模板编译来提高渲染速度。React使用虚拟DOM来优化性能。

2.vue diff算法,react diff算法?

Vue 和 React 都使用虚拟 DOM 和 diff 算法来实现高效的 UI 渲染。

Vue 的 diff 算法:

Vue 的 diff 算法被称为 "渐进式的近似算法"。它首先将新的虚拟 DOM 树和旧的虚拟 DOM 树进行比较,然后只对差异进行更新。它不会直接操作真实的 DOM,而是将更新操作放在一个队列中,并异步执行队列中的所有更新操作。

Vue 的 diff 算法主要有以下几个步骤:

  1. 通过深度优先遍历,比较新旧虚拟 DOM 树的节点,找到需要更新的节点。
  2. 对需要更新的节点进行更深层次的比较,找到具体的差异。
  3. 根据差异类型,进行相应的更新操作。例如,添加、移动或删除节点。
  4. 对于子节点的比较,递归地执行上述步骤。

React 的 diff 算法:

React 的 diff 算法也被称为 "双指针算法" 或 "Fiber 算法"。React 的 diff 算法将新的虚拟 DOM 树和旧的虚拟 DOM 树进行比较,然后只对差异进行更新。与 Vue 不同的是,React 直接操作真实的 DOM,而不是将更新操作放在队列中异步执行。

React 的 diff 算法主要有以下几个步骤:

  1. 对比新旧虚拟 DOM 树的根节点,找到需要更新的节点。
  2. 根据节点类型和 key 属性进行比较,找到具体的差异。
  3. 根据差异类型,进行相应的更新操作。例如,添加、移动或删除节点。
  4. 递归地执行上述步骤,直到完成整个虚拟 DOM 树的比较。

3.vue虚拟dom,react虚拟dom?

Vue 的虚拟 DOM:

Vue 的虚拟 DOM 是基于 渐进式的近似算法 实现的。每个 Vue 组件都有自己的虚拟 DOM 树,当组件的状态发生变化时,Vue 会使用新的状态生成新的虚拟 DOM 树,然后使用 diff 算法比较新旧虚拟 DOM 树的差异,最终只更新需要更新的部分。在更新虚拟 DOM 树时,Vue 会将所有操作都放在一个队列中,然后异步执行队列中的所有更新操作,以提高性能。

React 的虚拟 DOM:

React 的虚拟 DOM 是基于 Fiber 实现的。每个 React 组件也都有自己的虚拟 DOM 树,当组件的状态发生变化时,React 会使用新的状态生成新的虚拟 DOM 树,然后使用 diff 算法比较新旧虚拟 DOM 树的差异,最终只更新需要更新的部分。在更新虚拟 DOM 树时,React 会直接操作真实 DOM,而不是将更新操作放在队列中异步执行。为了避免阻塞 UI 线程,React 使用 Fiber 架构实现了异步渲染,并提供了一种优先级机制,以确保重要的更新优先被执行。

4.怎么实现深浅拷贝?

浅拷贝:浅拷贝只复制对象或数组的第一层内容,而不复制嵌套的子对象或数组。

实现浅拷贝的方法:

  1. 使用 Object.assign() 方法
const newObj = Object.assign({}, obj);
const newArr = Object.assign([], arr);
  1. 使用展开运算符(spread operator)

深拷贝:深拷贝会复制对象或数组的所有层级内容,包括嵌套的子对象或数组。

实现深拷贝的方法:

  1. 递归实现
function deepClone(obj) {
  // 首先判断拷贝的数据类型
  if (typeof obj !== 'object' || obj === null) {
    return obj; // 如果是基本数据类型或null,直接返回
  }

  // 创建一个新的目标对象或数组
  const newObj = Array.isArray(obj) ? [] : {};

  // 递归拷贝对象或数组的属性和元素
  for (let key in obj) {
    newObj[key] = deepClone(obj[key]);
  }

  return newObj;
}
  1. 使用 JSON 对象的 stringify() 和 parse() 方法

但是,需要注意的是,这种方法无法拷贝函数和循环引用的对象,因为 JSON 不支持这些数据类型。

5.es6新特性有哪些?

  1. let 和 const 关键字:用于声明块级作用域变量和常量。
  2. 箭头函数:使用箭头(=>)语法定义函数,可以更简洁地写出函数表达式和匿名函数。
  3. 模板字符串:使用反引号(`)包裹的字符串,可以包含变量和表达式,可以在字符串中换行,提高可读性和代码编写效率。
  4. 解构赋值:可以从数组和对象中解构出变量,使代码更简洁易懂。
  5. 默认参数:函数可以设置默认参数值,当函数调用时不传递参数时,将使用默认参数值。
  6. 展开运算符:使用三个点(...)运算符可以将数组或对象中的元素展开为单独的值。
  7. class 和继承:使用 class 和 extends 关键字定义类和实现继承。
  8. Promise:Promise 是一种处理异步操作的方法,可以更好地处理回调地狱和错误处理。
  9. 模块化:ES6 引入了模块化的语法,使用 import 和 export 关键字可以将代码模块化,提高代码的可维护性和可重用性。
  10. Symbol:Symbol 是一种新的原始数据类型,用于创建唯一的标识符,可以用于对象属性的命名。
  11. Map 和 Set:ES6 引入了 Map 和 Set 数据结构,分别用于存储键值对和一组唯一的值,提供了更灵活的数据操作方式。
  12. Generator:Generator 是一种特殊的函数,可以在执行过程中暂停和恢复,可以用于异步编程和迭代器的实现。

还有一些其他的特性,如 Proxy、Reflect、for...of 循环、字符串和数字的扩展方法等,这些特性一起构成了 ES6 的新特性。

6.async,await和promise有什么区别?

Promise、async/await 是两种处理异步操作的方式,它们与 JavaScript 的回调函数相比,可以更好地处理异步代码,并使其更加易于理解和维护。

Promise 是一种异步操作的表示方法,它可以在异步操作完成后,使用 resolve() 和 reject() 方法返回结果或错误。在使用 Promise 时,可以通过 then() 方法或 catch() 方法处理异步操作的结果或错误。

async/await 是基于 Promise 的语法糖,它让异步代码看起来更像同步代码,使其更易于理解和维护。使用 async/await 时,可以使用 async 关键字定义一个异步函数,然后在函数中使用 await 关键字等待异步操作的结果。在等待异步操作的过程中,async 函数会挂起并等待异步操作完成后再继续执行。在 async 函数中使用 try/catch 语句可以捕获异步操作的错误。

以下是它们之间的区别:

  1. 语法:使用 Promise 时需要创建一个 Promise 实例,并使用 then() 和 catch() 方法处理异步操作的结果或错误;使用 async/await 时,可以使用 async 关键字定义一个异步函数,然后使用 await 关键字等待异步操作的结果。
  2. 错误处理:Promise 使用 then() 方法和 catch() 方法处理异步操作的结果和错误;async/await 使用 try/catch 语句处理异步操作的错误。
  3. 可读性:使用 async/await 可以让异步代码看起来更像同步代码,易于理解和维护;Promise 的语法相对于回调函数来说更加简洁明了,但仍然需要使用 then() 和 catch() 方法。
  4. 功能:Promise 可以链式调用多个异步操作,可以使用 Promise.all() 和 Promise.race() 方法处理多个异步操作的结果;async/await 不能直接链式调用多个异步操作,但可以在 async 函数中调用其他的异步函数。

7.箭头函数在那些场景不能使用?

  1. 作为构造函数:箭头函数没有自己的 this 值,也没有 prototype 属性,因此不能被用作构造函数。
  2. 对象方法:如果要将箭头函数作为对象的方法使用,箭头函数中的 this 值会被绑定到当前作用域中的 this 值,而不是对象本身。
  3. 回调函数中的 this:如果箭头函数作为回调函数使用,箭头函数中的 this 值会被绑定到当前作用域中的 this 值,而不是回调函数所在的对象。
  4. 函数体内使用 arguments 对象:箭头函数没有自己的 arguments 对象,如果需要使用 arguments 对象,可以使用 rest 参数代替。
  5. 事件处理函数:如果将箭头函数作为事件处理函数使用,箭头函数中的 this 值会被绑定到当前作用域中的 this 值,而不是触发事件的元素。

8.有哪些请求方式?

  1. GET:获取资源。使用 GET 请求时,请求参数会被附加到 URL 后面,因此可以被浏览器缓存。GET 请求通常用于获取数据,不应该用于修改数据。
  2. POST:提交数据。使用 POST 请求时,请求参数会被包含在请求体中,因此不会被浏览器缓存。POST 请求通常用于提交数据,例如表单数据或者 JSON 数据。
  3. PUT:更新资源。使用 PUT 请求时,请求参数会被包含在请求体中,用于更新服务器上的资源。
  4. DELETE:删除资源。使用 DELETE 请求时,请求参数会被包含在 URL 中,用于删除服务器上的资源。
  5. HEAD:获取资源头部信息。与 GET 请求类似,但是只获取资源头部信息,不返回实际的资源内容。
  6. OPTIONS:获取资源支持的请求方法。用于查询服务器支持的请求方式,通常在跨域请求时使用。
  7. PATCH:部分更新资源。与 PUT 请求类似,但是只更新部分资源内容。

9.get和post请求有什么不同

  1. 使用方式:GET 请求和 POST 请求的使用方式不同。GET 请求通常用于从服务器获取数据,请求参数会被附加到 URL 的末尾,以 ? 开头,多个参数之间使用 & 连接。POST 请求通常用于向服务器提交数据,请求参数会被包含在请求体中,不会出现在 URL 中。
  2. 请求语义:GET 请求和 POST 请求的请求语义不同。GET 请求表示客户端希望从服务器获取资源,请求参数通常用于限定获取的资源范围或者提供查询条件。POST 请求表示客户端希望向服务器提交数据,请求参数通常用于传递提交的数据。
  3. 安全性:GET 请求和 POST 请求的安全性不同。GET 请求不具有安全性,因为请求参数暴露在 URL 中,容易被其他人窃取和篡改。POST 请求具有一定的安全性,因为请求参数不会被暴露在 URL 中,而是包含在请求体中。
  4. 请求大小:GET 请求和 POST 请求的请求大小有限制。GET 请求对 URL 的长度有限制,因此请求参数的大小也受到限制。POST 请求对请求体的大小有限制,但是相对于 GET 请求而言,请求参数可以更大。

10.什么是服务端渲染?

服务端渲染(SSR)是一种将服务器端的数据和页面模板组合成完整的 HTML 页面并将其发送到客户端的技术。相比于传统的客户端渲染)技术,服务端渲染可以提升首屏渲染速度、SEO 友好、更利于移动设备访问等优点。

服务端渲染的基本思路是,当用户发起请求时,服务器端根据请求的 URL 路径和参数等信息获取到所需的数据,并使用服务器端的模板引擎将数据和 HTML 模板进行组合,生成完整的 HTML 页面,并将其发送到客户端。客户端收到 HTML 页面后,直接进行展示,无需再进行数据获取和页面渲染等操作,从而提高了页面渲染的速度和性能。

11.节流防抖?

  1. 节流:指定时间间隔内只执行一次函数,多次调用函数会在时间间隔内合并为一次调用,适用于需要频繁触发的事件,例如页面滚动、鼠标移动、窗口大小变化等。
  2. 防抖:等待指定时间间隔后执行函数,如果在等待时间间隔内再次触发事件,则重新计时,适用于用户输入、搜索框输入等场景,可以避免频繁的 API 请求。

下面分别介绍一下节流和防抖的实现方法:

  1. 节流实现方法:
function throttle(func, wait) {
  let timer = null;
  return function() {
    const context = this;
    const args = arguments;
    if (!timer) {
      timer = setTimeout(function() {
        func.apply(context, args);
        timer = null;
      }, wait);
    }
  }
}
  1. 防抖实现方法:
function debounce(func, delay) {
  let timerId;

  return function(...args) {
    clearTimeout(timerId);

    timerId = setTimeout(() => {
      func.apply(this, args);
    }, delay);
  };
}

12.语义化标签有哪些?

  1. :表示文档的页眉,通常包含网站的标题、导航栏等内容。
  2. :表示文档的主要内容部分,通常包含页面的核心内容。
  3. :表示一个独立的、完整的内容单元,通常包含一篇文章、一段新闻等内容。
  4. :表示文档中的一个区块,通常包含一组相关的内容。
  5. :表示文档的页脚,通常包含版权信息、联系方式等内容。
  6. ~

    :表示标题,通常用于标识文档的章节和子章节。
  7. :表示段落,通常用于显示一段文字内容。

      1. :表示无序列表、有序列表和列表项。

      使用语义化标签可以提高文档的可读性和可维护性,同时也可以为搜索引擎优化和无障碍阅读提供帮助。

      13.怎么让一个元素水平垂直居中

      可以使用 CSS 实现让一个元素水平垂直居中,以下是几种常用的方法:

      1. 使用 flexbox 布局:

      设置父容器的 display 属性为 flex,并设置 align-items: center 和 justify-content: center,即可让子元素水平垂直居中。

      .container {
        display: flex;
        align-items: center;
        justify-content: center;
      }
      1. 使用绝对定位:设置子元素的 position 属性为 absolute,并设置 top: 50% 和 left: 50%,然后再设置 transform: translate(-50%, -50%),即可实现水平垂直居中。
      2. 使用表格布局:设置父容器的 display 属性为 table,并设置子元素的 display 属性为 table-cell,然后再设置 vertical-align: middle 和 text-align: center,即可实现水平垂直居中。

      以上三种方法都可以实现让一个元素水平垂直居中,具体使用哪种方法,可以根据具体的需求和场景来选择。

      14.usestate是同步还是异步

      在 React 中,useState 是一个异步函数,它并不是同步更新状态的。当我们调用 useState 修改状态时,React 并不会立即更新组件的状态,而是将状态更新请求添加到更新队列中,等到合适的时机再进行更新,这个过程是异步进行的。

      调用 useState 修改状态时,React 会将更新请求添加到更新队列中,并且会在后续的更新过程中进行批量处理,以提高性能。在进行批量处理时,React 会对多个状态更新请求进行合并,然后进行一次更新,这个过程是异步的,因此可能会存在一定的延迟。但是,由于 React 内部对状态更新进行了封装和优化,因此我们通常不需要考虑异步更新状态的具体实现细节,只需要在逻辑上认为 useState 是同步更新状态的就可以了。

      15.大屏数据可视化项目屏幕怎么适配?

      以下是几种常用的屏幕适配方法:

      1. 百分比布局:使用百分比进行布局,根据父容器的尺寸自适应缩放。例如,可以将父容器的宽度设置为 100%,然后将子元素的宽度和高度设置为百分比值,以适应不同的屏幕尺寸。
      2. rem 布局:使用 rem 单位进行布局,根据根元素字体大小自适应缩放。可以将根元素的字体大小设置为屏幕宽度的一定比例,然后使用 rem 单位进行布局,以适应不同的屏幕尺寸。
      3. vw/vh 布局:使用 vw/vh 单位进行布局,根据视口尺寸自适应缩放。可以使用 vw/vh 单位进行布局,以适应不同的视口尺寸。
      4. 响应式布局:使用 CSS3 的媒体查询功能,根据不同的屏幕尺寸和分辨率,选择不同的布局方案。例如,可以使用媒体查询选择不同的样式表或设置不同的样式属性,以适应不同的屏幕尺寸和分辨率。

      需要注意的是,在大屏数据可视化项目中,一般会使用多种布局方式进行组合,以实现更加灵活和自适应的效果。另外,为了提高性能和用户体验,还可以使用前端性能优化的技术,如懒加载、图片压缩、缓存等。

      16.ECharts怎么适配?

      在 ECharts 中,可以通过以下方式进行适配:

      1. 自适应容器大小:ECharts 提供了 resize 方法,可以在容器大小发生变化时调用该方法,使图表自适应容器大小。可以通过监听窗口大小变化事件或者使用定时器等方式来触发 resize 方法。
      2. 使用响应式布局:ECharts 支持响应式布局,可以通过设置 grid 组件的属性来实现。例如,可以设置 grid 组件的 containLabel 属性为 true,使标签在容器内部居中显示。
      3. 使用多个图表:可以将一个页面分成多个区域,每个区域展示不同的图表,以适应不同的屏幕尺寸和分辨率。例如,可以使用 Bootstrap 等前端框架将页面分成多个列,然后在每个列中展示不同的图表。
      4. 使用响应式图表:ECharts 还提供了多个响应式图表,例如饼图、折线图等,可以根据屏幕尺寸和分辨率自适应缩放。例如,可以使用饼图来展示数据占比,当屏幕尺寸较小时,可以将饼图改为环形图,以便更好地展示数据。
      5. 使用响应式交互功能:ECharts 还提供了多种响应式交互功能,例如缩放、拖拽等,可以根据屏幕尺寸和分辨率自适应缩放和移动。例如,在地图中展示数据时,可以使用缩放和拖拽等功能来查看不同区域的数据。

      ECharts 提供了多种适配方法,可以根据不同的需求和场景进行选择和组合,以实现更加灵活和自适应的效果。

      17.使用ECharts怎么渲染十万条数据?

      在使用 ECharts 渲染大量数据时,通常会遇到性能问题。以下是几种常用的优化方法:

      1. 数据预处理:对于大量数据,可以在前端进行数据预处理,例如对数据进行采样、过滤、聚合等操作,从而减少需要渲染的数据量。
      2. 数据分片:将大量数据分成多个小块,每次渲染一部分数据,以减少一次性渲染大量数据的压力。
      3. 使用 Web Worker:将数据处理和图表渲染放在 Web Worker 中进行,从而不会阻塞主线程,提高渲染效率。
      4. 禁用动画:对于大量数据,通常不需要使用动画效果,可以通过设置 animation 属性为 false 来禁用动画,从而提高渲染效率。
      5. 使用 Canvas 渲染:ECharts 支持使用 Canvas 渲染,可以通过设置 renderer 属性为 'canvas' 来启用 Canvas 渲染,从而提高渲染效率。
      6. 使用大数据量专用的图表类型:ECharts 提供了多个大数据量专用的图表类型,例如散点图、热力图等,可以通过选择适合的图表类型来提高渲染效率。
      7. 延迟渲染:对于需要交互的图表,可以通过延迟渲染的方式来提高渲染效率。例如,在用户进行交互操作时再渲染图表,而不是在页面加载时就渲染图表。

      总之,ECharts 渲染大量数据时需要综合考虑多种因素,包括数据量、数据复杂度、交互需求等,选择适合的优化方法,从而实现更高效的渲染效果。

      18.js闭包理解优点和缺点?

      闭包可以让函数访问外部函数中定义的变量和参数,即使外部函数已经返回,闭包仍然可以访问这些变量和参数。

      优点:

      1. 保护变量:闭包可以保护函数内的变量,防止其被外部访问和修改。这种特性使得闭包非常适合用于实现模块化,可以将变量和函数封装在闭包中,只对外暴露需要的接口。
      2. 实现高阶函数:闭包可以用来实现高阶函数,即一个函数返回另一个函数。高阶函数可以简化代码,提高代码的复用性。
      3. 实现函数柯里化:闭包可以用来实现函数柯里化,即将一个接受多个参数的函数转换为接受单个参数的函数序列,这种技术可以使代码更加简洁和易于维护。

      3.1函数柯里化是一种将接受多个参数的函数转换为一系列只接受单个参数的函数的技术。通过函数柯里化,我们可以将一个接受 n 个参数的函数转换为 n 个只接受一个参数的函数,这些函数可以依次调用,最终完成原函数的调用。

      缺点:

      1. 内存占用:闭包会占用更多的内存,因为它会保留外部函数的变量和参数,导致内存泄漏和性能问题。如果闭包不再被使用,应该手动释放它占用的内存,比如将闭包变量设置为null。
      2. 安全问题:闭包可能会导致安全问题,因为它可以访问外部函数中的变量和参数,如果这些变量和参数包含敏感信息,可能会造成信息泄露和攻击风险。
      3. 可读性问题:使用闭包可能会降低代码的可读性和可维护性,因为它增加了代码的复杂性和难度。在使用闭包时,应该注意代码的规范和标准,保证代码的可读性和可维护性。

      19.vue权限验证?

      1. 路由守卫:通过在路由配置中添加meta字段来存储当前页面需要的权限信息,然后在路由跳转时通过beforeEach钩子函数进行权限验证。如果当前用户没有访问该页面的权限,则跳转到登录页面或其他提示页面。
      • 在路由守卫中实现权限验证的步骤如下:
      • 定义路由守卫:在路由配置中定义路由守卫,可以使用 beforeEach 或 beforeResolve 方法来定义路由守卫。
      • 获取用户信息:在路由守卫中获取用户信息,例如从本地存储或服务器获取用户信息。
      • 验证用户权限:根据用户信息验证用户权限,例如判断用户的角色、权限等。
      • 跳转到对应页面:根据用户权限决定是否跳转到对应页面,可以使用 next 方法来实现跳转。

      1. Vuex:通过在Vuex中存储用户信息及权限等相关信息,在组件中通过计算属性或者方法判断当前用户是否具有访问该组件的权限,如果没有则禁止访问。
      • 在 Vuex 中实现权限验证的步骤如下:
      • 定义状态:在 Vuex 的 state 中定义用户信息、权限信息等状态。
      • 定义 actions:在 Vuex 的 actions 中定义获取用户信息、验证用户权限等操作。可以通过调用后端API来获取用户信息,也可以从本地存储中获取。
      • 定义 mutations:在 Vuex 的 mutations 中定义修改用户信息、权限信息等状态的方法。
      • 在组件中调用:在需要验证权限的组件中调用 actions 中的方法来获取用户信息并验证权限。

      1. 后端接口:在后端接口中进行权限验证,通过在请求头中添加token等身份信息判断当前用户是否具有访问该接口的权限,如果没有则返回相应的错误信息。
      2. 自定义指令:通过自定义指令来控制页面元素的显示和隐藏。根据当前用户的权限信息,指令会动态地添加或移除元素的v-show或v-if指令,以实现页面元素的动态控制。
      3. mixin混入:通过在mixin中定义一些公共的权限验证逻辑,然后在组件中使用mixins选项来引入mixin,以实现权限验证的复用。

      以上几种Vue权限验证方式都可以在实际项目中使用,具体实现方式根据具体项目需求和开发经验而定。

      20.Vue组件之间的通信方式都有哪些?

      1. Props 和 $emit:父组件通过 props 属性向子组件传递数据,子组件通过 $emit 方法触发一个事件,向父组件传递数据。
      2. $parent 和 $children:可以通过 $parent 和 $children 访问父组件和子组件的实例,从而实现组件之间的通信。
      3. $refs:可以使用 $refs 来访问组件实例,从而实现组件之间的通信。
      4. Event Bus:通过一个空的 Vue 实例作为中央事件总线,用它来触发事件和监听事件,从而实现任意组件之间的通信。
      5. Vuex:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,使得不同组件之间可以共享状态,从而实现组件之间的通信。
      6. Provide 和 Inject:父组件通过 provide 属性向子孙组件传递数据,子孙组件通过 inject 属性接收数据,从而实现组件之间的通信。
      7. $attrs 和 $listeners:可以使用 $attrs 和 $listeners 来访问组件的属性和事件,从而实现组件之间的通信。

      $bus:可以使用 $bus 来创建一个全局的事件 总线,从而实现组件之间的通信。

      1. Mixin:可以使用 Mixin 将一些通用的逻辑和方法混入到多个组件中,从而实现组件之间的通信。
      2. Render Props:可以使用 Render Props 在组件之间共享逻辑和状态,从而实现组件之间的通信。

      21.vue生命周期

      Vue的生命周期是指组件在经历创建、更新和销毁过程中所经历的一系列钩子函数。这些钩子函数可以让开发者在特定的阶段执行自定义的逻辑。

      Vue的生命周期可以分为8个阶段,分别是:

      1. beforeCreate:在实例被创建之前执行,此时数据观测和初始化都未开始。
      2. created:在实例被创建之后执行,此时已完成数据观测、属性和方法的运算,但尚未开始DOM编译和挂载。
      3. beforeMount:在挂载之前被调用,此时已完成模板编译,但尚未开始挂载。
      4. mounted:在挂载之后被调用,此时已经完成了DOM的挂载,可以进行DOM操作。
      5. beforeUpdate:在更新之前被调用,此时数据已经被更新,但DOM尚未重新渲染。
      6. updated:在更新之后被调用,此时数据和DOM都已经更新完成。
      7. beforeDestroy:在实例销毁之前调用,此时实例仍然可用。
      8. destroyed:在实例销毁之后调用,此时实例已经被销毁,所有的事件监听和子实例都已经被移除。

      22.http与https区别

      HTTP(Hypertext Transfer Protocol)和HTTPS(Hypertext Transfer Protocol Secure)都是用于客户端和服务器之间进行通信的协议,但是它们之间有几个重要的区别。

      1. 安全性:HTTP是明文传输,数据在传输过程中容易被窃听和篡改,而HTTPS采用了SSL/TLS协议进行加密传输,数据传输过程中被加密,安全性更高。
      2. 端口号:HTTP默认使用80端口,HTTPS默认使用443端口。
      3. 证书:HTTPS需要使用SSL证书,证书中包含了网站的信息和公钥等,用于加密和解密数据。
      4. 速度:由于HTTPS需要进行加密解密过程,所以相比HTTP来说速度会略慢一些。
      5. 缓存:HTTP可以被缓存,而HTTPS不太容易被缓存。

      总之,HTTPS比HTTP更加安全,但是会稍微降低一些速度。在需要保护用户隐私和敏感数据的场景下,应该使用HTTPS。

      23.vue性能优化方案

      1. 使用异步组件和路由懒加载:将页面中不必要的组件和模块进行异步加载,提高首屏加载速度。
      2. 减少不必要的计算和渲染:避免在模板中使用过于复杂的计算和渲染逻辑,减少不必要的渲染,提高页面渲染速度。
      3. 使用虚拟滚动:对于大量的列表数据,使用虚拟滚动技术,只渲染可视区域的数据,减少不必要的渲染和计算。
      4. 使用keep-alive缓存组件:对于需要频繁切换的组件,可以使用keep-alive缓存组件,避免重复渲染和计算,提高性能。
      5. 使用Vue的内置优化:Vue提供了一些内置的优化选项,如v-if和v-for的key属性,可以避免不必要的重新渲染和计算。
      6. 使用CDN加速:使用CDN可以加速静态资源的加载速度,减少页面加载时间。
      7. 使用Webpack等构建工具进行打包优化:对于项目中的静态资源,可以使用Webpack等构建工具进行打包优化,减少文件大小,提高页面加载速度。
      8. 避免频繁的DOM操作:频繁的DOM操作会导致页面重排和重绘,影响页面性能,应避免频繁的DOM操作。
      9. 路由懒加载:对于大型的单页面应用,使用Vue Router的懒加载功能,按需加载路由组件,减少初始加载的资源体积。

      24. Vue响应式原理


      Vue的响应式原理是指Vue如何实现数据双向绑定和自动更新视图的机制。核心概念是Vue使用了一种名为"依赖追踪"的技术,通过建立数据与视图之间的关联关系,实现数据的变化能够自动更新到视图,而视图的变化也能够反映到数据上。

      具体实现如下:

      1. 数据劫持:Vue使用了ES5的Object.defineProperty方法来劫持数据对象的属性。通过将属性的get和set方法进行重定义,实现在数据访问和赋值时触发对应的操作。
      2. 监听器:Vue在数据劫持的过程中,为每个属性创建了一个监听器(Watcher)对象。监听器负责收集依赖(即视图中使用该属性的地方),当属性发生变化时,通知依赖进行更新。
      3. 依赖收集:在模板编译过程中,Vue会解析模板中的指令、表达式等,建立起依赖关系图。当访问数据时,Watcher会将自身添加到对应数据属性的依赖列表中,以便在属性变化时能够通知到相关的依赖进行更新。
      4. 响应式更新:当数据发生变化时,触发对应属性的set方法。在set方法中,Watcher会通知相关的依赖进行更新,从而触发视图的重新渲染。Vue使用了异步更新队列,将多次数据变化的更新合并成一次,以提高性能。

      总结:Vue的响应式原理是通过数据劫持和依赖追踪来实现数据与视图的自动双向绑定。Vue使用Object.defineProperty方法劫持数据对象的属性,创建监听器对象,以收集依赖和触发更新。在模板编译过程中建立依赖关系图,当数据发生变化时,触发对应属性的set方法,通知相关依赖进行更新,进而重新渲染视图。这种响应式机制使得开发者能够更专注于数据处理和业务逻辑,无需手动操作DOM来保持数据和视图的同步,提高了开发效率和代码的可维护性。

      data为什么是个函数?

      当data选项是一个对象时,该对象会被多个组件实例共享,这样在一个组件实例中修改数据会影响到其他实例,从而导致数据状态混乱和不可预料的行为。

      为了避免这种情况,Vue要求data选项必须是一个函数。每个组件实例在创建时会调用该函数,返回一个独立的数据对象。这样每个组件实例都会拥有自己的数据对象,它们之间相互独立且互不干扰。

      使用函数返回data的形式有以下几个好处:

      1. 数据隔离:每个组件实例拥有独立的数据对象,避免了数据之间的冲突和混乱。
      2. 可复用性:组件可以复用,每个实例都可以根据自身的需求返回不同的初始数据。
      3. 数据动态更新:每次调用data函数时都可以根据需要动态生成数据,例如从服务器获取最新的数据。

      通过将data选项设为函数,Vue能够实现组件实例之间数据的独立性和隔离性,保证每个组件实例都具有独立的数据对象,从而提高了组件的可维护性和可复用性。

      Vue3的响应式数据原理是使用Proxy实现的。Proxy是ECMAScript 6中新增的特性,它可以拦截并修改对象的操作。Vue3在创建组件实例时,会使用Proxy对组件的数据进行代理,当数据发生变化时,Proxy会自动触发更新视图的操作。

      具体来说,Vue3会将组件的data、computed、methods等属性值进行响应式处理。当数据发生变化时,Proxy会拦截并触发依赖该数据的所有组件进行更新。同时,Vue3还对数组和对象进行了优化,对于数组的修改操作,Vue3会使用封装后的方法进行代理,使得数组的响应式更新更加高效。

      25.浏览器渲染机制

      浏览器渲染机制是指浏览器将HTML、CSS和JavaScript代码转化为可视化的网页的过程。其主要步骤如下:

      1. 解析HTML文件,构建DOM树。浏览器将HTML文件解析成DOM树,DOM树是由节点构成的树状结构,每个节点代表HTML中的一个元素或标签。
      2. 解析CSS文件,构建CSSOM树。浏览器将CSS文件解析成CSSOM树,CSSOM树也是由节点构成的树状结构,每个节点代表CSS中的一个样式规则。
      3. 将DOM树和CSSOM树结合,生成渲染树。浏览器将DOM树和CSSOM树结合起来,生成渲染树,渲染树只包含需要显示的节点和这些节点的样式信息。
      4. 布局渲染树。浏览器会根据渲染树的结构和样式信息,计算每个节点在屏幕上的位置和大小。
      5. 绘制渲染树。浏览器会遍历渲染树,将每个节点绘制到屏幕上,最终呈现给用户。
      6. 不断重绘和合成。当渲染树中的节点发生变化,浏览器会重绘这些节点,并将它们合成到屏幕上,以保持页面的实时更新。

      以上就是浏览器渲染机制的主要步骤,其中每个步骤都会影响页面的性能和加载速度,因此优化这些步骤是提高页面性能的关键。

      26.当在浏览器中输入 Google.com 并且按下回车之后发生了什么?

      (1)解析URL: 首先会对 URL 进行解析,分析所需要使用的传输协议和请求的资源的路径。如果输入的 URL 中的协议或者主机名不合法,将会把地址栏中输入的内容传递给搜索引擎。如果没有问题,浏览器会检查 URL 中是否出现了非法字符,如果存在非法字符,则对非法字符进行转义后再进行下一过程。

      (2)缓存判断: 浏览器会判断所请求的资源是否在缓存里,如果请求的资源在缓存里并且没有失效,那么就直接使用,否则向服务器发起新的请求。

      (3)DNS解析: 下一步首先需要获取的是输入的 URL 中的域名的 IP 地址,首先会判断本地是否有该域名的 IP 地址的缓存,如果有则使用,如果没有则向本地 DNS 服务器发起请求。本地 DNS 服务器也会先检查是否存在缓存,如果没有就会先向根域名服务器发起请求,获得负责的顶级域名服务器的地址后,再向顶级域名服务器请求,然后获得负责的权威域名服务器的地址后,再向权威域名服务器发起请求,最终获得域名的 IP 地址后,本地 DNS 服务器再将这个 IP 地址返回给请求的用户。用户向本地 DNS 服务器发起请求属于递归请求,本地 DNS 服务器向各级域名服务器发起请求属于迭代请求。

      (4)获取MAC地址: 当浏览器得到 IP 地址后,数据传输还需要知道目的主机 MAC 地址,因为应用层下发数据给传输层,TCP 协议会指定源端口号和目的端口号,然后下发给网络层。网络层会将本机地址作为源地址,获取的 IP 地址作为目的地址。然后将下发给数据链路层,数据链路层的发送需要加入通信双方的 MAC 地址,本机的 MAC 地址作为源 MAC 地址,目的 MAC 地址需要分情况处理。通过将 IP 地址与本机的子网掩码相与,可以判断是否与请求主机在同一个子网里,如果在同一个子网里,可以使用 APR 协议获取到目的主机的 MAC 地址,如果不在一个子网里,那么请求应该转发给网关,由它代为转发,此时同样可以通过 ARP 协议来获取网关的 MAC 地址,此时目的主机的 MAC 地址应该为网关的地址。

      (5)TCP三次握手: 下面是 TCP 建立连接的三次握手的过程,首先客户端向服务器发送一个 SYN 连接请求报文段和一个随机序号,服务端接收到请求后向服务器端发送一个 SYN ACK报文段,确认连接请求,并且也向客户端发送一个随机序号。客户端接收服务器的确认应答后,进入连接建立的状态,同时向服务器也发送一个ACK 确认报文段,服务器端接收到确认后,也进入连接建立状态,此时双方的连接就建立起来了。

      (6)HTTPS握手: 如果使用的是 HTTPS 协议,在通信前还存在 TLS 的一个四次握手的过程。首先由客户端向服务器端发送使用的协议的版本号、一个随机数和可以使用的加密方法。服务器端收到后,确认加密的方法,也向客户端发送一个随机数和自己的数字证书。客户端收到后,首先检查数字证书是否有效,如果有效,则再生成一个随机数,并使用证书中的公钥对随机数加密,然后发送给服务器端,并且还会提供一个前面所有内容的 hash 值供服务器端检验。服务器端接收后,使用自己的私钥对数据解密,同时向客户端发送一个前面所有内容的 hash 值供客户端检验。这个时候双方都有了三个随机数,按照之前所约定的加密方法,使用这三个随机数生成一把秘钥,以后双方通信前,就使用这个秘钥对数据进行加密后再传输。

      (7)返回数据: 当页面请求发送到服务器端后,服务器端会返回一个 html 文件作为响应,浏览器接收到响应后,开始对 html 文件进行解析,开始页面的渲染过程。

      (8)页面渲染: 浏览器首先会根据 html 文件构建 DOM 树,根据解析到的 css 文件构建 CSSOM 树,如果遇到 script 标签,则判端是否含有 defer 或者 async 属性,要不然 script 的加载和执行会造成页面的渲染的阻塞。当 DOM 树和 CSSOM 树建立好后,根据它们来构建渲染树。渲染树构建好后,会根据渲染树来进行布局。布局完成后,最后使用浏览器的 UI 接口对页面进行绘制。这个时候整个页面就显示出来了。

      (9)TCP四次挥手: 最后一步是 TCP 断开连接的四次挥手过程。若客户端认为数据发送完成,则它需要向服务端发送连接释放请求。服务端收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,此时表明客户端到服务端的连接已经释放,不再接收客户端发的数据了。但是因为 TCP 连接是双向的,所以服务端仍旧可以发送数据给客户端。服务端如果此时还有没发完的数据会继续发送,完毕后会向客户端发送连接释放请求,然后服务端便进入 LAST-ACK 状态。客户端收到释放请求后,向服务端发送确认应答,此时客户端进入 TIME-WAIT 状态。该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,若该时间段内没有服务端的重发请求的话,就进入 CLOSED 状态。当服务端收到确认应答后,也便进入 CLOSED 状态。

      从输入URL到页面加载完成的主要流程如下:
      1. 用户输入URL:用户在浏览器地址栏中输入要访问的网站的URL。
      2. DNS解析:浏览器首先将URL中的域名解析为对应的IP地址。这涉及向DNS服务器发送DNS查询请求,以获取域名对应的IP地址。
      3. 建立TCP连接:浏览器使用HTTP协议通过TCP/IP建立与目标服务器的连接。这需要进行三次握手来确保双方的连接可靠。
      4. 发送HTTP请求:建立TCP连接后,浏览器向目标服务器发送HTTP请求。请求中包含请求的方法(GET、POST等)、URL、请求头信息和可选的请求体。
      5. 服务器处理请求:目标服务器接收到HTTP请求后,根据请求的URL和其他信息来处理请求。这可能涉及路由解析、数据查询、处理业务逻辑等操作。
      6. 服务器返回HTTP响应:服务器根据请求处理的结果生成HTTP响应,包括状态码、响应头信息和响应体。响应体中通常包含请求的资源,如HTML、CSS、JavaScript等文件。
      7. 浏览器接收HTTP响应:浏览器接收到来自服务器的HTTP响应后,开始解析响应。解析过程包括检查状态码、解析响应头信息和获取响应体。
      8. 渲染页面:浏览器根据接收到的响应内容开始解析HTML,并构建DOM树。同时,解析过程中会下载和解析CSS文件、JavaScript文件等,以构建渲染树。
      9. 页面布局和绘制:浏览器根据DOM树和渲染树进行页面布局和绘制。这包括计算元素的位置、大小、样式等,以及将渲染结果绘制到屏幕上。
      10. 加载并执行JavaScript:在页面渲染过程中,如果遇到JavaScript代码,浏览器会下载并执行这些代码。JavaScript的执行可能会修改DOM结构、更新页面内容或执行其他操作。
      11. 页面加载完成:当页面的DOM树、渲染树和JavaScript执行完成后,页面加载过程就算完成了。此时,页面上的内容将完全呈现给用户。
         
      TCP三次握手和TCP四次挥手有什么区别?
      1. 三次握手(TCP连接建立):三次握手是为了确保客户端和服务器都能够相互确认对方的可达性和接收能力,并同意建立连接。
        • 第一步:客户端发送一个带有SYN标志的包给服务器,表示请求建立连接。
        • 第二步:服务器接收到请求后,发送一个带有SYN/ACK标志的包给客户端,表示确认请求,并告知客户端准备好接收数据。
        • 第三步:客户端收到服务器的确认后,再发送一个带有ACK标志的包给服务器,表示客户端确认连接建立。
      1. 四次挥手(TCP连接终止):四次挥手是为了保证双方都能正确地关闭连接,避免数据丢失或断开连接的不完整性。
        • 第一步:当客户端需要关闭连接时,发送一个带有FIN标志的包给服务器,表示不再发送数据。
        • 第二步:服务器收到FIN后,发送一个带有ACK标志的包给客户端,表示收到关闭请求。
        • 第三步:服务器关闭与客户端的连接,并发送一个带有FIN标志的包给客户端,表示服务器也不再发送数据。
        • 第四步:客户端收到服务器的关闭请求后,发送一个带有ACK标志的包给服务器,表示确认关闭请求,并关闭连接。

      总结:

      • 三次握手是建立连接时的过程,确保双方都能够确认对方的可达性和接收能力,并同意建立连接。
      • 四次挥手是断开连接时的过程,确保双方都能正确地关闭连接,避免数据丢失或断开连接的不完整性。

      27.项目优化方案

      1. 代码优化:
        • 优化算法和数据结构,减少不必要的计算和内存消耗。
        • 减少代码冗余,提取重复代码为函数或组件。
        • 使用合适的数据缓存策略,避免重复获取和计算数据。
        • 使用合适的代码压缩工具,减小代码体积,提升加载速度。
      1. 页面加载优化:
        • 减少HTTP请求,合并和压缩静态资源文件。
        • 使用CDN加速静态资源的加载。
        • 延迟加载非关键资源,如图片懒加载。
        • 使用浏览器缓存策略,减少重复请求。
      1. 前端性能优化:
        • 使用合适的图片格式和压缩技术,减小图片大小。
        • 避免过多的DOM操作和重绘,优化页面渲染性能。
        • 使用虚拟列表或分页加载,处理大量数据的展示。
        • 合理使用异步操作,避免阻塞主线程。
      1. 数据请求优化:
        • 合并网络请求,减少请求数量。
        • 使用缓存策略,避免重复请求相同的数据。
        • 对数据进行压缩和分页处理,减小数据传输量。
      1. 移动端优化:
        • 使用合适的移动端框架和组件库,提升页面性能和用户体验。
        • 使用GPU加速动画效果,提高动画流畅度。
        • 避免使用过多的占用CPU的特性,如复杂的阴影效果和动态模糊。

      28.hash和history

      hash 和 history 是两种前端路由模式,用于在单页面应用中管理页面的导航和URL变化。

      1. Hash 模式:
        • 在 URL 中使用 # 符号来表示路由,例如:http://example.com/#/home。
        • Hash 模式的路由变化不会导致页面的完全刷新,而只是改变 URL 中的 hash 部分。
        • 通过监听 hashchange 事件来响应路由的变化。
        • Hash 模式的优点是兼容性好,支持在不同浏览器和服务器环境下使用。缺点是 URL 中带有 # 符号,不够美观。
      1. History 模式:
        • 使用 HTML5 的 history API 来管理页面的导航,例如:http://example.com/home。
        • History 模式通过修改 URL 的路径部分来表示路由,可以使用更友好的 URL。
        • 使用 pushState 或 replaceState 方法来改变 URL,不会导致页面的刷新。
        • 服务器需要正确配置,以便在刷新页面时正确响应对应的路由。
        • 通过监听 popstate 事件来响应路由的变化。
        • History 模式的优点是 URL 更美观,不带有 # 符号,缺点是需要服务器支持,并且在某些环境下可能出现问题。

      在 Vue Router 中,可以通过配置 mode 属性来选择使用 Hash 模式还是 History 模式,默认为 Hash 模式。

      29.sort排序,从大到小,从小到大

      sort 是 JavaScript 中的一个数组方法,用于对数组进行排序。sort 方法会原地修改数组,不会创建新的数组。

      sort 方法可以接受一个可选的比较函数作为参数,用于定义排序的规则。如果没有提供比较函数,sort 方法会将数组元素默认转换为字符串,并按照 Unicode 顺序进行排序。

      下面是一些常见的使用方式和示例:

      1. 默认排序:
      const numbers = [5, 2, 8, 1, 9];
      numbers.sort();
      console.log(numbers); // [1, 2, 5, 8, 9]

      2.使用比较函数进行排序:

      const numbers = [5, 2, 8, 1, 9];
      numbers.sort((a, b) => a - b); // 升序排序
      console.log(numbers); // [1, 2, 5, 8, 9]
      
      numbers.sort((a, b) => b - a); // 降序排序
      console.log(numbers); // [9, 8, 5, 2, 1]

      30.MVVM和MVC有什么区别

      在 MVVM 中,View 和 ViewModel 是双向绑定的,当 ViewModel 的数据发生变化时,会自动更新 View;当用户与 View 交互时,ViewModel 会处理相应的逻辑和更新数据。

      在 MVC 中,View 和 Controller 之间通过事件和命令的方式进行交互,Controller 接收用户的输入并处理逻辑,然后更新数据和通知 View 更新界面。

      MVVM 和 MVC 有一些相似之处,但也有一些区别:

      • MVVM 强调数据驱动的双向绑定,通过 ViewModel 将数据和视图绑定在一起,使得数据的变化可以自动反映在视图上。
      • MVC 使用事件和命令的方式进行交互,Controller 负责控制和管理视图的更新和数据的传递。
      • MVVM 更适合前端开发,特别是响应式的数据驱动开发,适用于构建复杂的交互界面。
      • MVC 更通用,适用于各种类型的应用程序,可以用于后端开发和前端开发。

      31.首屏优化

      1. 压缩和合并资源:减小 CSS、JavaScript 和图像等文件的大小,通过压缩和合并减少网络请求的数量,从而加快页面加载速度。
      2. 延迟加载非关键资源:将不是首屏必需的资源,如图片、广告等,进行延迟加载,等待页面主要内容加载完成后再加载这些资源,避免阻塞首屏渲染。
      3. 预加载关键资源:使用预加载(prefetching)或预渲染(prerendering)技术,在首屏加载完成后,提前加载下一个页面可能需要的资源,以减少后续页面的加载时间。
      4. 优化关键渲染路径:通过将关键资源放在 HTML 中靠前的位置,优化 CSS 和 JavaScript 的加载顺序,以最小化渲染阻塞,快速呈现首屏内容。
      5. 使用浏览器缓存:合理设置资源的缓存策略,使得重复访问的资源可以从本地缓存加载,减少网络请求。
      6. 懒加载和分片加载:将页面内容进行拆分,按需加载,延迟加载不可见区域的内容,提高首屏的加载速度。
      7. 图片优化:使用适当的图像格式和压缩算法,减小图像文件的大小,同时保持足够的视觉质量。使用响应式图片,在不同设备上加载适合的图像。
      8. 避免阻塞渲染的脚本:将阻塞渲染的 JavaScript 移至页面底部,或使用 async 或 defer 属性,使其在后台加载,不阻塞首屏的渲染。
      9. 服务器端渲染(SSR):对于需要动态生成内容的页面,考虑使用服务器端渲染技术,将部分渲染工作提前在服务器端完成,加快页面的呈现速度。
      10. 使用 CDN 加速:将静态资源部署到全球分布的 CDN(内容分发网络)上,加快资源的加载速度,提供更好的用户体验。

      32.keep-alive

      是 Vue.js 的一个内置组件,用于缓存和复用组件实例,以提高性能和响应速度。它可以包裹动态组件,并在组件切换时保留组件的状态,避免重复创建和销毁组件。

      使用 组件非常简单,只需将需要缓存的组件包裹在 标签内即可。

      缓存的组件会有一些生命周期钩子函数被调用。具体而言,当一个组件被缓存后,它的 activated 钩子函数会被调用,表示组件被激活;而在组件离开缓存时,deactivated 钩子函数会被调用,表示组件被停用。你可以在这些钩子函数中执行相应的操作,如初始化数据、加载资源等。

      组件是 Vue.js 提供的一种优化手段,用于缓存和复用组件实例,以提高性能和响应速度。它对于频繁切换的组件或需要保持状态的组件尤其有用。通过合理地使用 ,你可以优化Vue.js 应用的性能和用户体验。

      33.路由导航守卫

      Vue Router 提供了三种类型的导航守卫:

      1. 全局导航守卫:
        • beforeEach(to, from, next):在每个路由切换前执行,可以用来进行全局的前置验证或跳转逻辑。
        • afterEach(to, from):在每个路由切换后执行,可以用来进行全局的后置处理或统计分析。
      1. 路由独享的守卫:
        • beforeEnter(to, from, next):在某个具体路由配置中定义的守卫,只对该路由生效。
      1. 组件内的守卫:
        • beforeRouteEnter(to, from, next):在进入路由对应组件之前执行,可以访问不到组件实例,因此不能直接访问组件的数据和方法。
        • beforeRouteUpdate(to, from, next):在当前路由复用组件时执行,例如在动态路由参数发生变化时。
        • beforeRouteLeave(to, from, next):在离开路由对应组件之前执行,可以用来询问用户是否保存表单数据或做一些其他确认操作。

      这些导航守卫可以用来进行诸如身份验证、权限控制、页面跳转等操作。通过在导航守卫中调用 next 方法,你可以决定是否继续导航、导航到其他路由或取消导航。

      34.回流和重绘


      回流(Reflow)和重绘(Repaint)是浏览器渲染页面时的两个关键概念。

      回流指的是浏览器根据 DOM 结构和样式计算元素的位置和大小,并确定页面的布局。当页面的布局发生变化时,浏览器需要重新计算和渲染受影响的部分,这个过程称为回流。回流是比较耗费性能的操作,因为它会触发页面重新布局和重新绘制。

      重绘指的是浏览器根据元素的样式进行绘制,将元素呈现在屏幕上。当元素的样式发生变化但不影响其布局时,浏览器只需要重新绘制受影响的部分,这个过程称为重绘。相比回流,重绘的性能开销较小。

      回流和重绘的触发条件:

      1. 回流的触发条件:
        • 添加、删除、修改 DOM 元素的结构或内容
        • 修改元素的样式(如宽度、高度、边距等)
        • 修改页面的布局属性(如改变窗口大小、滚动页面等)
      1. 重绘的触发条件:
        • 修改元素的样式(如颜色、背景色、边框等)
        • 元素的可见性变化(如使用 display: none)

      优化回流和重绘的方法:

      1. 避免频繁访问和修改布局信息:尽量一次性获取或修改 DOM 元素的样式或布局属性,避免多次操作导致多次回流。
      2. 使用文档片段(Document Fragment)进行多个元素的批量插入:将要插入的多个元素先添加到文档片段中,再将文档片段一次性插入到文档中,减少回流次数。
      3. 使用 display: none 隐藏元素:相比修改元素的可见性,使用 display: none 可以避免重绘,减少性能开销。
      4. 使用 CSS3 动画或转换:利用硬件加速(如使用 GPU)来优化动画效果,减少回流和重绘的开销。
      5. 使用批量修改样式的技术:例如使用 CSS 类名切换样式,而不是直接修改元素的样式属性。
      6. 避免在循环中频繁操作样式或布局:将需要修改的元素缓存起来,在循环结束后一次性进行修改,减少回流次数。

      35.set

      Set 具有以下特点:

      1. 唯一性:Set 中的值是唯一的,不会重复出现。
      2. 无序性:Set 中的值没有固定的顺序,不会按照插入的顺序存储。

      Set 的常用方法包括:

      • add(value): 向 Set 中添加一个值。
      • delete(value): 删除 Set 中的一个值。
      • has(value): 判断 Set 中是否存在指定的值。
      • clear(): 清空 Set 中的所有值。
      • size: 返回 Set 中值的数量。

      Set 是一种用于存储唯一值的数据结构,在处理数据时非常有用。它提供了添加、删除、判断存在等基本操作,并且可以迭代值的顺序。在需要处理唯一值的场景下,使用 Set 可以简化代码,并提高性能。

      36.判断数据类型的方法

      1. typeof 操作符:typeof 操作符可以返回一个值的基本数据类型。
        1. 需要注意的是,typeof null 返回的是 "object",这是一个历史遗留问题。
      1. instanceof 操作符:instanceof 操作符用于检查对象是否是特定类的实例。
        1. instanceof 可以判断一个对象是否是某个类的实例,但不能用于判断基本数据类型。
      1. Object.prototype.toString.call() 方法:使用 Object.prototype.toString.call() 方法可以返回一个对象的具体类型。
        1. Object.prototype.toString.call() 方法返回的是一个字符串,表示对象的具体类型。
      1. Array.isArray() 方法:Array.isArray() 方法用于判断一个值是否是数组。
        1. Array.isArray() 方法可以准确地判断一个值是否为数组。

      37.BFC,即块级格式化上下文

      BFC 的应用场景:

      1. 清除浮动:创建一个父级 BFC 元素,可以阻止浮动元素导致的高度塌陷问题。
      2. 防止边距重叠:相邻的两个块级元素如果位于不同的 BFC 区域中,它们的垂直边距不会发生重叠。
      3. 创建自适应的布局:BFC 元素可以根据浮动元素的大小进行布局,适用于实现自适应的多列布局。
      4. 防止浮动元素覆盖:BFC 元素可以阻止浮动元素覆盖其内部的内容。

      BFC 是一种用于描述块级元素布局和渲染行为的概念。它具有一些特性和应用场景,通过创建 BFC 可以解决一些常见的布局问题,如清除浮动、防止边距重叠等。

      38.重构


      重构(Refactoring)是指对现有代码进行优化和改进,以改善代码的可读性、可维护性、性能或扩展性,而不会改变其外部行为。重构是一种迭代的过程,通过一系列小的改动来逐步改进代码的结构和设计,使代码更加健壮、可理解和易于修改。

      重构的目的包括:

      1. 提高代码质量:通过优化代码结构和设计,使代码更加清晰、简洁、可读,降低代码的复杂性和维护成本。
      2. 增加可维护性:通过重构,使代码易于理解、修改和扩展,方便后续的维护和改进。
      3. 提升性能:通过重构,可以优化算法、减少不必要的计算和资源消耗,提高代码的执行效率。
      4. 改进代码设计:通过重构,可以遵循更好的设计原则和模式,提高代码的灵活性、可扩展性和可测试性。

      常见的重构技术包括:

      1. 提取函数(Extract Function):将一段代码提取成一个独立的函数,提高代码的可读性和复用性。
      2. 合并函数(Inline Function):将一个函数的内容直接替换到调用处,简化代码结构。
      3. 移动函数(Move Function):将一个函数移动到合适的位置,提高代码的组织性和模块化。
      4. 重命名变量和函数(Rename):改善变量和函数的命名,使其更加清晰和表达意图。
      5. 拆分类(Split Class):将一个庞大的类拆分成多个小的类,提高类的职责单一性和可维护性。
      6. 合并类(Merge Class):将多个类合并为一个类,减少不必要的类和依赖关系。
      7. 提取接口(Extract Interface):将类的一部分接口提取成一个独立的接口,提高代码的可扩展性和可替换性。

      重构需要慎重进行,确保在每次重构后能够通过测试确保代码的正确性。同时,使用版本控制工具可以保证在重构过程中能够方便地回滚到之前的代码版本。

      重构是一个持续的过程,应该结合实际需求和项目情况进行。通过不断地重构,可以改进代码质量,提高开发效率,使代码更加健壮和可维护。

      39.谈谈你对原型和原型链的理解

      1. 原型:
        • 在JavaScript中,每个对象(除了 null 和 undefined)都有一个原型(prototype)。
        • 原型是一个对象,它包含了共享的属性和方法。
        • 对象可以通过原型继承属性和方法,即通过原型链来访问和使用原型中的属性和方法。
      1. 原型链:
        • 原型链是一种机制,用于在对象之间实现属性和方法的继承。
        • 当访问对象的属性或方法时,如果对象本身没有定义该属性或方法,JavaScript会自动查找对象的原型,然后在原型中查找,一直追溯到原型链的顶端。
        • 原型链的顶端是 Object.prototype,它是所有对象的最终原型。

      具体来说,当我们访问对象的属性或方法时,JavaScript会按照以下顺序进行查找:

      1. 首先检查对象本身是否具有该属性或方法。
      2. 如果对象本身没有该属性或方法,它会沿着原型链向上查找,即在对象的原型上进行查找。
      3. 如果原型上仍然没有找到,就会继续沿着原型链向上查找,直到到达原型链的顶端(Object.prototype)。

      当一个对象在访问属性或方法时,如果自身没有定义,它会沿着原型链向上查找,直到找到匹配的属性或方法,或者到达原型链的顶端仍未找到,最终返回 undefined。

      40.作用域

      1. 全局作用域(Global Scope):全局作用域是整个程序中的最外层作用域,其中定义的变量可以在程序的任何地方访问。在浏览器环境中,全局作用域通常是指在

        watch属性用于监听特定的数据变化,并在数据变化时执行相应的操作。与computed不同,watch是一个观察者,它可以监测到数据的每一次变化,无论是简单的数据变化还是复杂的对象或数组的变化。

        以下是一些适合使用watch的场景:

        1. 异步操作:当数据发生变化时,需要进行异步操作,如发起网络请求或执行定时器。
        2. 监听对象属性:当对象的属性发生变化时,需要执行特定的操作,如对象属性的增加、删除、更新等。
        3. 复杂计算:当需要进行复杂的计算操作,或需要根据多个数据的变化来触发特定操作时。
        4. 监听数组变化:当需要监听数组的变化,如数组的增加、删除、排序等。
        
        
          

        在上述示例中,使用watch属性监听

        49.事件冒泡和事件捕获

        事件冒泡: 事件冒泡是指当一个元素触发某个事件时,事件会从该元素开始向上冒泡传播到父元素,直至传播到根节点。换句话说,事件会先触发最内层元素的事件处理函数,然后依次触发父元素的事件处理函数,直到到达根节点或阻止事件冒泡。

        事件捕获: 事件捕获是指当一个元素触发某个事件时,事件会从根节点向下捕获传播到最内层元素。换句话说,事件会先触发根节点的事件处理函数,然后依次触发父元素的事件处理函数,直到到达最内层元素或阻止事件捕获。

        在 DOM 中,事件传播分为三个阶段:

        1. 捕获阶段(Capture Phase):事件从根节点向下捕获传播,依次触发父元素的事件处理函数,直到到达目标元素。
        2. 目标阶段(Target Phase):事件到达目标元素,触发目标元素上的事件处理函数。
        3. 冒泡阶段(Bubble Phase):事件从目标元素开始向上冒泡传播,依次触发父元素的事件处理函数,直到到达根节点。

        50.事件委托

        事件委托是利用事件冒泡机制,将事件处理程序绑定在父元素上,而不是绑定在每个子元素上的技术。通过事件委托,我们可以在父元素上统一处理多个子元素的事件,从而减少事件处理程序的数量,提高性能并简化代码。

        事件委托的实现步骤如下:

        1. 选取一个合适的父元素作为事件委托的目标,该父元素应包含所有需要监听事件的子元素。
        2. 在父元素上绑定事件处理程序,例如使用 addEventListener 方法。
        3. 在事件处理程序中,通过事件对象的属性(如 event.target)来判断事件的目标元素是哪个子元素。
        4. 根据事件目标元素的条件,执行相应的处理逻辑。

        事件委托的优点包括:

        1. 减少事件处理程序的数量,简化代码结构。
        2. 动态添加和删除的子元素无需重新绑定事件处理程序。
        3. 提高性能,减少内存占用和事件注册的开销。
        4. 适用于处理具有相同处理逻辑的子元素。

        需要注意以下几点:

        1. 父元素应选择合适的层级,以包含所有需要监听事件的子元素。
        2. 父元素应在页面加载时存在,或者在动态添加时及时绑定事件处理程序。
        3. 事件委托适用于具有相同处理逻辑的子元素,不适用于每个子元素都有不同处理逻辑的情况。
        4. 避免过度嵌套的父元素,以避免性能问题。

        51.本地存储

        在Web开发中,常用的本地存储技术包括:

        1. Cookie:Cookie 是一种在客户端存储少量数据的机制。它可以存储在浏览器中,并在每次请求时自动发送到服务器。Cookie 的大小受到限制,并且会随着每次请求自动发送,因此适合存储小量的数据和会话相关信息。
        2. Web Storage(localStorage 和 sessionStorage):Web Storage 提供了更大的存储容量,可以在客户端保存更多的数据。它分为两种类型:Web Storage 使用简单的键值对(key-value)形式进行数据存储,可以通过 JavaScript 的 API 进行读取、写入和删除等操作。
          • localStorage:localStorage 提供了持久化的本地存储,数据在浏览器关闭后仍然存在,并且在下次打开网页时可被访问。
          • sessionStorage:sessionStorage 提供了会话级别的本地存储,数据在用户关闭网页或浏览器标签页后会被清除。

        52.vue3为什么要用 Proxy 替代defineProperty?

        1. 更好的性能:Proxy API 比 defineProperty API 具有更高的性能。在 Vue 2.x 中,响应式系统使用 defineProperty 监听对象属性的变化,但它需要遍历对象的每个属性,并为每个属性设置 getter 和 setter。而 Proxy API 在底层实现上更接近于原生 JavaScript 对象,能够在访问对象属性时提供更好的性能。
        2. 更好的扩展性和灵活性:Proxy API 提供了丰富的陷阱(trap)和反射操作,允许我们对对象的行为进行自定义。我们可以通过定义陷阱来拦截和处理对象的各种操作,如读取属性、写入属性、删除属性等。这使得我们能够在响应式系统之外做更多的扩展和定制。
        3. 更好的类型检查和错误提示:使用 Proxy API 可以提供更准确的类型检查和错误提示。Vue 3.0 借助 TypeScript 的支持,能够更好地推断和验证对象的类型,并在开发过程中提供更好的类型检查和错误提示,以减少开发中的潜在错误。
        4. 更好的嵌套响应式支持:Vue 3.0 中,使用 Proxy API 可以更好地支持嵌套对象的响应式。在 Vue 2.x 中,嵌套对象需要手动设置为响应式,而在 Vue 3.0 中,通过 Proxy API,嵌套对象会自动被转换为响应式,无需手动设置。

        Vue 3.0 使用 Proxy API 替代 defineProperty API 是为了改进响应式系统的实现方式,提供更好的性能、更灵活的特性以及更好的类型检查和错误提示。这使得 Vue 3.0 在开发体验和性能方面有了显著的改进。

        53.对proxy的理解

        Proxy 提供了多个陷阱方法,包括 get、set、deleteProperty、apply、construct 等,用于拦截不同的操作。我们可以在这些陷阱方法中定义自定义的行为来改变或增强对象的操作。

        Proxy 提供了更好的性能和灵活性,相比之前的 defineProperty API,它更接近原生 JavaScript 对象的行为。它还可以与其他 JavaScript 特性如 Reflect API 结合使用,提供更强大的功能。

        54.事件循环机制

        JavaScript 是单线程的,意味着一次只能执行一个任务。然而,JavaScript 也支持异步操作,如定时器、网络请求、事件处理等,这些异步操作不会阻塞主线程的执行。事件循环机制使得 JavaScript 能够处理这些异步操作,并保持单线程的特性。

        事件循环的基本流程如下:

        1. 执行同步任务:首先,JavaScript 引擎会执行当前处于主线程上的同步任务,按照顺序逐个执行。
        2. 处理微任务:在执行同步任务期间,如果产生了微任务(Promise、MutationObserver 等),它们会被添加到微任务队列中。
        3. 执行宏任务:当主线程上的同步任务执行完毕后,JavaScript 引擎会查找宏任务队列中的第一个任务,并执行该任务。常见的宏任务包括定时器回调、事件回调和网络请求等。
        4. 处理微任务:在执行宏任务期间,如果产生了微任务,它们会被添加到微任务队列中。
        5. 重复步骤 3 和步骤 4:循环执行宏任务和微任务的过程,直到任务队列中的所有任务都被执行完毕。

        在事件循环中,微任务的执行优先级高于宏任务。也就是说,每次执行完一个宏任务后,会立即处理微任务队列中的所有任务,然后再执行下一个宏任务。

        55.父子组件生命周期执行顺序

        父:beforecreate -->父:created --> 父:beforeMount --> 子:beforecreate -->

        子:created --> 子:beforeMount --> 子:mounted --> 父:mounted

        56.Vuex

        Vuex的核心概念包括:

        1. State(状态):Vuex使用一个单一的状态树来存储整个应用程序的状态。它类似于组件中的data,但是可以被多个组件共享。
        2. Mutations(突变):突变是修改状态的唯一方式。它们是同步的操作,用于更改状态的值。通过提交一个突变,可以跟踪状态的变化,并使其成为可追踪的。
        3. Actions(动作):动作是用于处理异步操作的方法。它可以包含多个突变,用于执行一系列的突变操作,或者触发其他动作。通过调度一个动作,可以执行异步操作,然后再提交一个或多个突变来修改状态。
        4. Getters(获取器):获取器允许从状态树中派生出一些衍生状态,类似于组件中的计算属性。它们可以用于根据应用程序的状态计算一些派生数据,供组件使用。
        5. Modules(模块):模块允许将大型的状态树拆分为多个模块,每个模块都有自己的状态、突变、动作和获取器。模块化的状态管理可以更好地组织和管理复杂的应用程序。

        57.常用的数组API

        1. push(): 将一个或多个元素添加到数组的末尾,并返回数组的新长度。
        2. pop(): 删除数组的最后一个元素,并返回删除的元素。
        3. shift(): 删除数组的第一个元素,并返回删除的元素。
        4. unshift(): 将一个或多个元素添加到数组的开头,并返回数组的新长度。
        5. concat(): 将两个或多个数组合并为一个新数组。
        6. slice(): 返回一个新数组,其中包含从开始到结束(不包括结束)选择的数组的元素。
        7. splice(): 通过删除、替换或添加元素来修改数组,可以在指定的索引位置插入、删除或替换元素。
        8. forEach(): 对数组中的每个元素执行提供的回调函数。
        9. map(): 对数组中的每个元素执行提供的回调函数,并返回一个新数组,新数组包含每个回调函数的返回值。
        10. filter(): 对数组中的每个元素执行提供的回调函数,并返回一个新数组,新数组只包含符合条件的元素。
        11. find(): 返回数组中满足提供的测试函数的第一个元素的值。
        12. findIndex(): 返回数组中满足提供的测试函数的第一个元素的索引。
        13. reduce(): 对数组中的每个元素执行提供的回调函数,并将其结果汇总为单个值。
        14. some(): 对数组中的每个元素执行提供的测试函数,如果至少有一个元素满足条件,则返回true,否则返回false。
        15. every(): 对数组中的每个元素执行提供的测试函数,如果所有元素都满足条件,则返回true,否则返回false。

        这些是常见的数组操作API,可以根据具体需求选择合适的API来处理数组。

        1. indexOf(): 返回数组中第一个匹配给定元素的索引,如果没有找到则返回-1。
        2. lastIndexOf(): 返回数组中最后一个匹配给定元素的索引,如果没有找到则返回-1。
        3. includes(): 判断数组是否包含指定元素,如果包含则返回true,否则返回false。
        4. reverse(): 反转数组中元素的顺序。
        5. sort(): 对数组进行排序,默认按照元素的Unicode编码进行排序,也可以传入自定义的比较函数。
        6. join(): 将数组中的所有元素转换为字符串,并使用指定的分隔符将它们连接起来。
        7. fill(): 用指定的值填充数组的所有元素。
        8. isArray(): 判断给定值是否为数组类型。
        9. flat(): 将多维数组转换为一维数组。
        10. flatMap(): 对数组中的每个元素执行提供的回调函数,并将结果展平为一维数组。

        58.谈谈你对Promise的理解

        以下是对Promise的理解:

        1. 异步操作的状态管理:Promise具有三种状态:pending(进行中)、fulfilled(已完成)和rejected(已拒绝)。当一个Promise被创建时,它处于pending状态。异步操作的结果可以通过调用resolve函数来将Promise状态转变为fulfilled,或通过调用reject函数将Promise状态转变为rejected。一旦状态转变为fulfilled或rejected,Promise的状态将不再改变。
        2. 链式调用和方法链:Promise提供了链式调用的语法,通过返回一个新的Promise实例,可以在连续的Promise之间串联起来进行操作。这种方法链的形式使得异步操作可以按照顺序执行,并且可以在每个Promise中进行进一步的处理、转换或错误处理。
        3. 错误处理:Promise提供了.catch()方法来捕获Promise链中的任何错误,并提供一种统一的错误处理机制。如果任何一个Promise状态转变为rejected,错误信息将被传递到最近的.catch()块,并且可以在其中进行适当的处理。
        4. 并行和串行执行:Promise可以通过Promise.all()和Promise.race()方法来实现并行或串行执行多个异步操作。Promise.all()用于等待多个Promise全部完成,返回一个包含所有结果的Promise数组;而Promise.race()用于等待多个Promise中的任何一个完成,返回第一个完成的Promise的结果。
        5. 异常冒泡:Promise允许异常在Promise链中进行冒泡,即可以通过在任何一个Promise中抛出错误,然后在后续的.catch()块中捕获和处理错误。这种异常冒泡的机制使得错误处理更加灵活和统一。

        59.Promise.all() 执行多个异步操作 其中一个出现错误会出现什么情况


        当使用Promise.all()执行多个异步操作时,如果其中一个Promise实例的状态变为rejected(即出现错误),Promise.all()会立即返回一个rejected状态的Promise,并且返回的Promise的错误信息将是第一个被拒绝的Promise的错误信息。

        具体情况如下:

        1. 如果所有的Promise都fulfilled:Promise.all()返回一个新的Promise,该Promise的状态为fulfilled,其值是一个包含所有Promise结果的数组。
        2. 如果其中一个Promise被rejected:Promise.all()会立即返回一个新的Promise,该Promise的状态为rejected,其错误信息是第一个被拒绝的Promise的错误信息。

        这意味着如果在多个异步操作中,有一个操作出现错误,整个Promise.all()的结果将被拒绝,并且无法获取其他异步操作的结果。这种机制使得在并行执行多个异步操作时,可以快速捕获和处理错误,并且避免等待所有异步操作完成后才处理错误的情况。

        需要注意的是,Promise.all()的行为是"一旦有一个Promise被拒绝就立即拒绝",因此如果有些异步操作不关心错误,可以在对应的Promise上使用.catch()方法进行错误处理,以防止整个Promise.all()被拒绝。

        60.谈谈你对堆和栈的理解

        堆和栈是计算机内存中两种不同的数据存储方式,用于管理程序运行时的内存分配和释放。

        1. 栈(Stack):
          • 栈是一种具有特定的数据结构,采用先进后出(Last-In-First-Out,LIFO)的方式进行数据存储和访问。
          • 栈中的数据以"压栈"(push)和"出栈"(pop)的方式进行操作。
          • 栈的大小在程序编译时就确定,并且内存分配是连续的。
          • 栈主要用于存储局部变量、函数调用和程序执行过程中的上下文信息(函数调用栈帧)等。
        1. 堆(Heap):
          • 堆是一种无序、动态分配和释放内存的数据区域。
          • 堆的内存分配和释放由程序员手动控制,需要显式地进行内存分配(如通过new、malloc等操作)和释放(如通过delete、free等操作)。
          • 堆的大小在程序运行时可以动态改变,并且内存分配是非连续的。
          • 堆主要用于存储动态分配的对象、数据结构(如数组、链表)和大型数据等。

        在编程中,栈和堆的选择有不同的用途和影响:

        • 栈的内存管理由编译器自动处理,分配和释放速度快,但栈的容量有限。
        • 堆的内存管理由程序员手动控制,分配和释放速度较慢,但堆的容量较大。


        61.WebSocket的使用

        以下是使用WebSocket的基本步骤:

        1. 创建WebSocket对象:在客户端代码中,使用JavaScript的WebSocket API创建一个WebSocket对象。通过指定WebSocket服务器的URL,可以与服务器建立连接。
        var socket = new WebSocket("ws://example.com/socket");
        1. 监听事件:WebSocket对象提供了一些事件,用于处理连接的打开、消息接收、错误和关闭等情况。可以通过添加相应的事件处理程序来处理这些事件。
        socket.onopen = function() {
          // 连接已打开
        };
        
        socket.onmessage = function(event) {
          // 接收到服务器发送的消息
          var message = event.data;
          // 处理消息
        };
        
        socket.onerror = function(error) {
          // 发生错误
        };
        
        socket.onclose = function(event) {
          // 连接已关闭
        };
        1. 发送和接收消息:使用WebSocket对象的send()方法向服务器发送消息,服务器可以通过WebSocket对象的onmessage事件监听接收到的消息。
        // 发送消息
        socket.send("Hello, server!");
        
        // 接收消息(通过onmessage事件处理程序)
        socket.onmessage = function(event) {
          var message = event.data;
          // 处理接收到的消息
        };
        1. 关闭连接:通过调用WebSocket对象的close()方法可以手动关闭连接。
        socket.close();

        62.你在axios二次封装做过什么操作。

        1. 设置默认配置:可以在封装过程中设置一些默认配置,如请求的基本 URL、请求头、超时时间等,使得每次发起请求时无需重复设置这些配置。
        2. 请求拦截器:可以在请求被发送之前进行拦截和处理,例如添加认证信息、处理请求参数、设置请求头等。请求拦截器还可以用于全局的错误处理、loading 状态的控制等。
        3. 响应拦截器:可以在接收到响应之后进行拦截和处理,例如处理响应数据、根据状态码进行统一的错误处理、转换数据格式等。
        4. 错误处理:可以根据具体的业务需求对错误进行统一处理,例如根据不同的错误码进行相应的操作,如重新登录、跳转错误页面等。
        5. 统一数据格式处理:可以对请求和响应的数据进行统一的格式化处理,以适应前端的数据需求,例如将接口返回的数据进行转换、添加额外的字段等。
        6. 处理 loading 状态:可以在请求开始和结束时控制全局的 loading 状态,以提供更好的用户体验。
        7. 封装常用请求方法:可以根据业务需求封装常用的请求方法,如 GET、POST、PUT、DELETE 等,简化请求代码的编写。
        8. 处理请求取消:可以在封装过程中处理请求的取消操作,以便在组件销毁或取消请求时能够正确地取消请求,避免造成资源浪费或潜在的错误。
        9. 处理请求重试:可以针对某些特定的请求错误情况,实现请求的自动重试机制,以增加请求的稳定性和容错性。

        通过对 Axios 进行二次封装,可以实现对请求和响应的统一处理、简化代码编写、提供更好的错误处理和数据格式处理等功能,从而提高开发效率和代码质量。

        63.浏览器缓存怎么做?

        1. HTTP缓存:利用HTTP协议中的缓存机制,通过设置响应头信息控制缓存行为。常见的缓存控制头有:通过合理配置这些缓存控制头,可以让浏览器在满足缓存条件时直接使用本地缓存,减少网络请求。
          • Cache-Control:设置缓存的方式,如max-age设置缓存的最大时间。
          • Expires:设置过期时间,告诉浏览器何时需要重新请求资源。
          • ETag:标识资源的唯一性,用于判断资源是否发生变化。
          • Last-Modified:标记资源的最后修改时间,配合If-Modified-Since进行缓存验证。
        1. 文件版本控制:通过给文件名或URL添加版本号、哈希值等唯一标识,当文件内容发生变化时,修改其版本号或哈希值。这样可以确保浏览器能够正确地识别并获取最新的文件版本,避免缓存过期问题。
        2. Service Worker:使用 Service Worker 技术可以在浏览器中拦截网络请求,缓存资源并在离线时提供缓存的资源。通过 Service Worker,可以实现更高级的缓存策略,如离线缓存、缓存优先策略等。
        3. 缓存策略:根据资源的特性和使用场景,可以选择不同的缓存策略。常见的缓存策略有:
          • 强缓存:通过设置响应头信息,直接使用本地缓存,如 Cache-Control 和 Expires。
          • 协商缓存:通过设置响应头信息,与服务器进行缓存验证,如 ETag 和 Last-Modified。
          • 离线缓存:使用 Service Worker 技术,缓存资源以在离线时提供访问。

        综合使用上述方法,可以实现浏览器缓存,减少网络请求,提高页面加载速度和用户体验。需要根据具体的项目需求和资源特性选择合适的缓存策略,并进行适当的调试和测试以确保缓存机制的正确性和性能优化效果。

        64.HTML5新特性有哪些?

        HTML5引入了许多新特性和改进,以下是HTML5的一些主要新特性:

        1. 语义化标签:HTML5引入了一系列的语义化标签,如
        2. 媒体支持:HTML5提供了更好的媒体支持,包括
        3. Canvas绘图:HTML5的元素允许开发者使用JavaScript进行动态的图形绘制,包括绘制图形、渲染图像和创建动画等,为开发游戏、图表和数据可视化等提供了更灵活的方式。
        4. 地理位置定位:HTML5的地理位置API允许网页获取用户的地理位置信息,通过navigator.geolocation对象可以获取用户的经纬度坐标,为基于地理位置的应用和服务提供支持。
        5. 表单增强:HTML5提供了一些表单的增强功能,如输入类型的扩展(如日期选择、邮箱、电话等)、表单验证、自动填充和表单数据存储等,提升了用户体验和数据交互的能力。
        6. Web存储:HTML5引入了本地存储机制,包括Web Storage(localStorage和sessionStorage)和IndexedDB,允许网页在客户端存储数据,提供了更好的离线和缓存能力。
        7. Web Worker:HTML5的Web Worker允许在后台运行多个JavaScript线程,可以进行复杂的计算和处理,而不阻塞用户界面的响应。
        8. WebSocket:HTML5引入了WebSocket协议,提供了双向通信的能力,实现了实时数据传输,适用于聊天应用、实时通知和多人协作等场景。

        这些只是HTML5的一些主要特性,HTML5还包括许多其他改进和功能,使得开发者能够创建更丰富、更交互和更高效的网页应用。


        66.CSS3新特性有哪些?

        1. 弹性布局(Flexbox):提供了一种灵活的布局方式,可以方便地实现响应式布局和自适应布局。
        2. 网格布局(Grid):提供了一种二维的布局方式,可以将页面分成多个区域,并且可以控制每个区域的大小和位置。
        3. 自定义字体(@font-face):可以在网页上使用自定义字体,而不需要依赖于用户计算机上已安装的字体。
        4. 渐变效果(Gradient):可以通过CSS3实现直线渐变和径向渐变,可以用来实现背景、边框和文本等多种效果。
        5. 动画效果(Animation):可以通过CSS3实现动画效果,比如旋转、缩放、淡入淡出等多种效果。
        6. 2D和3D转换(Transform):可以通过CSS3实现元素的二维和三维转换,比如旋转、缩放、倾斜、平移等多种效果。
        7. 过渡效果(Transition):可以通过CSS3实现过渡效果,比如颜色、背景、边框等多种效果。
        8. 媒体查询(Media Queries):可以通过CSS3实现响应式布局,根据屏幕大小和设备类型等条件来调整页面布局和样式。
        9. 伸缩盒模型(Box-sizing):可以通过CSS3设置盒模型的大小计算方式,可以方便地控制元素的大小和位置。

        67.WebSocket 和服务端建立连接后,服务端修改token会不会断开连接?

        WebSocket 建立连接后,服务端更换 Token 不会直接导致连接断开。WebSocket 的连接是一种持久连接,一旦建立,它可以保持活跃状态,并允许双方之间进行实时的双向通信。

        更换 Token 可能涉及到一些与身份验证或会话管理相关的操作。在某些情况下,服务端可能需要通知客户端进行重新认证或重新建立连接。这可以通过特定的协议消息或自定义的应用层协议来实现。

        具体的实现方式可能会根据你使用的 WebSocket 库、框架或服务端实现而有所不同。一般情况下,服务端可以发送一个特定的消息,提示客户端 Token 已更换,并要求客户端进行相应的处理。客户端接收到这个消息后,可以根据协议规定的方式进行重新认证或重新建立连接,以确保继续保持有效的通信状态。

        需要注意的是,具体的实现方式和流程可能会受到你的应用程序设计和需求的影响。建议参考所使用的 WebSocket 库或框架的文档,以了解更多关于 Token 更换时的最佳实践和实现方式。


        68.Vue3中常用的组合式API及作用

        组合式API通过setup函数提供了一种更直接的方式来编写组件逻辑。

        1. ref:ref函数用于创建一个响应式的数据引用。它将一个普通的JavaScript值转换为一个响应式对象,并返回一个可以访问和修改该值的引用。在模板或setup函数中使用这个引用,当引用的值发生变化时,相关的界面会自动更新。
        2. reactive:reactive函数用于创建一个响应式的数据对象。它将一个普通的JavaScript对象转换为一个响应式对象,并返回一个可以访问和修改该对象的引用。与ref不同,reactive可以处理更复杂的对象,包括嵌套对象和数组。
        3. computed:computed函数用于创建一个计算属性。计算属性是基于响应式数据的衍生值,它会根据依赖的响应式数据自动更新。通过computed函数创建的计算属性可以像普通属性一样访问,但它的值是根据相关的响应式数据动态计算得出的。
        4. watch:watch函数用于监听一个响应式数据的变化,并在数据变化时执行相应的回调函数。你可以使用watch来执行一些副作用操作,例如发送网络请求、处理数据更新等。watch函数还可以传递一个可选的配置对象,用于更详细地控制监听行为。
        5. onMounted、onUpdated和onUnmounted:这些函数分别用于在组件挂载后、更新后和卸载前执行一些操作。你可以在这些生命周期钩子函数中执行一些初始化、清理或其他副作用操作。这些钩子函数是基于组合式API的方式来定义的,与Vue 2.x中的选项式API中的生命周期钩子对应。
        6. toRefs:toRefs函数用于将一个响应式对象转换为一组只读的响应式引用。当你需要将一个响应式对象的属性解构出来,并在模板或其他上下文中使用时,可以使用toRefs来确保属性保持响应式。
        7. provide和inject:这对函数用于在父子组件之间进行依赖注入。通过provide函数在父组件中提供一些值,然后在子组件中使用inject函数来注入这些值。这可以方便地实现跨层级组件之间的通信和共享数据。
        8. onBeforeMount和onBeforeUnmount:这些函数类似于生命周期钩子函数,但在组件挂载或卸载之前触发。你可以在这些函数中执行一些准备工作或清理操作。
        9. onBeforeUpdate和onUpdated:这对函数也类似于生命周期钩子函数,但在组件更新之前和之后触发。你可以在onBeforeUpdate中执行一些更新前的准备工作,而在onUpdated中执行一些更新后的操作。
        10. watchEffect:watchEffect函数用于创建一个响应式的副作用。它会自动追踪其依赖的响应式数据,并在这些数据发生变化时重新运行副作用函数。与watch不同,watchEffect不需要指定具体的数据依赖,它会自动推断出所需的依赖。
        11. onErrorCaptured:这个函数用于捕获组件内部的错误。你可以在组件层级中定义onErrorCaptured函数来捕获子组件中的错误,并进行相应的处理。
        12. isRef和isReactive:这些函数用于判断一个值是否为响应式引用或响应式对象。你可以使用isRef来检查一个值是否为ref创建的引用,使用isReactive来检查一个值是否为reactive创建的响应式对象。

        69.v-model使用场景

        v-model是Vue.js中的一个指令,用于实现表单元素与数据的双向绑定。通过v-model指令,可以将表单元素的值与Vue实例中的数据进行关联,实现数据的同步更新。下面是一些常见的使用场景:

        1. 表单输入:v-model最常见的用法是在表单输入元素(如