面试

lishihuan大约 39 分钟

面试

1. 前端

参考:https://www.yuque.com/cuggz/interviewopen in new window

基础问题

html/css

  • 如何实现一个两栏布局,左侧固定宽度,右侧自适应?
    • 百分比布局、如果具体的px值 calc
    • flex:布局,然后右侧指定flex:1
  • 响应式布局可了解,如果实现
    • 解释一下媒体查询(Media Queries)的使用
    • 是否有适配
  • 如何处理自定义按钮的双击问题
    • 防抖,节流
    • loding
  • 请描述一个你曾经做过的复杂静态页面项目,你是如何实现布局和样式的?
    • 候选人是否有实际项目经验,能否清晰地描述项目的实现过程和技术细节
  • 什么是CSS的盒模型?标准盒模型和IE盒模型有什么区别?
    • 考察点: 候选人是否理解盒模型的概念,以及box-sizing: content-boxbox-sizing: border-box的区别。
  • 什么是CSS选择器的优先级?如何计算选择器的优先级?
    • 考察点: 候选人是否了解选择器优先级的计算规则(如内联样式、ID、类、标签等),以及如何避免选择器优先级过高的问题。
    • 样式污染
  • 如何实现一个元素的渐变背景?你能写出代码吗?
    • 考察点: 候选人是否熟悉CSS的linear-gradientradial-gradient属性,能否实现复杂的渐变效果。
  • 如何实现一个元素的动画效果?你能写出一个简单的CSS动画吗?
    • 考察点: 候选人是否熟悉CSS的@keyframesanimation属性,能否实现复杂的动画效果。

js【待确定】

  • JavaScript中的数据类型有哪些?如何判断一个变量的类型?

    • 考察点: 候选人是否了解JavaScript的基本数据类型(如stringnumberbooleanobject等),以及typeofinstanceof的使用。
  • 如何实现一个深拷贝(Deep Clone)?你能写出代码吗?

    • 考察点: 候选人是否了解深拷贝的实现方法,如递归、JSON.parse(JSON.stringify())structuredClone等。
  • 如何优化前端页面的加载速度?你能列举几种优化方法吗?

    • 考察点: 候选人是否了解前端性能优化的常见方法,如减少HTTP请求、压缩资源、使用CDN、懒加载等。
  • 什么是Web安全?你能列举几种常见的前端安全问题及其解决方案吗?

  • 考察点: 候选人是否了解常见的安全问题,如XSS、CSRF、SQL注入等,以及如何防范。

  • 什么是前端模块化?你能解释一下CommonJS和ES Module的区别吗?

    • 考察点: 候选人是否了解前端模块化的概念及其实现方式,能否区分CommonJS和ES Module。
  • 什么是前端工程化?你常用的构建工具是什么?它们的作用是什么?

    • 考察点: 候选人是否了解前端工程化的概念及其工具链,如Webpack、Vite、Babel等。
  • 请手写一个JavaScript函数,实现数组去重。

    • 考察点: 候选人是否熟悉JavaScript的数组操作方法,能否写出高效的代码。

1.1 HTML5新增新特性(可做过了解)

HTML5相对于之前的HTML版本引入了许多新特性和改进

  • 语义化标签(Semantic Tags): HTML5引入了一些新的语义化标签,如<header><nav><section><article><aside><footer>等,使网页结构更加清晰明了。
  • 视频和音频支持: HTML5提供了<video><audio>标签,使得在网页上嵌入视频和音频内容变得更加简单和直观。
    • audio:音频 <audio src='' controls autoplay loop='true'></audio>
    • video视频: <video src='' poster='imgs/aa.jpg' controls></video>
  • 画布Canvas: HTML5中新增了<canvas>元素,可以使用JavaScript在其中绘制图形、动画和图像等,提供了强大的图形处理能力。
  • 本地存储: HTML5引入了本地存储技术,包括localStorage和sessionStorage,可以在浏览器端存储数据,减少与服务器的交互。
  • 表单增强: HTML5为表单元素提供了一些新的属性和类型,如日期选择、邮箱验证、数值范围控制等,提升了用户体验和数据验证的能力。
  • 响应式网页设计(Responsive Web Design): HTML5鼓励使用CSS3的媒体查询和弹性布局来创建响应式网页,使得网页能够自适应不同屏幕尺寸和设备。
  • Geolocation API: HTML5的Geolocation API使得网页可以获取用户的地理位置信息,用于实现基于位置的服务和功能。
  • Web存储(Web Storage): HTML5提供了Web存储API,包括localStorage和sessionStorage,可以在浏览器端存储大量结构化数据。
  • Web Workers: HTML5的Web Workers允许在后台运行JavaScript代码,提高了网页的性能和响应能力。

1.2 行内元素有哪些?块级元素有哪些

行内元素 <span><a><strong><em><img><br><input><label>

块级元素: <div><p><h1> to <h6><ul>, <ol>, <li><table>, <tr>, <td><form><header>, <footer>, <nav>

**问题1:**如何实现行转块和块转行

  • **行转块:**使用CSS的display属性,将行内元素转换为块级元素。具体做法是给行内元素设置display: block;即可将其转换为块级元素。

  • 块转行: 给块级元素设置display: inline;即可将其转换为行内元素

