自我介绍

问题

具体介绍一下在公司的主要项目,在项目中的角色,在项目中遇到的难点

上一个项目或者做过的主要项目的简介

前端工作,前端架构,后台交互

在项目中遇到的难点

promise

1、promisel
2、Promise.all()
Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入,并且只返回一个Promise实例,那个输入的所有 promise 的 resolve 回调的结果是一个数组。这个Promise的 resolve 回调执行是在所有输入的 promise 的 resolve 回调都结束,或者输入的 iterable 里没有 promise 了的时候。它的 reject 回调执行时,只要任何一个输入的 promise 的 reject 回调执行或者输入不合法的 promise 就会立即抛出错误,并且 reject 的是第一个抛出的错误信息。
3、Promise.race()
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。
4、Promise.any()
接收一个 promise 对象的集合,当其中的任意一个 promise 成功,就返回那个成功的 promise 的值。

问题

关于promise的执行顺序,参考看这篇文章
1、有三个await在执行,其中第一个程序报错了后面还会执行吗?

1
2
3
4
5
const func = async() => {
await func1();
await func2();
await func3();
}

答案: 不会,会在控制台报错并终止执行
2、如果想按顺序执行几个await,有什么办法
办法1 使用Promise.all()
办法2 使用for循环进行执行
3、根据上一个返回结果进行下一步操作
方法1 使用promise的链式结构
方法2 使用reduce

1
2
3
4
5
6
7
// reduce() 方法为数组的每个值(从左到右)执行提供的函数。
const numbers = [175, 50, 25];
const newNum = numbers.reduce(async function myFunc(total, num) {
const ccc = await http.request()
return ccc;
});
console.log(newNum)

4、题目如下,打印结果为

1
2
3
4
5
6
console.log(1)
new Promise(function(resolve, reject) {
console.log(2)
})
console.log(3)
// 打印结果为 1 2 3

原因:Promise 里面的函数会立即执行,因为 Promise 是同步的,而.then 是异步的

react的底层原理

对底层原理有稍微了解过,从不同的角度来讲每个人都有自己的理解
参考:https://zhuanlan.zhihu.com/p/101507773

场景问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* useState定义一个变量count,
* 用一个按钮点击后count+1,
* useEffect里面写一个定时器,
* 5s后进行一个打印,
* 在组件初始化后点击按钮五次,打印的结果为?
*/
import React, { useState, useEffect } from 'react'
export const ButttonCount = () => {
const [count, set_count] = useState(0)
useEffect(() => {
const Timer = setTimeout(() => {
console.log("useEffect_count:", count)
clearTimeout(Timer)
}, 5000)
})
return <div>
<button
style={{ width: "80px", height: "40px", backgroundColor: "#ccc" }}
onClick={() => set_count(count + 1)}
>{count}</button>
</div>
}

打印结果

1
2
3
4
5
6
useEffect_count: 0  // 执行了两次,为第一次组件渲染的结果
useEffect_count: 1 // 第一次点击后的输出
useEffect_count: 2 // 第二次点击后的输出
useEffect_count: 3 // 第三次点击后的输出
useEffect_count: 4 // 第四次点击后的输出
useEffect_count: 5 // 第五次点击后的输出

结果产生原因
由于useEffect中形成一个闭包,只会拿到初始执行的数据缓存起来,所以打印的结果是useEffect的函数执行时的数值。

react事件机制

React并不是将click事件绑定到了div的真实DOM上,而是在document处监听了所有的事件,当事件发生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件。
除此之外,冒泡到 document上的事件也不是原生的浏览器事件,而是由react 自己实现的合成事件(SyntheticEvent)。因此如果不想要是事件冒泡的话应该调用event.preventDefault()方法,而不是调用 event.stopProppagation()方法。

虚拟dom

React中真实DOM的生成步骤:JSX -> createElement方法 -> JS对象(虚拟DOM) -> 真实的DOM
因此可见,JSX中的div等标签仅仅是JSX的语法,并不是DOM,仅用于生成JS对象
虚拟DOM的优点:
1、性能提升 DOM比对变成了js的比对
2、它使得跨平台应用得以实现,React Native(安卓和ios中没有DOM的概念,使用虚拟DOM(js对象)在所有应用中都可以被使用,然后变成原生客户端的组件)

react diff原理

