整理一些最近遇到的问题-2022春招

总结了最近的一些面试题和之前的知识点


面试问到较重要的(需掌握)

讲讲Dom渲染性能消耗 ()

回流重绘

URL从输入到页面展示的过程 (获取到HTTP响应报文 HtmlDom树 CSSdom树 JSdom树 )
重绘是一个非常昂贵的操作。
浏览器完成一个dom操作,大多时间都是花费在重绘上面的。**
回流(重绘)

全局范围就是从根节点 html 开始对整个渲染树进行重新布局,例如当我们改变了窗口尺寸或方向或者是修改了根元素的尺寸或者字体大小等;而局部布局可以是对渲染树的某部分或某一个渲染对象进行重新布局。

所谓重排,实际上是根据渲染树中每个渲染对象的信息,计算出各自渲染对象的几何信息(DOM对象的位置和尺寸大小),并将其安置在界面中的正确位置。

如何避免
CSS

避免使用table布局。
避免设置多层内联样式。
将动画效果应用到position属性为absolute或fixed的元素上。

JavaScript

避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
博客
js 操作 Dom 很耗性能,其实是在说很耗浏览器性能,具体和 js计算 性能没多大的关系,优化方案
DOM操作的背后,隐藏这不止止是文中所描述的这些代价。为了给用户更好的浏览体验,可以有以下优化的方案:
减少DOM操作如果在一个局部方法中需要多次访问同一个dom,则先暂存它的引用
采用更高效的API或者更高效的写法
1)用querySelectorAll()替代getElementByXX()。
2)开启动画的GPU加速,把渲染计算交给GPU
3)用事件委托来减少事件处理器的数量。
4)使用react、vue等页面框架来编写View页面。react采用虚拟dom,尽可能的讲多次重排浓缩成一次。
CSS及动画处理
1)用更高效的css3效果,通过类名控制动画,尽量避免直接操作DOM属性;
2)在动画的元素多嵌套一层div,尽量用绝对定位或者固定定位使其脱离文档流,再进行动画处理;
3)尽量在滚动的时候,停止动画;
4)动画实现的速度选择。以1px移动最为平滑,但是reflow就会果与频繁,建议以3px移动则会好很多。
知乎

计算属性的原理

博客:

Js 动画与 CSS 动画区别及相应实现(JS动画重绘回流,性能影响)

区别:
(1)transform仅描述元素的静态样式,常常配合transition和animation使用
(2)transition通常和hover等事件配合使用,animation是自发的,立即播放
(3)animation可设置循环次数
(4)transition只能设置头尾,animation可设置每一帧的样式和时间
(5)transition可与js配合使用,js设定要变化的样式,transition负责动画效果

CSS3 的动画的优点
在性能上会稍微好一些,浏览器会对 CSS3 的动画做一些优化,代码相对简单

缺点(在动画控制上不够灵活,兼容性不好)

Js动画
JavaScript 的动画正好弥补了这两个缺点,控制能力很强,可以单帧的控制、变换,同时写得好完全可以兼容 IE6,并且功能强大。对于一些复杂控制的动画,使用 javascript 会比较靠谱。而在实现一些小的交互动效的时候,就多考虑考虑 CSS 吧
如果动画相较复杂,我们可以采用 JS + canvas 去尝试,能不能实现最后再考虑纯 JS 实现
博客

css预处理 sass less stylus 有什么区别 哪个更好用

下面从特性上比较三者异同

  1. 变量:
    Sass声明变量必须是 『$』开头,后面紧跟变量名和变量值,而且变量名和变量值需要使用冒号:分隔开。
    Less 声明变量用 『@』开头,其余等同 Sass。
    Stylus 中声明变量没有任何限定,结尾的分号可有可无,但变量名和变量值之间必须要有『等号』。但需要注意的是,如果用“@”符号来声明变量,Stylus会进行编译,但不会赋值给变量。就是说,Stylus 不要使用『@』声明变量。Stylus 调用变量的方法和Less、Sass完全相同。
  2. 作用域:
    css 预编译器把变量赋予作用域,也就是存在生命周期。就像 js 一样,它会先从局部作用域查找变量,依次向上级作用域查找。
    Sass:三者最差,不存在全局变量的概念。也就是说在 Sass 中定义了相同名字的变量时你就要小心蛋疼了。

Less:我认为跟 JS 一样,逐级查找,向上冒泡。
Stylus:完全等同 Less。Stylus 和 Sass 则更倾向于指令式。

总结

个人认为:
Sass和Less语法严谨、Stylus相对自由。因为Less长得更像 css,所以它可能学习起来更容易。
Sass 和 Compass、Stylus 和 Nib 都是好基友。
Sass 和 Stylus 都具有类语言的逻辑方式处理:条件、循环等,而 Less 需要通过When等关键词模拟这些功能,这方面 Less 比不上 Sass 和 Stylus。
Less 在丰富性以及特色上都不及 Sass 和 Stylus,若不是因为 Bootstrap 引入了 Less,可能它不会像现在这样被广泛应用(个人愚见)。
身边有几个朋友在 css 预编译器的选择上犹豫不决,其实我认为选择什么无所谓,关键在于你的熟练程度以及团队合作方面的有利性。
当然,在大致学习、使用和研究了这三种 css 预编译器之后,我想我会选择 Stylus,它的语法自由度很高,而且写出来的代码非常简洁,这点十分吸引我。

TCP和UDP的区别

TCP 和 UDP 应用场景

传送门 ☞# 深度剖析TCP与UDP的区别
博客

请求头信息

路由页面跳转后状态保存

需求场景
首页搜索内容,点击跳转至详情页,页面后退返回主页,保留搜索结果。

这里介绍两种比较容易实现的解决方案

方案一:将搜索参数存储在路由参数(route.query)中,加载页面时根据参数搜索

优点:刷新不影响;实现简单
缺点:参数只能是基础类型、长度受限;路径看起来比较难看;只对浏览器返回有效,手动跳转回首页不行

