前端面试重点

2026-05-23 15:49:0257 阅读量

文章目录

基础:HTML、CSS

在浏览器中输入url之后会发生那些操作

  1. 解析出url中的域名、协议、主机、端口、路由,并构造一个http请求(强缓存、协商缓存)
  2. DNS域名解析(DNS:域名系统,将主机号解析成ip地址)
  3. 建立TCP连接(三次握手)
  4. 发出http请求
  5. 服务器处理http请求,并返回http报文
  6. 浏览器渲染页面
  7. 断开TCP连接(四次挥手)

强缓存、协商缓存

强缓存:在加载资源时,为避免资源未过期,用户就再次刷新拉取数据,就设置一个资源过期时间,如果客户点击刷新时,资源未过期,那么就不向服务器发送请求。

相关服务:泰国服务器

协商缓存:当用户点击刷新时,资源已经过期了,那么就向服务器发送资源拉取请求,这是会发送在上一次跟服务器交流时在header 里配置好的 etag和 last-modified,服务器进行对比,看资源是否已经更改,如果有更改,就带着新的 etag和 last-modified和资源发送给客户端。

三次握手

第一次:客户端向服务器发送一个SYN(TCP协议的第一个报文,告诉服务器,客户端已做好会话准备),并进入SYN_SEND状态,等待服务器响应

第二次:服务器接收到然后返回一个SYN_ACK(ACK:表示响应),然后服务器进入SYN_RECEIVE状态;

第三次:;客户端再向服务端发送一个ACK包建立响应连接

握手完成后,开始传输数据

四次挥手

第一次:客户端发送FIN(关闭连接)包,进入FIN_WAIT_1状态,这时候客户端数据传输完毕,服务器端的数据可能还在传输。

第二次:服务器收到FIN后,会返回一个ACK报文响应,表示知道客户端数据传输完毕;客户端接收到ACK状态改变为FIN_WAIT_2状态。

第三次:当服务器数据传输完毕,给前端发送FIN报文,表示数据发送完毕,服务器状态改变为LAST_ACK状态。

第四次:客户端收到FIN报文,会改变成TIME_WAIT状态,不直接关闭,以防出错,客户端还会再发一次ACK消息给后台,等待特定时间没有收到服务器回应,则关闭连接.

盒子模型有哪些

两种:

  1. W3C盒子模型(标准盒子模型):box-sizing:content-box(元素默认)
    -width=设置的宽度+border+padding
  2. ie盒子模型(怪异盒子模型):box-sizing:border-box
    • 实际宽度=设置的宽度(设置了padding和border的话,从实际宽高中减去,减去后才是实际宽度)

web安全有哪些

CSRF攻击

跨站请求伪造
这就是类似于钓鱼网站的原理,利用浏览器的漏洞。用户发送请求时,服务器会验证用户信息,如果信息不符合,就会对该请求不作回应。但是如果有心人做了一个链接,你点进去向服务器发送请求,恰好这时你浏览器还存有请求所需的验证信息,那么这次请求就成功了,但是用户本身不知情。

解决:

  1. 在请求中添加一个token,这个token是随机产生的,服务器在收到请求之后对其进行验证
  2. 双重cookie:在请求中加一个csrfcookie的字段,用于验证。

cookie安全:

如果用cookie检验用户是否登录,存的name为用户信息,那么可以恶意伪造name,造成已经登陆的假象;

  • 解决
    • 我们可以多设置一项,但是这一项的值必须是随机的,没有任何规律可循的。

xss攻击:

XSS攻击使用Javascript脚本注入进行攻击

三类:DOM型、反射性型、存储型

  • 比如评论功能,直接将评论的内容放在表里显示的时候,就可能会受到恶意
  • 在url中拼接js代码
  • 解决
    - 不需要展示HTML标签的表单内容,入库时直接转成实体显示
    - 可以用正则检测输入框必须为合法数据
    - 需要展示HTML标签的部分, 仅允许展示有限的标签,如p,a,img等;如strip_tags 来过滤html标签

sessionstorage和localStorage的区别和相同

区别
1.生命周期
- localStorage是永久存在的,只要不手动删除,就会一直存在
- sessionStorage的生命周期是一次会话,随着浏览器窗口关闭消失
共同点:

  1. 大小:5M
  2. 存储数据类型:字符串
  3. 存储位置:客户端,不与服务器通信

gitee常用命令