1、Diff算法用于比较原始虚拟DOM和新的虚拟DOM的区别,即两个js对象该如何比对。
2、diff算法全称叫做difference算法
3、setState实际上是异步的,这是为了提升react底层的性能,是为了防止时间间隔很短的情况下多次改变state,React会在这种情况下将几次改变state合并成一次从而提高性能。
4、diff算法是同级比较,假设第一层两个虚拟DOM节点不一致,就不会往下比了,就会将原始页面虚拟DOM全部删除掉,然后用新的虚拟DOM进行全部的替换,虽然这有可能有一些性能的浪费,但是由于同层比对的算法性能很高,因此又弥补了性能的损耗。
5、做list循环的时候有一个key值,这样比对的时候就可以相对应的比对,找出要改变的,以及不需要渲染的,这样使用key做关联,极大的提升了虚拟DOM比对的性能,这要保证在新的虚拟DOM后key值不变,这就说明了为什么做list循环的时候key的值不要是index,因为这样没有办法保证原虚拟DOM和新虚拟DOM的key值一致性,而造成性能的损耗即会产生性能问题,一般这个key对应后台数据的唯一id字段,而不是循环的index。(列表不推荐使用index作为key,为什么)

1
2
3
4
5
6
7
8
9
10
11
12
getTodoItem(){
return this.state.list.map((item,index)=>{
return (
<TodoItem
key = {item}
content={item}
index={index}
deleteItem = {this.handleItemDelete}
/>
)
})
}

react 调度原理

react hooks原理

参考:https://segmentfault.com/a/1190000038768433#item-6

useEffect

主要功能:
组件加载后执行创建函数,创建函数执行后会返回一个销毁函数,在组件销毁前执行。若依赖项为数组且不为空,则依赖项改变时,会执行上一个销毁函数和重新执行创建函数.useEffect直接被调用的过程是组件初始化和组件更新,其销毁函数被调用的过程是组件销毁。

1
useEffect(创建函数,依赖项)

组件初始化:
1、生成一个effect对象,包含创建函数
2、缓存effect和依赖项
3、当React进入提交阶段,执行effect中的创建函数,获取销毁函数。若销毁函数不为空,则将其放入effect。
组件更新:
1、生成一个effect对象, 包含创建函数
2、检查已缓存effect中是否有销毁函数,有的话则放入新effect对象
3、缓存effect
4、若依赖项和已缓存依赖项不同,则将hasEffect标记添加到effect,并缓存新依赖项
5、当React进入提交阶段:

1
2
3
4
if (effect有hasEffect标记) {
若effect中有销毁函数,则先执行销毁函数
执行effect中的创建函数,获取销毁函数。若销毁函数不为空,则将其放入effect
}

组件销毁:若effect中有销毁函数,则执行。

useState

useState的功能是设置一个状态的初始值,并返回当前状态和设置状态的函数。useState直接被调用的过程也是组件初始化和组件更新,其还有一个调用设置状态函数的过程。

1
[状态,设置状态函数] = useState(初始状态)

组件初始化:
1、若初始状态为函数,则将函数执行结果设为当前状态。否则将初始状态设为当前状态。
2、生成设置状态函数
3、缓存当前状态和设置状态函数
4、返回当前状态
组件更新:
1、读取缓存状态和设置状态函数
2、返回缓存状态
执行设置状态函数:
1、更新缓存状态
2、触发React组件树更新
3、在下一次组件更新时,将返回已被更新的缓存状态

useReducer

useReducer的功能和原理与useState一致,区别在于useReducer使用函数管理状态,使用派发动作指令函数作为设置状态函数。Reducer概念可参看redux。

1
[状态,派发动作指令函数]=useReducer(reducer函数,初始状态)

useRef

1
{current: 当前值} = useRef(初始当前值)

useRef的功能是生成一个对象,结构是:{current: 当前值}, 对象一旦初始化,不会因为组件更新而改变,即不会触发重新渲染。

用过哪些中间件(koa、express、egg)

什么是中间件

处理 Web 请求时,我们常常需要进行验证请求来源、检查登录状态、确定是否有足够权限、打印日志等操作,而这些重复的操作如果写在具体的路由处理函数中,明显会导致代码冗余,这个时候,我们就可以将这些通用的流程抽象为中间件函数,减少重复代码。
我们可以将 Web 请求想象为一条串联的管道,在管道中有多个关卡,请求数据由源头起,依次流过各关卡,每个关卡独立运作,既可以直接响应数据,又可以对请求稍作调整,并使之流向下一关卡,这个关卡,就是中间件。
中间件模型

koa

参考:https://juejin.cn/post/7022626279048347679#heading-11
sass端项目有使用到koa,但是pass端没有使用到,但是有了解过koa的实现模式。
Koa 实现了基于洋葱模型的中间件,在处理请求时,总是从外层中间件开始,到最内层,最后回到最外层。