方案二:使用路由守卫钩子,在离开页面前本地存储页面参数(vuex、Local Storage 等等)

优点:参数类型长度都比较自由;路径看起来清爽美观;对任意方式返回主页都有效
缺点:需要额外进行数据存储操作,如果使用store模式或vuex则刷新页面失效
参考
前端 Vue路由返回恢复页面状态的实现方案

keep-alive 组件有什么作用?

参考文章

浏览器兼容性问题

虽然面试官的问题十分的笼统,浏览器的兼容性无非还是样式兼容性(css),交互兼容性(javascript),浏览器 hack 三个方面。

一、了解浏览器
主流浏览器 有五个:IE(Trident内核)、Firefox(火狐:Gecko内核)、Safari(苹果:webkit内核)、Google Chrome(谷歌:Blink内核)、Opera(欧朋:Blink内核)
四大内核:Trident(IE内核)、Gecko(Firefox内核)、webkit内核、Blink(Chrome内核)
1、什么是兼容问题?

答:同样的代码,在不同的浏览器上显示的页面效果不一样

2、不一样的原因是什么?

答:浏览器各浏览器使用了不同的内核,并且它们处理同一件事情的时候思路不同。

样式兼容性(css)方面

  1. 因为历史原因,不同的浏览器样式存在差异,可以通过 Normalize.css 抹平差异,也可以定制自己的 reset.css,例如通过通配符选择器,全局重置样式
    1、不要使用*{} 类似的通配符
    这种方法虽然写起来简单,但是渲染起来,浏览器引擎要遍历所有的标签,很影响效率。为了对浏览器友好,可以把自己经常用的标签进行重置操作。
1
* { margin: 0; padding: 0; }

2、addEventListener 与 attachEvent 区别
attachEvent ——兼容:IE7、IE8;不兼容firefox、chrome、IE9、IE10、IE11、safari、opera。addEventListener——兼容:firefox、chrome、IE、safari、opera;不兼容IE7、IE8解决方案:

1
2
3
4
5
6
7
8
9
10
11
function addEvent(elm, evType, fn, useCapture) {
if (elm.addEventListener) { // W3C标准
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent) { // IE
var r = elm.attachEvent('on' + evType, fn); // IE5+
return r;
} else {
elm['on' + evType] = fn; // DOM事件
}
}

浏览器的兼容问题及解决方案整理(建议收藏)
如何机智地回答浏览器兼容性问题

JS垃圾回收机制 (GC)

博客

前端性能优化建议

  1. 减少 HTTP 请求
  2. 使用 HTTP2
  3. 静态资源使用 CDN
  4. 将 CSS 放在文件头部,JavaScript 文件放在底部(CSS 执行会阻塞渲染,阻止 JS 执行;
    JS 加载和执行会阻塞 HTML 解析,阻止 CSSOM 构建)
  5. 图片优化
  6. 减少重绘重排
  7. CSS 选择器优先级
    博客

使用es5实现一个继承 (原型继承,构造函数继承,组合继承,组合继承)

创建子类Child,使用原型和构造函数的方式继承父类People的方法,并调用say函数说出姓名和年龄(es5方法)

父类:

1
2
3
4
5
6
7
function People(name,age){
this.name=name;
this.age=age;
this.say=function(){
console.log("我的名字是:"+this.name+"我今年"+this.age+"岁!");
};
}

原型继承:

1
2
3
4
5
6
7
function Child(name, age){
this.name = name;
this.age = age;
}
Child.prototype = new People();
var child = new Child('Rainy', 20);
child.say()

构造函数继承:

1
2
3
4
5
6
7
function Child(name, age){
People.call(this)
this.name = name;
this.age = age;
}
var child = new Child('Rainy', 20);
child.say()

组合继承:

1
2
3
4
5
6
7
8
function Child(name, age){
People.call(this);
this.name = name;
this.age = age;
}
Child.prototype = People.prototype;
var child = new Child('Rainy', 20);
child.say()

组合继承优化:

1
2
3
4
5
6
7
8
9
function Child(name, age){
People.call(this);
this.name = name;
this.age = age;
}
Child.prototype = Object.create(People.prototype);
Child.prototype.constructor = Child;
var child = new Child('Rainy', 20);
child.say()
es6 class继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Point {
constructor(x, y) {
this.x = x
this.y = y
}

toString() {
return this.x + '' + this.y
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y) //调用父类的constructor(x, y)
this.color = color
}

toString() {
return this.color + ' ' + super.toString() // 调用父类的toString()
}
}
var colorPoint = new ColorPoint('1', '2', 'red')
console.log(colorPoint.toString()) // red 12

参考连接

个人简历项目(要熟悉)

项目上的难点,(说一下)(图片上传那+大文件怎么上传+js怎么压缩一张图片)(图片么转换为base64+图片怎么转化为base64的过程+裁剪这一块怎么弄的,FileReader+怎么裁剪的+怎么压缩一张图片)

预览图片

1. 使用 URL.createObjectURL 预览

URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的 URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。用法用下:

objectURL = URL.createObjectURL(object);

其中,object 参数指 用于创建 URL 的 File 对象、Blob 对象或者 MediaSource 对象。

2. 使用 FileReader 预览

FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。同理的,我们也可以通过input.files[0] 获取到当前选中的图片的 File 对象

特别注意,FileReader 和 是异步读取文件或数据的!

两种方法的对比

我个人更加倾向于使用 URL.createObjectURL() 。主要原先它的 API 简洁,同步读取,并且他返回的是一个 URL ,比 FileReaer 返回的base64 更加精简。兼容性上,两者都差不多,都是在 WD 的阶段。性能上的对比, 在 chrome 上, 选择了一张 2M 的图片, URL.createObjectURL() 用时是 0 , 而 FileReader 用时 20ms 左右。 0 感觉不太合理,虽然这个方法立刻就会返回一个 URL ,但是我猜测实际上这个 URL 指定的内容还没有生成好,应该是异步生成的,然后才渲染出来的。所以并没有很好的办法来对比他们的性能。

