常见面试题集合

css题目集合

1.css3的新特性

  • border-radius:圆角边框

  • box-shadow:盒子阴影

  • background-size:背景图片大小

  • transition:过渡

  • transform:转换(位移 旋转 缩放)

  • animation:动画

  • linear-gradient:线性渐变

  • box-sizing:css3 盒子模型

2.BFC是什么

BFC 的中文意思是块级格式化上下文,是用于布局块级盒子的独立渲染区域,一个创建了新的 BFC 的盒子是独立布局的,盒子内元素的布局 不会影响盒子外面的元素。简单来说就是 BFC 就是 css 的一个布局概念,是一个独立的区域(容器) 满足下列条件之一就可以触发 BFC:

  • HTML 根元素
  • position 为 absolute 或 fixed
  • float 属性不为 none(常用)
  • overflow 不为 visible(常用)
  • display 为 inline-block, table-cell, table-caption, flex(常用)

可以解决什么问题:margin 重叠,清除浮动,自适应布局.

3.盒子模型

  • 分为表准盒子模型和怪异盒子模型
  • 标准盒子模型=content(内容)+border(边框)+padding(内边距)

  • 怪异盒子模型=content(内容)(已经包含了 padding 和 border)

  • css3 种可以通过设置 box-sizing 属性来完成标准或许怪异盒子模型之间的切换,怪异盒子模型:box-sizing: border-box;标准盒子模型:box-sizing:content-box

4.flex(弹性布局)

  • flexbox 是一种布局模型,让网页布局更简单灵活,基于 Flex 精确灵活控制块级盒子的布局方式,避免浮动布局中脱离文档流现象发生;给父元素添加 display: flex,子元素可以自动的挤压或拉伸;组成部分有弹性容器 弹性盒子 主轴和侧抽.

  • 应用场景:我们通过 flex 布局可以实现元素水平垂直居中以及左右固定,中间自适应;移动端小程序开发都建议使用 flex 布局.

  • flex-direction(改变主轴方向)
  1. row(默认值):主轴为水平方向,起点在左端
  2. row-reverse:主轴为水平方向,起点在右端
  3. column:主轴为垂直方向,起点在上沿。
  4. column-reverse:主轴为垂直方向,起点在下沿
  • flex-wrap(换行)
  1. nowrap(默认值):不换行
  2. wrap:换行,第一行在下方
  3. wrap-reverse:换行,第一行在上方
  • justify-content(主轴对齐方式)
  1. flex-start(默认值):左对齐
  2. flex-end:右对齐
  3. center:居中
  4. space-between:两端对齐,项目之间的间隔都相等
  5. space-around:两个项目两侧间隔相等
  • align-items(侧轴对齐方式)
  1. flex-start:交叉轴的起点对齐
  2. flex-end:交叉轴的终点对齐
  3. center:交叉轴的中点对齐
  4. baseline: 项目的第一行文字的基线对齐
  5. stretch(默认值):如果项目未设置高度或设为 auto,将占满整个容器的高度
  • align-content(多根轴线的对齐方式)
  1. flex-start:与交叉轴的起点对齐
  2. flex-end:与交叉轴的终点对齐
  3. center:与交叉轴的中点对齐
  4. space-between:与交叉轴两端对齐,轴线之间的间隔平均分布
  5. space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍
  6. stretch(默认值):轴线占满整个交叉轴

5.CSS 如何画一个三角形

css 画三角形的原理是利用盒子边框完成的,实现步骤可以分为以下四步: 1.设置一个盒子 2.设置四周不同颜色的边框 3.将盒子宽高设置为 0,仅保留边框 4.得到四个三角形,选择其中一个后,其他三角形(边框)设置颜色为透明

div {
  width: 0px;
  height: 0px;
  border-top: 20px solid transparent;
  border-bottom: 20px solid red;
  border-left: 20px solid transparent;
  border-right: 20px solid transparent;
}

 6.css 选择器有哪些?优先级?哪些属性可以继承?

选择器

  • css 选择器非常多,可以分为基础选择器 复合选择器,伪类选择器;伪元素选择器
  • 基础选择器: id 选择器 ,class 类选择器,标签选择器,
  • 复合选择器: 后代选择器,子代选择器,并集选择器,交集选择器

优先级

!Important>行内样式 > ID 选择器 > 类选择器 > 标签选择器>通配符>继承

继承

  • 字体系列属性
font:组合字体
font-family:规定元素的字体系列
font-weight:设置字体的粗细
font-size:设置字体的尺寸
font-style:定义字体的风格
font-variant:偏大或偏小的字体
  • 文本系列属性
text-indent:文本缩进
text-align:文本水平对刘
line-height:行高
word-spacing:增加或减少单词间的空白
letter-spacing:增加或减少字符间的空白
text-transform:控制文本大小写
direction:规定文本的书写方向
color:文本颜色
  • 元素可见性
visibility
  • 表格布局属性
caption-side:定位表格标题位置
border-collapse:合并表格边框
border-spacing:设置相邻单元格的边框间的距离
empty-cells:单元格的边框的出现与消失
table-layout:表格的宽度由什么决定
  • 列表属性
list-style-type:文字前面的小点点样式
list-style-position:小点点位置
list-style:以上的属性可通过这属性集合
  • 引用
quotes:设置嵌套引用的引号类型
  • 光标属性
cursor:箭头可以变成需要的形状

继承中比较特殊的几点:

  • a 标签的字体颜色不能被继承

  • h1-h6 标签字体的大下也是不能被继承的

7.无属性继承

  • display

  • 文本属性:vertical-align、text-decoration

  • 盒子模型的属性:宽度、高度、内外边距、边框等

  • 背景属性:背景图片、颜色、位置等

  • 定位属性:浮动、清除浮动、定位 position 等

  • 生成内容属性:content、counter-reset、counter-increment

  • 轮廓样式属性:outline-style、outline-width、outline-color、outline

  • 页面样式属性:size、page-break-before、page-break-after

8.em/px/rem/vh/vw 区别

  • em/px/rem/vh/vw 都属于 css 的单位,这些单位可以分为相对单位、绝对单位
  • px:绝对单位,网页按照精确像素来显示.
  • em:相对单位,相对自身定义的 font-size 来计算,自身没有设置字体大小,相对父元素的字体大小
  • rem:相对单位,相对根元素 html 的字体大小来计算
  • vw/vh:相对视口大小布局,把屏幕平均划分为 100 等份.

9.响应式设计

我这么理解:响应式布局就是一个网站能够兼容多个设备,可以根据屏幕的大小自动调整页面的的展示方式以及布局,我们不用为每一个设备做一个特定的版本; 应式网站的特点:

  • 同时适配 PC + 平板 + 手机等

  • 网站的布局会根据视口来调整模块的大小和位置

  • 响应式设计的基本原理是通过媒体查询检测不同的设备屏幕尺寸做处理来设置差异化的 css 样式.

