当前位置: 首页 > news >正文

2025年01月03日微创网络(杭州银行外包)前端面试

目录

  1. html 块级元素和行内元素有哪些
  2. 阴影的几个属性
  3. 垂直水平居中的实现方式
  4. 定位的几种方式
  5. 盒子模型的方式
  6. js的数组方法有哪些
  7. vue2 vue3 区别
  8. vuex
  9. 哈希路由和浏览器路由的区别
  10. 浏览器缓存的几个方式
  11. react hooks的优势
  12. react 组件传值
  13. vue 组件传值
  14. 如何进行性能优化
  15. 前端监控
  16. get post 区别
  17. 跨域解决方案
  18. ref 和 reative 区别

1. html 块级元素和行内元素有哪些

在 HTML 中,元素根据其显示特性主要分为块级元素和行内元素,它们在页面布局和显示方式上有明显区别。

块级元素

块级元素在页面中会独占一行,并且可以设置宽度和高度,即使设置的宽度小于父元素宽度,元素也会换行显示。常见的块级元素及其用途如下:

  • <div>:是最常用的块级元素之一,本身没有特定的语义,主要用于对页面进行布局和分组,可将相关的元素组合在一起,方便进行样式设置和操作。
  • <p>:用于定义段落文本,浏览器会自动在段落前后添加一定的空白间距,使文本排版更清晰。
  • <h1> - <h6>:表示不同级别的标题,<h1> 是最高级别的标题,<h6> 是最低级别的标题。标题元素用于突出显示页面的结构和内容层次。
  • <ul><ol>:分别是无序列表和有序列表的容器。<ul> 中的列表项通常以圆点标记,<ol> 中的列表项以数字标记。列表项使用 <li> 元素表示。
  • <dl>:用于定义描述列表,通常包含一个或多个 <dt>(定义术语)和 <dd>(术语描述)对,常用于展示术语及其解释。
  • <pre>:用于显示预格式化的文本,会保留文本中的空格、换行符等格式,通常用于显示代码片段或需要保持特定格式的文本。
  • <form>:用于创建 HTML 表单,用户可以在表单中输入数据并提交到服务器。表单可以包含各种表单元素,如输入框、下拉框、按钮等。
  • <table>:用于创建表格,包含 <thead>(表头)、<tbody>(表体)和 <tfoot>(表脚)等部分,每个部分又由 <tr>(表格行)和 <td>(表格单元格)组成。
行内元素

行内元素不会独占一行,而是会在同一行内依次排列,并且宽度和高度由其内容决定,不能直接设置宽度和高度(某些情况下可以通过 CSS 的 display 属性改变其显示方式)。常见的行内元素及其用途如下:

  • <a>:用于创建超链接,通过 href 属性指定链接的目标地址,可以是网页、文件、邮件地址等。
  • <img>:用于在页面中插入图片,通过 src 属性指定图片的来源地址。图片元素没有闭合标签。
  • <input>:用于创建各种表单输入元素,如文本框、密码框、单选框、复选框等,通过 type 属性指定输入框的类型。
  • <label>:用于为表单元素定义标签,通过 for 属性与对应的表单元素关联,提高表单的可用性和可访问性。
  • <select>:用于创建下拉选择框,包含一个或多个 <option> 元素,表示下拉选项。
  • <textarea>:用于创建多行文本输入框,用户可以在其中输入较长的文本内容。
  • <span>:是最常用的行内元素之一,本身没有特定的语义,主要用于对文本进行样式设置或操作,可将部分文本进行分组。
  • <br>:用于在文本中插入换行符,使文本在指定位置换行显示。该元素没有闭合标签。
  • <i><em><strong><i> 通常用于显示斜体文本;<em> 用于强调文本,浏览器通常会将其显示为斜体;<strong> 用于表示重要的文本,浏览器通常会将其显示为粗体。

2. 阴影的几个属性

在 CSS 中,用于添加阴影效果的属性主要有 box-shadow(用于给元素添加盒阴影)和 text-shadow(用于给文本添加阴影),下面分别介绍它们参数的含义。

box-shadow 参数含义

box-shadow 属性可以为元素添加一个或多个阴影,其基本语法如下:

box-shadow: h-shadow v-shadow blur spread color inset;