掘金

图片上传压缩canvas

1. 用户通过input框选择图片

let file = e.target.files[0]

2. 将_file文件流通过FileReader转化成base64 (能够预览)

FileReade就是这个对象是用来读取File对象或Blob对象的

FileReader的钩子与方法: (onload/readAsDataURL)

FileReader.onload:处理load事件。即该钩子在读取操作完成时触发,通过该钩子函数可以完成例如读取完图片后进行预览的操作,或读取完图片后对图片内容进行二次处理等操作。

FileReader.readAsDataURL:读取方法,并且读取完成后,result属性将返回 Data URL 格式(Base64 编码)的字符串,代表图片内容。

3. 将图片绘制到canvas画布上

可以理解为一个标签,可以通过这个标签上的属性来绘制图片并且可以实现压缩效果。

canvas.getContext(‘2d’): 获得渲染上下文和它的2d绘画功能,返回ctx对象

ctx.drawImage(img,x,y,width,height):绘制图像 | x,y对应的是坐标轴,width、height绘制的图片大小 (这个方法是CanvasRenderingContext2D上的绘制图片的方法)

4. 使用canvas画布的能力进行图片压缩  Canvas.toDataURL(mimeType,quailty) (核心)

Canvas.toDataURL()方法可以将canvas画布上的信息转换为base64(DataURL)格式的图像信息,纯字符的图片表示形式。该方法接收2个参数:

mimeType(可选):String;表示需要转换的图像的mimeType类型。默认值是image/png,还可以是image/jpeg,甚至image/webp(前提浏览器支持)等。

quailty(可选):Number;quality表示转换的图片质量。范围是0到1。此参数要想有效,图片的mimeType需要是image/jpeg或者image/webp,其他mimeType值无效。默认压缩质量是0.92

1
2
3
const canvas = document.createElement('canvas'); 
const quality = 0.2; //设置压缩比例
canvas.toDataURL(file.type, quality)

本文中设置了压缩质量为0.2,需要注意的是不是说压缩质量设置为0.2实际压缩效果就为5倍压缩(在本文中,压缩质量设置成了0.2但实际压缩效果确实7-9倍),简单的说,当到达了这一步以后,其实图片已经完成了压缩,我们已经可以直接拿着返回的base64(DataURL)格式的数据去渲染图片,但是,如果你的目的是将图片先进行压缩,压缩后再上传给服务器,并且服务器只接受二进制的图片信息的话,那就得好好考虑怎么将base64转换成二进制Blob对象了

当quality在0.2~0.5之间,图片质量变化并不大,quality的值越小,压缩效率越可观(也就是在0.2左右时,压缩图片可以最大化,同时并不对图片质量造成太大影响)
参考
DataURL带来的便利原因就是一个:它不需要发起http请求;而它的缺点归纳起来就是两个:体积比原有还要大、不会被缓存。

解决小体积图片压缩问题

经过多组测试发现,使用canvas压缩体积的时候,文件体积过小反而会出现越压缩体积越大的情况

设置一个体积阀门,小于这个体积的图片我们不做限制。毕竟我们的初心就是为了压缩大体积文件
在上述代码的基础上添加 minSize,当小于300kb的时候不进行canvas压缩

3*8bit 的字节转换为 4*6bit 的字节,剩下的两位用 00 补齐,所以Base64 编码后的数据比编码之前大 1/3

5. 将压缩后的Base64(DataURL)格式的数据转换成Blob对象进行上传

3.atob 数据解码

对经过 base-64 编码的字符串进行解码。

1
2
let encodedData = window.btoa("Hello, world"); // 编码  SGVsbG8sIHdvcmxk
let decodedData = window.atob(encodedData); // 解码 Hello, world

4.ArrayBuffer 类数组对象 Array

ArrayBuffer对象代表储存二进制数据的一段内存

1
const buf = new ArrayBuffer(8);

上面代码生成了一段 8 字节的内存区域,每个字节的值默认都是 0。(看不太懂,不着急👇接着往下看)
5.File 构造函数 MDN-File
canvas还能这么用?🤨 图片压缩70% | base64转换原理

1
2
3
4
5
6
7
8
export const dataURItoBlob = (dataURI: string) => {
const binary = atob(dataURI.split(",")[1]);
const array: number[] = [];
for (let i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], { type: "image/jpeg" });
};

为何使用DataURL–想做图片预览,而图片预览是通过FileReader对象实现的 (FileReader.readAsDataURL())

前端图片压缩上传(压缩篇):可能是最适合小白的前端图片压缩文章了!

拓展问题:这个图片压缩的原理是啥?这个压缩影响图片质量么

图片压缩原理,简单来说,通过算法减少一张图片上的颜色差异,牺牲图片画质。比如紧挨着的颜色相近的四个像素的颜色信息压缩前大概占16个字节,压缩后变成一个颜色就能减少近4倍。quality用来控制色差的力度,值越小力度越大,颜色相差较大的两个像素也会被处理,自然被压缩后文件就越小,画质就越烂,所以是会影响图片质量的,官方文档中也有说到。

封装公共方法拖拽事件函数,使用节流优化。(mousedown,mousemove,mouseup)

(横向的scroll电脑上是要靠鼠标拖动scroll bar 才能移动的,而这个函数就能够实现拖动里面的内容滑动,效果和手机端一样),这样实现的效果,拖动元素时不会出现一卡一卡的效果,比较流畅,并且每秒触发的事件会很少性能上会有很大的提升
鼠标按下开始拖拽

1
2
3
4
记录摁下鼠标时的鼠标位置以及元素位置
拖动鼠标记下当前鼠标的位置
鼠标当前位置-摁下时鼠标位置= 鼠标移动距离
元素位置= 鼠标移动距离+鼠标摁下时元素的位置
1
2
3
event.clientX、event.clientY,event.offsetX、event.offsetY)