git clone:克隆
git add:添加到暂存区
git commit:上传至本地仓库
git push:上传至远程仓库
git branch fenzhi1:创建本地分支
git chea
kout fenzhi1:切换分支
git merge fenzhi1:合并分支

svg与canvas

svg:可伸缩矢量图形,不依赖分辨率,常用于静态图形绘制;基于对象绘制图像。
canvas:基于HTML5的画布,使用js动态绘制像素点,常用于动态图像绘制。

grid布局与flex布局

flex布局:

  • 是一维布局,只在一个方向上定义布局方式。
  • 属性:justify-content,flex-wrap,flex-direction,align-item等
    grid布局:
  • 二维布局,在行和列的方向上布局,有容器、单元格、单元格线等。
  • 属性:grid-template-rows(单元格行高),grid-template-columns(单元格列宽), grid-template-areas(每个单元格命名)
   <!DOCTYPE html>
   <html lang="en">
     <style>
       #page {
         display: grid; /* 1.设置 display 为 grid */
         width: 100%;
         height: 250px;
         grid-template-areas:
           "head head"
           "nav  main"
           "nav  foot"; /* 2.区域划分 当前为 三行 两列 */
         grid-template-rows: 50px 1fr 30px; /* 3.各区域 宽高设置 */
         grid-template-columns: 150px 1fr;
       }
   
       #page > header {
         grid-area: head; /* 4. 指定当前元素所在的区域位置,从 grid-template-areas 选取值 */
         background-color: #8ca0ff;
       }
   
       #page > nav {
         grid-area: nav;
         background-color: #ffa08c;
       }
   
       #page > main {
         grid-area: main;
         background-color: #ffff64;
       }
   
       #page > footer {
         grid-area: foot;
         background-color: #8cffa0;
       }
     </style>
     <body>
       <section id="page">
         <header>Header</header>
         <nav>Navigation</nav>
         <main>Main area</main>
         <footer>Footer</footer>
       </section>
     </body>
   </html>

在这里插入图片描述

自适应方案

viewport方案:使用postcss插件,最后运行出来的代码会将配置好的元素中的px转化为rem
使用rem:根据根元素的字号自动调整大小

解决父元素内容塌陷

使用伪元素法:

div::after {
    display: block;    
    content: "";
    clear: both;
}

添加属性:overflow:hidden
添加元素法:在元素后面添加一个空白标签,并添加属性clear:both

JS、ES

数据类型转换

转成String
symbol只能强制转换(隐式,比如与字符串相加会出错)
对象:[object Object]

转成数值:
undefined:NaN
null:0
true——1,false——0
string——只要包含非数字的字符,就是NaN;全是数字,直接转换;空为0
symbol:报错
Object:先转化为基本数据类型,再转换

转成布尔:
假:undefined,null,false,+0,-0,NaN,“” ——false

包装类型:
调用基本数据类型的属性或方法时,会在后台隐式地将基本数据类型转化为对象

const a='abc'
a.length//3
a.toUpperCase()//'ABC'

隐式数据类型转换:
+:双方至少有一个string,就转化为string;其他情况都转为number

-、*、/:
结果可能是NaN,NaN也是一个数字

==:尽量转化为number

<、>:两边都是字符串,比较字母顺序;其他情况都是转化为数字

undefined与null

undefined——未定义
null——空对象

数字相加是二进制相加,会出现误差
解决四舍五入:(0.1+0.2).toFixed()

==会携带隐式数据类型转换,转换后再比较
string会转换成number在比较
boolean会转换成number再比较
object会转换成基本数据类型再比较

new一个实例的步骤

1.创建一个空对象作为this
2.this.proto指向构造函数的prototype
3.运行构造函数
4.返回this(如果函数没有return,有的话如果是对象,则返回这个对象,如果是其他数据类型,则返回该数据类型的原型)
源码:

function myNew(Fn,...args){
    // 1.新建空对象
    let o = {};
    // 2.对象的__proto__ 属性指向原型
    o.__proto__ = Fn.prototype;
    // 3.以对象为this,执行构造函数
    let res = Fn.apply(o,args);
    // 4.返回合法值
    return res instanceof Object? res : o;
}

let p1 = myNew(Person,'pxx',18);
console.log(p1);

防抖

思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,

然后:

  • 如果在200ms内没有再次触发滚动事件,那么就执行函数
  • 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时