各参数的具体含义如下:

  • h-shadow(必需):水平阴影的位置。该参数的值可以是正数、负数或零。正数表示阴影在元素的右侧,负数表示阴影在元素的左侧,零表示阴影与元素在水平方向上重合。例如,box-shadow: 5px 0 0 0 black; 会在元素右侧创建一个水平阴影。
  • v-shadow(必需):垂直阴影的位置。同样,该参数的值可以是正数、负数或零。正数表示阴影在元素的下方,负数表示阴影在元素的上方,零表示阴影与元素在垂直方向上重合。例如,box-shadow: 0 5px 0 0 black; 会在元素下方创建一个垂直阴影。
  • blur(可选):阴影的模糊半径。该参数的值不能为负数,值越大,阴影越模糊,范围也越大;值为零表示阴影边缘清晰。例如,box-shadow: 0 0 10px 0 black; 会创建一个模糊的阴影。
  • spread(可选):阴影的扩展半径。该参数的值可以是正数、负数或零。正数会使阴影扩大,负数会使阴影缩小。例如,box-shadow: 0 0 0 5px black; 会创建一个比元素本身大 5px 的阴影。
  • color(可选):阴影的颜色。可以使用颜色名称(如 red)、十六进制值(如 #ff0000)、RGB 值(如 rgb(255, 0, 0))等表示。如果省略该参数,浏览器会使用当前元素的文本颜色作为阴影颜色。
  • inset(可选):这是一个关键字,如果使用该关键字,则阴影会被设置在元素内部,成为内阴影;如果省略该关键字,则默认是外阴影。例如,box-shadow: inset 0 0 10px 0 black; 会创建一个内阴影。
text-shadow 参数含义

text-shadow 属性用于为文本添加阴影,其基本语法如下:

text-shadow: h-shadow v-shadow blur color;

各参数的具体含义如下:

  • h-shadow(必需):与 box-shadow 中的 h-shadow 类似,指定文本阴影的水平偏移量。正数表示阴影在文本的右侧,负数表示阴影在文本的左侧。
  • v-shadow(必需):与 box-shadow 中的 v-shadow 类似,指定文本阴影的垂直偏移量。正数表示阴影在文本的下方,负数表示阴影在文本的上方。
  • blur(可选):与 box-shadow 中的 blur 类似,指定文本阴影的模糊半径。值越大,阴影越模糊。
  • color(可选):指定文本阴影的颜色,可以使用各种颜色表示方法。如果省略该参数,浏览器会使用当前文本的颜色作为阴影颜色。
示例代码
/* 为元素添加外阴影 */
.box {width: 100px;height: 100px;background-color: #eee;box-shadow: 5px 5px 10px 2px rgba(0, 0, 0, 0.5);
}/* 为文本添加阴影 */
.text {font-size: 24px;text-shadow: 2px 2px 4px #000;
}/* 为元素添加内阴影 */
.inset-box {width: 100px;height: 100px;background-color: #eee;box-shadow: inset 0 0 10px 2px rgba(0, 0, 0, 0.5);
}

在上述示例中,.box 类的元素添加了一个外阴影,.text 类的文本添加了一个阴影,.inset-box 类的元素添加了一个内阴影。

3. 垂直水平居中的实现方式

在前端开发中,实现元素的垂直水平居中是一个常见的需求,以下分别介绍不同场景下实现垂直水平居中的方法。

行内元素或行内块元素
使用 text-align: centerline-height

当需要将单行文本或行内元素在其父元素中垂直水平居中时,可以利用 text-align: center 实现水平居中,使用 line-height 等于父元素高度来实现垂直居中。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.parent {width: 200px;height: 200px;background-color: #f0f0f0;text-align: center;line-height: 200px;}</style>
</head><body><div class="parent"><span>居中的文本</span></div>
</body></html>
使用 flexbox

对于行内元素或行内块元素,使用 flexbox 布局可以更方便地实现垂直水平居中。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.parent {display: flex;justify-content: center;align-items: center;width: 200px;height: 200px;background-color: #f0f0f0;}</style>
</head><body><div class="parent"><span>居中的文本</span></div>
</body></html>
块级元素
使用 flexbox

flexbox 是一种强大的布局模型,通过设置父元素的 displayflexinline-flex,并结合 justify-contentalign-items 属性,可以轻松实现子元素的垂直水平居中。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.parent {display: flex;justify-content: center;align-items: center;width: 200px;height: 200px;background-color: #f0f0f0;}.child {width: 50px;height: 50px;background-color: #007BFF;}</style>
</head><body><div class="parent"><div class="child"></div></div>
</body></html>
使用 grid 布局

grid 布局也是一种现代的布局方式,通过设置父元素的 displaygridinline-grid,并使用 place-items 属性可以实现子元素的垂直水平居中。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.parent {display: grid;place-items: center;width: 200px;height: 200px;background-color: #f0f0f0;}.child {width: 50px;height: 50px;background-color: #007BFF;}</style>
</head><body><div class="parent"><div class="child"></div></div>
</body></html>
使用绝对定位和负边距(已知子元素宽高)

如果知道子元素的宽度和高度,可以使用绝对定位和负边距来实现垂直水平居中。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.parent {position: relative;width: 200px;height: 200px;background-color: #f0f0f0;}.child {position: absolute;top: 50%;left: 50%;width: 50px;height: 50px;background-color: #007BFF;margin-top: -25px;margin-left: -25px;}</style>
</head><body><div class="parent"><div class="child"></div></div>
</body></html>
使用绝对定位和 transform(未知子元素宽高)

当不知道子元素的宽度和高度时,可以使用绝对定位和 transform 属性来实现垂直水平居中。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.parent {position: relative;width: 200px;height: 200px;background-color: #f0f0f0;}.child {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);background-color: #007BFF;}</style>
</head><body><div class="parent"><div class="child">内容</div></div>
</body></html>
图片元素
使用 flexboxgrid

和块级元素类似,使用 flexboxgrid 布局可以方便地将图片垂直水平居中。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.parent {display: flex;justify-content: center;align-items: center;width: 200px;height: 200px;background-color: #f0f0f0;}img {max-width: 100%;max-height: 100%;}</style>
</head><body><div class="parent"><img src="your-image.jpg" alt="图片"></div>
</body></html>

4. 定位的几种方式

在 CSS 中,定位(Positioning)是一种强大的布局技术,它允许你精确地控制元素在页面中的位置。主要有五种定位方式,分别是 staticrelativeabsolutefixedsticky,下面为你详细介绍:

static(静态定位)
  • 特点:这是元素的默认定位方式。元素会按照正常的文档流进行布局,即元素会根据 HTML 代码的先后顺序依次排列,toprightbottomleftz-index 属性对其没有影响。
  • 示例代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.static-box {position: static;background-color: lightblue;width: 100px;height: 100px;}</style>
</head><body><div class="static-box">静态定位元素</div>
</body></html>
relative(相对定位)
  • 特点:元素会相对于其正常位置进行定位。使用 toprightbottomleft 属性可以将元素从其正常位置偏移,但不会影响其他元素的布局,即元素原来占据的空间仍然会保留。
  • 示例代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.relative-box {position: relative;top: 20px;left: 20px;background-color: lightgreen;width: 100px;height: 100px;}</style>
</head><body><div class="relative-box">相对定位元素</div>
</body></html>
absolute(绝对定位)
  • 特点:元素会相对于最近的已定位祖先元素(即 position 属性值不为 static 的祖先元素)进行定位。如果没有已定位的祖先元素,则相对于初始包含块(通常是浏览器窗口)进行定位。元素会脱离正常的文档流,不再占据原来的空间,可能会覆盖其他元素。
  • 示例代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.parent {position: relative;width: 200px;height: 200px;background-color: lightgray;}.absolute-box {position: absolute;top: 50px;left: 50px;background-color: lightcoral;width: 100px;height: 100px;}</style>
</head><body><div class="parent"><div class="absolute-box">绝对定位元素</div></div>
</body></html>
fixed(固定定位)
  • 特点:元素会相对于浏览器窗口进行定位,无论页面如何滚动,元素都会保持在固定的位置。元素同样会脱离正常的文档流,不占据原来的空间。
  • 示例代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.fixed-box {position: fixed;top: 20px;right: 20px;background-color: lightyellow;width: 100px;height: 100px;}</style>
</head><body><p>这是一段很长的文本,用于测试滚动效果。</p><!-- 重复很多次以产生滚动条 --><p>这是一段很长的文本,用于测试滚动效果。</p><div class="fixed-box">固定定位元素</div>
</body></html>
sticky(粘性定位)
  • 特点:元素在正常滚动时会按照文档流进行布局,但当滚动到某个位置时,会固定在屏幕上的某个位置,就像 fixed 定位一样。它结合了 relativefixed 定位的特点,需要指定 toprightbottomleft 中的一个值来确定粘性位置。
  • 示例代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.sticky-box {position: sticky;top: 0;background-color: lightpink;width: 100%;height: 50px;}</style>
</head><body><p>这是一段很长的文本,用于测试滚动效果。</p><!-- 重复很多次以产生滚动条 --><p>这是一段很长的文本,用于测试滚动效果。</p><div class="sticky-box">粘性定位元素</div><p>这是一段很长的文本,用于测试滚动效果。</p>
</body></html>

综上所述,不同的定位方式适用于不同的布局需求,开发者可以根据具体情况选择合适的定位方式来实现所需的页面布局效果。

5. 盒子模型的方式

盒子模型(Box Model)是 CSS 中一个重要的概念,它描述了元素在页面中所占的空间大小。理解盒子模型有助于精确控制元素的布局和样式。以下为你详细介绍盒子模型的组成、相关属性及计算方式。

盒子模型的组成

一个元素的盒子模型由内容区(Content)、内边距(Padding)、边框(Border)和外边距(Margin)四部分组成,从内到外依次排列:

  • 内容区(Content):是盒子的核心部分,用于显示元素的实际内容,如文本、图片等。内容区的大小由 widthheight 属性控制。
  • 内边距(Padding):是内容区与边框之间的距离,用于在内容周围添加额外的空间。内边距会增加盒子的整体大小,可以使用 padding-toppadding-rightpadding-bottompadding-left 分别设置四个方向的内边距,也可以使用 padding 简写属性一次性设置。
  • 边框(Border):围绕在内边距的外部,用于界定盒子的边界。边框的样式、宽度和颜色可以使用 border-styleborder-widthborder-color 属性来控制,同样也有 border 简写属性。
  • 外边距(Margin):是盒子与其他元素之间的距离,用于控制元素在页面中的位置。外边距不会影响盒子本身的大小,但会影响元素在页面中的布局。可以使用 margin-topmargin-rightmargin-bottommargin-left 分别设置四个方向的外边距,也可以使用 margin 简写属性一次性设置。
盒子模型的相关属性
  • width 和 height:用于设置内容区的宽度和高度。注意,这两个属性只影响内容区,不包括内边距、边框和外边距。
  • padding:可以使用以下几种方式设置内边距:
    • 一个值:如 padding: 10px;,表示四个方向的内边距都是 10px。
    • 两个值:如 padding: 10px 20px;,表示上下内边距为 10px,左右内边距为 20px。
    • 三个值:如 padding: 10px 20px 30px;,表示上内边距为 10px,左右内边距为 20px,下内边距为 30px。
    • 四个值:如 padding: 10px 20px 30px 40px;,分别表示上、右、下、左四个方向的内边距。
  • border:可以使用以下几种方式设置边框:
    • 简写属性:如 border: 1px solid black;,依次设置边框的宽度、样式和颜色。
    • 分别设置:使用 border-widthborder-styleborder-color 属性分别设置边框的宽度、样式和颜色。
  • margin:设置方式与 padding 类似,可以使用一个值、两个值、三个值或四个值来分别设置四个方向的外边距。
盒子模型的宽度和高度计算
  • 标准盒子模型:在标准盒子模型中,元素的总宽度和总高度的计算方式如下:
    • 总宽度 = width + padding-left + padding-right + border-left-width + border-right-width
    • 总高度 = height + padding-top + padding-bottom + border-top-width + border-bottom-width
    • 示例代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.box {width: 200px;height: 100px;padding: 10px;border: 5px solid black;margin: 20px;}</style>
</head><body><div class="box">标准盒子模型</div>
</body></html>

在上述示例中,.box 元素的总宽度为 200 + 10 + 10 + 5 + 5 = 230px,总高度为 100 + 10 + 10 + 5 + 5 = 130px。

  • 怪异盒子模型(IE 盒子模型):在怪异盒子模型中,widthheight 属性包含了内容区、内边距和边框的大小,但不包括外边距。即:
    • 总宽度 = width(包含内边距和边框) + margin-left + margin-right
    • 总高度 = height(包含内边距和边框) + margin-top + margin-bottom
    • 可以通过 box-sizing 属性来切换盒子模型,将其值设置为 border-box 可以使用怪异盒子模型,设置为 content-box 可以使用标准盒子模型。
    • 示例代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><style>.box {width: 200px;height: 100px;padding: 10px;border: 5px solid black;margin: 20px;box-sizing: border-box;}</style>
</head><body><div class="box">怪异盒子模型</div>
</body></html>

在上述示例中,.box 元素的总宽度为 200 + 20 + 20 = 240px,总高度为 100 + 20 + 20 = 140px,因为 widthheight 已经包含了内边距和边框的大小。

6. js的数组方法有哪些

在 JavaScript 中,数组是一种非常常用的数据结构,提供了许多实用的方法来操作和处理数组元素。以下将从数组的增删改查、排序与反转、迭代、转换等方面详细介绍常见的数组方法。

数组元素的增删改查
  • push():用于在数组的末尾添加一个或多个元素,并返回新的数组长度。
const arr = [1, 2, 3];
const newLength = arr.push(4, 5);
console.log(arr); // 输出: [1, 2, 3, 4, 5]
console.log(newLength); // 输出: 5
  • pop():用于移除数组的最后一个元素,并返回该元素。
const arr = [1, 2, 3];
const lastElement = arr.pop();
console.log(arr); // 输出: [1, 2]
console.log(lastElement); // 输出: 3
  • unshift():用于在数组的开头添加一个或多个元素,并返回新的数组长度。
const arr = [1, 2, 3];
const newLength = arr.unshift(-1, 0);
console.log(arr); // 输出: [-1, 0, 1, 2, 3]
console.log(newLength); // 输出: 5
  • shift():用于移除数组的第一个元素,并返回该元素。
const arr = [1, 2, 3];
const firstElement = arr.shift();
console.log(arr); // 输出: [2, 3]
console.log(firstElement); // 输出: 1
  • splice():可以用于删除、插入或替换数组中的元素。它接受三个参数,第一个参数是起始位置,第二个参数是要删除的元素个数,第三个及以后的参数是要插入的元素。
const arr = [1, 2, 3, 4, 5];
// 删除从索引 2 开始的 2 个元素
const removed = arr.splice(2, 2);
console.log(arr); // 输出: [1, 2, 5]
console.log(removed); // 输出: [3, 4]// 在索引 1 处插入元素 6 和 7
arr.splice(1, 0, 6, 7);
console.log(arr); // 输出: [1, 6, 7, 2, 5]
  • slice():用于从数组中提取指定范围的元素,并返回一个新的数组,原数组不受影响。它接受两个参数,第一个参数是起始位置,第二个参数是结束位置(不包含该位置的元素)。
const arr = [1, 2, 3, 4, 5];
const newArr = arr.slice(1, 3);
console.log(newArr); // 输出: [2, 3]
console.log(arr); // 输出: [1, 2, 3, 4, 5]
  • indexOf():用于查找数组中某个元素第一次出现的索引位置,如果未找到则返回 -1。
const arr = [1, 2, 3, 2];
const index = arr.indexOf(2);
console.log(index); // 输出: 1
  • lastIndexOf():用于查找数组中某个元素最后一次出现的索引位置,如果未找到则返回 -1。
const arr = [1, 2, 3, 2];
const lastIndex = arr.lastIndexOf(2);
console.log(lastIndex); // 输出: 3
  • includes():用于判断数组中是否包含某个元素,返回一个布尔值。
const arr = [1, 2, 3];
const hasTwo = arr.includes(2);
console.log(hasTwo); // 输出: true
数组的排序与反转
  • sort():用于对数组元素进行排序,默认情况下会将元素转换为字符串并按 Unicode 编码进行排序。也可以传入一个比较函数来自定义排序规则。
const arr = [3, 1, 2];
// 默认排序
arr.sort();
console.log(arr); // 输出: [1, 2, 3]// 自定义排序
const numbers = [3, 1, 2];
numbers.sort((a, b) => a - b);
console.log(numbers); // 输出: [1, 2, 3]
  • reverse():用于反转数组中元素的顺序,原数组会被修改。
const arr = [1, 2, 3];
arr.reverse();
console.log(arr); // 输出: [3, 2, 1]
数组的迭代方法
  • forEach():用于遍历数组中的每个元素,并对每个元素执行一次提供的函数。
const arr = [1, 2, 3];
arr.forEach((element) => {console.log(element);
});
// 输出:
// 1
// 2
// 3
  • map():用于创建一个新数组,新数组中的元素是原数组中每个元素经过某种处理后的结果。
const arr = [1, 2, 3];
const newArr = arr.map((element) => element * 2);
console.log(newArr); // 输出: [2, 4, 6]
  • filter():用于创建一个新数组,新数组中的元素是原数组中满足某个条件的元素。
const arr = [1, 2, 3, 4, 5];
const filteredArr = arr.filter((element) => element % 2 === 0);
console.log(filteredArr); // 输出: [2, 4]
  • reduce():用于将数组中的元素.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
    console.log(sum); // 输出: 15

  • some():用于判断数组中是否至少有一个元素满足某个条件,如果有则返回 true,否则返回 false

const arr = [1, 2, 3, 4, 5];
const hasEven = arr.some((element) => element % 2 === 0);
console.log(hasEven); // 输出: true
  • every():用于判断数组中的所有元素是否都满足某个条件,如果都满足则返回 true,否则返回 false
const arr = [2, 4, 6];
const allEven = arr.every((element) => element % 2 === 0);
console.log(allEven); // 输出: true
数组的转换方法
  • join():用于将数组中的所有元素连接成一个字符串,可以指定连接符。
const arr = [1, 2, 3];
const str = arr.join('-');
console.log(str); // 输出: '1-2-3'
  • concat():用于合并两个或多个数组,并返回一个新的数组,原数组不受影响。
const arr1 = [1, 2];
const arr2 = [3, 4];
const newArr = arr1.concat(arr2);
console.log(newArr); // 输出: [1, 2, 3, 4]

这些是 JavaScript 中一些常见的数组方法,掌握它们可以帮助你更高效地处理和操作数组。

7. vue2 vue3 区别

Vue 2 和 Vue 3 在多个方面存在区别,以下从架构设计、语法与 API、性能、生态系统等方面进行详细介绍:

架构设计
  • 响应式系统
    • Vue 2:基于 Object.defineProperty() 实现响应式。这种方式有一定局限性,例如无法检测对象属性的添加和删除,对于数组,部分方法(如通过索引修改元素)也不能触发响应式更新。
    • Vue 3:采用 Proxy 对象实现响应式系统。Proxy 可以劫持整个对象,并能拦截更多操作,解决了 Vue 2 中响应式的一些限制,能更好地检测对象属性的变化,包括属性的添加、删除以及数组元素的修改等。
  • 代码组织
    • Vue 2:主要使用选项式 API(Options API),将不同的逻辑(如数据、方法、生命周期钩子等)分散在不同的选项中,在处理复杂组件时,可能会导致代码碎片化,逻辑分散难以维护。
    • Vue 3:引入了组合式 API(Composition API),允许开发者根据逻辑关注点来组织代码,将相关的逻辑封装在一起,提高了代码的复用性和可维护性,尤其适合大型项目。
语法与 API
  • 组件定义
    • Vue 2:使用 Vue.extend() 或单文件组件(SFC)来定义组件,通过 export default 导出一个包含各种选项的对象。
    • Vue 3:仍然支持单文件组件,但在组合式 API 中,可以使用 <script setup> 语法糖来简化组件的定义,减少样板代码。
<!-- Vue 2 组件定义 -->
<template><div>{{ message }}</div>
</template><script>
export default {data() {return {message: 'Hello, Vue 2!'};}
};
</script><!-- Vue 3 组件定义(<script setup>) -->
<template><div>{{ message }}</div>
</template><script setup>
import { ref } from 'vue';
const message = ref('Hello, Vue 3!');
</script>
  • 生命周期钩子
    • Vue 2:有 beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestroyed 等生命周期钩子。
    • Vue 3:部分钩子名称发生了变化,beforeDestroy 改为 beforeUnmountdestroyed 改为 unmounted,并且在组合式 API 中可以使用 onBeforeMountonMounted 等函数来注册生命周期钩子。
// Vue 2 生命周期钩子
export default {created() {console.log('Vue 2: Component created');}
};// Vue 3 组合式 API 生命周期钩子
import { onMounted } from 'vue';export default {setup() {onMounted(() => {console.log('Vue 3: Component mounted');});}
};
  • 响应式数据定义
    • Vue 2:在 data 选项中定义响应式数据,使用 this 来访问。
    • Vue 3:使用 ref()reactive() 函数来创建响应式数据。ref() 用于创建单个值的响应式数据,reactive() 用于创建对象的响应式数据。
// Vue 2 响应式数据定义
export default {data() {return {count: 0};},methods: {increment() {this.count++;}}
};// Vue 3 响应式数据定义
import { ref } from 'vue';export default {setup() {const count = ref(0);const increment = () => {count.value++;};return {count,increment};}
};
性能
  • 渲染性能
    • Vue 2:渲染器在更新 DOM 时,使用虚拟 DOM 进行比较和更新,在处理大型组件树时,可能会有一定的性能开销。
    • Vue 3:重写了渲染器,采用了静态提升、PatchFlag 等优化技术,减少了虚拟 DOM 的比较范围,提高了渲染性能,尤其是在处理大型组件和频繁更新的场景下表现更优。
  • 内存占用
    • Vue 2:由于响应式系统的实现方式,在创建大量响应式对象时,可能会占用较多的内存。
    • Vue 3:Proxy 实现的响应式系统在内存使用上更加高效,减少了不必要的内存开销。
生态系统
  • 插件兼容性
    • Vue 2:拥有丰富的插件生态系统,但部分插件可能需要进行适配才能在 Vue 3 中使用。
    • Vue 3:随着时间的推移,越来越多的插件开始支持 Vue 3,但在过渡期间,可能会面临一些插件兼容性问题。
  • 工具链支持
    • Vue 2:与之配套的工具链(如 Vue CLI)已经非常成熟。
    • Vue 3:官方推出了 Vite 作为构建工具,它具有更快的冷启动和热更新速度,更适合现代前端开发。

8. vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。下面从核心概念、工作流程、使用方法、优缺点等方面详细介绍 Vuex。

核心概念
  • state:用于存储应用的状态数据,是一个对象,类似于组件中的 data。它是单一数据源,整个应用的所有组件共享这些状态。
  • getters:可以理解为 store 的计算属性,用于获取 state 中的数据,类似于组件中的 computed。当 state 中的数据发生变化时,getters 会自动更新。
  • mutations:是唯一可以修改 state 的地方,它是一些同步的方法。每个 mutation 都有一个字符串的事件类型和一个回调函数,回调函数接收 state 作为第一个参数。
  • actions:用于处理异步操作,如发送网络请求等。它提交的是 mutations,而不是直接变更状态。actions 可以包含任何异步操作。
  • modules:当应用变得非常复杂时,store 对象会变得臃肿。为了解决这个问题,Vuex 允许将 store 分割成多个模块(module),每个模块都有自己的 state、getters、mutations 和 actions。
工作流程
  1. 组件通过 $store.dispatch 方法触发一个 action。
  2. action 中可以执行异步操作,操作完成后通过 commit 方法触发一个 mutation。
  3. mutation 会同步地修改 state。
  4. state 的变化会触发组件的重新渲染,从而更新视图。
使用方法

以下是一个简单的 Vuex 使用示例:

安装 Vuex
npm install vuex --save
创建 store
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';Vue.use(Vuex);export default new Vuex.Store({state: {count: 0},getters: {doubleCount: state => state.count * 2},mutations: {increment(state) {state.count++;}},actions: {incrementAsync(context) {setTimeout(() => {context.commit('increment');}, 1000);}}
});
在 Vue 应用中使用 store
// main.js
import Vue from 'vue';
import App from './App.vue';
import store from './store';new Vue({store,render: h => h(App)
}).$mount('#app');
在组件中使用 store
<template><div><p>Count: {{ count }}</p><p>Double Count: {{ doubleCount }}</p><button @click="increment">Increment</button><button @click="incrementAsync">Increment Async</button></div>
</template><script>
export default {computed: {count() {return this.$store.state.count;},doubleCount() {return this.$store.getters.doubleCount;}},methods: {increment() {this.$store.commit('increment');},incrementAsync() {this.$store.dispatch('incrementAsync');}}
};
</script>
优缺点
优点
  • 集中式管理:将应用的所有状态集中存储在一个地方,方便管理和维护,特别是在大型应用中,可以清晰地了解状态的变化。
  • 可预测性:通过 mutations 来修改 state,保证了状态的变化是可预测的,便于调试和测试。
  • 插件支持:Vuex 支持插件,可以方便地实现日志记录、持久化存储等功能。
缺点
  • 增加复杂度:对于小型应用来说,使用 Vuex 可能会增加代码的复杂度,因为需要额外定义 state、getters、mutations 和 actions 等。
  • 学习成本:初学者需要花费一定的时间来理解 Vuex 的核心概念和工作流程。

9. 哈希路由和浏览器路由的区别

在前端开发中,哈希路由和浏览器路由(History 路由)是实现单页面应用(SPA)路由功能的两种常见方式,它们在原理、URL 表现形式、兼容性、服务器配置等方面存在明显区别,以下为你详细介绍:

原理
  • 哈希路由
    • 基于 URL 中的哈希值(即 # 后面的部分)变化来实现路由切换。当哈希值发生改变时,浏览器不会向服务器发送新的请求,而是触发 hashchange 事件,前端框架监听这个事件,根据不同的哈希值渲染对应的页面组件。
    • 例如,当 URL 从 https://example.com/#/home 变为 https://example.com/#/about 时,前端框架会捕获到哈希值的变化,然后根据配置的路由规则渲染关于页面的组件。
  • 浏览器路由
    • 利用 HTML5 的 History API(pushStatereplaceState)来实现。通过 pushState 方法可以在不刷新页面的情况下向浏览器历史记录中添加一条新的记录,同时改变 URL;replaceState 方法则是替换当前的历史记录。
    • 当用户点击浏览器的前进、后退按钮或者调用相应的 API 时,会触发 popstate 事件,前端框架监听该事件,根据新的 URL 渲染对应的页面组件。
URL 表现形式
  • 哈希路由
    • URL 中会包含一个 # 符号,后面跟着具体的路由路径。例如,https://example.com/#/products/123,其中 #/products/123 就是哈希路由的路径。
    • 哈希值的变化不会影响服务器端的请求,服务器始终只处理 # 之前的部分。
  • 浏览器路由
    • URL 看起来更像传统的 URL 路径,不包含 # 符号。例如,https://example.com/products/123,这种 URL 更简洁、美观,也更符合用户的使用习惯。
兼容性
  • 哈希路由
    • 兼容性非常好,几乎所有的浏览器都支持哈希值的变化和 hashchange 事件,不需要考虑浏览器的版本问题,即使是很旧的浏览器也能正常使用。
  • 浏览器路由
    • 依赖于 HTML5 的 History API,因此在一些旧版本的浏览器中可能不支持。在使用时需要考虑目标用户群体所使用的浏览器版本,如果需要兼容旧浏览器,可能需要进行额外的处理或降级处理。
服务器配置
  • 哈希路由
    • 服务器只需要返回单页面应用的入口文件(通常是 index.html)即可,因为哈希值的变化不会触发服务器的请求。无论用户访问的哈希路径是什么,服务器都不需要做特殊处理。
  • 浏览器路由
    • 服务器需要进行特殊配置。当用户直接访问某个路由路径或者刷新页面时,浏览器会向服务器发送该路径的请求。为了保证单页面应用能够正常工作,服务器需要在接收到这些请求时,始终返回单页面应用的入口文件 index.html,然后由前端框架根据 URL 来渲染相应的页面组件。
    • 例如,在使用 Node.js 和 Express 框架时,可以进行如下配置:
const express = require('express');
const app = express();// 静态文件服务
app.use(express.static(__dirname + '/public'));// 处理所有路由请求,返回 index.html
app.get('*', function(req, res) {res.sendFile(__dirname + '/public/index.html');
});const port = process.env.PORT || 3000;
app.listen(port, function() {console.log(`Server is running on port ${port}`);
});
历史记录管理
  • 哈希路由
    • 哈希值的变化会被记录到浏览器的历史记录中,用户可以使用浏览器的前进、后退按钮在不同的哈希路由之间切换。
  • 浏览器路由
    • 通过 pushStatereplaceState 方法可以更灵活地管理浏览器的历史记录。pushState 会添加一条新的历史记录,而 replaceState 会替换当前的历史记录,这在一些需要精确控制历史记录的场景中非常有用。

10. 浏览器缓存的几个方式

在浏览器中,缓存是一种重要的机制,它可以减少对服务器的请求,提高页面的加载速度和性能。常见的浏览器缓存方式主要分为强缓存和协商缓存,以下为你详细介绍:

强缓存

强缓存是指浏览器直接从本地磁盘或内存中读取资源,而不需要向服务器发送请求。通过设置响应头中的 ExpiresCache-Control 字段来控制。

  • Expires

    • 原理Expires 是 HTTP 1.0 中用于控制缓存的字段,它的值是一个具体的时间点(GMT 格式),表示资源的过期时间。在这个时间点之前,浏览器会直接使用本地缓存的资源,而不会向服务器发送请求。
    • 示例:服务器响应头中设置 Expires: Thu, 31 Dec 2025 23:59:59 GMT,表示该资源在 2025 年 12 月 31 日 23:59:59 之前都可以使用本地缓存。
    • 缺点:由于 Expires 使用的是服务器的时间,而客户端和服务器的时间可能存在差异,这可能导致缓存的过期时间不准确。
  • Cache-Control

    • 原理Cache-Control 是 HTTP 1.1 中引入的字段,用于更精确地控制缓存。它可以设置多个指令,常见的指令有 max-ageno-cacheno-store 等。
    • 常见指令及示例
      • max-age:表示资源的有效时间(以秒为单位)。例如,Cache-Control: max-age=3600 表示该资源在 3600 秒(即 1 小时)内是有效的,在这个时间内浏览器会直接使用本地缓存。
      • no-cache:表示浏览器在使用缓存之前必须先向服务器验证资源的有效性,即需要进行协商缓存。
      • no-store:表示禁止使用缓存,每次请求都必须从服务器获取最新的资源。
协商缓存

协商缓存是指浏览器在使用缓存之前会先向服务器发送一个请求,询问服务器该资源是否有更新。如果资源没有更新,服务器会返回 304 状态码,浏览器可以继续使用本地缓存;如果资源有更新,服务器会返回新的资源。通过设置响应头中的 ETagLast-Modified 字段来控制。

  • Last-Modified
    • 原理Last-Modified 是服务器返回资源时设置的字段,表示该资源的最后修改时间。当浏览器下次请求该资源时,会在请求头中添加 If-Modified-Since 字段,其值为上次响应头中 Last-Modified 的值。服务器接收到请求后,会比较资源的当前修改时间和 If-Modified-Since 的值,如果相同则返回 304 状态码,否则返回新的资源。
    • 示例:服务器响应头中设置 Last-Modified: Thu, 15 Feb 2024 12:00:00 GMT,浏览器下次请求时会在请求头中添加 If-Modified-Since: Thu, 15 Feb 2024 12:00:00 GMT
    • 缺点Last-Modified 只能精确到秒级,如果在 1 秒内资源发生了多次修改,它无法准确判断资源是否有更新。
  • ETag
    • 原理ETag 是服务器为资源生成的一个唯一标识符,通常是根据资源的内容生成的哈希值。当浏览器下次请求该资源时,会在请求头中添加 If-None-Match 字段,其值为上次响应头中 ETag 的值。服务器接收到请求后,会比较当前资源的 ETagIf-None-Match 的值,如果相同则返回 304 状态码,否则返回新的资源。
    • 示例:服务器响应头中设置 ETag: "123456789abcdef",浏览器下次请求时会在请求头中添加 If-None-Match: "123456789abcdef"
    • 优点ETag 可以更精确地判断资源是否有更新,即使资源在 1 秒内发生了多次修改,只要内容发生了变化,ETag 就会不同。
其他缓存方式
  • Service Worker
    • 原理Service Worker 是一种在浏览器后台运行的脚本,它可以拦截网络请求,实现离线缓存和消息推送等功能。开发者可以通过编写 Service Worker 代码,手动控制哪些资源需要缓存以及如何更新缓存。
    • 使用场景:适用于需要离线访问的应用,如 PWA(渐进式 Web 应用)。通过 Service Worker 缓存关键资源,用户在离线状态下也可以访问应用的部分功能。
  • Local Storage 和 Session Storage
    • 原理Local StorageSession Storage 是 HTML5 提供的用于在浏览器中存储数据的 API。Local Storage 用于长期存储数据,除非手动删除,否则数据不会过期;Session Storage 用于临时存储数据,数据在会话结束(即浏览器窗口关闭)时会被清除。
    • 使用场景:可以用于存储一些不经常变化的数据,如用户的偏好设置、应用的配置信息等,以减少对服务器的请求。

11. react hooks的优势

React Hooks 是 React 16.8 引入的新特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。相较于传统的 React class 组件,它具备诸多显著优势,以下为你详细介绍:

状态逻辑复用与代码组织
  • 复用状态逻辑:在 React Hooks 之前,复用有状态的逻辑是比较困难的,通常需要使用高阶组件(HOC)或 render props 等模式,这些模式会增加组件的嵌套层级,使代码变得复杂且难以理解。而 React Hooks 允许你将有状态的逻辑提取到自定义 Hook 中,在多个组件间复用,且不会增加组件的嵌套层级。
    • 例如,创建一个自定义 Hook 来处理表单输入的逻辑:
import { useState } from 'react';function useInput(initialValue) {const [value, setValue] = useState(initialValue);const onChange = (e) => {setValue(e.target.value);};return {value,onChange};
}function MyForm() {const nameInput = useInput('');const emailInput = useInput('');return (<form><input type="text" {...nameInput} placeholder="Name" /><input type="email" {...emailInput} placeholder="Email" /></form>);
}
  • 代码组织更清晰:Hooks 使你能够按照逻辑关注点来组织代码,将相关的逻辑放在一起,而不是像 class 组件那样将不同的逻辑分散在不同的生命周期方法中。比如,将与订阅相关的逻辑放在一起,将与状态管理相关的逻辑放在一起,提高了代码的可读性和可维护性。
简化复杂组件
  • 告别复杂的生命周期方法:在 class 组件中,处理副作用(如数据获取、订阅等)通常需要在多个生命周期方法中编写重复的代码,容易出错且难以维护。而 Hooks 提供了 useEffect 钩子,它可以将不同的副作用逻辑封装在一个函数中,并且可以根据依赖项的变化来控制副作用的执行时机,简化了代码逻辑。
import React, { useState, useEffect } from 'react';function MyComponent() {const [data, setData] = useState(null);useEffect(() => {// 模拟数据获取const fetchData = async () => {const response = await fetch('https://api.example.com/data');const result = await response.json();setData(result);};fetchData();// 清理函数,在组件卸载时执行return () => {// 取消订阅或清理资源};}, []);return (<div>{data ? <p>{data}</p> : <p>Loading...</p>}</div>);
}
  • 避免 this 指向问题:在 class 组件中,this 的指向问题常常会给开发者带来困扰,需要手动绑定方法的 this 或者使用箭头函数来避免问题。而 Hooks 是基于函数的,不存在 this 指向问题,使代码更加简洁明了。
更好的类型推断与静态分析
  • 类型推断更友好:对于使用 TypeScript 进行开发的项目,函数组件和 Hooks 比 class 组件更容易进行类型推断。因为函数组件的参数和返回值类型更加直观,TypeScript 可以更准确地推断出类型,减少了手动编写类型注解的工作量,提高了开发效率。
  • 便于静态分析:由于 Hooks 的函数式特性,静态分析工具(如 ESLint)可以更方便地对代码进行分析,检测潜在的错误和不规范的代码,有助于提高代码质量。
渐进式采用与未来扩展性
  • 渐进式采用:React Hooks 是渐进式的,你可以在现有的 class 组件中逐步引入 Hooks,而不需要立即重写整个应用。这使得团队可以在不影响现有业务的前提下,逐步享受 Hooks 带来的好处,降低了技术升级的成本和风险。
  • 未来扩展性:Hooks 为 React 的未来发展提供了更广阔的空间,新的 Hooks 可以不断被创建和添加,以满足不同的开发需求。同时,Hooks 的设计也使得 React 能够更好地与其他技术(如 Suspense、Concurrent Mode 等)集成,提升应用的性能和用户体验。

12. react 组件传值

在 React 中,组件传值是非常常见的操作,根据组件之间的关系不同,传值方式也有所不同,下面为你详细介绍几种常见的组件传值场景及方法。

1. 父组件向子组件传值

这是最常见的一种传值方式,通过在子组件标签上添加属性来传递数据,子组件通过 props 接收。

示例代码

// 子组件
import React from 'react';const ChildComponent = (props) => {return (<div>{/* 显示从父组件传递过来的值 */}<p>从父组件传递过来的值是: {props.message}</p></div>);
};export default ChildComponent;// 父组件
import React from 'react';
import ChildComponent from './ChildComponent';const ParentComponent = () => {const message = 'Hello from parent!';return (<div>{/* 将 message 作为属性传递给子组件 */}<ChildComponent message={message} /></div>);
};export default ParentComponent;

在上述代码中,父组件 ParentComponentmessage 变量作为属性传递给子组件 ChildComponent,子组件通过 props.message 来获取该值。

2. 子组件向父组件传值

子组件向父组件传值通常是通过父组件传递一个回调函数给子组件,子组件在需要的时候调用这个回调函数并传递数据。

示例代码

// 子组件
import React from 'react';const ChildComponent = (props) => {const handleClick = () => {// 调用父组件传递的回调函数,并传递数据props.onClick('Hello from child!');};return (<div><button onClick={handleClick}>点击向父组件传值</button></div>);
};export default ChildComponent;// 父组件
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';const ParentComponent = () => {const [messageFromChild, setMessageFromChild] = useState('');const handleChildClick = (message) => {// 更新状态,保存从子组件传递过来的消息setMessageFromChild(message);};return (<div><p>从子组件接收到的消息: {messageFromChild}</p>{/* 将回调函数传递给子组件 */}<ChildComponent onClick={handleChildClick} /></div>);
};export default ParentComponent;

在上述代码中,父组件 ParentComponenthandleChildClick 回调函数传递给子组件 ChildComponent,子组件在按钮点击时调用该回调函数并传递数据,父组件在回调函数中更新状态以保存接收到的数据。

3. 非父子组件传值(兄弟组件或跨层级组件)

对于非父子关系的组件传值,可以使用以下几种方法:

3.1 状态提升

将共享状态提升到最近的公共父组件中,然后通过上述的父传子和子传父的方式进行数据传递。

3.2 使用 Context API

Context 提供了一种在组件之间共享数据的方式,而不必显式地通过每个组件层级传递 props

示例代码

import React, { createContext, useContext, useState } from 'react';// 创建一个 Context 对象
const MyContext = createContext();// 提供 Context 的组件
const ContextProvider = ({ children }) => {const [sharedData, setSharedData] = useState('Initial data');return (<MyContext.Provider value={{ sharedData, setSharedData }}>{children}</MyContext.Provider>);
};// 使用 Context 的组件
const ComponentA = () => {const { setSharedData } = useContext(MyContext);const handleClick = () => {// 更新共享数据setSharedData('New data from ComponentA');};return (<div><button onClick={handleClick}>更新共享数据</button></div>);
};const ComponentB = () => {const { sharedData } = useContext(MyContext);return (<div><p>共享数据: {sharedData}</p></div>);
};const App = () => {return (<ContextProvider><ComponentA /><ComponentB /></ContextProvider>);
};export default App;

在上述代码中,通过 createContext 创建了一个 MyContextContextProvider 组件作为 MyContext 的提供者,将共享状态 sharedData 和更新状态的函数 setSharedData 传递给子组件。ComponentAComponentB 通过 useContext 钩子来获取共享数据和更新函数。

3.3 使用第三方库

如 Redux、MobX 等,这些库可以帮助管理应用的状态,实现组件之间的数据共享。

13. vue 组件传值

在 Vue 中,组件之间的通信和数据传递是非常重要的功能,根据组件之间的关系不同,有多种传值方式,以下是详细介绍:

1. 父组件向子组件传值

通过在子组件标签上绑定属性来传递数据,子组件使用 props 选项接收。

示例代码

<!-- 子组件 ChildComponent.vue -->
<template><div><!-- 显示从父组件传递过来的值 --><p>从父组件传递过来的值是: {{ message }}</p></div>
</template><script>
export default {props: {message: {type: String,required: true}}
};
</script><!-- 父组件 ParentComponent.vue -->
<template><div><!-- 将 message 作为属性传递给子组件 --><ChildComponent :message="parentMessage" /></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},data() {return {parentMessage: 'Hello from parent!'};}
};
</script>

在上述代码中,父组件 ParentComponent 通过 :message 绑定将 parentMessage 传递给子组件 ChildComponent,子组件通过 props 接收并显示该值。

2. 子组件向父组件传值

子组件通过 $emit 触发自定义事件,并传递数据,父组件监听该事件并处理数据。

示例代码

<!-- 子组件 ChildComponent.vue -->
<template><div><button @click="sendMessageToParent">点击向父组件传值</button></div>
</template><script>
export default {methods: {sendMessageToParent() {// 触发自定义事件并传递数据this.$emit('child-event', 'Hello from child!');}}
};
</script><!-- 父组件 ParentComponent.vue -->
<template><div><p>从子组件接收到的消息: {{ messageFromChild }}</p><!-- 监听子组件的自定义事件 --><ChildComponent @child-event="handleChildEvent" /></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},data() {return {messageFromChild: ''};},methods: {handleChildEvent(message) {// 更新状态,保存从子组件传递过来的消息this.messageFromChild = message;}}
};
</script>

在上述代码中,子组件 ChildComponent 通过 $emit 触发 child-event 事件并传递数据,父组件 ParentComponent 监听该事件,并在 handleChildEvent 方法中处理接收到的数据。

3. 非父子组件传值(兄弟组件或跨层级组件)
3.1 事件总线(Event Bus)

创建一个全局的事件总线对象,用于组件之间的通信。

示例代码

// event-bus.js
import Vue from 'vue';
export const eventBus = new Vue();
<!-- 发送数据的组件 SenderComponent.vue -->
<template><div><button @click="sendMessage">发送消息</button></div>
</template><script>
import { eventBus } from './event-bus.js';export default {methods: {sendMessage() {// 通过事件总线触发自定义事件并传递数据eventBus.$emit('message-sent', 'Hello from sender!');}}
};
</script><!-- 接收数据的组件 ReceiverComponent.vue -->
<template><div><p>接收到的消息: {{ receivedMessage }}</p></div>
</template><script>
import { eventBus } from './event-bus.js';export default {data() {return {receivedMessage: ''};},created() {// 通过事件总线监听自定义事件eventBus.$on('message-sent', (message) => {this.receivedMessage = message;});},beforeDestroy() {// 销毁时取消监听,避免内存泄漏eventBus.$off('message-sent');}
};
</script>

在上述代码中,eventBus 作为全局的事件总线对象,SenderComponent 通过 $emit 触发事件,ReceiverComponent 通过 $on 监听事件并处理数据。

3.2 Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

示例代码

// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';Vue.use(Vuex);export default new Vuex.Store({state: {sharedData: 'Initial data'},mutations: {updateSharedData(state, newData) {state.sharedData = newData;}},actions: {updateData({ commit }, newData) {commit('updateSharedData', newData);}},getters: {getSharedData: (state) => state.sharedData}
});
<!-- 修改数据的组件 ModifyComponent.vue -->
<template><div><button @click="updateData">更新共享数据</button></div>
</template><script>
export default {methods: {updateData() {// 调用 action 更新共享数据this.$store.dispatch('updateData', 'New data from ModifyComponent');}}
};
</script><!-- 显示数据的组件 DisplayComponent.vue -->
<template><div><p>共享数据: {{ sharedData }}</p></div>
</template><script>
export default {computed: {sharedData() {// 通过 getter 获取共享数据return this.$store.getters.getSharedData;}}
};
</script><!-- 根组件 App.vue -->
<template><div><ModifyComponent /><DisplayComponent /></div>
</template><script>
import ModifyComponent from './ModifyComponent.vue';
import DisplayComponent from './DisplayComponent.vue';
import store from './store';export default {components: {ModifyComponent,DisplayComponent},store
};
</script>

在上述代码中,通过 Vuex 创建了一个全局的状态管理仓库,ModifyComponent 通过 dispatch 调用 action 来更新共享数据,DisplayComponent 通过 getter 获取共享数据。

14. 如何进行性能优化

在前端开发中,无论是 React 还是 Vue 项目,性能优化都是至关重要的,它可以提升用户体验,减少加载时间。下面分别从不同方面介绍性能优化的方法。

代码层面优化
1. 组件渲染优化
  • React
    • 使用 React.memo(函数组件)React.memo 是一个高阶组件,它会对组件的 props 进行浅比较,如果 props 没有变化,则跳过渲染。
import React from 'react';const MyComponent = React.memo((props) => {return <div>{props.message}</div>;
});export default MyComponent;

使用 shouldComponentUpdate(类组件)**:在类组件中,可以通过 shouldComponentUpdate 生命周期方法来控制组件是否重新渲染。

import React, { Component } from 'react';class MyClassComponent extends Component {shouldComponentUpdate(nextProps, nextState) {return this.props.message!== nextProps.message;}render() {return <div>{this.props.message}</div>;}
}export default MyClassComponent;
  • Vue
    • 使用 v-if 代替 v-showv-if 是真正的条件渲染,当条件为 false 时,组件不会被渲染到 DOM 中;而 v-show 只是通过 CSS 的 display 属性来控制元素的显示与隐藏,即使条件为 false,元素仍然会被渲染到 DOM 中。因此,当元素需要频繁切换显示状态时,使用 v-show;当元素的显示状态不经常改变时,使用 v-if
<template><div><div v-if="isShow">显示内容</div><div v-show="isShow">显示内容</div></div>
</template><script>
export default {data() {return {isShow: true};}
};
</script>

使用 keep-alive**:keep-alive 是一个抽象组件,它可以缓存包裹的组件实例,避免重复渲染。当组件在 keep-alive 中切换时,不会销毁和重新创建组件实例,而是直接从缓存中获取。

<template><div><keep-alive><component :is="currentComponent"></component></keep-alive></div>
</template><script>
export default {data() {return {currentComponent: 'ComponentA'};}
};
</script>
2. 减少不必要的渲染
  • React:避免在渲染函数中使用内联函数,因为每次渲染时内联函数都会重新创建,这可能会导致子组件不必要的重新渲染。可以将内联函数提取到组件外部或使用 useCallback 钩子进行缓存。
import React, { useCallback } from 'react';const ParentComponent = () => {const handleClick = useCallback(() => {console.log('Clicked');}, []);return <ChildComponent onClick={handleClick} />;
};const ChildComponent = React.memo((props) => {return <button onClick={props.onClick}>Click me</button>;
});export default ParentComponent;
  • Vue:避免在模板中使用复杂的计算属性或方法,因为每次数据变化时,这些计算属性或方法都会重新计算。可以将复杂的逻辑提取到计算属性或方法中,并使用缓存机制。
<template><div><p>{{ filteredList.length }}</p></div>
</template><script>
export default {data() {return {list: [1, 2, 3, 4, 5]};},computed: {filteredList() {return this.list.filter(item => item > 3);}}
};
</script>
资源加载优化
1. 代码分割
  • React:使用动态导入(import())和 React.lazySuspense 来实现代码分割,将应用分割成多个小块,按需加载。
import React, { lazy, Suspense } from 'react';const LazyComponent = lazy(() => import('./LazyComponent'));const App = () => {return (<div><Suspense fallback={<div>Loading...</div>}><LazyComponent /></Suspense></div>);
};export default App;
  • Vue:使用动态导入(import())来实现代码分割,将组件按需加载。
<template><div><button @click="loadComponent">加载组件</button><component v-if="componentLoaded" :is="LazyComponent"></component></div>
</template><script>
export default {data() {return {componentLoaded: false,LazyComponent: null};},methods: {async loadComponent() {const { default: LazyComponent } = await import('./LazyComponent.vue');this.LazyComponent = LazyComponent;this.componentLoaded = true;}}
};
</script>
2. 图片优化
  • 压缩图片:使用图片压缩工具(如 TinyPNG)对图片进行压缩,减少图片的文件大小。
  • 使用响应式图片:使用 srcsetsizes 属性,根据不同的屏幕尺寸和设备像素比加载不同分辨率的图片。
<imgsrcset="small.jpg 500w, medium.jpg 1000w, large.jpg 2000w"sizes="(max-width: 500px) 500px, (max-width: 1000px) 1000px, 2000px"src="medium.jpg"alt="Responsive Image"
/>
网络请求优化
1. 缓存请求结果
  • 使用浏览器的缓存机制(如 localStoragesessionStorageIndexedDB)来缓存网络请求的结果,避免重复请求相同的数据。
async function fetchData() {const cachedData = localStorage.getItem('myData');if (cachedData) {return JSON.parse(cachedData);}const response = await fetch('https://api.example.com/data');const data = await response.json();localStorage.setItem('myData', JSON.stringify(data));return data;
}
2. 合并请求

将多个相关的请求合并为一个请求,减少网络请求的次数,从而提高性能。例如,在后端提供一个接口,将多个数据一次性返回。

其他优化
1. 服务器端渲染(SSR)
  • React:可以使用 Next.js 等框架来实现服务器端渲染,在服务器端生成 HTML 内容并发送到客户端,减少客户端的渲染时间,提高首屏加载速度。

  • Vue:可以使用 Nuxt.js 等框架来实现服务器端渲染,将 Vue 应用在服务器端渲染成 HTML 字符串,然后发送到客户端。

2. 懒加载插件和库

只在需要的时候加载插件和库,避免一次性加载过多的代码。例如,使用 IntersectionObserver 来实现图片或组件的懒加载。

15. 前端监控

前端监控是指对网页或 Web 应用程序在用户端的运行情况进行监测和数据收集,以帮助开发者及时发现并解决性能问题、错误异常等,从而提升用户体验。下面从监控的内容、实现方式和常用工具几个方面详细介绍。

监控内容
1. 性能监控
  • 页面加载性能
    • 首屏加载时间:从用户请求页面到首屏内容完全展示的时间。这是衡量用户体验的重要指标,过长的首屏加载时间会导致用户流失。
    • DOMContentLoaded 时间:表示 HTML 文档完全加载并解析完成的时间,不等待样式表、图片等资源加载。
    • Load 时间:页面所有资源(包括图片、脚本等)加载完成的时间。
  • 资源加载性能:监控各种资源(如 CSS、JavaScript、图片等)的加载时间、加载是否成功等,找出加载缓慢或失败的资源,进行针对性优化。
2. 错误监控
  • JavaScript 错误:包括语法错误、运行时错误等。例如,未定义变量、函数调用错误等,这些错误会导致页面功能异常。
  • 资源加载错误:如图片、CSS、JavaScript 文件加载失败,会影响页面的正常显示和功能。
  • 接口请求错误:监控与后端接口的交互情况,捕获请求失败、响应超时等问题,确保数据的正常获取。
3. 用户行为监控
  • 页面浏览行为:记录用户访问的页面路径、停留时间、滚动行为等,了解用户的浏览习惯和关注点。
  • 交互行为:监控用户的点击、输入、表单提交等操作,分析用户与页面的交互情况,优化交互设计。
实现方式
1. 性能监控实现
  • 使用浏览器的 Performance API:现代浏览器提供了 Performance 对象,可以方便地获取页面性能相关的数据。
window.addEventListener('load', function () {const performanceData = window.performance.timing;// 首屏加载时间(这里简单用 loadEventEnd - navigationStart 表示)const firstScreenLoadTime = performanceData.loadEventEnd - performanceData.navigationStart;console.log('首屏加载时间:', firstScreenLoadTime);// DOMContentLoaded 时间const domContentLoadedTime = performanceData.domContentLoadedEventEnd - performanceData.navigationStart;console.log('DOMContentLoaded 时间:', domContentLoadedTime);// Load 时间const loadTime = performanceData.loadEventEnd - performanceData.navigationStart;console.log('Load 时间:', loadTime);
});
  • 资源加载监控:可以通过监听 PerformanceObserver 来监控资源加载情况。
const observer = new PerformanceObserver((list) => {const entries = list.getEntries();entries.forEach((entry) => {console.log('资源名称:', entry.name);console.log('资源加载时间:', entry.duration);});
});observer.observe({ entryTypes: ['resource'] });
2. 错误监控实现
  • JavaScript 错误监控:使用 window.onerror 全局错误捕获函数来捕获未处理的 JavaScript 错误。
window.onerror = function (message, source, lineno, colno, error) {console.log('JavaScript 错误:', message);console.log('错误文件:', source);console.log('错误行号:', lineno);console.log('错误列号:', colno);console.log('错误对象:', error);// 可以将错误信息发送到后端服务器进行记录// sendErrorToServer(message, source, lineno, colno, error);return true;
};
  • 资源加载错误监控:监听 error 事件来捕获资源加载错误。
<img src="nonexistent-image.jpg" onerror="console.log('图片加载失败')" />
<script src="nonexistent-script.js" onerror="console.log('脚本加载失败')"></script>
  • 接口请求错误监控:在使用 fetchXMLHttpRequest 进行接口请求时,捕获请求过程中的错误。
fetch('https://api.example.com/data').then((response) => {if (!response.ok) {throw new Error('接口请求失败');}return response.json();}).catch((error) => {console.log('接口请求错误:', error);});
3. 用户行为监控实现
  • 页面浏览行为监控:通过监听 window.onloadwindow.onunload 等事件来记录用户的页面访问和停留时间。
let pageLoadTime;
window.addEventListener('load', function () {pageLoadTime = new Date().getTime();
});window.addEventListener('unload', function () {const pageStayTime = new Date().getTime() - pageLoadTime;console.log('页面停留时间:', pageStayTime);// 可以将停留时间数据发送到后端服务器
});
  • 交互行为监控:监听元素的 clickinput 等事件来记录用户的交互操作。
<button id="myButton">点击我</button>
<script>const button = document.getElementById('myButton');button.addEventListener('click', function () {console.log('按钮被点击');// 可以将点击事件数据发送到后端服务器});
</script>
常用工具
1. 开源工具
  • Sentry:是一个强大的错误监控平台,支持多种前端框架(如 React、Vue 等),可以实时捕获和分析 JavaScript 错误、性能问题等,并提供详细的错误堆栈信息和可视化界面。
  • Google Analytics:是一款免费的网站分析工具,主要用于用户行为监控。可以跟踪用户的页面浏览、流量来源、转化率等数据,帮助开发者了解用户行为和优化网站。
2. 商业工具
  • 听云:提供全面的前端性能监控解决方案,包括页面加载性能、资源加载性能、错误监控、用户行为分析等功能,支持多种浏览器和移动设备。
  • OneAPM:可以实时监控前端应用的性能和错误,提供详细的性能指标和分析报告,帮助开发者快速定位和解决问题。

16. get post 区别

GETPOST 是 HTTP 协议中常用的两种请求方法,它们在多个方面存在区别,以下为你详细介绍:

1. 语义
  • GET:通常用于从服务器获取资源。它是一种只读操作,意味着使用 GET 请求不会对服务器上的资源产生实质性的修改。例如,当你在浏览器中输入一个网址访问网页,或者在搜索引擎中输入关键词进行搜索时,浏览器通常会使用 GET 请求来获取相应的页面或搜索结果。
  • POST:主要用于向服务器提交数据,可能会导致服务器上的资源发生创建、更新或删除等变化。比如在注册新用户、提交表单数据、上传文件等场景下,常使用 POST 请求将用户输入的数据发送到服务器进行处理。
2. 参数传递方式
  • GET:请求参数会附加在 URL 的后面,以键值对的形式出现,多个参数之间用 & 符号分隔。例如:https://example.com/api/users?name=John&age=30。这种方式使得参数在浏览器的地址栏中可见,因此不适合传递敏感信息,如密码等。
  • POST:请求参数通常放在请求体(body)中,不会显示在 URL 里。这使得 POST 更适合传递大量数据或敏感信息,如用户的登录密码、信用卡号等。例如在使用 fetch API 发送 POST 请求时:
const data = {name: 'John',age: 30
};
fetch('https://example.com/api/users', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(data)
});
3. 安全性
  • GET:由于参数暴露在 URL 中,容易被他人获取,存在一定的安全风险。此外,URL 的长度是有限制的(不同浏览器和服务器的限制不同),这也限制了 GET 请求所能携带的参数数量和大小。而且,GET 请求可能会被浏览器缓存,包含敏感信息的 GET 请求被缓存后可能会导致信息泄露。
  • POST:参数在请求体中传输,相对更安全。同时,POST 请求一般不会被缓存(除非特别设置),避免了因缓存导致的信息泄露问题。不过,这并不意味着 POST 请求就绝对安全,在传输敏感数据时,仍需要使用 HTTPS 协议进行加密传输。
4. 幂等性
  • 幂等性:指的是对同一操作的多次请求所产生的影响与一次请求的影响相同。
  • GET:是幂等的请求方法。多次发送相同的 GET 请求,得到的结果应该是相同的,不会对服务器上的资源产生额外的影响。例如,多次请求同一篇文章的详情页,每次得到的文章内容应该是一样的。
  • POST:通常不是幂等的。每次发送相同的 POST 请求可能会在服务器上创建新的资源或对现有资源进行不同的修改。比如多次提交注册表单,可能会在数据库中创建多个相同的用户记录。
5. 缓存
  • GET:浏览器通常会对 GET 请求进行缓存。当再次发送相同的 GET 请求时,浏览器可能会直接从本地缓存中获取响应结果,而不再向服务器发送请求。这可以提高页面的加载速度,但在某些情况下,如数据实时性要求较高时,可能会导致显示的信息不是最新的。可以通过设置请求头或在 URL 中添加随机参数等方式来避免缓存。
  • POST:默认情况下,浏览器不会对 POST 请求进行缓存。因为 POST 请求通常用于提交数据,每次请求可能会对服务器资源产生不同的影响,缓存 POST 请求可能会导致数据不一致的问题。
6. 数据长度限制
  • GET:由于参数是附加在 URL 后面,而 URL 有长度限制,所以 GET 请求所能携带的参数长度是有限的。不同浏览器和服务器对 URL 长度的限制不同,一般来说,大多数浏览器支持的 URL 长度在 2000 个字符左右。
  • POSTPOST 请求的数据放在请求体中,理论上对数据长度没有限制。但实际上,服务器可能会对请求体的大小进行限制,以防止恶意用户上传过大的文件或数据。

17. 跨域解决方案

在 Web 开发中,由于浏览器的同源策略限制,不同源(协议、域名、端口任意一个不同)的页面之间进行数据交互和资源共享会受到限制,这就产生了跨域问题。以下为你介绍几种常见的跨域解决方案:

1. JSONP(JSON with Padding)
  • 原理:JSONP 是一种古老的跨域数据交互技术,它利用了 <script> 标签的 src 属性不受同源策略限制的特点。服务器将数据包装在一个回调函数中返回给客户端,客户端通过动态创建 <script> 标签来请求服务器数据,当脚本加载完成后,会自动执行回调函数,从而获取到服务器返回的数据。

  • 示例代码

    • 前端代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8">
</head><body><script>function handleResponse(data) {console.log('Received data:', data);}const script = document.createElement('script');script.src = 'http://example.com/api/data?callback=handleResponse';document.body.appendChild(script);</script>
</body></html>

后端代码(Node.js + Express)**

const express = require('express');
const app = express();app.get('/api/data', (req, res) => {const callback = req.query.callback;const data = { message: 'Hello from server!' };const jsonpResponse = `${callback}(${JSON.stringify(data)})`;res.send(jsonpResponse);
});const port = 3000;
app.listen(port, () => {console.log(`Server is running on port ${port}`);
});
  • 优缺点
    • 优点:兼容性好,可在古老的浏览器中使用。
    • 缺点:只支持 GET 请求,安全性较低,容易受到 XSS 攻击。
2. CORS(Cross-Origin Resource Sharing)
  • 原理:CORS 是一种现代的跨域解决方案,它是 W3C 标准,允许浏览器和服务器进行跨域通信。服务器通过设置响应头来告诉浏览器哪些跨域请求是被允许的。当浏览器发起跨域请求时,会自动在请求头中添加 Origin 字段,服务器根据该字段判断是否允许该请求,并在响应头中添加相应的跨域许可信息。

  • 示例代码

    • 后端代码(Node.js + Express)
const express = require('express');
const app = express();// 允许所有域名的跨域请求
app.use((req, res, next) => {res.setHeader('Access-Control-Allow-Origin', '*');res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');next();
});app.get('/api/data', (req, res) => {const data = { message: 'Hello from server!' };res.json(data);
});const port = 3000;
app.listen(port, () => {console.log(`Server is running on port ${port}`);
});

前端代码

fetch('http://example.com/api/data').then(response => response.json()).then(data => console.log(data)).catch(error => console.error(error));
  • 优缺点
    • 优点:支持所有的 HTTP 请求方法,安全性高,是现代浏览器推荐的跨域解决方案。
    • 缺点:需要服务器端进行配置,对于一些老旧的服务器可能不支持。
3. 代理服务器
  • 原理:在同源的服务器上设置一个代理,前端将请求发送到同源的代理服务器,代理服务器再将请求转发到目标服务器,并将目标服务器的响应返回给前端。由于前端和代理服务器是同源的,所以不会受到同源策略的限制。
  • 示例代码(开发环境下使用 Webpack Dev Server 代理)
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {// ...其他配置devServer: {proxy: {'/api': {target: 'http://example.com',changeOrigin: true,pathRewrite: { '^/api': '' }}}}
};
  • 优缺点
    • 优点:配置相对简单,不需要修改目标服务器的代码,适用于开发环境和生产环境。
    • 缺点:增加了服务器的负载,需要额外的服务器资源来运行代理服务器。
4. WebSocket
  • 原理:WebSocket 是一种双向通信协议,它不受同源策略的限制。通过 WebSocket 建立的连接可以在不同源的页面之间进行实时数据传输。
  • 示例代码
    • 后端代码(Node.js + ws 库)
const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', (ws) => {ws.on('message', (message) => {console.log('Received message:', message);ws.send('Message received!');});
});

前端代码

const socket = new WebSocket('ws://example.com:8080');socket.onopen = () => {console.log('Connected to server');socket.send('Hello from client');
};socket.onmessage = (event) => {console.log('Received message from server:', event.data);
};socket.onclose = () => {console.log('Disconnected from server');
};
  • 优缺点
    • 优点:支持双向通信,实时性高,不受同源策略限制。
    • 缺点:需要服务器端支持 WebSocket 协议,开发和维护成本相对较高。

18. ref 和 reative 区别

在 Vue 3 中,refreactive 都是用于创建响应式数据的 API,但它们在使用场景、实现方式和数据类型处理上存在一些区别,下面为你详细介绍:

1. 定义和基本使用
ref
  • 定义ref 用于创建一个包含响应式数据的引用对象。它可以将基本数据类型(如 numberstringboolean 等)或对象转换为响应式数据。
  • 基本使用:通过 ref 函数创建一个响应式引用,使用时需要通过 .value 来访问和修改其值。
<template><div><p>{{ count.value }}</p><button @click="increment">增加</button></div>
</template><script setup>
import { ref } from 'vue';const count = ref(0);const increment = () => {count.value++;
};
</script>
reactive
  • 定义reactive 用于创建一个响应式对象。它接收一个普通对象作为参数,并返回一个响应式的代理对象。
  • 基本使用:直接使用响应式对象的属性进行访问和修改。
<template><div><p>{{ user.name }}</p><button @click="changeName">修改姓名</button></div>
</template><script setup>
import { reactive } from 'vue';const user = reactive({name: 'John',age: 30
});const changeName = () => {user.name = 'Jane';
};
</script>
2. 适用数据类型
ref
  • 适用于基本数据类型(如 numberstringboolean 等),因为基本数据类型在 JavaScript 中是按值传递的,使用 ref 可以将其包装成响应式对象。
  • 也可以用于引用对象、数组等复杂数据类型。
<script setup>
import { ref } from 'vue';const message = ref('Hello'); // 基本数据类型
const person = ref({ name: 'Alice', age: 25 }); // 对象
const numbers = ref([1, 2, 3]); // 数组
</script>
reactive
  • 主要用于创建响应式的对象和数组,不能直接用于基本数据类型。如果尝试将基本数据类型传递给 reactive,它不会产生响应式效果。
<script setup>
import { reactive } from 'vue';const user = reactive({name: 'Bob',hobbies: ['Reading', 'Swimming']
}); // 响应式对象const list = reactive([{ id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }]); // 响应式数组
</script>
3. 响应式原理和内部实现
ref
  • ref 创建的是一个包含 .value 属性的对象,Vue 会对这个 .value 属性进行响应式劫持。当 .value 的值发生变化时,Vue 会自动更新与之绑定的 DOM。
  • 在模板中使用 ref 时,Vue 会自动解包 .value,因此可以直接使用 {{ count }} 而不是 {{ count.value }},但在 JavaScript 代码中仍然需要通过 .value 来访问和修改值。
reactive
  • reactive 使用 JavaScript 的 Proxy 对象来实现响应式。它会对传入的对象进行代理,拦截对象属性的读取和设置操作,当属性值发生变化时,触发相应的更新。
  • 由于 reactive 直接返回一个响应式的代理对象,所以可以直接通过对象属性来访问和修改数据,无需额外的 .value 操作。
4. 解构和展开操作
ref
  • 当对 ref 对象进行解构时,会失去响应式特性。如果需要在解构后仍然保持响应式,需要使用 toRefs 函数。
<script setup>
import { ref, toRefs } from 'vue';const person = ref({ name: 'Tom', age: 20 });
const { name, age } = toRefs(person.value); // 使用 toRefs 保持响应式
</script>
reactive
  • reactive 对象进行解构时,解构出的属性仍然保持响应式,因为 reactive 是基于 Proxy 实现的。
<script setup>
import { reactive } from 'vue';const user = reactive({ name: 'Lily', gender: 'Female' });
const { name, gender } = user; // 解构后属性仍保持响应式
</script>

综上所述,ref 更适合处理基本数据类型和需要在函数之间传递单个值的场景,而 reactive 则更适合处理复杂的对象和数组结构。

相关文章:

2025年01月03日微创网络(杭州银行外包)前端面试

目录 html 块级元素和行内元素有哪些阴影的几个属性垂直水平居中的实现方式定位的几种方式盒子模型的方式js的数组方法有哪些vue2 vue3 区别vuex哈希路由和浏览器路由的区别浏览器缓存的几个方式react hooks的优势react 组件传值vue 组件传值如何进行性能优化前端监控get post…...

工单分类总结

微调BERT-base模型,构建层次化分类器,Top-3准确率达97.2%,并自动识别出问题的关键类别 1. 具体微调的BERT-base模型是什么模型? BERT-base模型是一个预训练的Transformer模型,包含12个Transformer块、12个自注意头和隐藏大小为768。该模型在大规模文本数据上进行了预训练…...

2025年了,5G还有三个新变化

最近舆论开始讨论5G为什么不火了&#xff1f;5G是不是停滞发展了。 实际上&#xff0c;5G不仅在发展&#xff0c;还迎来了它的升级版5G-A。 在今年西班牙举行的世界移动通信大会上&#xff0c;5G-A就是焦点之一。 被誉为全球通信领域风向标的MWC&#xff0c;汇聚了华为、中兴通…...

区块链交易所平台开发全解析

在数字化飞速发展的今天&#xff0c;区块链技术已成为金融领域的核心驱动力之一。作为数字货币交易的关键平台&#xff0c;区块链交易所的开发不仅涉及复杂的技术环节&#xff0c;还需要兼顾用户体验、安全性、合规性等多个方面。本文将深入探讨区块链交易所平台的开发流程、关…...

hexo+butterfly博客功能完善和美化(四)---博客上传

hexobutterfly博客功能完善和美化&#xff08;四&#xff09;—博客上传 这是最后一期讲美化和功能完善了&#xff0c;笔者会陆续把csdn上面的博客转移到我的博客网站上面&#xff0c;大家可以来访问 Darlingの妙妙屋 文章目录 hexobutterfly博客功能完善和美化&#xff08;…...

源码分析之Leaflet中dom模块DomEvent.DoubleTap的实现原理

概述 DomEvent.DoubleTap模块是Leaflet中用于模拟双击(dbclick)事件的模块&#xff0c;主要解决移动端浏览器对双击事件支持不完善或延迟的问题&#xff0c;同时避免与标签(<label>)关联的表单元素误触发。 源码分析 源码实现如下 DomEvent.DoubleTap的源码实现如下&…...

记录一次,rabbitmq开启stomp插件之后,还是连不上15674端口的问题

原因是装在docker 里面的rabbitmq 没有映射15674端口&#xff0c;需重新删除容器之后重新运行 docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 -p 15674:15674 -p 1883:1883 -p 15675:15675 rabbitmq:版本号 进入docker容器开启插件 docker exec -it rabbitm…...

git clone项目报错fatal: fetch-pack: invalid index-pack output问题

前情回顾&#xff1a;git项目放在公司服务器上面&#xff0c;克隆等操作需要连接VPN才能操作。由于项目比较大&#xff0c;网速比较慢&#xff0c;克隆项目经常出现fetch-pack: invalid index-pack output。在网上查找各种解决方法。也就这一种有点效果。仅供参考&#xff0c;不…...

【access开发】导入excel 并生成表

hi&#xff0c;大家好呀&#xff01; 最近天气越来越暖了&#xff0c;在这个春暖花开的季节了&#xff0c;每天心情应该都是美美的&#xff0c;正所谓一年之计在于春&#xff0c;在这个美好的季节&#xff0c;大家一起努力学习学习吧&#xff01;那我们来看看今天学点啥呢&…...

利用knn算法实现手写数字分类

利用knn算法实现手写数字分类 1.作者介绍2.KNN算法2.1KNN&#xff08;K-Nearest Neighbors&#xff09;算法核心思想2.2KNN算法的工作流程2.3优缺点2.4 KNN算法图示介绍 3.实验过程3.1安装所需库3.2 MNIST数据集3.3 导入手写数字图像进行分类3.4 完整代码3.5 实验结果 1.作者介…...

从零开始实现 C++ TinyWebServer 处理请求 HttpRequest类详解

文章目录 HTTP 请求报文HttpRequest 类实现 Init() 函数实现 ParseRequestLine() 函数实现 ParseHeader() 函数实现 ParsePath() 函数实现 ParseBody() 函数实现 ParsePost() 函数实现 ParseFromUrlEncoded() 函数实现 UserVerify() 函数实现 Parse() 函数HttpRequest 代码Http…...

Android开发layer-list

Android开发layer-list 它的用处可以在drawable上进行多图拼接&#xff0c;比如启动页&#xff0c;不想图片被拉伸就这么做。还有做某些线突出来。 示例代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <layer-list xmlns:android&q…...

如何提升库存系统的高并发和稳定性:算法与设计模式

库存系统是企业运营的核心模块&#xff0c;尤其是在电商、零售和供应链管理中&#xff0c;系统的高并发和稳定性直接影响订单处理的准确性和效率。面对海量订单、复杂的库存管理需求&#xff0c;如何在高并发环境下确保库存数据的准确性和系统的稳定性&#xff1f;本文将从架构…...

关于CNN,RNN,GAN,GNN,DQN,Transformer,LSTM,DBN你了解多少

以下是神经网络中常见的几种模型的简要介绍&#xff1a; 1. ​CNN (Convolutional Neural Network, 卷积神经网络) ​用途: 主要用于图像处理和计算机视觉任务。​特点: 通过卷积核提取局部特征&#xff0c;具有平移不变性&#xff0c;能够有效处理高维数据&#xff08;如图像…...

设计模式之装饰器模式

装饰器模式(Decorator)依然是我们设计模式中的结构型模式&#xff0c;其中的构造思想仍然是对多个类进行组合使用&#xff0c;以达成系统调用实现指定功能的设计模式。装饰器模式不论在我们日常开发过程中还是在我们提升技术阅读源码过程中都是比较常见的&#xff0c;但是整体学…...

Mistral AI发布开源多模态模型Mistral Small 3.1:240亿参数实现超越GPT-4o Mini的性能

法国人工智能初创公司Mistral AI于2025年3月正式推出新一代开源模型Mistral Small 3.1 &#xff0c;该模型凭借240亿参数的轻量级设计&#xff0c;在多项基准测试中表现优异&#xff0c;甚至超越了Google的Gemma 3和OpenAI的GPT-4o Mini等主流专有模型。 1、核心特性与优势 多…...

SpringBoot3实战(SpringBoot3+Vue3基本增删改查、前后端通信交互、配置后端跨域请求、数据批量删除(超详细))(3)

目录 一、从0快速搭建SpringBoot3工程、SpringBoot3集成MyBatis、PageHelper分页查询的详细教程。(博客链接) 二、实现前端与后端通信对接数据。(axios工具) &#xff08;1&#xff09;安装axios。(vue工程目录) &#xff08;2&#xff09;封装请求工具类。(request.js) <1&…...

LabVIEW发电平台数据采集系统

本文详细介绍了基于LabVIEW的摇臂式波浪发电平台数据采集系统的设计与实现。通过整合LabVIEW软件与多种传感器技术&#xff0c;本系统能够有效提升数据采集的准确性和效率&#xff0c;为波浪能的利用和发电设备的优化提供科学依据。 ​ 项目背景 随着全球能源需求增长和环境保…...

人工智能(AI)系统化学习路线

一、为什么需要系统化学习AI&#xff1f; 人工智能技术正在重塑各行各业&#xff0c;但许多初学者容易陷入误区&#xff1a; ❌ 盲目跟风&#xff1a;直接学习TensorFlow/PyTorch&#xff0c;忽视数学与算法基础。 ❌ 纸上谈兵&#xff1a;只看理论不写代码&#xff0c;无法解…...

oracle事务的组成

1)数据库事务由以下的部分组成: 一个或多个DML 语句 ; 一个 DDL(Data Definition Language – 数据定义语言) 语句&#xff1b; 一个 DCL(Data Control Language – 数据控制语言)语句&#xff1b; 2)事务的执行开始&#xff1a; 以第一个 DML 语句的执行作为开始 &#xff0c;…...

第二十八篇 数据获取与数据分析:数仓体系下的专业化分工与协同

声明&#xff1a;文章内容仅供参考&#xff0c;需仔细甄别。文中技术名称属相关方商标&#xff0c;仅作技术描述&#xff1b;代码示例为交流学习用途&#xff0c;部分参考开源文档&#xff08;Apache 2.0/GPLv3&#xff09;&#xff1b;案例数据已脱敏&#xff0c;技术推荐保持…...

SpringSecurity——前后端分离登录认证

SpringSecurity——前后端分离登录认证的整个过程 前端&#xff1a; 使用Axios向后端发送请求 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>登录</title><script src"https://cdn…...

【AI】Orin Nano+ubuntu22.04上移植YoloV11,并使用DeepStream测试成功

1、准备工作 使用 sdk-manager 烧写 OrinNano, JetPack版本为6.0 DP,对应操作系统为:Ubuntu22.04 参见博客:【NVIDIA】Jetson Orin Nano系列:烧写Ubuntu22.04 2、安装 PyTorch 2.1 下载依赖 1)安装onnx pip install onnx -i https://pypi.tuna.tsinghua.edu.cn/sim…...

RHCE 使用nginx搭建网站

一。准备工作 Windows dns映射 创建目录网页 vim 编辑内容 添加如下 重启nginx服务&#xff0c;在Windows浏览器进行测试...

arm linux下的读写信号量rw_semphore的实现

本文基于arm linux 5.10来介绍内核中使用的读写信号量rw remphore的实现代码。 内核中信号量结构体struct rw_semaphore的定义在include/linux/rwsem.h 32位architectures下&#xff0c;结构体struct rw_semaphore中的count的使用如下&#xff1a; 先来看信号量的定义和初始化…...

搭建主从DNS、nfs、nginx

任务需求&#xff1a; 客户端通过访问 www.nihao.com 后&#xff0c;能够通过 dns 域名解析&#xff0c;访问到 nginx 服务中由 nfs 共享的首页文件&#xff0c;内容为&#xff1a;Very good, you have successfully set up the system. 各个主机能够实现时间同步&#xff0c;…...

Qt6+QML实现Windows屏幕录制

前言 Qt6提供了更丰富的多媒体支持类&#xff0c;使用Qt6 QMediaCaptureSession、QScreenCapture、QMediaRecorder&#xff0c;来实现一个屏幕录制的demo&#xff0c;其中QScreenCapture 最低版本 Qt6.5。支持录制的清晰度设置&#xff0c;选择视频保存位置&#xff0c;UI使用…...

python二级每日十题(1)

\ 第一题,在Python中&#xff0c;变量名的命名规则&#xff1a;以字母或下划线开头&#xff0c;后面跟字母、下划线和数字&#xff1b;不能以数字开头.故选c项,博主正确 缩进&#xff1a;在逻辑行首的空白&#xff08;空格和制表符&#xff09;用来决定逻辑行的缩进层次&…...

记录一次truncate导致MySQL夯住的故障

目录 环境信息&#xff1a; 故障描述&#xff1a; 处理过程&#xff1a; 原理分析&#xff1a; show processlist结果中的system lock含义&#xff1a; truncate原理&#xff1a; 1. TRUNCATE 的执行流程 2、TRUNCATE 表导致数据库夯住的原因 3、 TRUNCATE 表导致…...

Java Web应用程序实现用户登录、学生信息管理和验证码验证以及页面跳转等基本功能(IDEA)含(Ajax、JSTL)

一、具体框架以及代码功能的展示&#xff1a; 1. 文件结构 web03: 项目根目录。 src: 包含Java源代码。 cn.lvb: 主包。 bean: 包含实体类&#xff0c;如 Book 和 Student。 controller: 包含处理HTTP请求的Servlet类&#xff0c;如 DoLogin, Index, StuList1, VerifyCode。 …...

【Mybatis-plus】在mybatis-plus中 if test标签如何判断 list不为空

博主介绍&#xff1a;✌全网粉丝22W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

MySQL 事务(Transaction)详解

MySQL 事务&#xff08;Transaction&#xff09;详解 1. 什么是事务&#xff1f; 事务&#xff08;Transaction&#xff09;是一组要么全部执行&#xff0c;要么全部回滚的 SQL 语句&#xff0c;用于保证数据一致性。事务一般用于银行转账、订单支付等操作&#xff0c;确保多个…...

Redis 知识点梳理

第一章 NoSQL数据库发展历史简介 1、 Web的历史发展历程 web1.0时代简介 web 1.0是以编辑为特征&#xff0c;网站提供给用户的内容是网站编辑进行编辑处理后提供的&#xff0c;用户阅读网站提供的内容这个过程是网站到用户的单向行为web1.0时代的代表站点为新浪&#xff0c;…...

github上传操作简单说明

前期准备 0.下载git&#xff08;如果已经有了就不用了&#xff09; 1.在GitHub上新建一个存储库 2.先在本地创建一个目录作为本地库目录&#xff0c;在目录里打开git bash进行上传 上传过程 echo "# Garbled_repair" >> README.md 作用&#xff1a;创建一个…...

在 ASP .NET Core 9.0 中使用 Scalar 创建漂亮的 API 文档

示例代码&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90407900 Scalar 是一款可帮助我们为 API 创建精美文档的工具。与感觉有些过时的默认 Swagger 文档不同&#xff0c;Scalar 为 API 文档提供了全新而现代的 UI。其简洁的设计让开发人员可以轻松找到测试…...

针对 pdf.mjs 文件因 MIME 类型错误导致的 Failed to load module script 问题解决方案

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of “application/octet-stream”. Strict MIME type checking is enforced for module scripts per HTML spec. pdf.mjs 这种问题该如何处理 nginx 针对 pdf.…...

Flink介绍与安装

Apache Flink是一个在有界数据流和无界数据流上进行有状态计算分布式处理引擎和框架。Flink 设计旨在所有常见的集群环境中运行&#xff0c;以任意规模和内存级速度执行计算。 一、主要特点和功能 1. 实时流处理: 低延迟: Flink 能够以亚秒级的延迟处理数据流&#xff0c;非常…...

《双指针算法指南:LeetCode 经典题解(C++实现)》

《双指针算法指南&#xff1a;LeetCode 经典题解&#xff08;C实现&#xff09;》 —— 从快慢指针到对撞指针&#xff0c;刷题效率提升 200%&#xff01; 常⻅的双指针有两种形式&#xff0c;⼀种是对撞指针&#xff0c;⼀种是左右指针。 对撞指针&#xff1a; ⼀般⽤于顺…...

kaggle上经典泰坦尼克项目数据分析探索

之前了解在kaggle上这个项目很火&#xff0c;最近想要加强一下python数据分析&#xff0c;所以在kaggle上找到这个项目进行学习探索&#xff0c;下面是将一些学习资料以及过程整理出来。 一、首先我们了解一下项目背景以及如何找到这个项目。 kaggle项目地址: https://www.k…...

【深度学习】多目标融合算法(五):定制门控网络CGC(Customized Gate Control)

目录 一、引言 二、CGC&#xff08;Customized Gate Control&#xff0c;定制门控网络&#xff09; 2.1 技术原理 2.2 技术优缺点 2.3 业务代码实践 2.3.1 业务场景与建模 2.3.2 模型代码实现 2.3.3 模型训练与推理测试 2.3.4 打印模型结构 三、总结 一、引言 上一…...

Ubuntu上查看GPU使用情况并释放内存

先用nvidia-smi查看GPU当前使用情况 再用fuser 命令查找对应显卡上占用 GPU 的进程 最后查到了用kill -9强制杀掉进程&#xff08;PID&#xff09;即可...

大数据学习栈记——HBase安装

本文介绍大数据技术中流行的非关系型数据库HBase的安装&#xff0c;操作系统&#xff1a;Ubuntu24.04 安装Zookeeper 安装HBase前需要先安装Zookeeper&#xff0c;HBase使用Zookeeper作为其分布式协同服务&#xff0c;存储了HBase集群的元数据信息&#xff0c;并提供了分布式…...

[入门]NUC13配置Ubuntu20.04详细步骤

文章目录 1. 安装Ubuntu20.041.1 制作系统启动盘1.1.1 下载镜像文件1.1.2 配置启动盘 1.2 安装内存条、硬盘1.3 安装系统 2. 网卡驱动配置2.1 关闭安全启动2.2 安装intel官方网卡驱动backport2.2.1 第四步可能会出现问题 2.3 ubuntu官方的驱动2.4 重启 3. 软件安装3.1 录屏软件…...

【实战指南】用MongoDB存储文档和图片等大文件(Java实现)

一、前言 在现代应用开发中,经常需要处理和存储大量的文档、图片等大文件。传统的关系型数据库在处理这类大文件时,往往会面临性能瓶颈、存储成本高等问题。而 MongoDB 作为一款流行的 NoSQL 数据库,提供了 GridFS 规范,能够很好地解决大文件存储的问题。GridFS 可以将大文…...

使用Gitee Go流水线部署个人项目到服务器指南

使用Gitee Go流水线部署个人项目到服务器指南 前言&#xff01;&#xff01;&#xff01; 本文解决的问题&#xff1a; 你有一台ECS服务器&#xff0c;你在上面部署了一个Java服务也就是一个jar&#xff0c;你觉着你每次手动本地打包&#xff0c;上传&#xff0c;在通过命令去…...

使用Three.js渲染器创建炫酷3D场景

引言 在当今数字化的时代&#xff0c;3D图形技术正以其独特的魅力在各个领域掀起波澜。从影视制作到游戏开发&#xff0c;从虚拟现实到网页交互&#xff0c;3D场景以其强烈的视觉冲击力和沉浸式的体验&#xff0c;成为了吸引用户、传达信息的重要手段。而Three.js&#xff0c;…...

Spring Boot 集成 Elasticsearch怎样在不启动es的情况下正常启动服务

解释 在spingboot 集成es客户端后&#xff0c;每当服务启动时&#xff0c;服务默认都会查看es中是否已经创建了对应的索引&#xff0c;如果没有索引则创建。基于上面的规则我们可以通过配置不自动创建索引来达到在没有es服务的情况下正常启动服务。 解决办法 在entity类的Docu…...

明远智睿SD2351核心板:多接口融合,破解边缘计算难题

在边缘计算领域&#xff0c;明讯智睿SD2351核心板凭借丰富的接口资源与异构计算架构&#xff0c;成为工业网关与智能终端的理想选择。硬件配置升级 :处理器:四核Cortex-A35&#xff0c;256KB L2缓存&#xff0c;动态调频降低功耗;存储性能:emMC 5.0 HS400模式读写速度提升40%&a…...

xampp安装教程与配置

一、安装 XAMPP &#xff08;一&#xff09;下载 访问官网&#xff1a;打开浏览器&#xff0c;访问 XAMPP 官方网站&#xff08;https://www.apachefriends.org/zh_cn/index.html&#xff09;。 选择版本&#xff1a;根据你的操作系统&#xff08;Windows、macOS 或 Linux&am…...

设计模式之单例模式(Singleton Pattern)

单例模式&#xff08;Singleton Pattern&#xff09;是一种常用的设计模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。单例模式在许多场景中非常有用&#xff0c;例如配置管理、日志记录、线程池等。 ### **1. 单例模式的特点** 1. **唯一实例**&a…...