event.clientX、event.clientY

鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条。IE事件和标准事件都定义了这2个属性event.pageX、event.pageY

类似于event.clientX、event.clientY,但它们使用的是文档坐标而非窗口坐标。这2个属性不是标准属性,但得到了广泛支持。IE事件中没有这2个属性。event.offsetX、event.offsetY
鼠标相对于事件源元素(srcElement)的X,Y坐标,只有IE事件有这2个属性,标准事件没有对应的属性。
event.screenX、event.screenY
鼠标相对于用户显示器屏幕左上角的X,Y坐标。标准事件和IE事件都定义了这2个属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var node1 = document.getElementById("div1");//获取节点
drag(node1);//调用方法
function drag(node){
//1、添加mousedown,记录相对距离
node.onmousedown = function(ev){
var e = ev || window.event;//兼容事件对象
var offsetX = e.clientX - node.offsetLeft;//鼠标点击相对当前div的距离
var offsetY = e.clientY - node.offsetTop;

//2、mousemove,保持相对距离跟随鼠标移动
document.onmousemove = function(ev){
var e = ev || window.event;

node.style.left = e.clientX - offsetX + 'px';//鼠标位置减去相对距离即为新的位置
node.style.top = e.clientY - offsetY + 'px';
}
}

//3、取消拖拽
document.onmouseup = function(){
document.onmousemove = null;//取消移动事件
}
}

参考文章
参考文章

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
export const slide = (value: any) => {
  let isDown = false;
  let startX: any;
  let scrollLeft: any;
  const slider = value;
  slider.addEventListener("mousedown", (e: any) => {
    isDown = true;
    slider.classList.add("active");
    startX = e.pageX - slider.offsetLeft;
    scrollLeft = slider.scrollLeft;
  });
  slider.addEventListener("mouseleave", () => {
    isDown = false;
    slider.classList.remove("active");
  });
  slider.addEventListener("mouseup", () => {
    isDown = false;
    slider.classList.remove("active");
  });
  slider.addEventListener("mousemove", (e: any) => {
    if (!isDown) {
      return;
    }
    e.preventDefault();
    const x = e.pageX - slider.offsetLeft;
    const walk = (x - startX) * 3;
    slider.scrollLeft = scrollLeft - walk;
    // console.log(walk);
  });
};

实现移动端与网页端的适配,解决本地,测试,生产环境样式不一致的问题。 ( 可能有缓存,打包版本的 ?x 不同的版本) 排查错误的能力

(rem /媒体查询 css3 的media /flex 布局 dialog弹框的形式打开的) webkit
问题 苹果浏览器 scrollbar 高度修改不了 (苹果safari13的官方文档 默认修改不了)
img 网站中样式污染了 max-width 100% 导致图片不能正常展开 ,设置后覆盖样式就可以了,和网站中的类名一样也会有样式污染

与后端加购信息对接,实现插件与网站的信息交互。 (dispatchEvent)

1
2
3
    // 这里通知外部,定制完成,已经加购
    const eventAwesome = new CustomEvent("customeFinished", {});
    window.dispatchEvent(eventAwesome);

了解Ts,Ts的好处是?

静态类型

静态类型化是一种功能,可以在开发人员编写脚本是检测错误,有了这项功能,就会允许开发人员编写更健壮的代码并对其进行维护,以便使得代码质量更好、更清晰。
(从代码可知变量num是number类型,如果我们给num赋予其他类型的值就会报错。)

大型项目的优势

对于大型项目的开发,有时为了优化改进项目,对代码进行小小更改。这些小小的变化可能会产生严重的、意想不到的后果,因此有必要撤销这些变化。使用TypeScript工具来进行重构更变的容易、快捷。

更好的协作

对于大型项目的开发一般会有很多开发人员一起开发,此时乱码和错误的机也会增加。类型安全是一种在编码期间检测错误的功能,而不是在编译项目时检测错误。这为开发团队创建了一个更高效的编码和调试过程。

typescript 中 interface 和 type 的区别

相同点 (都可以描述一个对象或者函数&&都允许拓展(extends))

不同点 (
type可以interface不可以::

type 可以声明基本类型别名,联合类型,元组等类型,type 语句中还可以使用 typeof 获取实例的 类型进行赋值 ,

interface可以type不可以:

interface 能够声明合并)
参考
一般来说,如果不清楚什么时候用interface/type,能用 interface 实现,就用 interface , 如果不能就用 type

说一下前端登录的流程? JWT鉴权

初次登录的时候,前端调后调的登录接口,发送用户名和密码,后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token,和一个用户信息的值,前端拿到token,将token储存到Vuex中,然后从Vuex中把token的值存入浏览器Cookies中。

把用户信息存到Vuex然后再存储到LocalStrobe中,然后跳转到下一个页面,根据后端接口的要求,只要不登录就不能访问的页面需要在前端每次跳转页面师判断Cookies中是否有token,没有就跳转到登录页,有就跳转到相应的页面,

我们应该再每次发送post/get请求的时候应该加入token,常用方法再项目utils/service.js中添加全局拦截器,将token的值放入请求头中 后端判断请求头中有无token,有token,就拿到token并验证token是否过期,在这里过期会返回无效的token,然后有个跳回登录页面重新登录并且清除本地用户的信息

token过期了怎么处理

没有绝对的安全, 所谓的安全处理, 就是提高攻击者攻击的难度, 对他造成了一定的麻烦, 我们这个网站就是安全的! 网站安全性就是高的! 所以: token 必须要有过期时间! 每隔一段时间, 必须有一个新的token, 旧的token失效。
token的过期问题

你登陆成功之后,接口会返回一个token值,这个值在后续请求时带上(就像是开门钥匙)。
但是,这个值一般会有有效期(具体是多长,是由后端决定),token失效,再去发请求时,就会报401错误。

token