**问题2:**块级元素水平布局

  • **使用浮动(Float)**可以通过给块级元素设置float: left;或者float: right;来让块级元素在水平方向上进行布局。这种方式会使得元素脱离正常文档流
  • 使用Flexbox布局: 使用CSS3的Flexbox布局可以非常方便地实现块级元素的水平布局
  • 使用Grid布局: CSS3的Grid布局也提供了强大的水平布局能力,通过定义网格容器和网格项来实现水平布局。
  • 定位

1.2 前端常用布局方案

常用布局方案或者你知道那些布局方案

最好的是百分比布局和弹性盒子布局(又叫flex布局) 如果没用过基本就可以pas

  1. **百分比布局:**使用百分比单位来设置元素的宽度、高度和位置,使得元素能够根据父容器或浏览器窗口的大小进行自适应调整。
  2. 流式布局(Fluid Layout): 使用相对单位(如百分比、em、rem)而不是固定单位(如像素)来定义网页元素的尺寸,使其能够根据屏幕尺寸进行自适应调整。这种布局方式适合于响应式设计。
  3. 网格布局(Grid Layout): 使用CSS的Grid布局,通过定义网格容器和网格项的属性,实现复杂的二维布局。这种布局方式提供了更强大的布局能力,适合于构建复杂的网页布局。
  4. 弹性盒子布局(Flexbox Layout): 使用CSS的Flexbox布局,通过设置容器和子元素的属性,实现灵活的一维布局。这种布局方式简单易用,适合于构建各种排列方式的布局。
  5. 定位布局(Positioning Layout): 使用CSS的定位属性(如position: relative;position: absolute;)来控制元素在页面中的位置。这种布局方式适合于实现一些特定的布局效果,但需要注意兼容性和维护性。
  6. 栅格布局(Grid System): 使用栅格系统(如Bootstrap的栅格系统)来划分页面的列和行,通过设置元素的class来实现网格布局。这种布局方式常用于快速构建响应式网页。
  7. 多列布局(Multi-column Layout): 使用CSS的多列属性(如column-countcolumn-width)来实现多列文本布局。这种布局方式适用于需要展示大量文本内容的情况。

1.3 什么是响应式设计?如何实现响应式网页?

响应式设计(Responsive Design)是一种前端网页设计和开发的方法,旨在使网页能够适应不同的设备和屏幕尺寸,以提供一致的用户体验。在响应式设计中,网页的布局、内容和功能会根据用户所使用的设备自动进行调整和优化

实现

  • 流式布局(Fluid Layout):使用相对单位(如百分比、em、rem)而不是固定单位(如像素)来定义网页元素的尺寸,使其能够根据屏幕尺寸进行自适应调整。
  • 媒体查询(Media Queries): 使用CSS3的媒体查询功能,根据屏幕尺寸、设备特性等条件来应用不同的样式规则,以适应不同的屏幕大小和设备类型。
/* 在不同的屏幕尺寸下应用不同的样式 */
@media screen and (max-width: 768px) {
    /* 样式规则 */
}

@media screen and (min-width: 1200px) {
    /* 样式规则 */
}
  • 弹性图片和媒体:使用CSS的max-width: 100%;属性来确保图像和媒体元素能够根据容器的大小自动调整尺寸,避免溢出并保持比例。

1.4 隐藏元素的方法有哪些

可以在问问 display: nonevisibility: hidden 的区别

  • display:none会让元素完全从渲染树中消失,渲染时不会占据任何空间;
  • visibility:hidden不会让元素从渲染树中消失,渲染的元素还会占据相应的空间,只是内容不可见。
  • display: none:渲染树不会包含该渲染对象,因此该元素不会在页面中占据位置,也不会响应绑定的监听事件。
  • visibility: hidden:元素在页面中仍占据空间,但是不会响应绑定的监听事件。
  • opacity: 0:将元素的透明度设置为 0,以此来实现元素的隐藏。元素在页面中仍然占据空间,并且能够响应元素绑定的监听事件。
  • position: absolute:通过使用绝对定位将元素移除可视区域内,以此来实现元素的隐藏。
  • z-index: 负值:来使其他元素遮盖住该元素,以此来实现隐藏。
  • clip/clip-path :使用元素裁剪的方法来实现元素的隐藏,这种方法下,元素仍在页面中占据位置,但是不会响应绑定的监听事件。
  • transform: scale(0,0):将元素缩放为 0,来实现元素的隐藏。这种方法下,元素仍在页面中占据位置,但是不会响应绑定的监听事件。

1.5 CSS3中有哪些新特性

  1. CSS选择器:not() 用于选择除了指定元素以外的其他元素
  2. 边框(Border): CSS3引入了border-radius属性,可以用来创建圆角边框;border-image属性可以使用图片来定义边框样式。
  3. 阴影(Box Shadow): 可以通过box-shadow属性为元素添加阴影效果,包括水平偏移、垂直偏移、模糊半径和颜色等参数。
  4. 渐变(Gradient): 引入了线性渐变(linear-gradient)和径向渐变(radial-gradient),可以用来创建平滑的渐变背景。
  5. 过渡(Transition): 通过transition属性可以实现元素在状态改变时平滑过渡,比如鼠标悬停时改变颜色或大小。
  6. 动画(Animation): 引入了@keyframes规则,可以定义复杂的动画效果,并使用animation属性将动画应用到元素上。
  7. 字体(Font): 引入了@font-face规则,可以使用自定义字体文件来显示特定字体。
  8. 弹性盒子(Flexbox): 引入了弹性盒子布局模型,可以更轻松地实现灵活的布局。
  9. 网格布局(Grid Layout): 引入了网格布局系统,可以更方便地实现复杂的二维布局。
  10. 多列布局(Multi-column Layout): 引入了column-countcolumn-width等属性,用于实现文本内容的多列布局。
  11. 媒体查询(Media Queries): 可以根据设备的特性和屏幕尺寸来应用不同的样式规则,实现响应式设计。