响应式设计的基本原理是通过媒体查询检测不同的设备屏幕尺寸做处理,为了处理移动端,页面头部必须有meta声明viewport

10.媒体查询 
  

CSS3中的增加了更多的媒体查询,就像if条件表达式一样,我们可以设置不同类型的媒体条件,并根据对应的条件,给相应符合条件的媒体调用相对应的样式表

使用@Media查询,可以针对不同的媒体类型定义不同的样式,如:

@media screen and (max-width: 1920px) {
  ...;
}

当视口在 375px - 600px 之间,设置特定字体大小 18px

@media screen (min-width: 375px) and (max-width: 600px) {
  body {
    font-size: 18px;
  }
}

通过媒体查询,可以通过给不同分辨率的设备编写不同的样式来实现响应式的布局,比如我们为不同分辨率的屏幕,设置不同的背景图片

11.文本溢出的省略样式

单行文本溢出换省略号

​ white-space: nowrap; // 文字强制不换行
 text-overflow: ellipsis; // 文字溢出换省略号
 overflow: hidden; // 溢出文字隐藏

多行文本溢出换省略号

​ display: -webkit-box; // 元素转换为弹性容器,在一行排列
 -webkit-box-orient: vertical; /_ 表示盒子对象的子元素的排列方式 _/
 -webkit-line-clamp: 3; /_ 限制文本的行数,表示文本第多少行省略 _/
 text-overflow: ellipsis;/_ 打点展示 _/
 overflow: hidden;/_ 超出部分进行隐藏 _/

 js题目集合

1.数据类型和存储上的差别

数据类型分为:

基本数据类型

  • Number
  • String
  • Boolean
  • Undefined
  • null
  • symbol

引用数据类型

  • Object
  • Array
  • Function

存储上的差别:

基本数据类型和引用数据类型存储在内存中的位置不同:

  • 基本数据类型存储在栈中

  • 引用类型的对象存储于堆中

当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值

2.typeof 与 instanceof 区别

typeof 一般是用来判断变量是否存在,返回他的类型,其中基本数据类型 null 返回的是一个 object,但 null 不属于引用数据类型,typeof 除了判断 function 函数会识别,其他的引用类型输出为 object

instanceof 一般是用来判断引用数据类型,但不能正确判断基本数据类型,根据在原型链中查找判断当前数据的原型对象是否存在返回布尔类型

2 种方法各有各的优缺点,一般我们推荐判断数据类型使用 Object.prototype.toString,返回统一的格式[object Xxx]

3.深拷贝浅拷贝

浅拷贝:拷贝基本数据类型为他的值,拷贝引用数据类型为地址,生成新的数据,修改新的数据会影响原数据,实际开发常用的方法有:object.assgin,扩展运算符等等

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝,如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址,即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址

深拷贝:在内存中开辟一个新的栈空间保存新的数据,修改新数据不会影响到原数据,开发中常用的方法有:loadsh 中的_.cloneDeep()方法,JSON.stringify()

深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

4.作用域和作用域链

作用域一般可以理解为函数或变量的生效范围,我们一般把作用域分成全局作用域,函数(局部)作用域,块级作用域(es6 推出)

那么当我们在 js 中访问某个变量他就会在当前作用域进行查找,如果访问不到,他会一层一层向外进行查找,整个逐级向上查找的过程我们称为作用域链.

5.箭头函数

箭头函数是定义函数一种新的方式,他比传统函数 function 定义更加方便和简单,他没有绑定自己的 this 指向和伪数组 arguments,无法调用 super 方法生成实例化对象,因为他不是构造函数,一般用来取代匿名函数的写法,最主要的是箭头函数的 this 指向他的上一级作用域中的 this 也可以理解为他的 this 是固定的,而普通函数的 this 是可变的

6.JavaScript 原型,原型链 ? 有什么特点?

原型是我们创建函数的时候,系统帮我们自动生成的一个对象。 主要作用是解决构造函数内部方法内存资源浪费问题。

原型链是 js 对象一种查找机制,遵循就近原则。当我们访问一个对象中的成员的时候,会优先访问自己的,如果自己没有就访问原型的,如果原型也没有就会访问原型的原型,直到原型链的终点 null. 如果还没有,此时属性就会获取 undefined,方法就会报错 xxx is not a function。

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

闭包是一个可以访问其他函数内部变量的函数,主要作用是解决变量污染问题,也可以用来延长局部变量的生命周期。闭包在 js 中使用比较多,几乎是无处不在的。一般大多数情况下,在回调函数中闭包用的是最多的。

8.说说 JavaScript 中内存泄漏的几种情况?

内存泄漏一般是指变量的内存没有及时的回收,导致内存资源浪费。一般有三种情况出现内存泄露比较多。1)常见的声明了一个全局变量,但是又没有用上,那么就有点浪费内存了,(2)定时器没清除 (3)循环引用:A 对象里面有一个属性指向 B 对象,B 对象有一个属性指向 A 对象。互相引用

解决内存泄露:我们编译器有一个自动的内存清理。常见的主要是引用记数 和 标记清除。

9.继承

js 实现继承的方式有很多种,比如 原型链继承、借用构造函数继承、组合继承、寄生继承、寄生组合继承、ES6 新增的 extends 继承

10.new 操作符

new 操作符主要做四件事情(1)声明一个空的实例对象(2)将 this 指向这个对象(3)对象赋值(4)返回实例对象

11.数组的常用方法

数组的方法有很多,大概分为以下几类

  • 遍历方法
    • map : 映射数组,得到一个映射之后的新数组
    • filter:筛选数组
    • forEach:遍历数组
    • some:判断数组是否有元素满足条件(相当于逻辑或:一真则真,全假为假)
    • every:判断数组是否所有满足都满足条件(相当于逻辑与:一假则假,全真为真)
    • findIndex:查找元素下标,一般用于元素是引用类型
    • reduce:给数组每一个元素执行一次回调,一般用于数组元素求和(也可以求最大值、最小值)
  • 增删改查方法
    • push() : 末尾新增元素,返回值是新数组长度
    • unshift():开头新增元素,返回值是新数组长度
    • pop() :末尾删除元素,返回值是删除的那个末尾元素
    • shift(): 开头删除元素,返回值是开头的那个末尾元素
    • splice() : 删除指定下标元素,第三个参数是一个剩余参数,可以在删除的元素后面插入元素
  • 其他方法
    • reverse:翻转数组,会修改数组自身
    • sort: 数组排序,会修改数组自身
    • json: 拼接数组元素,返回值是拼接之后的字符串
    • slice: 根据下标范围查询数组元素,返回值是查询后的新数组
    • indexOf: 查询元素下标,一般用于元素是值类型