作用:在访问一些接口时,需要传入token,就是它。
有效期:2小时。

refresh_token

作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token
有效期:14天。(最理想的情况下,一次登陆可以持续14天。)

处理

对于 某次请求A 的响应,如果是401错误

  1. 有refresh_token,用refresh_token去请求回新的token

    1. 新token请求成功 (更新本地token,再发一次请求A)
    2. 新token请求失败(清空vuex中的token,携带请求地址,跳转到登陆页)
  2. 没有refresh_token
    (清空vuex中的token,携带请求地址,跳转到登陆页)

对于一个请求的响应 401, 要这么处理, 对于十个请求的响应 401, 也要这么处理,
我们可以统一将这个token过期处理放在响应拦截器中
请求拦截器: 所有的请求, 在真正被发送出去之前, 都会先经过请求拦截器 (可以携带token)
响应拦截器: 所有的响应, 在真正被(.then.catch await)处理之前, 都会先经过响应拦截器, 可以在这个响应拦截器中统一对响应做判断
掘金

登录权限怎么处理 (动态路由)Vue实现动态路由(和面试官吹项目亮点)

登录逻辑

每个系统都有自己的登录登出逻辑,而我们前端所要做的其实是请求后台,拿到登录权限,带上登录权限,获取用户信息和菜单信息。 在vue项目开发当中,我们一般都是在全局路由钩子做这一系列判断。

菜单权限

通过带有 token 请求头的请求方法,后端可以判断到是哪一个用户,前端也可以通过获取权限接口获得该用户的权限列表,根据权限列表做一份路由映射表,如果后端返回的数据结构与前端的路由设置的数据结构不同,此时还需编写此映射路由的业务功能函数。

如果该用户拥有此路由权限,则通过在全局路由监控中 router.beforeEach 进行 router 中的 addRoutes 方法将有权限的路由配置添加到路由当中,侧边栏也可根据路由列表中的 meta 字段中关键字的判断进行相应的渲染。如果权限的颗粒度小到一个按钮,则可根据后端返回的权限列表映射出的权限参数,通过v-if 进行判断该功能组件是否渲染。在路由管理中通过 router.beforeEach 钩子中判断当前的路由权限是否为空,是的话则可执行获取权限路由的接口:

以前的菜单路由是直接写死在前端,但是当我们直接访问这个路由时,用户还是可以进入到这个功能页面;后来直接改成动态添加路由的方式router.addRoutes。

一个是操作权限,一个是路由访问权限(这里指前端路由)
1.后端返回一个json格式的路由表
2.因为后端传回来的是都是字符串格式的,但前端这里需要的是一个组件对象,写个方法遍历一下,将字符串转换为组件对象
3.利用vue-router的beforeEach、addRoutes、vuex来配合上边两步实现效果
4.左侧菜单拦截根据拿到转换好的路由列表进行展示拦截路由 -> 后端取到路由 -> 保存路由到vuex(用户登录进来只会从后端取一次,其余都从本地取,所以用户,只有退出在登录路由才会更新)

前端有一份动态路由表,等到用户登录拿到用户的角色之后根据当前登录用户的角色去筛选出可以访问的路由,形成一份定制路由表,然后动态挂载路由。,这样做的好处就是,前端每开发一个页面不需要让后端再去配一下路由和权限了,从而避免被后端支配。

然而,路由表又是跟角色挂钩。考虑一种情况,项目上线之后,管理员添加了一个新角色,并且要给这个角色分配菜单。
如果采用将路由表放在前端的话那么每个路由的可访问角色都是写死的,要给新添加的角色分配菜单,只能改前端代码,显然不是很合适
所以我才用了后者,就是把路由信息放在后端,后端将路由信息和角色关联起来,用户登录之后请求对应的接口拿到属于这个用户的路由信息(也就是菜单),

然后前端对返回的数据格式化,转换成符合vue-router的路由格式,然后动态挂载。将路由信息放在后端,这样就可以对路由进行配置了,比如说超级管理员今天很不高兴,不想让某个角色下的用户访问某个路由,直接在该路由下剔除这个角色就可以了

Vue后台管理系统怎么做权限验证和动态路由,谁来做?

什么是 Token(令牌)

Acesss Token

访问资源接口(API)时所需要的资源凭证

简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)

特点

服务端无状态化、可扩展性好,支持移动端设备,安全,支持跨程序调用,每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里

说一下前端权限管理怎么实现

参考文章

Vue3 Composition API 和 script setup (toRefs设置响应式)

博客

使用mongodb的优势

为什么使用mongodb 好处(连接mongose)

学习成本较低,非常简单

在需求不明确/需求变化频繁(表结构不明确)的情况下,使用MongoDB的开发和维护成本最低(MongoDB语法简单,修改表结构简单)。
部署简单
应该不需要事务/不存在大量的复杂事务逻辑操作,不需要复杂的锁
逻辑比较单一、简单,不存在数据结构化查询(表之间join)的情况

模式自由

面向集合存储,易存储对象类型的数据

为什所谓“面向集合”(Collenction-Oriented),意思是数据被分组存储在数据集中,被称为一个集合(Collenction)。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定义任何模式(schema)。

使用组件库遇到的问题(难点)项目中用的两个库的有遇到什么难点

面试问你用组件库有什么体会,面试官主要想听到的是啥(用组件库有遇到什么问题难点 -好几次面试)

能复用通用 –ElementUi 的tab是三个都渲染了(为了高度和宽度一样)

  1. 性能问题(按需引入) tree shaking 打包体积
    开发中有没有遇见过数据修改了页面不刷新的情况 ($set())
  2. 从业务组件(对功能组件的具体实现)和功能组件(不涉及业务逻辑,比如下拉框,列表)两方面来说
    定向了什么业务,然后 ,你的功能组件,入参怎么设计的,有什么抽象的属性,使用于什么…

开发中项目有什么优化的点(项目中引用轻量级的库)