1.6 单行、多行文本溢出隐藏

询问:文本溢出你们都是怎么处理的

  • 单行文本溢出
overflow: hidden;            // 溢出隐藏
text-overflow: ellipsis;      // 溢出用省略号显示
white-space: nowrap;         // 规定段落中的文本不进行换行
  • 多行文本溢出
overflow: hidden;            // 溢出隐藏
text-overflow: ellipsis;     // 溢出用省略号显示
display:-webkit-box;         // 作为弹性伸缩盒子模型显示。
-webkit-box-orient:vertical; // 设置伸缩盒子的子元素排列方式:从上到下垂直排列
-webkit-line-clamp:3;        // 显示的行数

注意:由于上面的三个属性都是 CSS3 的属性,没有浏览器可以兼容,所以要在前面加一个-webkit- 来兼容一部分浏览器。

1.7 z-index属性在什么情况下会失效

  • 父元素position为relative时,子元素的z-index失效。解决:父元素position改为absolute或static;
  • 元素没有设置position属性为非static属性。解决:设置该元素的position属性为relative,absolute或是fixed中的一种;
  • 元素在设置z-index的同时还设置了float浮动。解决:float去除,改为display:inline-block;

10.CSS模块化---Css的命名规范 【CSS的模块化开发,能否通过BEM等命名规范提高代码的可维护性】

Css的命名规范(BEM,OOCSS)

https://www.jianshu.com/p/900e26060c09open in new window

https://blog.csdn.net/weixin_34688110/article/details/112090373open in new windowhttps://blog.csdn.net/weixin_41996102/article/details/121658875open in new window

css模块化的好处:
  • 提高代码重用率
  • 降低耦合
  • 提高开发效率、减少沟通成本
  • 易于维护

10.1 BEM 方式 Bem是块(block)、元素(element)、修饰符(modifier)

.site-search{} /* 块 */
.site-search__field{} /* 元素 */
.site-search--full{} /* 修饰符 */	
Block:一个独立的,可以复用而不依赖其他组件的部分,可作为一个块
Element:属于块的某部分,可作为一个元素
Modifier:用于修饰块或元素,体现出外形行为状态等特征的,可作为一个修饰器
1)保证各个部分只有一级B__E–M,修饰器需要和对应的块或元素一起使用,避免单独使用。
2)仅以类名作为选择器,不使用ID或标签名来约束选择器,且css中的选择器嵌套不超过2层
3)避免 .block__el1__el2 的格式

10.2 OOCSS 表示的是面向对象 CSS

OOCSS 表示的是面向对象 CSS(Object Oriented CSS)

OOCSS最关键的一点就是:提高他的灵活性和可重用性。这个也是OOCSS最重要的一点。OOCSS主张是通过在基础组件中添加更多的类,从而扩展基础组件的CSS规则,从而使CSS有更好的扩展性。

https://www.w3cplus.com/css/oocss-conceptopen in new window

  • 减少CSS代码。
  • 具有清洁的HTML标记,有语义的类名,逻辑性强的层次关系。
  • 语义标记,有助于SEO。
  • 更好的页面优化,更快的加载时间(因为有很多组件重用)。
  • 可扩展的标记和CSS样式,有更多的组件可以放到库中,而不影响其他的组件。
  • 能轻松构造新的页面布局,或制作新的页面风格。

伪类

17 引入自定义字体

/*引入 自定义数字样式 字体*/
@font-face {
    font-family: 'electronicFont';
    src: url("../images_new/LESLIEB_.TTF");
}

/*自定义数字样式*/
.number_style {
    font-family: 'electronicFont';
    color: #018BFF;
}

1.8 CSS预处理器

对比css优点

Less、 Sass 等

共同特性:

  1. 变量(Variables): 可以定义变量来存储颜色、尺寸、字体等样式属性,方便统一管理和修改。
  2. 嵌套规则(Nested Rules): 可以将选择器嵌套在另一个选择器内部,避免重复书写父选择器。
  3. 混合(Mixins): 可以定义可以重复使用的样式块,并在需要时引入,类似函数的功能。
  4. 导入(Imports): 可以将样式文件分割成多个文件,并通过导入语句引入到主文件中,便于模块化管理。
  5. 继承(Inheritance): 可以实现选择器之间的样式继承,减少重复代码。
  6. 运算(Operations): 支持数学运算,如加法、减法、乘法等,方便计算样式属性值。
  7. 函数(Functions): 可以定义和调用函数,实现复杂的样式逻辑和计算。
  8. 条件语句(Control Directives): 支持条件语句和循环,可以根据条件控制样式的输出。