如果短时间内大量触发同一事件,只会执行一次函数。

定义:对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。

节流

效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。

闭包

定义:闭包就是能够读取另一个函数作用域的变量的函数
闭包作用:闭包的作用 防止函数内部的变量或函数被污染,同时函数内部的变量不会被垃圾回收机制回收
一个闭包的使用:

 for (var i = 0; i < 10; i++) {
 		//这里的i被强制滞留再内存里
        (function (j) {
            setTimeout(() => {
                console.log(j);
            }, j * 1000)
        })(i)
    }

Blob

定义:Blob类型的对象表示不可变的类似文件对象的原始数据,其实就是二进制对象,类似文件的二进制对象,文件也是由它继承而来

$http.get('/receivePDFUrl', {responseType: 'arraybuffer'}) // 设置$http get请求的responseType为arraybuffer
 .success(function(data){
  var file = new Blob([data], {type: 'application/pdf'}); // 使用Blob将PDF Stream 转换为file
  var fileUrl = URL.createOjectURL(file);
  window.open(fileUrl); // 在新的页面中打开PDF
 })

ES6数组新增方法

  1. JSON转数组:Array.from()——只留下key值
  2. 将一堆文本或变量合并成数组:Array.of()
  3. 寻找符合条件的第一个元素:find(),是一个实例方法,需要实例调用。参数是一个匿名函数,匿名函数三个参数
    - value:当前查找的值
    - index:当前查找的下标
    - arr:当前查找的数组
let arr=[1,2,3,4,5,6,7,8,9];
console.log(arr.find(function(value,index,arr){
    return value > 5;
})) //输出6  注意:在函数中如果找到符合条件的数组元素就return,并停止查找
  1. 填充数组:fill(),是一个实例方法,三个参数
    - 第一个:填充的变量
    - 第二个:起始位置
    - 第三个:填充到的位置
  2. 遍历数组:for...of
    注意,有时候需要数组的索引
 let arr=['js','java','python','c','c++'];
 for (let index of arr.keys()){
    console.log(index);//0 1 2 3 4
}

arr.keys()//是对key值得遍历
arr.values()//是对value值的遍历
arr.entries()//是对键值对的遍历

let arr=['js','java','python','c','c++'];
for (let [index,val] of arr.entries()){
   console.log(index,val);   //0 'js' 1 'java' 2 'python' 3 'c' 4 'c++'
}

es6新增

let、const
变量的解构赋值、模板字符串、

新增字符串方法:

includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
repeat():返回一个新字符串,表示将原字符串重复n次
padStart()用于头部补全,padEnd()用于尾部补全
trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。(trim()两者结合)
replaceAll()方法,可以一次性替换所有匹配。

Object新增方法

  1. Object.is(a,b):比较a和b是否严格相等。
  • 与‘===’不同之处:
    - ===的-0=+0,NaN!=NaN
    - 该方法的-0!=+0,NaN=NaN
  1. Object.assign():方法用于对象的合并,将源对象(source)的所有可枚举属性(浅拷贝)
    • 可以处理数组,但是会处理成对象,key为下标,所以如果两个数组的话,同下标的会被合并项覆盖
    • 当合并项是函数时,会取函数的返回值
  2. Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象
onst obj = {
  foo: 123,
  get bar() { return 'abc' }
};

Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }
  1. __proto__,Object.setPrototypeOf(),Object.getPrototypeOf() ——都是处理原型

es5遍历数组

forEach()方法(ES5):
不可打断循环

filter()方法(ES5):
返回一个满足条件的子集

//eg:1
let arr=['js','java','jquery'];
arr.filter(x=>console.log(x));//js java jquery   返回一个空数组

//eg:2
a = [5,4,3,2,1];
values = a.filter(function(x){return x<3});// [2,1]

some():有一个满足条件返回true
every():所有满足条件返回true

//some方法
let arr=['js','java','jquery'];
arr.some(x=>console.log(x));     //js java jquery   返回false
let a = [1,2,3,4,5];
a.some((x)=>{return x<5}) // true 存在一个满足条件返回true

//every方法
let arr=['js','java','jquery'];
arr.every(x=>{console.log(x)});     //js   返回false
let a = [1,2,3,4,5];
a.every((x)=>{return x<10}) // true 所有的满足条件返回true

map()方法