面试(记录)

同花顺一面(凉经) 2022.3.7 10:00  1h5min

HTML 语义化

概念:HTML5的语义化指的是合理正确的使用语义化的标签来创建页面结构。【正确的标签做正确的事】
语义化标签:header nav main article section aside footer
语义化的优点:
在没CSS样式的情况下,页面整体也会呈现很好的结构效果
代码结构清晰,易于阅读,
利于开发和维护 方便其他设备解析(如屏幕阅读器)根据语义渲染网页。
有利于搜索引擎优化(SEO),搜索引擎爬虫会根据不同的标签来赋予不同的权重

说一下跨域,如何解决跨域

博客

讲讲Dom渲染性能消耗 (没理解–讲了diff算法去了)

v-if v-show

数组的key的作用

博客

vue data为什么不是一个对象

博客

计算属性的原理

双向数据绑定原理 defineProperty proxy

博客
Object.defineProperty和Proxy,Vue3.0为什么采用Proxy?

vue3有了解吗 为什么可以监听数组

这种说法是有问题的,事实上,Object.defineProperty 本身是可以监控到数组下标的变化的,只是在 Vue 的实现中,从性能 / 体验的性价比考虑,放弃了这个特性。

可以看到,Vue 的 Observer 对数组做了单独的处理。
hasProto 判断数组的实例是否有 proto 属性,如果有 proto 属性就会执行 protoAugment 方法,将 arrayMethods 重写到原型上

Object.defineProperty VS Proxy(重点)

Object.defineProperty 只能劫持对象的属性,而 Proxy 是直接代理对象。
由于 Object.defineProperty 只能对属性进行劫持,需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作。

Object.defineProperty 对新增属性需要手动进行 Observe。
由于 Object.defineProperty 劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新增属性再使用 Object.defineProperty 进行劫持。

也正是因为这个原因,使用 Vue 给 data 中的数组或对象新增属性时,需要使用 vm.$set 才能保证新增的属性也是响应式的。
博客

defineProperty proxy的区别 源码

react –不了解

选择器 权重

博客

动画了解吗

js动画了解吗 css动画 canvas
Js 动画与 CSS 动画区别及相应实现(JS动画重绘回流,性能影响)

给一张图片用canvas怎么画出来 (api)

drawImage()向画布上绘制图像、画布或视频。

css预处理 sass less stylus 有什么区别 哪个更好用

es6新特性 let const 模板字符串 扩展运算符 箭头函数 promise async await

暂时性死区

在代码块内,使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区

promise 讲讲 async await

博客

浏览器事件循环机制

博客

闭包讲讲 闭包优化 使用场景 (防抖节流)

博客
防抖节流

垃圾回收机制 *

博客

基本数据类型 BigInt讲讲(项目中有用过吗) Symbol

博客

深拷贝 浅拷贝

博客

网站性能优化(实习时给公司维护的网站) 懒加载 (src先设置为字符串或者指向一张小图片,data-src后改变图片路径src)

预加载:

  1. 使用html标签,设置为dispaly:none
  2. 使用Image对象
    博客

pc端和移动端 适配 媒体查询

判断屏幕宽度(现在有一个折叠屏宽度怎么区分呢)他说要怎么区分,我答的是如何适配(好像不是)

单位 px em rem vh vw

浏览器输入url发生了什么 tcp连接3次握手,四次挥手(详细讲讲)

博客

tcp与udp的区别 (再讲一下两个主要的区别)

我们现在视频传输的协议是? 答的是https 百度是udp

Https和Http的区别

  1. 最简单的,HTTP 在地址栏上的协议是以 http:// 开头,而 HTTPS 在地址栏上的协议是以 https:// 开头
1
2
http://pengzhenglong.github.io/
https://pengzhenglong.github.io/
  1. HTTP 是未经安全加密的协议,它的传输过程容易被攻击者监听、数据容易被窃取、发送方和接收方容易被伪造;而 HTTPS 是安全的协议,它通过 密钥交换算法 - 签名算法 - 对称加密算法 - 摘要算法 能够解决上面这些问题。

  2. HTTP 的默认端口是 80,而 HTTPS 的默认端口是 443。
    博客

项目中用的两个库的有遇到什么难点

typescript 中 interface 和 type 的区别

开始做题(一个数如何转换为千分位(1756346-> 1,756,346),手撕防抖节流)(没时间了讲讲思路)

千分位分割

方法一:Number.prototype.toLocaleString()
返回这个数字在特定语言环境下的表示字符串。
该方法的详细介绍请看mdn developer.mozilla.org / zh - CN / docs /…

1
2
let num = 123456789;
num.toLocaleString()// "123,456,789"

该方法的很方便,但是兼容性不是很好

方法二: 通过正则匹配

1
2
3
let num = '12345678'
let reg = /(?=\B(\d{3})+$)/g
console.log(num.replace(reg, ",")) //12,345,678

方法三: 自定义方法分隔

实现思路:

将用户传入的数字转为字符串。
使用字符串的split方法将其分隔成数组,然后在使用reverse方法进行反转数组。
通过用户传入的分割位数来确定需要将数值分为几组。
定义一个新数组用来存放添加分隔符的数组。
通过分组数来循环确定分隔符的位置。
传入的分割位数刚好将数组分组,可能分割符出现在第一位,这时我们需要删除该分隔符,然后反转该添加完分隔符的数组,最后通过join方法转为字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function fn(num, sep, size) {
num += "";
let numArr = num.split("").reverse();
let group = parseInt(numArr.length / size);
let resArr = []
// 设置一个变量来控制数组分割的起始和终止
let i = 0;
while (group) {
// 确定分隔符的位置
resArr = [...resArr, ...numArr.slice(size * i, size * (i + 1)), sep]
group--;
i++;
}
// 表示参与分割的数组元素个数
const restIndex = resArr.length - parseInt(numArr.length / size);

// 将没参与分组的元素添加到已经添加分隔符的数组中
resArr = [...resArr, ...numArr.slice(restIndex)]
// 将分隔好的数组反转,并转为字符串。
let strNum = resArr.reverse().join('')
if (strNum[0] === sep) {
strNum = strNum.slice(1)
}
return strNum;
}
console.log(fn(12345678, ',', 3)) //12,345,678
防抖节流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 防抖 -->
function debounce (fn,delay) {
var timer = null;
return function () {
clearTimeout(timer)
timer = setTimeout(function () {
fn.apply(this)
},delay)
}
}
<!-- 节流 -->
function throttle(fn,delay) {
let flag= true;
return function () {
if(flag) {
setTimeout(()=>{
fn.call(this)
flag= true;
},delay);
}
flag= false;
}
}