Sass独有特性:

  1. 两种语法格式: Sass提供了两种语法格式,一种是类似CSS的缩进格式(Sass),另一种是SCSS(Sassy CSS)格式,更接近传统的CSS语法。
  2. 强大的控制结构: Sass提供了强大的控制结构,如条件语句和循环,可以实现复杂的样式逻辑。
  3. 模块化能力: Sass支持模块化开发,可以将样式代码分割成多个文件,便于管理和维护。
  4. 丰富的工具和库: Sass拥有许多第三方库和工具,如Compass框架,可以进一步扩展其功能。

Less独有特性:

  1. 类似JavaScript的语法: Less使用类似JavaScript的语法,比较简洁和易学。
  2. 变量作用域: Less中的变量可以具有局部作用域,也可以定义全局变量。
  3. 混合的参数: Less的混合支持参数,可以实现更灵活和定制化的样式组合。

1.9 px、em、rem,vw/vh区别及使用场景

三者的区别:

  • px是固定的像素,一旦设置了就无法因为适应页面大小而改变。
  • em和rem相对于px更具有灵活性,他们是相对长度单位,其长度不是固定的,更适用于响应式布局。
  • em是相对于其父元素来设置字体大小,这样就会存在一个问题,进行任何元素设置,都有可能需要知道他父元素的大小。
  • rem是相对于根元素(html),这样就意味着,只需要在根元素确定一个参考值。

使用场景:

  • 对于只需要适配少部分移动设备,且分辨率对页面影响不大的,使用px即可 。
  • 对于需要适配各种移动设备,使用rem,例如需要适配iPhone和iPad等分辨率差别比较挺大的设备。

1.9.元素居中的几个方式

垂直或者水平居中,最优的回答是flex布局

1.9.1 利用相对定位

利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过translate来调整元素的中心点到页面的中心(通过)

parentElement{
	position:relative;
}
childElement{
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
}

1.9.2 flex布局

 display: flex;
 align-items: center;
 justify-content: center;/*子元素水平居中*/

1.9.3 通过line-height

通过设置height和line-height 同一值来实现垂直居中

1.9.4 margin:auto

1.9.5 不设置高度,通过padding 将内容撑起来

实现垂直和水平居中

1.9.6 text-align: center;

1.10 适配

移动端和大屏项目一般都要进行适配,目前毕竟常用的2种

一个是通过flexible.js (通过封装的js自动计算根字体大小),另一个是通过媒体查询

不用具体回答,只要大概知道这2个就行

  • flexible.js + rem
  • less + 媒体查询 + rem

1.11 JavaScript有哪些数据类型,它们的区别?

JavaScript共有八种数据类型,分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。

1.12 ES6中有哪些新特性

  • 能够说出使用let关键字声明变量的特点
  • 能够使用解构赋值从数组中提取值
  • 能够说出箭头函数拥有的特性
  • 能够使用剩余参数接收剩余的函数参数
  • 能够使用拓展运算符拆分数组
  • 能够说出模板字符串拥有的特性

平时常用那些