//eg:1
let arr=['js','java','jquery'];
console.log(arr.map(x=>'web'));   //["web", "web", "web"]
//eg:2
a = [1,2,3];
b = a.map(function(x){return x*x;}); //[1,4,9]

map与filter区别

filter执行返回的结果要么是自身,要么是自身的子集
map返回的结果可能是经过处理过的,新的数组

数据类型检测

typeof:

数组、对象、null都判断为Object(都是Object的实例)
typeof NaN——Number

instanceof:

原理:原型链上找该数据的类型

原型链:引用类型(数组、对象)都有一个_proto_(隐式原型)属性,这个属性的值是一个对象的构造函数,原型链就是当访问该引用数据的时候会一直往上寻找,这就形成了一个原型链

var a=[1,2,3]
//true
a._proto_===Array.prototype
//Object的原型的_proto_是null
Object.prototype.__proto__ === null//true

instanceof只能正确判断引用数据类型(数组、函数、对象)

也可以判断某个实例对象是否在某个构造函数的原型链上

function C () {}
var o = new C()
console.log(o instanceof C, o.__proto__ === C.prototype) //true

constructor

判断数据的类型
a.constructor===Array

Object.prototype.toString.call()

Object.prototype.toString.call(2)//Number

深拷贝的源码

在这里插入图片描述

扩展运算符深浅拷贝

第一层数据数深拷贝,如果第一层数据里嵌套引用数据类型,那么里面的数据就是浅拷贝

let outObj = {
        inObj: {
            a: 1,
            b: 2
        },
        c: 1
    }
let newObj = { ...outObj }
newObj.inObj.b = 3
newObj.c = 4
console.log(newObj);//inObj:{a:1,b:3},c:4
console.log(outObj);//inObj:{a:1,b:3},c:1
//inObj:里面的a、输入浅拷贝
//c:深拷贝

Object.assign({}, outObj)

对象的结构是按照属性名来解构的