http协议、http缓存、http版本、浏览器请求上限、跨域

参考:https://juejin.cn/post/6844903844216832007#heading-22

TCP/IP 协议

应用层:应用层规定了向用户提供应用服务时通信的协议
传输层:传输层对接上层应用层,提供处于网络连接中两台计算机之间的数据传输所使用的协议
网络层:网络层规定了数据通过怎样的传输路线到达对方计算机传送给对方(IP协议等)
链路层:用来处理连接网络的硬件部分,包括控制操作系统、硬件的设备驱动、网卡及光纤等物理可见部分
通信传输流

TCP和UDP的区别

1、TCP协议是全双工的,即发送数据和接收数据是同步进行的,就好像我们打电话一样,说话的同时也能听见。
2、TCP协议在建立和断开连接时有三次握手和四次挥手,因此在传输的过程中更稳定可靠但同时就没UDP那么高效了。
3、UDP协议是面向无连接的,也就是说在正式传递数据之前不需要先建立连接。
4、UDP 协议不保证有序且不丢失的传递到对端,也就是说不够稳定,但也正因如此,UDP协议比TCP更加高效和轻便。

http/https

  • http
    1、HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。
    2、HTTP协议采用了请求/响应模型。
    3、客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。
    4、服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
  • https
    HTTPS基于HTTP协议,通过SSL或TLS(可以看作SSL3.0)提供加密处理数据、验证对方身份以及数据完整性保护,特点如下:
    1、内容加密:采用混合加密技术,中间者无法直接查看明文内容
    2、验证身份:通过证书认证客户端访问的是自己的服务器
    3、保护数据完整性:防止传输的内容被中间人冒充或者篡改
  • http和https的主要区别
    1、HTTPS协议需要到CA(证书颁发机构)申请证书,一般免费证书很少,需要交费。
    2、HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
    3、HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
    4、http的连接很简单,是无状态的;HTTPS协议是由HTTP+SSL协议构建的可进行加密传输、身份认证的网络协议,可以有效的防止运营商劫持,解决了防劫持的一个大问题,较http协议安全。

http版本

  • http1.0
    最早的http只是使用在一些较为简单的网页上和网络请求上,所以比较简单,每次请求都打开一个新的TCP链接,收到响应之后立即断开连接。
  • http1.1
    1、HTTP/1.1 引入了更多的缓存控制策略,如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等
    2、HTTP/1.1 允许范围请求,即在请求头中加入Range头部
    3、HTTP/1.1 的请求消息和响应消息都必须包含Host头部,以区分同一个物理主机中的不同虚拟主机的域名
    4、HTTP/1.1 默认开启持久连接,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。
  • http2.0
    1、新的二进制格式: HTTP/1.x的解析是基于文本的。基于文本协议的解析存在天然缺陷,文本的表现形式有多样性,要做到全面性考虑的场景必然很多。二进制则不同,只识别0和1的组合。基于这种考虑HTTP/2.0的协议解析采用二进制格式,方便且强大。
    2、多路复用: HTTP/2.0支持多路复用,这是HTTP/1.1持久连接的升级版。多路复用,就是在一个 TCP 连接中可以存在多条流,也就是可以发送多个请求,服务端则可以通过帧中的标识知道该帧属于哪个流(即请求),通过重新排序还原请求。多路复用允许并发的发起多个请求,每个请求及该请求的响应不需要等待其他的请求或响应,避免了线头阻塞问题。这样某个请求任务耗时严重,不会影响到其它连接的正常执行,极大的提高传输性能。
    3、头部压缩: HTTP/1.x的请求和响应头部带有大量信息,而且每次请求都要重复发送,HTTP/2.0使用encoder来减少需要传输的头部大小,通讯双方各自cache一份头部 fields表,既避免了重复头部的传输,又减小了需要传输的大小。
    4、服务端推送: 这里的服务端推送指把客户端所需要的css/js/img资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤(从缓存中取)。
  • http3.0
    Google 基于 UDP 协议推出了一个的 QUIC 协议,并且使用在了 HTTP/3 上。
    QUIC 基于 UDP,但是UDP本身存在不稳定性等诸多问题,所以QUIC在UDP的基础上新增了很多功能,比如多路复用、0-RTT、使用 TLS1.3 加密、流量控制、有序交付、重传等等功能。优点诸多,参考这里:
    1、避免包阻塞: 多个流的数据包在TCP连接上传输时,若一个流中的数据包传输出现问题,TCP需要等待该包重传后,才能继续传输其它流的数据包。但在基于UDP的QUIC协议中,不同的流之间的数据传输真正实现了相互独立互不干扰,某个流的数据包在出问题需要重传时,并不会对其他流的数据包传输产生影响。
    2、快速重启会话: 普通基于tcp的连接,是基于两端的ip和端口和协议来建立的。在网络切换场景,例如手机端切换了无线网,使用4G网络,会改变本身的ip,这就导致tcp连接必须重新创建。而QUIC协议使用特有的UUID来标记每一次连接,在网络环境发生变化的时候,只要UUID不变,就能不需要握手,继续传输数据。