ES6(也称为ES2015)是ECMAScript标准的第六个版本,引入了许多新的特性和语法,以增强JavaScript语言的功能和可读性。以下是ES6中一些主要的新特性:

  1. let 和 const 声明: 引入了块级作用域的变量声明方式,let用于声明变量,const用于声明常量。
  2. 箭头函数(Arrow Functions): 提供了更简洁的函数定义语法,并且自动绑定上下文。
    1. 简洁
    2. 解决this指向
  3. 模板字符串(Template Strings): 使用反引号 (`) 来创建字符串模板,支持变量插值和多行文本。
  4. 解构赋值(Destructuring Assignment): 可以方便地从数组或对象中提取值并赋给变量。
  5. 默认参数(Default Parameters): 在函数定义时可以指定参数的默认值。
  6. 展开运算符(Spread Operator): 可以在数组和对象字面量中展开表达式。
  7. 类(Class): 引入了类的语法糖,更易于面向对象编程。
  8. 模块化(Modules): 提供了更好的模块化支持,使用 exportimport 声明和引入模块。
  9. Promise 对象: 提供了原生的 Promise 对象,简化了异步编程。
  10. 生成器(Generators): 允许在函数内部暂停和恢复代码执行,以便实现更复杂的控制流。
  11. Map 和 Set 数据结构: 引入了新的数据结构,分别用于存储键值对和唯一值。
  12. Symbol 类型: 引入了一种新的基本数据类型,用于创建唯一的标识符。
  13. Proxy 和 Reflect 对象: 提供了元编程的能力,可以拦截并修改对象的默认行为。

1. Set 和 Map 区别

Set对象是值的集合,你可以按照插入的顺序迭代它的元素。Set 中的元素只会出现一次,即 Set 中的元素是唯一的。

Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储

2 模板字符串

document.write(`<font color="red">${name}</font>的主要技能是${skill}<br>`);

3 == 和 ===区别

相等操作符()会做类型转换,再进行值的比较,全等运算符(=)不会做类型转换

4 声明变量的关键字 let、const、var 的区别

  • 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象
  • 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
  • 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值

5 解构赋值

数组解构

 let [a, b, c] = [1, 2, 3];
 console.log(a)//1
 console.log(b)//2
 console.log(c)//3
//如果解构不成功,变量的值为undefined

对象解构

注意: let {name: myName} = person;

name 是 实际的对象的属性, myName 属于别名

6 扩展运算符 (...)

使用场景是什么

可以解决

  1. 对象赋值,修改新对象导致老对象变更
  2. 合并

拓展: 浅拷贝 深拷贝

7. 如何解决对象赋值,源对象被影响

在JavaScript中,对象是引用类型,当你将一个对象赋值给另一个变量时,实际上是将对象的引用传递给了新的变量。这就意味着,如果你修改了其中一个变量指向的对象,另一个变量也会受到影响,因为它们指向同一个对象。

  1. 浅拷贝(Shallow Copy): 使用Object.assign()方法或扩展运算符(...)进行浅拷贝。这种方式只会复制对象的第一层属性,而不会复制嵌套对象的引用。

    const newObj = Object.assign({}, oldObj); // 使用Object.assign()
    const newObj = { ...oldObj }; // 使用扩展运算符(...)
    
  2. 深拷贝(Deep Copy): 使用深拷贝方法创建一个完全独立的对象副本,不受原始对象的更改影响。这样可以复制嵌套对象的所有层级。

    • 使用第三方库,如Lodash的_.cloneDeep()方法。

      const newObj = _.cloneDeep(oldObj); // 使用Lodash的_.cloneDeep()
      
    • 使用JSON.parse(JSON.stringify())方法进行序列化和反序列化。

      const newObj = JSON.parse(JSON.stringify(oldObj));
      

      注意:使用JSON.parse(JSON.stringify())方法时,注意避免循环引用的情况,因为该方法无法正确处理循环引用。

1.13 数组常用方法

数组在js中经常用于保存数据,是操作频率非常高的数据类型,js也提供了很多方法来对数组进行操作。下面介绍常用的方法。

join()、push()、pop()、shift() 、 unshift()、sort()、reverse()、concatopen in new window()、slice()、splice()、indexOf()、 lastIndexOf()、forEach() 、map() 、filter() 、every() 、some()

会改变原数组的方法

  • unshift(); //从头部添加
  • shift(); //从头部删除
  • push(); //从尾部添加
  • pop(); //从尾部删除
  • sort(); //数组排序
  • reverse(); //数组倒叙
  • splice(); //从数组中添加、删除、替换数据

some和every 的区别

some()原数组不会发生改变

some():判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true

every()原数组不会发生改变

音标 [ˈevri]

every():判断数组中每一项是否都满足条件,只有所有项都满足条件,才会返回true,否则返回false

map和forEache

都是循环 但是map 返回新数组

1.15 数组转对象

可以使用 Array.prototype.reduce() 方法将数组转换为对象

const arr = [
  { dictValue: "1", dictLabel: "苹果" },
  { dictValue: "2", dictLabel: "橘子" },
  { dictValue: "3", dictLabel: "香蕉" },
];

const map = arr.reduce((acc, cur) => {
  acc[cur.dictValue] = cur.dictLabel;
  return acc;
}, {});

console.log(map); // 输出:{ '1': '苹果', '2': '橘子', '3': '香蕉' }

js实现分组

通过 Array.prototype.reduce() 进行分组

const groupedData = data.reduce((result, item) => {
  if (!result[item.weekNum]) { // 针对数据会多次的情况
    result[item.weekNum] = [];
  }
  result[item.weekNum].push(item);
  return result;
}, {});

// 针对每组只会一个的情况
const groupedData = data.reduce((result, item) => {
  result[item.weekNum] = item;
  return result;
}, {});

console.log(groupedData);
const groups = {}
arr.forEach(item=>{
    const key = item.xx; 
    if (!groups[key]) {
        groups[key] = []
    }
    groups[key].push(item);
})
// 组装的的map 格式
ItemGroupBy (arr, key) {
    let newArr = []
    let groups = {}
    arr.forEach(item => {
        if (!(item[key] in groups)) {
            groups[item[key]] = { title: item[key], list: [] }
            newArr.push(groups[item[key]])
        }
        groups[item[key]].list.push(item)
    })
    return newArr
},
    // 组装的是对象
    handleGroup (arr) {
        let obj = {}
        let groups = []
        arr.forEach(item => {
            if (!obj[item.title]) { //根据id分组
                groups.push({
                    groupName: item.title,
                    title: item.title,
                    type: item.type,
                    list: [item]
                })
                obj[item.title] = item
            } else {
                groups.find(v => {//选择符合条件的第一个元素
                    if (v.groupName === item.title) {
                        v.list.push(item)
                    }
                })

            }
        })
        return groups
    },

map分组

  /** @type Map<int, Set<string>> */
  const enumToStringsMap = new Map();
  function addEnumsFromAPI(api) {
    for (const key in api) {
      const value = api[key];
      if (typeof value === 'number') {
        if (!enumToStringsMap.has(value)) {
          enumToStringsMap.set(value, new Set());
        }
        enumToStringsMap.get(value).add(key);
      }
    }
  }

TypeScript

1. 接口

export interface LinkData {
    img?: string; // 网站图标(可无)
    name: string; // 网站名称(必填,在没有 img和iconFont 属性时,用 name的第一个字符作为 网站的图标)
    description: string; // 网站说明(必填)
    url: string;    // 网站url地址(必填)
    tag?: string; // 卡片右上角 标签,主要用于描述 该网站 为 官网,防止 相同的网站很多无法区分开
    iconFont?: string; // 计划作为 替代 img的,
}

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

javaScript本地缓存的方法我们主要讲述以下四种:

  • cookie
  • sessionStorage
  • localStorage
  • indexedDB

1 区别

关于cookiesessionStoragelocalStorage三者的区别主要如下:

  • 存储大小:cookie数据大小不能超过4ksessionStoragelocalStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
  • 有效时间:localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除数据; sessionStorage数据在当前浏览器窗口关闭后自动删除;cookie设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
  • 数据与服务器之间的交互方式,cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端; sessionStoragelocalStorage不会自动把数据发给服务器,仅在本地保存

2 应用场景

在了解了上述的前端的缓存方式后,我们可以看看针对不对场景的使用选择:

  • 标记用户与跟踪用户行为的情况,推荐使用cookie
  • 适合长期保存在本地的数据(令牌),推荐使用localStorage
  • 敏感账号一次性登录,推荐使用sessionStorage
  • 存储大量数据的情况、在线文档(富文本编辑器)保存编辑历史的情况,推荐使用indexedDB

跨域

跨域解决方案 https://www.cnblogs.com/sdcs/p/8484905.htmlopen in new window 1、 通过jsonp跨域 2、 document.domain + iframe跨域 3、 location.hash + iframe 4、 window.nameopen in new window + iframe跨域 5、 postMessage跨域 6、 跨域资源共享(CORS) 7、 nginx代理跨域 8、 nodejs中间件代理跨域 9、 WebSocket协议跨域

2. vue 相关知识

现在有个问题,后台管理系统页面加载太慢了,我初步怀疑是代码太多导致的,所以如果解决

computed和watch的使用场景

计算属性和监听

computed: /kəmˈpjuːtɪd/

  • computed 属性用于声明一个计算属性,它会根据依赖的数据动态地计算出一个新的值,并将结果缓存起来,只有当依赖的数据发生改变时,才会重新计算。
  • computed 属性适合用于需要基于其他数据进行复杂计算,并且希望缓存计算结果的情况。
  • computed 的特点是具备缓存性,只有在依赖的响应式数据发生改变时,才会重新计算,否则直接返回缓存结果。
  • 由于 computed 属性是基于响应式数据的,所以它是具有响应性的,并可以像普通属性一样在模板中使用。

以下是一些适合使用 computed 属性的场景:

  • 对数据进行过滤或排序。
  • 根据多个数据计算出一个新的值。
  • 对数据进行格式化或处理,例如日期格式化。
  • 对列表进行计数、求和等操作。

watch:

  • watch 属性用于观察(监视)一个数据的变化,在数据发生变化时执行相应的回调函数。它可以监听一个或多个数据,并对其进行处理。
  • watch 适合用于需要在数据变化时执行异步操作、复杂操作或对数据变化做出响应的场景。
  • watch 的特点是它允许执行异步操作,例如发起一个 API 请求,或者在数据变化后执行一些昂贵的计算操作。
  • watch 还可以深度监听对象或数组的变化。
  • watch 不具备缓存性,每当被监听的数据发生改变,回调函数都会被执行。

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

  • 监听单个数据的变化并进行相应的处理,如发送网络请求或执行复杂的计算操作。
  • 监听多个数据的变化,并根据变化执行一些相关的操作。
  • 执行需要在数据变化后立即触发的操作。

createdmounted 的区别

在 Vue 组件的生命周期中,createdmounted 是两个不同的钩子函数,它们在组件的不同生命周期阶段被触发,并具有不同的用途和特点。

  1. created 钩子函数
    • created 是在组件实例被创建之后立即调用的钩子函数。
    • created 阶段,组件实例已经完成了数据观测(data observer)和事件初始化,但此时尚未将组件挂载到页面中。
    • 可以在 created 钩子函数中进行一些数据的初始化、异步请求、事件监听器的注册等操作。
    • 此时组件内部的 DOM 元素和其它子组件都还没有被渲染到页面上,因此无法获取到对应的 DOM 元素和其它组件实例。
  2. mounted 钩子函数
    • mounted 是在组件被挂载到页面之后调用的钩子函数。
    • mounted 阶段,组件已经被添加到了页面中,此时可以访问到组件的 DOM 元素,并且可以与其它已挂载的组件进行交互。
    • mounted 钩子函数常用于需要操作 DOM、执行异步请求、初始化第三方库等场景。
    • 通常情况下,如果需要在组件渲染完毕后进行一些操作,比如获取计算后的 DOM 元素尺寸、绑定事件监听器等,应该放在 mounted 钩子函数中。

总结来说,created 钩子函数在组件实例被创建后立即调用,适合进行数据初始化和一些异步操作。而 mounted 钩子函数在组件挂载到页面后调用,适合进行与 DOM 相关的操作、第三方库的初始化等场景。

请注意,在 Vue 3.x 版本中,beforeMount 钩子函数替代了 Vue 2.x 版本中的 mounted 钩子函数。因此,如果你使用的是 Vue 3.x 版本,可以参考 beforeMount 钩子函数的使用方式。

父子组件通信方式

整理vue中8种常规的通信方案

  1. 通过 props 传递
  2. 通过 $emit 触发自定义事件
  3. 使用 ref
  4. EventBus (没有相关的的2个组件通信,可以通过这个去实现)
  5. $parent$root
  6. attrs 与 listeners
  7. Provide 与 Inject
  8. Vuex

兄弟组件之间统计

自定义组件实现双向绑定

如何对自定义的组件实现双向绑定:

父组件数据会传递到子组件,子组件更改后传递给父组件,那这样就可能出现父组件的又回去修改子组件,思路:单向数据流规则

待整理

vue2赋值后值没变化

this.$set(this.form,'id','xxxx')

缓存+数据更新

列表页面一般都会加缓存,那如果跳转到详情界面操作保存,列表页面总不能手动刷新吧,所以对于这块你是怎么处理的,或者可有想法

data为什么是一个函数而不是对象

Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的;采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题。而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况。

什么是 mixin(混入)

  • Mixin 使我们能够为 Vue 组件编写可插拔和可重用的功能。
  • 如果希望在多个组件之间重用一组组件选项,例如生命周期 hook、 方法等,则可以将其编写为 mixin,并在组件中简单的引用它。
  • 然后将 mixin 的内容合并到组件中。如果你要在 mixin 中定义生命周期 hook,那么它在执行时将优化于组件自已的 hook。

对Vue组件化的理解

  1. 组件是独立和可复用的代码组织单元。组件系统是Vue核心特性之一,它使开发者使用小型、独立和通常可复用的组件构建大型应用;
  2. 组件化开发能大幅提高应用开发效率、测试性、复用性等;
  3. 组件使用按分类有:页面组件、业务组件、通用组件;
  4. vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent,扩展于Vue;
  5. vue中常见组件化技术有:属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等;6.合理的划分组件,有助于提升应用性能;
  6. 组件应该是高内聚、低耦合的;
  7. 遵循单向数据流的原则。

说一下Vue的生命周期

Vue 实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。

  1. beforeCreate(创建前):数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据。
  2. created**(创建后)** :实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 $el 属性。
  3. beforeMount(挂载前):在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。此时还没有挂载html到页面上。
  4. mounted(挂载后):在el被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html 页面中。此过程中进行ajax交互。
  5. beforeUpdate(更新前):响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。
  6. updated(更新后) :在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
  7. beforeDestroy(销毁前):实例销毁之前调用。这一步,实例仍然完全可用,this 仍能获取到实例。
  8. destroyed(销毁后):实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。

另外还有 keep-alive 独有的生命周期,分别为 activateddeactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 activated 钩子函数。

Vue 子组件和父组件执行顺序

加载渲染过程:

1.父组件 beforeCreate

2.父组件 created

3.父组件 beforeMount

4.子组件 beforeCreate

5.子组件 created

6.子组件 beforeMount

7.子组件 mounted

8.父组件 mounted

更新过程:

  1. 父组件 beforeUpdate
  2. 子组件 beforeUpdate
  3. 子组件 updated
  4. 父组件 updated

销毁过程:

  1. 父组件 beforeDestroy
  2. 2.子组件 beforeDestroy
  3. 子组件 destroyed
  4. 父组件 destoryed

Vuex 和 localStorage 的区别

(1)最重要的区别

  • vuex存储在内存中
  • localstorage 则以文件的方式存储在本地,只能存储字符串类型的数据,存储对象需要 JSON的stringify和parse方法进行处理。 读取内存比读取硬盘速度要快

(2)应用场景

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vuex用于组件之间的传值。
  • localstorage是本地存储,是将数据存储到浏览器的方法,一般是在跨页面传递数据时使用 。
  • Vuex能做到数据的响应式,localstorage不能

(3)永久性

刷新页面时vuex存储的值会丢失,localstorage不会。

**注意:**对于不变的数据确实可以用localstorage可以代替vuex,但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage无法做到,原因就是区别1。

Vuex有哪几种属性?

有五种,分别是 State、 Getter、Mutation 、Action、 Module

  • state => 基本数据(数据源存放地)
  • getters => 从基本数据派生出来的数据
  • mutations => 提交更改数据的方法,同步
  • actions => 像一个装饰器,包裹mutations,使之可以异步。
  • modules => 模块化Vuex

3. 后端

Redis

缓存,你们用作那些场景

Redis能干嘛

  • 内存存储和持久化 (运行在内存中但是可以持久化到磁盘) :redis支持异步将内存中的数据写到硬盘上

  • 发布、订阅消息系统

  • 地图信息分析

  • 定时器、计数器

特性

  • 多样的数据类型
  • 持久化
  • 集群
  • 事务

redis的应用场景

  • redis由于数据的读取和操作都在内存当中操作,读写的效率较高,所以经常被用来做数据的缓存。把一些需要频繁访问的数据,而且在短时间之内不会发生变化的,放入redis中进行操作。从而提高用户的请求速度和降低网站的负载,降低数据库的读写次数,就把这些数据放到缓存中。
  • 一些常用的实时计算的功能。需要实时变化和展示的功能,就可以把相关数据放在redis中进行操作。大大提高效率。
  • 消息队列,经常用来构建类似实时聊天系统的功能,大大提高应用的可用性。

Redis的持久化

持久化的2种方式,RDB和AOF

Redis主从复制

  • 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), 数据的复制是单向的!只能由主节点复制到从节点(主节点以写为主、从节点以读为主)。
  • 默认情况下,每台Redis服务器都是主节点,一个主节点可以有0个或者多个从节点,但每个从节点只能由一个主节点。

nginx

1.2 nginx可以提供的服务

  1. web 服务.
  2. 负载均衡 (反向代理)
  3. web cache(web 缓存)

1.3 nginx 的优点

  1. 高并发。静态小文件
  2. 占用资源少。2万并发、10个线程,内存消耗几百M。
  3. 功能种类比较多。web,cache,proxy。每一个功能都不是特别强。
  4. 支持epoll模型,使得nginx可以支持高并发。
  5. nginx 配合动态服务和Apache有区别。(FASTCGI 接口)
  6. 利用nginx可以对IP限速,可以限制连接数。
  7. 配置简单,更灵活。

1.4 nginx应用场合

  1. 静态服务器(图片,视频服务),另个lighttpd。并发几万,html,js,css,flv,jpg,gif等。
  2. 动态服务,nginx—fastcgi 方式运行PHP,jsp。(PHP并发约500-1500,MySQL 并发约300-1500)。
  3. 反向代理,负载均衡。日pv2000W以下,都可直接用nginx做代理。
  4. 缓存服务。类似 SQUID,VARNISH。

Nginx指定文件路径有两种方式root和alias =============

alias: [ˈeɪliəs]

  • root
  • alias /ˈeɪliəs/

这两者的用法区别在于对URI的处理方法不同。

location /folder/ {
	root         D:/workspace;
} 
location /folder/ {
    alias	D:/workspace;
} 

(1)alias是一个目录别名的定义,root则是最上层目录的定义。

​ 例如:上文,使用root 访问geojson_data 则 其会到 D:/workspace/geojson_data/ 目录下去找

​ 使用alias:则会到 D:/workspace 目录下查找

(2)还有一个重要的区别是alias后面必须要用“/”结束,否则会找不到文件的。而root则可有可无。

访问静态资源

D:\home\yjwyy\file\uploadPath\geojson_data\ 文件位置

server {
        listen       1889;
        server_name  192.168.4.24;
        
        location / {
            root   html;
            index  index.html index.htm;
        }
		location /geojson_data/ {
            #root   D:/home/yjwyy/file/uploadPath/;
            alias	D:\home\yjwyy\file\uploadPath/geojson_data/;
        } 
}

反向代理

反向代理(Reverse Proxy)是指服务器接受客户端的请求,并将这些请求转发到内部网络上的其他服务器。客户端只能看到反向代理服务器,而不知道真正提供服务的服务器。这种方式隐藏了实际服务器的细节,提供了更高的安全性和负载均衡。

在 Nginx 中配置反向代理非常简单。以下是一个简单的 Nginx 反向代理的示例:

复制代码server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend_server;
    }
}

在上面的示例中,Nginx 监听80端口,并将接收到的请求代理到名为 backend_server 的后端服务器。你需要将 backend_server 替换为实际提供服务的服务器的 IP 地址或域名。

此外,你还可以根据需要进行更多的配置,如设置代理请求的超时时间、添加请求头等。以下是一个带有进一步配置的示例:

复制代码server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend_server;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_connect_timeout 30s;
        proxy_read_timeout 60s;
    }
}

在上面的示例中,我们设置了 X-Real-IPHost 请求头,设置了连接超时时间为30秒,读取超时时间为60秒

MyBatis

MyBatis 的好处是什么?

1)MyBatis 把 sql 语句从 Java 源程序中独立出来,放在单独的 XML 文件中编写,给程序的 维护带来了很大便利。

2)MyBatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 Java Bean 对象, 大大简化了 Java 数据库编程的重复工作。

3)因为 MyBatis 需要程序员自己去编写 sql 语句,程序员可以结合数据库自身的特点灵活 控制 sql 语句,因此能够实现比 Hibernate 等全自动 orm 框架更高的查询效率,能够完成复 杂查询。

#{}${}的区别

1)#{}是预编译处理,${}是字符串替换。

2)Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法 来赋值;

3)Mybatis 在处理${}时,就是把${}替换成变量的值。

4)使用#{}可以有效的防止 SQL 注入,提高系统安全性。

如何获取⽣成的主键?

新增标签中添加:keyProperty=" ID " 即可

<insert id="insert" useGeneratedKeys="true" keyProperty="userId" >
 insert into user(
 user_name, user_password, create_time)
 values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
</insert>

批量操作

数据库连接属性allowMutiQueries=true

MyBatis 的缓存

MyBatis 的缓存分为一级缓存和二级缓存,一级缓存放在 session 里面,默认就有,二级缓 存放在它的命名空间里,默认是不打开的,使用二级缓存属性类需要实现 Serializable 序列化 接口(可用来保存对象的状态),可在它的映射文件中配置

Mybatis 是如何进行分页的、分页插件的原理是什么?

1)Mybatis 使用 RowBounds 对象进行分页,也可以直接编写 sql 实现分页,也可以使用 Mybatis 的分页插件。

2)分页插件的原理:实现 Mybatis 提供的接口,实现自定义插件,在插件的拦截方法内拦 截待执行的 sql,然后重写 sql。

举例:select * from student,拦截 sql 后重写为:select t.* from (select * from student)t limit 0,10