博客:防抖节流
防抖

反问

不要停留在使用层,多看看源码,要理解,http,tcp知识不熟练,沟通能力,表达

金桔科技(2022.3.8 53min 业务场景题偏多)

登录权限怎么处理 (动态路由)
说一下前端登录的流程?
什么是 Token(令牌)
说一下前端权限管理怎么实现

参考文章

全局引入,按需引入组件
1.项目中引入了第三方组件,该组件要升级了怎么处理,升级了要怎么检查(有什么好的替代方案)

杭州后起智能科技(2022.3.8 20min)

  1. 有100个请求,一次发10个,允许最大容错3个超过3个就抛出异常 怎么处理
2. keep-alive 原理

优倍快一面深圳(5-8k)

(听不清,有点像一直念题目)
项目上的难点,(说一下)(图片上传那+大文件怎么上传+js怎么压缩一张图片)(图片为什么转换为base64+图片怎么转化为base64的过程+裁剪这一块怎么弄的,FileReader+怎么裁剪的+怎么压缩一张图片)

canvas了解吗 (drawImg api 绘画图片)
js大文件上传(切片上传 - 并发控制-断点续传)
博客
Ts访问修饰符
class
interface 和type
promise /generotor /await (await后面接受的是什么 )
第一行是await 第二行是promise.then,执行顺序是

正常情况下,async中的await命令是一个Promise对象,返回该对象的结果。
但如果不是Promise对象的话,就会直接返回对应的值,相当于Promise.resolve()
如果在async函数中抛出了错误,则终止错误结果,不会继续向下执行。

then方法为什么可以链式调用

Promise可以链式调用,不过promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用, 它并不像一般我们任务的链式调用一样return this。
父子组件的生命周期
data为什么是函数而不是对象
v-if v-show 都会触发重绘回流
keep-alive 原理

vue数据双向绑定(对数组也是这样的吗)怎么重写数组的方法

实现一个轮播组件(使用display:none 这样能加动画吗)

方法一:
利用绝对定位absolute偏移量的改变来实现
具有往左往右滑动的效果演示代码:

方法二:
利用 display/opacity/visibility状态切换来实现
没有往左往右滑动的效果演示代码:

方法三
旋转木马轮播图
存储每个图片的位置信息(absolute位置信息+z-index属性+opacity透明度 等等)到一个数组。对数组进行pop push shift unshift等操作再引用到DOM元素上,产生轮播效果。
掘金

使用es5实现一个继承
原型链是什么
懒加载和预加载(怎么判断一张图片是否加载成功)(load complete)

实现一个div元素的拖拽 ((addEventListen/removeEventListen)mousedown,mousemove,mouseup)
博客
防抖节流(没怎么讲清楚)

实现一个三角形 (采用相邻边框连接处均分的原理)

怎么求多个数组之间的交集 (暴力解法/set)

1
2
3
如何求出两个数组的交集和差集?
let intersection = a.filter(v => b.includes(v))
let difference = a.concat(b).filter(v => !a.includes(v) || !b.includes(v))

set有什么特性

1
2
Set类似于数组,但是它里面每一项的值是唯一的,没有重复的值,Set是一个构造函数,用来生成set的数据结构
Set中两个对象永远是不相等的,即使键和值都是一样的

总结:讲的磕磕盼盼的,不清楚(表达能力不行)

优倍快二面

W3C标准及规范(简历上写了)
参考
为什么要遵循规范

答案这一个就够了:利人利己,便于维护!如果你写的代码不符合“规范”,查找代码的错误时或者后期的维护会让你痛恨自己当初怎么会写出那么乱的代码。
react 和vue的区别
什么是好的网页(往用户体验来说)
mysql了解吗
(迅速结束了,两面反问都没有,体验很差)

沃太能源(苏州)(一面3.16 20:00 –25分钟)

面试问你用组件库有什么体会,面试官主要想听到的是啥(用组件库有遇到什么问题难点 -好几次面试)

401(没有权限访问) 403 状态码(服务端拒绝访问) 同步请求,异步请求

浙江木链(3.16 一面电话)女面试官—40分钟)已凉

  1. 两栏布局
    (1. flex 2.左边浮动 width :200px,右边margin-left :200px width:auto )
  2. BFC解决什么问题
  3. margin塌陷 ()
  4. C3动画 transition animation
1
2
3
4
5
6
区别:
1transform仅描述元素的静态样式,常常配合transitionanimation使用
2transition通常和hover等事件配合使用,animation是自发的,立即播放
3animation可设置循环次数
4animation可设置每一帧的样式和时间,transition只能设置头尾
5transition可与js配合使用,js设定要变化的样式,transition负责动画效果
  1. 使用过css预处理嘛
  2. 判断js数据类型
    toString判断的原理
  3. 讲一下原型链
  4. 数组常用的方法 find filter(返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组。) every some
  5. 判断一个数组中有符合我需求的元素用哪个比较合适
    indexof find(返回数组中满足条件的第一个元素的值,如果没有,返回undefined)