浏览器请求上限

浏览器请求上限 chrome、Firefox、ie8都是6,opera、Safari都是4
1、Http get方法提交的数据大小长度并没有限制,Http协议规范没有对URL长度进行限制。
目前说的get长度有限制,是特定的浏览器及服务器对它的限制。各种浏览器和服务器的最大处理能力如下:

1
2
3
4
5
6
7
IE:对URL的最大限制为2083个字符,若超出这个数字,提交按钮没有任何反应。
Firefox:对Firefox浏览器URL的长度限制为:65536个字符。
Safari:URL最大长度限制为80000个字符。
Opera:URL最大长度限制为190000个字符。
Google(chrome):URL最大长度限制为8182个字符。
Apache(Server):能接受的最大url长度为8192个字符(这个准确度待定???)
Microsoft Internet Information Server(IIS):n能接受最大url的长度为16384个字符。

2、理论上讲,post是没有大小限制的。Http协议规范也没有进行大小限制,起限制作用的是服务器处理程序的处理能力。
Tomcat下默认post长度为2M,可通过修改conf/server.xml中的“maxPostSize=0”来取消对post大小的限制。

跨域

由于浏览器的同源策略影响,协议、域名、端口号不同就会产生跨域问题
解决方案
1、反向代理解决跨域问题
2、jsonP
3、后端去解决

前端优化

  • 高内聚低耦合
  • 减少HTTP的请求次数和大小
    因为HTTP的并发性、TCP的三握四挥、网络通道可能会被阻塞等众多原因,决定了HTTP请求次数越少越好
  • 图片、音视频资源懒加载/预加载
    1、第一次渲染页面不去加载真实图片(页面中基于默认图占位)
    2、使用字体图标/SVG(矢量图)代替位图(jpg/png/gif…)
  • 列表、表格数据分页
    1、列表、表格数据请求进行分页(数字分页或滚动加载分页)
    2、数据的分页加载、异步加载、下拉加载
  • 条件语句优化
    1、对嵌套代码进行优化,减少嵌套层数
    2、多条条件语句提前return,或者使用switch进行break
  • 第三方库按需引入
    按需引入,咱们使用的一些第三方库可以通过按需引入的方式加载。避免引入不需要使用的部分,无端增加项目体积。

前端安全

XSS攻击

XSS 攻击全称跨站脚本攻击,是利用html可以执行script标签的特性,(比如我在这里加了一段下面的代码)想尽办法将脚本注入页面中的攻击手段。XSS攻击有两种,一种是通过修改浏览器URL导致脚本被注入到页面,另一种是通过输入框将脚本代码注入数据库。前面一种会被chrome浏览器自动防御攻击(但最好还是手动也防御一下),后面一种则需要我们手动防御。

1
<script>alert("一段js代码,可用在本站做脚本攻击")</script>

CSRF 攻击

CSRF中文名为跨站请求伪造。攻击是源于Web的隐式身份验证机制!Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。CSRF攻击的问题一般是由服务端解决,防范 CSRF 攻击可以遵循以下几种规则:
1、Get 请求不用于对数据进行修改
2、Cookie设置HTTP Only
3、接口设置禁止跨域
4、请求时附带验证信息,比如验证码或者 Token

个人开发习惯

1、一个方法只做一件事情、把代码最大程度的拆分
2、尽量精简代码
3、一个函数或方法只做一件事情(提升代码能力)

移动端开发

React Native、flutter、taro(ui框架)?

es6

let、const

箭头函数

拓展运算符

模版字符串

TypeScript

数据类型

状态管理

mobx

https://zhuanlan.zhihu.com/p/369430688

我要问的问题

公司业务大致有哪些,
面试结果什么时候出,
公司有没有用阿米巴。
多久转正,多久提一次薪,
公司会经常使用新的技术吗