12.谈谈 this 对象的理解

  • 默认绑定

    • 全局环境中定义的函数,函数内部的 this 指向 window 对象
  • 隐式绑定

    • 函数还可以作为某个对象的方法调用,这时 this 就指这个上级对象

  • new 绑定

    • 通过构建函数 new 关键字生成一个实例对象,此时 this 指向这个实例对象
  • 显示绑定

    • apply()、call()、bind()是函数的一个方法,作用是改变函数的调用对象。它的第一个参数就表示改变后的调用这个函数的对象。因此,这时 this 指的就是这第一个参数
  • 箭头函数

    • 箭头函数没有自己的 this 值,箭头函数中所使用的 this 来自于函数作用域链

    • 箭头函数没有自己的 this 值,箭头函数中所使用的 this 都是来自函数作用域链,它的取值遵循普通普通变量一样的规则,在函数作用域链中一层一层往上找。

13.防抖和节流

防抖和节流是性能优化手段

什么是防抖? 防抖:单位时间内,频繁触发事件,只执行最后一次。 防抖的主要应用场景:

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测

什么是节流? 节流:单位时间内,频繁触发事件,只执行一次。 节流的主要应用场景:

  • 高频事件 例如 resize 事件、scroll 事件
  • 手机号、邮箱验证输入检测

相同点:

  • 都可以通过使用 setTimeout 来实现
  • 降低回调执行频率。节省计算资源

不同点:

  • 函数防抖,在一段连续操作结束后,处理回调,利用 clearTimeout 和 setTimeout 来实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能
  • 函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次

14.递归

递归是什么? 一个函数在内部调用自己,这个函数就是递归函数。

递归核心思想? 其核心思想是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

递归的边界条件? 递归需要有边界条件、递归前进阶段和递归返回阶段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

15.Promise

Promise 是异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,他的出现大大改善了异步编程的困境,避免了地狱回调,它比传统的解决方案回调函数和事件更合理和更强大。

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

promise 的三种状态改变

  1. pending 变为 resolved
  2. pending 变为 rejected
  3. 只有这两种 且一个 promise 对象只能改变一次无论成功还是失败,都会有个结果

promise 的常见 API

  1. 构造函数 Promise( excutor ){ }
  • executor 函数: 执行器 ( resolve, reject ) =>{ }
  • resolve 函数: 内部定义成功时我们调用的函数 value => { }
  • reject 函数: 内部定义失败时我们调用的函数 reason => { }
  • 说明: executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行
  1. Promise.prototype.then 方法: (onResolved, onRejected) => {}
  • onResolved 函数: 成功的回调函数 (value) => {}
  • onRejected 函数: 失败的回调函数 (reason) => {}
  • 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调

返回一个新的 promise 对象

  1. Promise.prototype.catch 方法: (onRejected) => {}
  • onRejected 函数: 失败的回调函数 (reason) => {}
  1. Promise.resolve 方法: (value) => {}
  • value: 成功的数据或 promise 对象
  • 说明: 返回一个成功/失败的 promise 对象
  1. Promise.reject 方法: (reason) => {}
  • reason: 失败的原因
  • 说明: 返回一个失败的 promise 对象
  1. Promise.all 方法: (promises) => {}
  • promises: 包含 n 个 promise 的数组
  • 说明: 返回一个新的 promise, 只有所有的 promise 都成功才成功, 只要有一个失败了就直接失败
  1. Promise.race 方法: (promises) => {}
  • promises: 包含 n 个 promise 的数组
  • 说明: 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态

16.数组去重

方法一:利用Set
const res1 = Array.from(new Set(arr));
#方法二:两层for循环+splice
const arr2 = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
const unique1 = arr => {
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);// 每删除一个树,j--保证j的值经过自加后不变。同时,len--,减少循环次数提升性能        
        len--;
        j--;
      }
    }
  }
  return arr;
}

#方法三:利用indexOf || include
// indexOf 
const unique2 = arr => {  
  const res = [];  
  for (let i = 0; i < arr.length; i++) {    
    if (res.indexOf(arr[i]) === -1) res.push(arr[i]);  
  }  
  return res;
}

// include
const unique3 = arr => {  
  const res = [];  
  for (let i = 0; i < arr.length; i++) {    
    if (!res.includes(arr[i])) res.push(arr[i]);  
  }  
  return res;
}

#方法四:利用Map
const unique4 = arr => {  
  const map = new Map();  
  const res = [];  
  for (let i = 0; i < arr.length; i++) {    
    if (!map.has(arr[i])) {      
      map.set(arr[i], true);
      res.push(arr[i]);    
    }  
  }  
  return res;
}

vue题目集合

1.响应式原理

  • vue2响应式原理

Vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

  • vue3 响应式原理

Vue3 使用 Proxy 来监控数据的变化。Proxy 是 ES6 中提供的功能,其作用为:用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等).相对于 Object.defineProperty() 其有以下特点:Proxy 直接代理整个对象而非对象属性,这样只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性。

Proxy 可以监听数组的变化

2.vue2中data的理解

data是一个组件选项,用于定义组件的数据对象。当一个组件被创建时,data中定义的数据将会被注入到组件实例中,可以在组件的模板中访问和使用这些数据。

data选项应该是一个函数,而不是一个对象。这是因为当组件被复用多次时,每个实例需要一个独立的数据副本,而不是共享同一个对象。因此,将data定义为一个函数,每次组件被创建时,都会返回一个新的数据对象。

data函数中,可以返回一个包含组件数据的对象,每个属性表示一个数据字段。数据字段可以是任何合法的JavaScript对象,例如基本类型(如字符串、数字、布尔值)或复杂类型(如对象、数组)。

3.uniapp如何封装组件

  1. 创建一个新的文件夹,命名为component-name,作为你的组件的文件夹。

  2. 在该文件夹中创建一个.vue文件,命名为component-name.vue,用于编写组件的模板、样式和逻辑。

  3. 在需要使用该组件的页面或组件中,引入组件并使用。

  4. uni-app支持使用插槽(slot)来在组件中定义可插入的内容,以增加组件的灵活性。你可以在组件的模板中使用标签,在使用组件时,将内容插入到该插槽中。

4.computed和watcher的区别 

watch 侦听某一数据的变化从而会触发函数,当数据为对象类型时,对象中的属性值变化时需要使用深度侦听 deep 属性,也可在页面第一次加载时使用立即侦听 immdiate 属性 computed 计算属性是触发函数内部任一依赖项的变化都会重新执行该函数,计算属性有缓存,多次重复使用计算属性时会从缓存中获取返回值,计算属性必须要有 return 关键词

  • 计算属性适用于基于已存在的响应式数据进行计算,结果需要在模板中使用。
  • Watcher适用于监听任意数据的变化,并在数据发生变化时执行自定义逻辑,不仅限于计算属性。
  • 计算属性具有缓存机制,只有相关响应式数据变化时才会重新计算,而Watcher不具备缓存,每次数据变化都会触发回调函数。
  1. computed 是在 HTML,DOM 加载后马上执行的,如赋值;(属性将被混入到 Vue 实例)
  2. watch 它用于观察 Vue 实例上的数据变动,一般情况下是依赖项的值变化之后再执行,当然可以设置立刻执行