```javascript
let stu={
 name:'Bob',
 age:18
}
const {age,name}=stu
//age:18
//name:'Bob'

扩展运算符字符串——>数组

[...'he']=>['h','e']

判断是不是数组

方法一:原型上的toString().call()方法

Object.prototype.toString.call(obj).slice(8,-1) === 'Array';

原型链:obj._proto_===Array.prototype

ES6:Array.isArray(obj)

instanceof:obj instanceof Array

原型函数:Array.prototype.isPrototypeOf(obj)

判断空对象

JSON.stringify(Obj)=='{}'

es6:Object.key(Obj).length<0

let、const、var的区别:

块级作用域:let、const有块级作用域;var没有块级作用域
变量提升:let、const不存在变量提升;var存在变量提升
给全局添加属性:浏览器的全局对象——window,Node——global;var声明之后会变成全局变量,并且成为全局对象的属性;
重复声明:var允许,let、const不允许
暂时性死区:声明之前不可用,var不存在
初始值设置:const必须设置初始值
指针指向:const不允许改变指针指向

箭头函数

箭头函数不能new(没有prototype、this、arguments)
箭头函数不能改变继承来的this指向(call()、apply()、bind()也不行)
不能作为构造函数

对象的this无法形成一个块级域,里面的箭头函数的this依然是全局(window)

改变this指向

call()、apply()、bind()

Object.assign(obj1,obj2)用于对象得合并,返回一个新对象

将obj2的所有可枚举对象复制到obj1,若有重复,则覆盖

正则

1. \d:数字(相当于[0-9])
2.  \D:非数字
3. \s:空格
4. \S:非空
5. \b:单词边界
6. \B:非单词边界
7. \w:单词(a-z,A-Z,0-9)
8. \W:非单词

具有特殊意义的符号(用之前都需要转义)
1. |:或
2. []:这里面的东西都是原本含义,除了^和-
- /a[ab]/——aa或ab
- /a[a|b]/——a或|或b
- /[0-39]/——0-3或9
3. .:任意字符
4. ^:除了
- /[^a-z]/:除了小写字母
5. [\u4e00-\u9fa5]:汉字范围
20. $:结束限制
21. g:全局
22. m:多行
23. i:忽略大小写

排序算法

冒泡排序法:

相邻的两个元素作比较,排列顺序错误的交换位置,正确的不处理。

外层循环次数是数组长度-1:减去第一个
内层循环次数是长度-i-1:i是减去前面已经处理过的,-1是除去第一个

处理结果:第一次全部循环完会找出最大(小)的数字,放在最后(前)面

代码:

function maopao(arr){
	for(let i=0;i<arr.length-1;i++){
		for(let j=0;j<arr.lenth-i-1;j++){
			if(arr[j]>arr[j+1]){
				let tmp=arr[j]
				arr[j]=arr[j+1]
				arr[j+1]=tmp
			}
		}
	}
	return arr
}

选择排序法:

将第n个数字与所有后面位置上的进行比较,比第n个小,就交换位置,否则不换。

实现:
外层循环长度-1:除去最后一个
内层循环:起始i+1——i之后里面寻找,i之前已经排好了;结束长度——要与i之后的所有比较

内层的目的是找出i之后的最小的数的下标

function xuanze(arr){
	let minIndex
	for(let i=0;i<arr.length-1;i++){
		minIndex=i
		//找出i之后的最小的下标
		for(let j=i+1;j<arr.length;j++){
			if(arr[j]<arr[minIndex]){
				minIndex=j
			}
		}
		let tmp=arr[i]
		arr[i]=arr[minIndex]
		arr[minIndex]=tmp
	}
}

二分查找:

先找到中间值,大的放右边小的放左边,两边又找中间值……

插入排序法:

认为第一个已经排好序了,从第二个元素开始,与已排好的数列从后向前比较,知道找到比他小或等于它的数字,就将该数字的后面所有元素向后挪一个位置,再将该数字插入到数列

function insertSort(arr){
	for(let i=1;i<arr.length;i++){
		//将要插入的第i个数据存起来
		let tmp=arr[i]
		//从已排的第i-1个数据开始比较
		let j=i-1
		//如果还没比较完,且第i个数还小于这个数
		while(j>=0&&arr[j]>tmp){
			//已排数列增加一个位置,后移一位
			arr[j+1]=arr[j]
			j--
		}
		arr[j+1]=tmp
	}
	return arr
}

快速排序法:

通过一趟排序,将待排记录分割成独立的两部分,其中一部分记录的关键字均比另外一部分记录的关键字小,则可分别对着两部分记录继续进行排序,以达到整个序列有序的目的。
时间复杂度:

冒泡和捕获

冒泡:点击子节点,会从触发的节点冒泡到document
捕获:点击会从document触发至点击的节点
js原生阻止冒泡和捕获:e.stopPropagation()、e.preventDefault()(取消目标元素默认行为,比如提交按钮等)和 return false

w3c规定,若一个节点同时绑定了捕获和冒泡,会先执行捕获,再执行冒泡
nh hy
react的事件处理机制采用合成事件处理机制,将所有事件委托至document
优点:

  1. 兼容性强,适应多个浏览器
  2. 统一事件处理,节省内存
    react阻止冒泡和捕获:e.preventDefault()

当同时绑定原生事件和react合成事件时,会先执行原生事件,因此,如果原生事件阻止了冒泡,可能会影响到react合成事件,因为react事件需要冒泡到document上执行。

promise

什么时promise:promise就是将异步操作以同步的流程表达出来,从而解决异步带来的问题(比如多层异步嵌套,ajax),是异步编程的一种解决方案。

里面保存着某个未来才会结束的事件的结果。

三个状态:pending、filfulled(匹配resolve())、rejected(匹配rejected())

promise的缺点:

1. promise不可以中断
2. 如果没有回调函数,promise内部抛出的错误不会反应到外部
3. 如pending状态的时候,无法得知当前进展

promise的方法:

  1. Promise.prototype.then:返回一个新的Promise对象
  2. Promise.prototype.catch:指定发生错误时的回调函数
  3. Promise.all方法
let p=Promise.all([p1,p2,p3])
//

一个基础的promise写法

function promiseClick(){
		let p = new Promise(function(resolve, reject){
			setTimeout(function(){
				var num = Math.ceil(Math.random()*20); //生成1-10的随机数
				console.log('随机数生成的值:',num)
				if(num<=10){
					resolve(num);
				}
				else{
					reject('数字太于10了即将执行失败回调');
				}
			}, 2000);
		   })
		   return p
	   }
 
	promiseClick().then(
		function(data){
			console.log('resolved成功回调');
			console.log('成功回调接受的值:',data);
		}, 
		function(reason){
			console.log('rejected失败回调');
			console.log('失败执行回调抛出失败原因:',reason);
		}
	);	

async awite

async awite是一种promise的语法糖,更直观的替代promise处理异步问题的能力

promise与async的区别

  • 处理方式:promise使用promise/catch的方式处理异步,async采用async函数和awite关键字来处理异步
  • 错误捕获:async采用try/catch捕获错误,promise采用catch捕获错误
  • 可读性:async可读性更强
  • 性能:promise性能稍强,不过总体差不多

webpack

原理:把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载。

它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Sass,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。

4、什么是bundle,什么是chunk,什么是module

bundle: 是由webpack打包出来的文件

chunk: 是指webpack在进行模块依赖分析的时候,代码分割出来的代码块

module: 是开发中的单个模块

vue

v-show原理

display:none||block

路由传参

params:

<Link to='/home/dx'>首页</Link>
<Route path="/home/:name" component={Home}>
//使用
this.props.match.params
//或者
let a = useParams()
console.log(a.name)

search

<Link to='/home?name=dx'>首页</Link>
//使用
this.props.location.search 
//或者
this.props.location.query
//或者
let [a]=useSearchParams()
a.get('name')

隐式路由state传参

<Link to={{
	pathname: 'about',
	 state: {
	   name: 'dx'
	 }
}}>关于</Link>

//使用
this.props.location.state

组件间传参

<Link to='/test'>测试</Link>
<Route path='/test' render={(routeProps) => {
 		//routeProps就是路由组件传递的参数
     	return (
     	//在原先路由组件参数的情况,扩展绑定父组件对子组件传递的参数
      	<Test {...routeProps} name='dx' age={18} />
    	)
 }}></Route> 

//this.props获取来自父组件传递的参数和路由组件自带的参数

vue实现路由守卫

在哪里实现,路由配置文件里

全局路由守卫

//前置路由守卫
router.beforeEach((to, from, next) 
//后置路由守卫
router.afterEach((to, from) => {})

组件内路由守卫

//前置
beforeRouteEnter:(to,from,next)=>{}——到达
//点击其他组件时,判断是否离开
 beforeRouteLeave:(to,from,next)=>{}——离开

独享路由守卫:单个页面使用

 beforeEnter:(to,from,next)=>{}

类组件与函数组件的区别

共同点:它们都可以接收属性并且返回React元素

  1. 开发时理念不同:类组件面向对象编程,函数组件的内核时函数式编程。
  2. 内存占用:类组件需要创建实例,会占用内存,函数组件不需要,所以节约内存。
  3. 生命周期:类组件有生命周期的概念,而函数组件没有,通过HOOK实现。
  4. 性能优化:类组件——shouldComponentUpdate和pureComponent阻止渲染来提升性能,函数组件——依靠React.memo缓存渲染结果来提升性能

vue和react的区别

相同:
- 都使用虚拟DOM的方式渲染页面
- 都使用组件化思想
-

  1. 机制方面:
    - 在react是函数式的思想,把组件设计成纯组件,是单向数据流,数据更新是手动的,但是性能更好,节约资源去监控数据,采用mvc模式,m:数据层(模型),v:用户界面,c:控制器 ,流程:v->c->m->v
    - vue是响应式的,是双向数据流的,数据更新是自动的,mvvm模式,m:数据层,v:用户界面,vm:双向数据流的实现,流程:v<->m<->m,原理:Object.definePrototype
  2. 操作:
    - react通过js生成html,使用jsx语法
    - vue将html、css、js组合到一起,采用各自的处理方式,html采用模板引擎
  3. 生命周期不同
    - react:componentDidMount()、componentWillUnmount()、componentDidUpdate()
    - vue:beforCreate()、created()、beforeMount()、mounted()、beforeUpdate()、updated()、beforDestoryed(),destoryed()
  4. 事件处理:
    - react:小驼峰命名法,需要传入一个事件处理函数,不是字符串,使用与普通DOM元素相似
    - vue:使用v-on(@),接收方法名称
  5. 构建工具
    - react:Create React App
    - vue:vue-cil
    在这里插入图片描述
  6. diff算法:
    • 节点对比处理方式:vue比对节点,当节点元素类型相同,但是className不同,认为是不同类型元素,删除重建,而react会认为是同类型节点,只是修改节点属性
    • 列表对比方式:vue的列表比对,采用从两端到中间的比对方式,而react则采用从左到右依次比对的方式。

vuex

state:

用来定义数据,存放公共数据
其他地方获取数据:$store.state.data

getters

类似computed,可以在里面进行运算,可以是函数
其他地方获取数据:$store.getter.data

mutations

更改mutations:{state.a++}
是修改state的唯一方法5
写法:

mutations:{functionName(state.a){state.a++}}

其他地方使用:this.$store.commit('functionName')

actions

用于提交mutation
action可以包含任意异步操作
例如:当数据是state里面的,就有action发送请求
写法:

action:{
	functionName(context){
		//异步操作
		//提交,context代表上下文,本质就是store
		context.commit('mutation里面的函数名')
	}
}

其他地方接收使用:

this.$store.dispatch('functionName')

modules

将store分开写,写在另一个js文件,然后再modules里面注册

const newStore={
	state:{},
	getters:{},
	mutations:{},
	actions:{}s
}
export default user
import newStore from '......'
......
modules:{}
......

proxy监听数据

target代表需要添加代理的对象,handler用来定义操作,get、set

express有哪些模块

日志模块、路由模块、数据库模块、multer (文件上传模块)、
express-session( session处理)

配置webpack,解决跨域

再config.env.js文件里配置代理

diff算法

为什么使用:渲染真实DOM的带价是非常昂贵的,有时候改变一个地方,我们就要把整个DOM树进行重绘和重排。但是我们需要改变的只有一小块地方,diff算法就实现了这一目标

核心:将更新前和更新后的DOM树进行对比

重绘和重排、回流

语法糖

通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会,它对语言本身的编辑结果和功能没有影响和改变。

虚拟dom

一种描述真实DOM的js对象
特点:

  1. 没有浏览器兼容问题,可以实现跨平台开发
  2. 避免操作真实DOM(diff算法)
  3. 内部自行解决了xss问题

setState是同步还是异步

setState在原生方法中是同步的,但是,在React的事件处理机制的环境中,是异步的。因为每一次的setState都会触发更新,采用异步的方式可以提升性能

react的路由模式

hash模式:通过监听 hashchange 事件, 在回调里拿到 window.location.hash 的值。
history模式:利用history API实现url地址改变,网页内容改变。window.history 属性指向 History 对象,它表示当前窗口的浏览历史。

react常用钩子

  1. useState()、useEffct()
  2. useContext():组件之间共享状态
import { useState, createContext, useContext } from 'react';
import { Button } from "antd";
 
const ContContext = createContext()
 
function TestCom() {
	//拿到共享的count
    let count = useContext(ContContext)
    return (
        <h2>
            测试子组件:{count}
        </h2>
    )
}
 
function Demo04() {
 
    const [count, setCount] = useState(0)
 
    return (
        <div>
            <h1>{count}</h1>
            <Button onClick={() => { setCount(count + 1) }}>点击按钮+1</Button>
 
            <ContContext.Provider value={count}>
                <TestCom />
            </ContContext.Provider>
        </div>
    )
}
 
export default Demo04

useRef():获取组件的实例;

time slicing——时间分片

为什么使用:同步模式会造成任务进程卡顿,因为每一次的任务要将当前任务完成之后才能进行下一次进程。

原理:通过中断任务循环(将任务进行分片),每一个进程都是可打断的,会优先处理紧急任务,不会阻塞进程。

好处:

  1. react在渲染时,不会阻塞进程
  2. 如果设备足够快,会感觉时同步的,如果设备非常慢,也不会觉得泰国卡顿
  3. 虽然是异步渲染,但是会看到完整的代码

redux

三个层面:
view的要获取state要从store中获取,而要改变view中的state必须构建action,在store中修改,这三个层面是单向的,actions->store->view->actions

三个原则:

  1. 单一事件源
  2. 状态只读
  3. 使用纯函数修改

定义:redux是一个专门做状态管理的js库

特点:

  1. 单一数据源:整个项目只有一个store
  2. 只有store能改变state,reducer函数只能通过当前state和action计算出新的state
  3. 使用纯函数reducer进行状态改变(纯函数:出参由入参决定,没有副作用)

redux使用

引入:

npm install redux --save

基本配置:

import { createStore } from 'redux'
// 创建 action,对象中需包含 type 字段,说明此次变动
const action = {
    type: 'add_val',
    value: 2
}
// reducers 纯函数
function reducers (state = { val: 0 }, action) {
    // 根据 action type 编写处理逻辑
    switch (action.type) {
        case 'add_val':
            // 注意先深拷贝,不能直接改变 state 的值
            const newState = JSON.parse(JSON.stringify(state))
            newState.val += action.value
            return newState
        default:
            return state
    }
}
// 传入 reducers 创建 store
const store = createStore(reducers)
// 获取状态
const value = store.getState().val
// 订阅 store 的变动,执行回调
store.subscribe(() => {
    console.log('store 更新啦')
})


// 调用 dispatch 来更新状态
store.dispatch(action)

redux thunk作用

action是一个函数,使一个action中可以进行多次的dispatch。

redux的工作流程

  1. 用户通过view发出action,发出时使用dispatch方法
  2. store调用reducer,并传入两个参数,一个是原来的state,一个是收到的action,reducer经过计算返回新的state。
  3. state一旦有变化,store就会调用监听函数,更新view

redux与mobx区别

  1. 数据存储位置: redux将数据保存在单一的store中,而mobx将数据保存在分散的多个store中。
  2. 是否响应式:redux需要手动处理变化后的操作;mobx使用observable保存数据,数据变化后自动处理响应的操作。
  3. 数据改变方式:redux使用的是不可变状态,意味着状态只是只读的,不能直接去修改它,而是应该返回一个新的状态,同时使用纯函数;mobx中的状态是可变的,可以直接对其进行修改。
  4. 编程思维:mobx在使用时比较简单,它是面向对象的编程思维,而redux是函数式编程思维。
  5. 调试:mobx有更多的抽象和封装,调试更加复杂,redux调试起来更加简单

面试记录:

  1. 实现动态路由:
  2. 全局存储变量怎么实现的:放在根原型上
  3. 跨域怎么解决:代理
    什么是跨域:
    跨域指的是浏览器不能执行其它网站的脚本,它是由浏览器的同源策略造成的

什么是同源策略:所谓同源是指"协议+域名+端口"三者相同

解决:
JSONP 方式
需要后台配合,利用了html种一些地方是不需要同源的,比如img的src也是不需要同源的

后端返回的是可执行的js代码
动态加载 script的src,把参数放在src里面
只能get请求,因为src中只能是get请求

 1	$.ajax({
 2             type:"get",
 3             async:false,
 4             url:"http://localhost:8080/JavaWeb01/getPassWordByUserNameServlet?userName=Tom",
 5             dataType:"jsonp",//数据类型为jsonp
 6             jsonp:"backFunction",//服务端用于接收callBack调用的function名的参数
 7             success:function (data) {
 8                 alert(data["passWord"]);
 9             },
10             error:function () {
11                 alert("error");
12             }
13 
14         });

代理:只需要前台配置

cors( 跨域资源共享):只需要后台配置

nginx代理跨域:nginx反向代理,将请求分发到部署到相应项目的tomcat服务器

  1. 如果前后端在不同的服务器上面,最后项目怎么打包?
    将前端打包之后,得到的打包文件,上传至服务器。
  2. 实现动态监听数据: Object.definePropety()

css怎么实现左右菜单栏布局——原生和flex

原生:
calc():父组件必须有宽度

.left1{width: 100px;background: yellow;}
.right1{background: #09c;width:calc(100% - 100px);}

margin-left

.left2{float: left;background: yellow;width: 100px;}
.right2{background: #09c;margin-left: 100px;}

position

#box5{position: relative;}
.left5{position: absolute;left: 0;top:0;width: 100px;background: yellow;}
.right5{position: absolute;left: 100px;top:0;right: 0;width: 100%;background: #09c;}

flex

#box6{display: flex;display: -webkit-flex;}
.left6{flex:0 1 100px;background: yellow;}
.right6{flex:1;background: #09c;}
  1. 浅拷贝和深拷贝。深拷贝有哪些方式:JSON.stringify()、解构、用json的方法

在这里插入图片描述
斐波那契数列:第n项的值等于(n-1)项的值加上(n-2)项的值


function climbStairs(n) {
    //使用裴波那契法(可以使用递归,但是可能超时)
    if(n === 1 || n === 2){
        return n;
    }
    let result;  //用来保存结果
    let preTwo = 1;  //需要记忆两个数
    let preOne = 2;
    for(let i = 3; i < n + 1; i++){
        result = preOne + preTwo;
        preTwo = preOne;
        preOne = result;
    }
    return result;
}

在这里插入图片描述

在这里插入图片描述

小程序与vue区别

本文地址:https://www.limyvps.com/news/26_70401.html