返回一个布尔值 includes some every
博客

  1. 遍历一个对象
    for of Object.key Object.value Object.entries
  2. 工作中遍历对象都用for in 嘛,遍历用的比较多的方法是
  3. promise promise.all
  4. async await
  5. js事件循环 evenLoop
  6. $nextTick是宏任务还是微任务
  7. 生命周期 还有父子组件的生命周期 执行
  8. vue3 ref refs reactive toref() 简单讲讲
既用到扩展运算符取到数据有让他是响应式的 (toRefs)
注意reactive封装的响应式对象,不要通过解构的方式return,这是不具有响应式的。可以通过 toRefs 处理,然后再解构返回,这样才具有响应式
1
2
3
const state = reactive({...});
return {...state}; // 这种方式将丢失响应式,是一种错误的方式
return toRefs(state); // works
  1. vue2 组件传值
    那组件调用特别深,用什么传值方式
    Provide inject $listeners $attars
  2. vuex mutation为什么不可以包含异步回调,action 为什么可以
    博客
  3. 简历中项目经历(拿错简历了哈哈哈)
    有封装过功能模块吗
  4. 有什么遇到项目中遇到的棘手的问题
  5. 开发中项目有什么优化的点(项目中引用轻量级的库)
  6. 反问

北京梧桐花开一面(2022.3.20 20min)

  1. 定制插件项目(简历上的讲一下)
  2. 双向数据绑定
  3. 生命周期(父子组件生命周期)
  4. 做过手机端的vue吗(如何适配手机端– 媒体查询-rem)

5. 了解Ts,Ts的好处是?

6. 使用mongodb的优势

使用组件库遇到的问题(难点)

3.26日后面试问题

行内元素 块级元素

块级元素:div、p、ol、ul、dl、li、table、td、th、tr、dd、dt、caption、h、
行内元素:i、img、input、select、label、textarea、button、b、span、a、u、em、

块级元素的特点
1、高度,行高以及外边距和内边距都可控制。
2、总是在新行上开始,占据一整行。
3、它可以容纳内联元素和其他块元素。
4、宽度始终是与浏览器宽度一样,与内容无关。

行内元素的特点
1、行内元素只能容纳文本或者其他行内元素。
2、宽度只与内容有关。
3、和其他元素都在一行上。
4、高,行高及外边距和内边距部分可改变。

区别
1、行内元素会在一条直线上分列,都是统一行的,程度偏向分列。
块级元素各盘踞一行,垂直偏向分列;块级元素重新行开端停止接着一个断行。
2、行内元素不可以包括块级元素,只能包容文本或许其余行内元素。
块级元素能够包括行内元素和块级元素,还能够包容内联元素和其余元素;。

3、行内元素与块级元素属性的分歧,主要在盒模子属性上。
行内元素设置width无效,height无效(能够设置line-height),margin、padding设置上下有效。

切换 display:inline-block、inline和block
link标签和import标签的区别

实现一个下拉刷新的组件

其实技术点也没什么难的,主要使用H5的touch事件:

touchstart: 手指触屏触发的事件,主要工作是在触发时获取鼠标点击的Y坐标,event.touches[0].pageY。
touchmove: 手指滑动触发的事件, 主要工作是在触发时获取移动的Y坐标减去开始时的Y坐标,得出移动的距离,然后利用transform改变容器的位置。
touchend: 手指松开触发的事件,主要工作是释放鼠标让div恢复原来位置。

手把手教学:Vue下拉刷新、上拉加载组件插件(超详细)

深浅拷贝(如何实现)

博客

浅拷贝的实现
  1. Object.assign()
  2. Array.prototype.concat()
  3. Array.prototype.slice()
  4. 展开运算符…
  5. 函数库lodash的_.clone方法
深拷贝的实现方式
  1. JSON.parse()
  2. JSON.stringify()
  3. 函数库lodash的_.cloneDeep方法
  4. jQuery.extend()方法
  5. 手写递归方法
    参考
    掘金

路由页面跳转后状态保存

需求场景
首页搜索内容,点击跳转至详情页,页面后退返回主页,保留搜索结果。

这里介绍两种比较容易实现的解决方案
方案一:将搜索参数存储在路由参数(route.query)中,加载页面时根据参数搜索

优点:刷新不影响;实现简单
缺点:参数只能是基础类型、长度受限;路径看起来比较难看;只对浏览器返回有效,手动跳转回首页不行

方案二:使用路由守卫钩子,在离开页面前本地存储页面参数(vuex、Local Storage 等等)

优点:参数类型长度都比较自由;路径看起来清爽美观;对任意方式返回主页都有效
缺点:需要额外进行数据存储操作,如果使用store模式或vuex则刷新页面失效
参考
前端 Vue路由返回恢复页面状态的实现方案

按需引入组件库

浏览器兼容性问题

垃圾回收机制

博客

前端性能优化建议

前端性能优化建议:博客

深圳小雨伞 (2022.4.8) 1h

一面
没有让自我介绍
1.聊毕设 (node express做的后台 为什么用express)为什么不用koa 洋葱模型是什么
2.async await (原理)
3. promise (做了个题目)
4. JS执行机制 Event Loop UI渲染(训练如何回答 )
5. Set Map 数据结构 WeakMap (何时用这个)
6. 垃圾回收机制 V8 ()变量怎么标记的,所有变量都这样标记的?
7.Tcp三次握手 (第三次不发Ack包会发生什么)
8.HTTP队头阻塞
9.HTTP2.0/1.0 优点(队头怎么压缩的 ,多路复用(限制6Tcp连接个,其他的是什么状态))
10.HTTPS加密过程 (越详细越好)
10.网路协议分层 (每一层都有什么)(HTTPS在哪一层加密)
11. 居中实现(越多越好,说了5种左右,还问有没有,问我了解table布局嘛)
(整个过程,会听你讲然后从你说的(说我打断一下),反问)

反问:技术栈,有什么建议 :问的偏基础,刚毕业,基础打扎实点,讲的过于表面(手写promise一遍)(框架都是基于底层的,vue /react问都没问,侧重基础就好了)

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!