5.路由模式有哪几种

Hash 模式(默认模式):URL 中的 hash(#)符号用来表示路由,即路由路径的改变不会引起浏览器发送请求,而是通过监听 hashchange 事件来进行路由的切换。

History 模式:使用 HTML5 history API 来实现路由,即通过修改 URL 的 path 部分来进行路由切换,不带有哈希符号。这种模式下,需要服务器的支持以避免在刷新页面时出现 404 错误。

6.set和map的区别

  1. 存储数据的方式

    • Set:Set是一种存储唯一值的集合,不允许重复的值存在。Set中的每个值都是唯一的。
    • Map:Map是一种键值对(Key-Value)的数据结构,可以将值与特定的键关联起来,键可以是任何数据类型。
  2. 数据存取的方式

    • Set:Set只能存储单个值,通过add()方法添加新的值,通过has()方法判断值是否存在,通过delete()方法删除值。
    • Map:Map可以存储键值对,通过set()方法添加新的键值对,通过get()方法获取对应键的值,通过has()方法判断键是否存在,通过delete()方法删除键值对。
  3. 遍历的顺序

    • Set:Set内部的元素是按照插入顺序进行排序的。可以使用forEach()for...of循环遍历Set中的元素。
    • Map:Map按照插入顺序维护键值对的顺序。可以使用forEach()for...of循环遍历Map中的键值对。
  4. 长度和迭代

    • Set:Set结构有size属性来获取它所包含的元素数量。可以使用forEach()for...of循环迭代Set中的元素。
    • Map:Map结构有size属性来获取它所包含的键值对数量。可以使用forEach()for...of循环迭代Map中的键值对。
  5. 键的类型限制

    • Set:Set中的键只能为值本身,键与值是同一个对象。
    • Map:Map中的键可以是任何数据类型,包括基本数据类型、对象、函数等。

7.虚拟DOM

vue2新提出来的一个特性,主要解决跨平台的问题.

本身是一个对象,用对象属性来描述节点,相当于在js和真实DOM之间加了一个缓存,利用diff算法避免没有必要的DOM操作,提升性能.

8.diff算法

一种对比算法,对比两者是旧虚拟dom和新虚拟dom,对比出是哪个虚拟节点更改了,找出这个虚拟节点并只更新这个虚拟节点所对应的真实节点而不用更新其他数据没发生改变的节点,实现精准地更新真实 DOM,进而提高效率.

更改数据之后,rander函数会重新执行,得到一个全新的虚拟dom,找到当前虚拟dom和之前虚拟dom的差异.

通过 sameVnode 中key的值来判断判断节点是否一样,一样时,直接递归调用 patchVnode去处理这两个节点

旧节点和新节点自身不一样,当两个节点不一样的时候,直接创建新节点,删除旧节点

9.key的作用

提升diff算法的效率

不设key值,key值为undefined.

设置key唯一值,通过判断key值是否相同,判断节点是否相同,从头节点开始对比,相同类型节点进行patch,数据相同,不发生dom操作.新旧节点对比顺序,头和头比,尾和尾比,头和尾比,尾和头比.

10.Vue 中的$nextTick

我们可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新

如果没有 nextTick 更新机制,那么 num 每次更新值都会触发视图更新(上面这段代码也就是会更新 10 万次视图),有了nextTick机制,只需要更新一次,所以nextTick本质是一种优化策略

11.$set

数据变化视图不更新问题, 当在项目中直接设置数组的某一项的值,或者直接设置对象的某个属性值,这个时候,你会发现页面并没有更新。这是因为 Object.defineProperty()限制,监听不到变化。

12.v-show 和 v-if

我们都知道在 vue 中 v-show 与 v-if 的作用效果是相同的(不含 v-else),都能控制元素在页面是否可见

控制手段:v-show隐藏则是为该元素添加css--display:none,元素依旧还在。v-if显示隐藏是将元素整个添加或删除

编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于 css 切换

编译条件:v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染

  • v-show 由false变为true的时候不会触发组件的生命周期

  • v-iffalse变为true的时候,触发组件的beforeCreatecreatebeforeMountmounted钩子,由true变为false的时候触发组件的beforeDestorydestoryed方法

性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;

v-if 与 v-show 都能控制元素在页面的显示

v-if 相比 v-show 开销更大的(直接操作dom节点增加与删除)

如果需要非常频繁地切换,则使用 v-show 较好,例如:手风琴菜单,tab 页签等

如果在运行时条件很少改变,则使用 v-if 较好。例如:用户登录之后,根据他的权限不同来显示不同的内容。

13.v-if 和 v-for

v-if 不能和 v-for 一起使用的原因是 v-for 的优先级比 v-if 高,先循环再做分支判断,一起使用会造成性能浪费

v2 中:v-for优先级比v-if

v3 中:v-if优先级比v-for

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true值的时候被渲染

v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组或者对象,而 item 则是被迭代的数组元素的别名

在 v-for 的时候,建议设置key值,并且保证每个key值是独一无二的,这便于diff算法进行优化

14.Vue 组件之间的通信方式

  1. 父向子通信: 在子组件的标签上通过自定义属性传递父组件的数据,子组件的内部通过 props 接收父向子传递的数据

  2. 子向父通信: 在子组件的标签上自定义事件,自定义事件的值是父组件的方法,在子组件内部通过 this.$emit()方法触发事件,第一个参数为自定义事件,第二个参数可以传递子组件的内部数据,此时父组件中的方法就可以执行了,

  3. 兄弟组件通信: 可以采取 eventbus 实现数据传递,但是这种方式我们在开发中基本不用,多组件共享数据都是用的 vuex

  4. 后代组件通信: 可以采取依赖注入的方式,在祖先组件中通过 provide 提供数据,在后代组件中通过 inject 接收数据

  5. 无关联关系组件通信: 在开发中我们都是使用 vuex

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

  1. 父子
    1. props +$emit
    2. v-model
    3. .sync
    4. 使用 ref
  2. 兄弟
    1. EventBus
  3. 祖先后代
    1. $parent 或$root
    2. attrs 与 listeners
    3. Provide 与 Inject
  4. Vuex
  • 适用场景: 复杂关系的组件数据传递
  • Vuex作用相当于一个用来存储共享变量的容器
  • state用来存放共享变量的地方

  • getter,可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值

  • mutations用来存放修改state的方法。

  • actions也是用来存放修改 state 的方法,不过action是在mutations的基础上进行。常用来做一些异步操作

15. vue 的 mixin 的理解

在Vue.js中,Mixin是一种可以复用和共享Vue组件选项的方式。Mixin提供了一种将公共的组件选项、方法和生命周期钩子封装为可重用模块的方法。

使用Mixin,你可以在多个组件中共享同样的逻辑和功能,而无需重复编写代码。当多个组件需要使用相似的选项、方法或生命周期钩子时,可以通过将它们提取到一个Mixin中来实现代码的复用。

通过这样的方式,你的组件就可以继承Mixin中的选项、方法和生命周期钩子。

需要注意的是,如果Mixin和组件中的选项或方法名称冲突,组件中的选项或方法将覆盖Mixin中的内容。

Mixin的使用可以帮助你更好地组织和管理你的Vue组件代码,提高代码的重用性和维护性。

16.slot

slot 是插槽,一般在组件内部使用

在封装组件时,在组件内部不确定该位置是以何种形式的元素展示时,,我们可以通过 slot 占据这个位置,该位置的元素需要父组件以内容形式传递过来.

slot 又分为:默认插槽,具名插槽,作用域插槽

17.SPA 单页面

SPA 称为单页应用程序,也就是整个网站只有一个 html 页面

优点: 相对于传统混合式开发,减少了请求体积,加快页面响应速度,降低了对服务器的压力

缺点: 因为技术使用了 ajax,导致页面不利于 SEO,

18.vuex

  • vuex 是为了解决整个网站状态数据共享问题的,虽然有父向子,子向父等数据传递,但在网站开发过程中一些无直接关联关系的组件也需要共享相同的数据时,就需要使用 vuex 了
vuex 中有五个主要的成员:
  • state 是用来存储数据的
  • mutations 是用来修改 state 中的数据的方法
  • actions 是用来处理一些异步操作数据的行为方法
  • getters 有点类似计算属性,是对 state 中的数据做了一些处理
  • modules 是用来对复杂业务分模块的,每个模块也可以有 state,mutaions,actions,getters
vuex 是什么

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

vuex 的出现解决了:

1.多个组件共享状态时,单向数据流的简洁性很容易被破坏: 2.多个视图依赖于同一状态。 3.来自不同视图的行为需要变更同一状态。

Vuex 的数据传递流程

当组件进行数据修改的时候我们需要调用 dispatch 来触发 actions 里面的方法。actions 里面的每个方法中都会 有一个 commit 方法,当方法执行的时候 会通过 commit 来触发 mutations 里面的方法进行数据的修改。 mutations 里面 的每个函数都会有一个 state 参数,这样就可以在 mutations 里面进行 state 的 数据修改 ,当数据修改完毕后,会传导给页面。页面的数据也会发生改变

Vuex 的 Mutation 和 Action 之间的区别

我们可以从以下三个方面对两者进行分析:

第一点 => 流程顺序

“相应视图--->修改 State”拆分成两部分,视图触发 Action,Action 再触发 Mutation。

第二点 => 角色定位

基于流程顺序,二者扮演不同的角色。

Mutation:专注于修改 State,理论上是修改 State 的唯一途径。

Action:业务代码、异步请求。

第三点 => 限制

角色不同,二者有不同的限制。

Mutation:必须同步执行。

Action:可以异步,但不能直接操作 State。而且可以通过 action 来提交 mutations

19.你有写过自定义指令吗?自定义指令的应用场景有哪些?

  • 【作用是什么】:可以对普通 DOM 进行底层操作,例如聚焦输入框。

  • 【怎么用】:可以通过 Vue.directive 全局注册一个指令,或者组件内部通过 directives 进行局部注册,它常用的钩子有 inserted,表示被绑定元素插入父节点时调用,还有 update,它表示指令所在组件的 VNode 更新时调用。

  • 【场景】:我在实际项目中,用自定义指令处理过图片懒加载,原理就是当图片进入可视区的时候把图片的地址给图片的 src 属性就好啦。

  • 【注意】:自定义指令相关的钩子在 Vue3 中发生了变化,主要体现在和组件的生命周期钩子保持了一致,更加容易记忆和理解了。

20.重绘和回流,怎么避免尽量回流 

  1. 使用样式集合(CSS Class):在操作元素样式时,尽量使用样式集合,而不是直接修改元素的样式属性。这样可以减少对样式的多次修改,从而减少回流次数。

  2. 批量修改样式:将对多个元素样式的修改集中在一次操作中,可以减少回流的次数。可以使用 CSS 类名的方式批量修改样式,或者使用 Element.style 对象在一次操作中修改多个样式属性。

  3. 离线操作:在对 DOM 进行大量修改时,可以使用离线 DOM 操作。将元素从 DOM 中移除,进行修改后再放回 DOM 中,这样可以避免回流的频繁发生。

  4. 文档片段(DocumentFragment):当需要创建多个元素时,可以使用文档片段进行批量插入。先将元素添加到文档片段中,然后再一次性插入到文档中,可以减少回流次数。

  5. 缓存布局信息:避免在循环中获取布局信息。获取布局信息(如宽度、高度、偏移量等)会导致回流的触发,可以将布局信息缓存在变量中,避免多次获取。

  6. 使用 display: none:在需要隐藏元素时,尽量使用 display: none 而不是修改 visibility 或者 opacity 属性。因为 display: none 可以完全移除元素的布局,不会触发回流。

  7. 动画优化:使用 CSS3 动画,如 transform 和 opacity,可以利用硬件加速,减少对布局的影响,提高动画的性能。

21.promise常用方法

构造函数 Promise( excutor ){ }
  • executor 函数: 执行器 ( resolve, reject ) =>{ }
  • resolve 函数: 内部定义成功时我们调用的函数 value => { }
  • reject 函数: 内部定义失败时我们调用的函数 reason => { }
  • 说明: executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行
Promise.prototype.then 方法: (onResolved, onRejected) => {}
  • onResolved 函数: 成功的回调函数 (value) => {}
  • onRejected 函数: 失败的回调函数 (reason) => {}
  • 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调
Promise.prototype.catch 方法: (onRejected) => {}
  • onRejected 函数: 失败的回调函数 (reason) => {}
  • Promise.resolve 方法: (value) => {}
  • value: 成功的数据或 promise 对象
  • 说明: 返回一个成功/失败的 promise 对象
Promise.reject 方法: (reason) => {}
  • reason: 失败的原因
  • 说明: 返回一个失败的 promise 对象
Promise.all 方法: (promises) => {}
  • promises: 包含 n 个 promise 的数组
  • 说明: 返回一个新的 promise, 只有所有的 promise 都成功才成功, 只要有一个失败了就直接失败
Promise.race 方法: (promises) => {}
  • promises: 包含 n 个 promise 的数组
  • 说明: 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态

22.异步编程有哪些实现方式

  1. 回调函数(Callbacks):使用回调函数作为参数,在异步操作完成后执行回调函数。这是一种传统的异步编程方式,在旧版JavaScript中广泛使用。但多层嵌套的回调函数(回调地狱)可能导致代码难以阅读和维护。

  2. Promise:Promise 是一种处理异步操作的对象,它代表了操作的完成或失败。通过使用 Promise,可以根据操作的状态(已完成、未完成、已拒绝)来处理异步操作,并使用 .then() 和 .catch() 等方法处理异步结果。

  3. Async/Await:Async/Await 是建立在 Promise 之上的语法糖,它提供了一种更直观和简洁的方式编写异步代码。通过 async 关键字标记一个函数为异步函数,并使用 await 关键字来等待 Promise 的解决。Async/Await 基于 Promise,它可以以同步的方式书写异步代码。

23.vue项目性能优化

  1. 使用Vue的生产环境版本:在生产环境中使用Vue的压缩版本,可以减小文件体积,提升加载速度。

  2. 使用异步组件:将页面中的重要组件拆分为异步加载的组件,可以优化初始加载时间,只在需要时才加载相应的组件。

  3. 合理使用延迟加载:根据页面内容的优先级,合理设置组件的延迟加载,以减少初始加载时间,提升用户感知的加载速度。

  4. 懒加载图片:对于长列表或需要滚动加载的页面,可以使用懒加载技术延迟加载列表中的图片,减少初始加载时间。

  5. 优化网络请求:合并多个请求,使用请求缓存,减少不必要的请求,使用CDN加速等方法来降低网络请求的开销。

  6. 使用Vue的keep-alive缓存组件:对于频繁切换的组件,使用Vue的keep-alive组件进行缓存,以避免重复渲染,提升性能。

  7. 避免不必要的计算和渲染:避免频繁且不必要的计算和渲染,减少不必要的性能开销。可以使用v-ifv-show等指令进行条件渲染。

  8. 优化列表和大数据集渲染:对于大数据列表,使用虚拟滚动或分页加载等技术进行优化,避免一次性渲染大量数据。

  9. 减少和优化Watch和计算属性:合理使用Vue的Watch和计算属性,避免Watch和计算属性的运行时间过长,尽量减少不必要的计算。

  10. 利用Vue Devtools进行性能分析:使用Vue Devtools等性能分析工具,对项目进行性能分析,找出性能瓶颈,并进行相应的优化。

24.为什么修改数据,视图也会更新

修改数据会触发object.defineProperty()方法属性里面的set函数,从而rander函数重新执行,视图得到更新

25.组件的封装

  1. 确定组件的功能和用途:首先,明确组件的功能和用途,确定组件需要实现哪些功能,以及其在应用中的具体用途。

  2. 创建组件文件:在项目中创建组件文件,通常是单独的.vue文件。可以使用Vue CLI工具创建组件模板,或手动创建。

  3. 编写组件的模板:在.vue文件中,编写组件的模板部分,即HTML结构,定义组件的外观和布局。

  4. 编写组件的样式:根据设计需求,编写组件的样式,可以使用CSS预处理器如Sass或Less来增强样式的可维护性。

  5. 编写组件的行为逻辑:在组件的.vue文件中,编写组件的行为逻辑部分,即JavaScript部分,包括组件的属性、方法和事件处理逻辑等。

  6. 导出和注册组件:在.vue文件中,将组件导出为模块,在需要使用组件的地方将其注册。可以通过export default导出组件,并在父组件中使用import语法导入并注册组件。

  7. 使用组件:在需要使用组件的地方,使用组件的标签进行引用和渲染。将组件的标签放置在父组件的模板中,即可在应用中使用该组件。

26.项目中遇到过什么难点?怎么解决的

  1. 缺乏知识:对于新的或陌生的技术,可能会遇到知识上的缺乏。可以通过阅读文档、教程和参考资料,参加培训课程或寻求社区的帮助来学习和获取所需的知识。

  2. 需求变更:在开发过程中,需求变更是常见的。要处理需求变更,可以与项目团队和利益相关者进行沟通,重新评估和优先排序需求,并适时调整计划和开发策略。

27.你在之前的项目中配置过公共路由吗,怎么配置的

  1. 创建公共路由组件:首先,创建公共路由所需的组件,比如登录页、404页面等。可以将这些组件独立出来,也可以放在一个特定的文件夹中。

  2. 配置路由文件:在项目的路由文件中,配置公共路由。在Vue.js中,可以使用Vue Router来实现路由配置。在路由配置中,通过path指定路由路径,通过component指定对应的组件。

  3. 添加公共路由:根据具体的需求,添加公共路由到路由配置中。比如,可以添加一个登录页的路由,将其component指向登录页的组件。

  4. 引入公共路由:在根组件中,引入公共路由。可以在主组件中通过标签来渲染公共路由的内容。

  5. 在其他页面使用路由:在其他页面组件中,可以通过标签创建链接到公共路由页面的导航链接。也可以通过编程式导航来跳转到公共路由页面。

28.Vue3.0 里为什么要用 Proxy API 替代 defineProperty API 

最核心的原因是性能,展开来说如下。

  • 因为 Proxy 代理的直接是整个对象,例如对象中有 100 个属性,用 defineProperty 劫持至少需要循环 100 次,而 proxy 至少一次搞定。

  • defineProperty 对数组中存在大量元素的劫持操作性能不好,所以 Vue2 并没有直接使用 defineProperty 对数组进行劫持,而是提供了额外的方法来处理数组的响应式,例如 $set,其实换成 proxy 就不存在这个问题了,当然 $set 方法也就没有必要存在了。

  • Vue2 中,劫持数据的操作在实例创建完成就已经进行完毕了,所以对对象后续新增的属性是劫持不到的,也就意味着后续新增的属性是不具备响应式能力的,所以 Vue 不得不提供了 $set 方法。而换成 proxy 就不存在这个问题了,因为它劫持的整个对象,后续添加的属性也是属于这个对象的,那么 $set 也就没有必要了(干掉 $set 方法本身也是性能的一个体现)。

29.说说你对 keep-alive 的理解

  • 【是什么】:它是 Vue 内置的一个组件,可以用来做组件缓存以提升性能。

  • 【怎么用】:一般用这个内置组件可以包裹住动态组件(component)或路由出口的地方,它提供的也有 include 属性可以控制缓存哪些组件名,exclude 属性控制不缓存哪些组件,它对应的也有两个钩子,分别是激活时触发的 activated 和失活时触发的 deactivated。

keep-alivevue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM

keep-alive它的工作原理是:包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们

30.动态给 vue 的 data 添加一个新的属性时会发生什么?怎样解决?

  • Vue2 中,劫持数据的操作在实例创建完成就已经进行完毕了,所以对对象后续新增的属性是劫持不到的,也就意味着后续新增的属性是不具备响应式能力的,可以通过 this.$set 或 Vue.set 来解决,语法是 Vue.set(对象, 属性, 值)。

  • Vue3 响应式的原理换成了 proxy 就不存在这个问题了,因为它劫持的是整个对象,而后续添加的属性也是属于这个对象的。

31.Vue 常用的修饰符有哪些有什么应用场景

  • .stop 相当于 event.stopPropagation(),用来阻止冒泡。

  • .prevent 相当于 envet.preventDefault(),用来阻止默认事件。

  • .native 监听组件根元素的原生事件。

  • .once 只触发一次。

32.vue3 有了解过吗?能说说跟 vue2 的区别吗?

  1. 性能更高了,主要得益于响应式的原理换成了 proxy,VNode diff 的算法进行了优化。

  2. 体积更小了,删除了一些没必要或不常用到的 API,例如 filter、EventBus 等;按需导入,能配合 Webpack 支持 Tree Shaking。

  3. 对 TS 支持更好啦,因为它本身源码就是用 TS 重写的。

  4. Composition API(组合 API),相比较 Vue2 的 options api,对于开发大型项目更利于代码的复用和维护。

  5. 新特性,例如 Fragment、Teleport、Suspense 等

微前端

微前端是一种用来构建现代化前端架构的理念和技术实践。它通过将单体应用拆分成更小的、可独立开发和部署的微服务形式,从而实现了前端的解耦和独立性。以下是微前端的一些关键概念和技术要点:

  1. 拆分应用:将大型单体应用拆分成多个小型应用,每个应用都是一个独立的前端微服务。每个微服务负责特定的业务功能,并可以使用不同的技术栈开发。

  2. 独立开发和部署:每个前端微服务都可以独立开发、测试和部署。不同团队可以并行开发不同的微服务,通过版本控制和集成流程来协同工作。

  3. 运行时集成:微前端应用可以在运行时集成到主应用中。可以通过路由、页面嵌入、IFrame等方式来组合和展示各个微服务,让用户感觉它们是一个无缝的整体。

  4. 共享组件和依赖:通过建立共享组件库和依赖管理机制,不同的微服务可以共享和复用组件、样式、逻辑和工具库等,实现代码的重用性和一致性。

  5. 独立部署和版本管理:每个前端微服务都可以独立部署和升级,不会影响整个应用的稳定性和可用性。可以使用容器技术和自动化部署工具来简化部署和管理流程。

  6. 沟通和协调:通过定义标准接口和通信协议,不同的前端团队可以进行沟通和协调,确保各个微服务之间的数据和信息流动顺畅。

说说你对 webpack 的理解

JS

Vue

  • WebPack 是一个模块打包工具,可以使用 WebPack 管理模块。在 webpack 看来,项目里所有资源皆模块,分析模块间的依赖关系,最终编绎输出模块为 HTML、JavaScript、CSS 以及各种静态文件(图片、字体等),让开发过程更加高效。

  • 对于不同类型的资源,webpack 有对应的模块加载器 loader

  • CSS

  • 解析 CSS 的 css-loader、style-loader,

  • 解析 less 的 less-loader,sass 的 sass-loader,

  • 解析将 TypeScript 转换成 JavaScript 的 ts-loader,
  • 解析.vue 文件的 vue-loader、
  • 静态资源:音视频、文件、json
  • 解析常用图片以及音视频资源的 url-loader、
  • 解析文件的 file-loader,
  • 解析 JSON 文件的 json-loader,
    • 解析 ES6 为 ES5 的 babel-loader,
    • 解析 JavaScript 代码规范的 eslint-loader

说说 webpack proxy 工作原理

proxy 工作原理实质上是利用 http-proxy-middleware 这个 http 代理中间件,实现请求转发给其他服务器

说说如何借助 webpack 来优化前端性能

随着前端的项目逐渐扩大,必然会带来的一个问题就是性能 尤其在大型复杂的项目中,前端业务可能因为一个小小的数据依赖,导致整个页面卡顿甚至奔溃 一般项目在完成后,会通过webpack进行打包,利用 webpack 对前端项目性能优化是一个十分重要的环节

二、如何优化 通过 webpack 优化前端的手段有:

  • JS 代码压缩
  • CSS 代码压缩
  • Html 文件代码压缩
  • 文件大小压缩
  • 图片压缩
  • Tree Shaking
  • 代码分离
  • 内联 chunk

Vue 项目中你是如何解决跨域的

跨域,是指浏览器不能执行其它网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 javascript 实施的安全限制。

同源策略是浏览器最核心也最基本的安全功能,具有以下特点:

  • 协议相同

  • 主机相同

  • 端口相同

只要其中一项不相同,就会产生跨域

常见的解决方案有 3 种:

JSONP: 利用 script 标签,不受跨域限制的特点,缺点是只能支持 get 请求

CORS: 后端设置响应头 Access-Control-Allow-Origin:*

服务器代理:在 DevServer 中配置 proxy

HTTP 和 HTTPS 的区别

http 是无状态的超文本传输协议,连接简单,信息是明文传输,端口为 80。

https 协议是由 http+ss 协议构建的可进行加密传输、身份认证的具有安全性网络协议,端口是 443

优缺点

https 有加密认证相对于 http 安全一些

https 因为需要进行加密解密等过程,因此速度会更慢

说说地址栏输入 URL 敲下回车后发生了什么

  1. 对www.baidu.com这个网址进行DNS域名解析,得到对应的IP地址
  2. 根据这个 IP,找到对应的服务器,发起 TCP 的三次握手
  3. 建立 TCP 连接后, 发起 HTTP 请求
  4. 服务器响应 HTTP 请求,浏览器得到 html 代码
  5. 浏览器解析 html 代码,并请求 html 代码中的资源(如 js、css、图片等)(先得到 html 代码,才能去找这些资源)
  6. 服务器响应对应的资源
  7. 响应数据完毕, 四次挥手,关闭 TCP 连接
  8. 浏览器对页面进行渲染呈现给用户

说说 TCP 为什么需要三次握手和四次挥手

三次握手是为了确认双方的接收与发送能力是否正常。

1、第一次握手:客户端给服务器发送一个 SYN 报文。

2、第二次握手:服务器收到 SYN 报文之后,会应答一个 SYN+ACK 报文。

3、第三次握手:客户端收到 SYN+ACK 报文之后,会回应一个 ACK 报文。 服务器收到 ACK 报文之后,三次握手建立完成。

当客户端和服务端通过三次握手建立 TCP 连接进行可靠数据传输后,当数据传送完毕,肯定是要断开 TCP 连接,所以就有了四次挥手。

四次挥手是为了断开连接后「资源」正常释放,双方都可以主动断开连接。

1、第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。

2、第二次握手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。

3、第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。

4、第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态

5、服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。

说说对 WebSocket 的理解

WebSocket 是 HTML5 下一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的。它与 HTTP 一样通过已建立的 TCP 连接来传输数据,但是它和 HTTP 最大不同是:

  • WebSocket 是一种双向通信协议。在建立连接后,WebSocket 服务器端和客户端都能主动向对方发送或接收数据,就像 Socket 一样;

  • WebSocket 需要像 TCP 一样,先建立连接,连接成功后才能相互通信。

相比 HTTP 长连接,WebSocket 有以下特点:

  • 是真正的全双工方式,建立连接后客户端与服务器端是完全平等的,可以互相主动请求。而 HTTP 长连接基于 HTTP,是传统的客户端对服务器发起请求的模式。

  • HTTP 长连接中,每次数据交换除了真正的数据部分外,服务器和客户端还要大量交换 HTTP header,信息交换效率很低。Websocket 协议通过第一个 request 建立了 TCP 连接之后,之后交换的数据都不需要发送 HTTP header 就能交换数据,这显然和原有的 HTTP 协议有区别所以它需要对服务器和客户端都进行升级才能实现(主流浏览器都已支持 HTML5)。

  • 此外还有 multiplexing、不同的 URL 可以复用同一个 WebSocket 连接等功能。这些都是 HTTP 长连接不能做到的。

WebSocket 优势:

浏览器和服务器只需要要做一个握手的动作,在建立连接之后,双方可以在任意时刻,相互推送信息。同时,服务器与客户端之间交换的头信息很小。

总结一下 Websocket 和 HTTP 的区别:

相同点:

  • 都是基于 tcp 的,都是可靠性传输协议

  • 都是应用层协议

不同点:

  • WebSocket 是双向通信协议,模拟 Socket 协议,可以双向发送或接受信息

  • HTTP 是单向的

  • WebSocket 是需要浏览器和服务器握手进行建立连接的

  • 而 http 是浏览器发起向服务器的连接,服务器预先并不知道这个连接

WebSocket 主要应用场景有以下几类:

即时通讯(QQ、微信、客服)

弹幕

协同编辑

体育实况更新

股票基金报价实时更新

可视化大屏数据实时更新

在TS中用到了哪些内置高阶类型

交叉类型

通过 & 将多个类型合并为一个类型,包含了所需的所有类型的特性,本质上是一种并的操作

语法如下:

T & U

适用于对象合并场景,如下将声明一个函数,将两个对象合并成一个对象并返回:

function extend(first: T, second: U) : T & U {
    let result:  = {}
    for (let key in first) {
        result[key] = first[key]
    }
    for (let key in second) {
        if(!result.hasOwnProperty(key)) {
            result[key] = second[key]
        }
    }
    return result
}
#联合类型

联合类型的语法规则和逻辑 “或” 的符号一致,表示其类型为连接的多个类型中的任意一个,本质上是一个交的关系

语法如下:

T | U

例如 number | string | boolean 的类型只能是这三个的一种,不能共存

如下所示:

function formatCommandline(command: string[] | string) {
  let line = ''
  if (typeof command === 'string') {
    line = command.trim()
  } else {
    line = command.join(' ').trim()
  }
}
#类型别名

类型别名会给一个类型起个新名字,类型别名有时和接口很像,但是可以作用于原始值、联合类型、元组以及其它任何你需要手写的类型

可以使用 type SomeName = someValidTypeAnnotation的语法来创建类型别名:

type some = boolean | string

const b: some = true // ok
const c: some = 'hello' // ok
const d: some = 123 // 不能将类型“123”分配给类型“some”

此外类型别名可以是泛型:

type Container = { value: T }

也可以使用类型别名来在属性里引用自己:

type Tree = {
  value: T
  left: Tree
  right: Tree
}

可以看到,类型别名和接口使用十分相似,都可以描述一个对象或者函数

两者最大的区别在于,interface只能用于定义对象类型,而 type 的声明方式除了对象之外还可以定义交叉、联合、原始类型等,类型声明的方式适用范围显然更加广泛

#类型索引

keyof 类似于 Object.keys ,用于获取一个接口中 Key 的联合类型。

interface Button {
  type: string
  text: string
}

type ButtonKeys = keyof Button
// 等效于
type ButtonKeys = 'type' | 'text'
#类型约束

通过关键字 extend 进行约束,不同于在 class 后使用 extends 的继承作用,泛型内使用的主要作用是对泛型加以约束

type BaseType = string | number | boolean

// 这里表示 copy 的参数
// 只能是字符串、数字、布尔这几种基础类型
function copy(arg: T): T {
  return arg
}

类型约束通常和类型索引一起使用,例如我们有一个方法专门用来获取对象的值,但是这个对象并不确定,我们就可以使用 extends 和 keyof 进行约束。

function getValue(obj: T, key: K) {
  return obj[key]
}

const obj = { a: 1 }
const a = getValue(obj, 'a')
#映射类型

通过 in 关键字做类型的映射,遍历已有接口的 key 或者是遍历联合类型,如下例子:

type Readonly = {
  readonly [P in keyof T]: T[P]
}

interface Obj {
  a: string
  b: string
}

type ReadOnlyObj = Readonly

上述的结构,可以分成这些步骤:

keyof T:通过类型索引 keyof 的得到联合类型 'a' | 'b'

P in keyof T 等同于 p in 'a' | 'b',相当于执行了一次 forEach 的逻辑,遍历 'a' | 'b'

小程序的支付怎样实现

对于小程序支付,商户需要进行以下步骤:

  1. 商户注册:首先需要注册商户账号,并通过微信的审核和认证。

  2. 配置小程序支付:在小程序后台进行支付配置,填写商户相关信息,包括商户号、支付密钥等。

  3. 生成预支付订单:在小程序中生成预支付订单,并获取到预支付订单号。

  4. 调起支付接口:通过小程序的支付接口,调起支付功能,传递预支付订单号等参数。

  5. 处理支付结果:在支付完成后,微信会返回支付结果通知,商户需要在服务器端进行支付结果验证,并根据支付结果完成后续的业务逻辑处理。

  6. 提示支付结果:根据支付结果的处理情况,在小程序中给用户展示相应的支付成功或失败信息,并进行相应的业务逻辑处理。

需要注意的是,小程序支付涉及到商户的支付流程和后台服务配置,因此需要在商户平台进行相关设置和操作。

小程序主要用过哪些 ui 库

  1. WeUI:WeUI是微信官方推出的一个基于Vue.js的UI框架,提供了一套符合微信设计风格的组件和样式,使用方便,适合快速开发小程序。WeUI包含丰富的UI组件,如按钮、卡片、表单、列表等。

  2. Vant Weapp:Vant Weapp是有赞团队开源的一套轻量、可靠的小程序UI组件库。Vant Weapp基于Vue.js的Vant组件库进行改造而成,提供了丰富的组件和样式,包括按钮、轮播图、导航栏、弹窗等,具有良好的可定制性和扩展性。

  3. Wux Weapp:Wux Weapp是一套基于Vue.js的小程序组件库,专注于提供高质量的、符合微信小程序设计规范的UI组件。Wux Weapp提供了大量的组件和工具,包括表单、下拉刷新、无限滚动、遮罩层等。

  4. ColorUI:ColorUI是一套基于CSS3的小程序UI组件库,设计简洁、功能齐全。ColorUI提供了一系列常用的小程序组件,如按钮、导航栏、列表、弹窗等,适合快速开发并定制小程序界面。

你可能感兴趣的:(前端,javascript,开发语言)