参考链接:juejin.cn/post/710837…
1. 网络方面:
a.静态资源使用CDN
当网站所挂服务器离用户越来越远时,访问网站的延迟也会越高,CDN(内容分发网络)在不同的地理位置部署Web服务器,根据用户位置分配最近的资源,缩短请求时间达到优化加载的速度的效果。
b.减少不必要的HTTP请求
一个完整的HTTP请求需要经历连接与释放的过程,需要一定的时间,减少HTTP可以节省一定时间。 比如在开发中,去减少在一些页面中根本没有用到的接口请求。
c.使用HTTP2.0
HTTP1.1版本存在线程阻塞的问题,在同一时间,同一域名的请求有一定的数量限制,超过限制数目的请求会被阻塞。
HTTP2.0特性:
- **支持二进制传输:**http1.1采用的是文本传输的方式,http2.0采用二进制传输,传输更快。
- **支持多路复用:**多个请求可以共用一个TCP连接,提高连接的利用率,降低延迟
- **头部压缩:**将头部信息采用压缩算法进行压缩,数据量更小。
d.采用HTTP缓存
可以通过设置强制缓存或者协商缓存来进行控制,符合缓存条件时直接读取缓存,减少发送请求,提高加载速度。
e.使用Gzip压缩
Gzip 通过 LZ77 算法与 Huffman 编码来压缩文件,重复度越高的文件可压缩的空间就越大,对 JS、CSS、HTML 等文本资源均有效。当 Nginx 返回 js 文件的时候,会判断是否开启 gzip,然后压缩后再返回给浏览器。
该压缩方法需要 Nginx 配置 也开启 Gzip 压缩,单纯前端通过 webpack 插件开启 Gzip 压缩是不能达到优化效果的
2.图片优化方面
a.采用图片懒加载
先不给图片设置src路径,当图片出现在浏览器可视区域时,才去加载图片,这就是延迟/懒加载。
图片设置data-src属性在页面不可见时图片不会加载。
图片懒加载思路:
- 先在img标签设置自定义属性data-src
- 计算当页面可见时,src值替换为data-src,加载图片。
b.采用图片压缩
可以利用webpack的image-webpack-loader对图片进行压缩。
c.采用略缩图
点击或加载到之后再查看清晰大图
如果有一个1920 * 1080大小的图片,用略缩图的方式展示给用户,并且当用户鼠标悬停在上面时才展示全图,如果用户从未真正将鼠标悬停在缩略图上,则不浪费了下载图片的时间。
所以,可以用两张图片来进行优化。一开始,只加载缩略图,当用户悬停在图片上时,才加载大图
d.使用Web Workers
JavaScript语言是单线程,所有与浏览器UI无关的长时间运行脚本任务只能在一个线程完成,一次只能做一件事。如果是复杂的计算,则页面运行可能会被阻塞。
此时可以使用Web Worker进行处理与浏览器UI无关的长时间运行脚本。Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法,线程可以执行任务而不干扰用户界面。
e.服务端渲染SSR
客户端渲染: 客户端获取HTML,下载JS文件运行生成DOM,渲染页面。
服务端渲染: 服务端直接返回HTML文件,客户端只需解析HTML
当前使用 Vue/React 开发的网站,只要不是对 SEO 有要求,大部分都是采用的客户端渲染。而如果网站要追求 SEO 好和首屏速度快的话,那就可采用服务端渲染。因为它只需加载一个渲染完毕的 HTML 文件,比客户端渲染要更快。
3.减少打包体积
a.压缩资源的体积
打包体积减少,可以减少文件下载时间,可以让用户体验更好。
在webpack可以使用如下插件进行压缩:
- HTML: html-webpack-plugin
- CSS: css-minimizer-webpack-plugin
- JavaScript: terser-webpack-plugin
b.使用tree shaking删除未使用的代码
Tree shaking 是一种优化 JavaScript 应用程序的技术,它通过静态代码分析和模块依赖关系来删除未使用的代码,从而减小最终打包后的代码体积。
4.使用事件委托
添加到页面上的事件数量会影响页面的运行性能,如果添加的事件过多,会导致网页的性能下降。采用事件委托的方式,可以大大减少注册事件的个数。
使用事件委托的好处:
- 提高性能
- 节省内存占用,减少事件注册
- 实现当新增子对象时无需再次对其绑定
5.CSS方面优化
a.降低CSS选择器的复杂性
- 保持简单,不要使用嵌套过多过于复杂的选择器
- 通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用
b.使用transform和opacity属性更改来实现动画
在CSS中,transforms和opacity这两个属性更改不会触发重排与重绘,他们由合成器单独处理的属性。
6.渲染优化方面
a.减少重绘重排
-
用 JavaScript 修改样式时,最好不要直接写样式,而是替换 class 来改变样式
-
需要对元素进行复杂操作时,可以先隐藏元素(display:none)操作完成后再显示
-
需要创建多个 DOM 节点时,使用 DocumentFragment 创建完最后再一次性加入文档
b.采用防抖节流优化高频事件
当页面有一些事件频繁触发时,为了优化体验,需要对这类事件进行调用次数的限制,于是可以使用防抖与节流来减少调用频率。
- 防抖:一段时间后只执行一次,将多次执行变为最后一次执行
- 节流:在固定的频率执行,将多次执行变为在规定时间内只执行一次
c.优化动画
- 优先使用 CSS 来实现动画效果
- 使用 translateZ/translate3d 开启硬件加速
- 合理使用 requestAnimationFrame 代替 setTimeout
7.预加载(加载顺序)
/ 可控制 HTTP 优先级,从而达到关键请求更快响应的目的。
- preload 优先级较高,提前加载较晚出现,但对当前页面非常重要的资源
- prefetch 优先级较低,提前加载后继路由需要的资源。一般用以加载其它路由资源,如当页面出现 Link,可 prefetch 当前 Link 的路由资源
答: 区别:
- 应用场景: Get请求一般用于对服务器资源不会产生影响的场景,比如请求一个网页资源,而Post一般用于对服务器有影响的操作,比如注册用户
- 是否缓存: 浏览器一般会对Get请求进行缓存,很少对post请求缓存。
- 发送报文的格式: Get请求的报文中实体部分为空,Post请求的报文中实体部分一般为向服务器发送的数据。
- 安全性: Get请求将请求参数放在url中相对post而言不太安全。url会被保留在历史记录中。
- 请求长度: get请求url长度限制1024字节。
答:
- put请求是向服务器端发送请求,从而修改数据的内容,但是不会增加数据的种类,也就是更新操作。
- post是向服务器端发送数据,他可以创建新的内容。
答: Cache-Control是HTTP协议中的一个请求头和响应头的字段,用于控制缓存的行为。
-
请求头中的cache-control:
-
- no-cache:表示客户端要求服务器不使用缓存响应,每次都必须向服务器发送请求,并在服务器返回响应前进行验证。
-
- no-store:表示客户端不希望请求和响应的任何部分被缓存,每次都必须向服务器发送请求并获取最新的响应。
-
- max-age:指定资源在被认为过期之前可以被缓存的时间,以秒为单位。例如,max-age=3600表示资源可以被缓存一小时。
-
- must-revalidate:表示如果缓存的资源过期,客户端必须向服务器发送请求进行验证。如果服务器返回响应码为304 Not Modified,则可以使用缓存的副本。
-
-
响应头中的cache-control:
- public:表示响应可以被任何缓存代理(包括客户端和中间代理服务器)缓存。
- private:表示响应只能被客户端缓存,中间代理服务器不能缓存。
- no-cache:表示响应可以被缓存,但在被使用之前必须向服务器发送请求进行验证。
- no-store:表示禁止缓存响应。
- max-age:指定响应在被认为过期之前可以被缓存的时间,以秒为单位。
答:
HTTP Request Header 常见的请求头:
- Host:指定要请求的服务器的主机名和端口号。
- User-Agent:标识发送请求的客户端应用程序或浏览器的信息。
- Accept:指定客户端可以处理的内容类型,以MIME类型表示。
- Accept-Language:指定客户端接受的语言偏好,以语言标签列表的形式表示。
- Accept-Encoding:指定客户端接受的内容编码,如gzip、deflate等。
- Referer:指定从哪个页面或URL发起当前请求。
- Authorization:用于进行身份验证的凭证,例如基本身份验证或Bearer令牌。
- Content-Type:指定请求主体的MIME类型,例如application/json、application/x-www-form-urlencoded等。
- Content-Length:指定请求主体的长度。
- Cache-Control:指定请求和响应的缓存行为,如no-cache、max-age等。
- Connection:指定是否保持持久连接或关闭连接。
- Cookie:包含服务器设置的HTTP Cookie。
- If-None-Match:与服务器的实体标签进行比较,用于检查缓存的实体是否仍然有效,避免重复请求。
- If-Modified-Since:与服务器上资源的修改日期进行比较,用于检查缓存的实体是否仍然有效,避免重复请求。
- Range:用于请求资源的部分内容,用于断点续传或分段下载。
- Origin:指定请求的来源,用于跨域请求时的安全验证。
- Upgrade-Insecure-Requests:指定客户端是否支持协议从HTTP升级到HTTPS。
HTTP Responses Header 常见的响应头:
-
Content-Type:指定响应主体的MIME类型,例如text/html、application/json等。
-
Content-Length:指定响应主体的长度。
-
Cache-Control:指定响应和请求的缓存行为,如no-cache、max-age等。
-
Expires:指定响应的过期时间。
-
Last-Modified:指定响应主体的最后修改时间。
-
ETag:用于标识响应主体的实体标签,用于缓存验证。
-
Location:指定重定向的目标URL。
-
Set-Cookie:用于在客户端设置 HTTP Cookie。
-
Server:指定服务器软件和版本信息。
-
Access-Control-Allow-Origin:指定允许跨域请求的来源。
-
Access-Control-Allow-Methods:指定允许跨域请求的 HTTP 方法。
-
Access-Control-Allow-Headers:指定允许跨域请求的自定义请求头。
-
Date:指定响应生成的时间。
-
Content-Encoding:指定响应主体的内容编码,如gzip、deflate等。
-
Allow:指定服务器支持的请求方法。
答:
-
GET: 向服务器获取数据;
-
POST:将实体提交到指定的资源,通常会造成服务器资源的修改;
-
PUT:上传文件,更新数据;
-
DELETE:删除服务器上的对象;
-
HEAD:获取报文首部,与GET相比,不返回报文主体部分;
-
OPTIONS:询问支持的请求方法,用来跨域请求;
- 1.实现预检请求(preflight request):在进行跨域请求时,浏览器会先发送一个OPTIONS请求,询问服务器是否可以发送真实的请求。服务器通过返回合适的响应头来告知浏览器是否允许跨域访问。
- 2.查询服务器支持的方法:通过发送OPTIONS请求,客户端可以了解服务器对某个资源支持的HTTP方法,从而决定后续的请求如何发送。
-
CONNECT:要求在与代理服务器通信时建立隧道,使用隧道进行TCP通信;
-
TRACE: 回显服务器收到的请求,主要⽤于测试或诊断。
答:
- 1.二进制协议: HTTP 1.1采用的文本的传输方式,效率低,而HTTP 2.0使用了二级制编码的传输方式,有效压缩了传输的数据,提高了性能。
- 2.多路复用: HTTP 1.1在同一个连接上每次只能发送一个请求,需要建立多个链接来获取多个资源,增加了开销。而HTTP2.0支持多路复用,同一个连接上可以发送多个请求和相应。
- 3.支持服务器推送: HTTP1.1不支持服务器推送,即服务器无法主动的向客户端发送未经请求的资源。HTTP2.0可以主动发送。
-
- 头部压缩: HTTP1.1带有大量的头部信息,HTTP2.0对头部信息进行了压缩处理。
补充:
HTTP3甩掉TCP、TSL的包袱,构建高效网络QUIC协议。
HTTP3选择了UDP协议,基于UDP实现了类似TCP的多路数据流、传输可靠性等功能,将这套功能称为QUIC协议。
特点
基于UDP协议改造,实现了快速握手
集成了TLS的加密功能
多路复用,彻底解决了头阻塞问题(一个物理连接上可以有多个独立的逻辑数据流,实现了数据流的单独传输)
实现了类似TCP的流量控制、传输可靠性的功能
答: 主要区别如下:
- 1.HTTPS协议需要CA证书,费用较高,而HTTP协议不需要。
- 2.HTTP协议是超文本传输协议,信息是明文传输的,HTTPS则具有安全性的SSL加密传输。
- 3.HTTP协议端口是80,HTTPS协议端口是443
CA证书补充:
CA证书是由认证机构(Certificate Authority,简称CA)签发的数字证书。它是用于认证和验证公共密钥的一种加密证书。
CA证书用于验证数据的完整性和真实性。当一个网站或服务器想要建立一个安全的HTTPS连接时,它需要提供一个有效的CA证书。浏览器通过验证该证书可以确定网站的身份,确保通信的安全性。
CA证书包含以下信息:
- 网站的公共密钥
- 网站的域名或IP地址
- 签发机构的信息
- 证书的有效期
- 证书的数字签名
浏览器收到网站的CA证书后,会验证签名是否有效,并检查证书是否过期。如果验证通过,浏览器会信任该证书,并建立安全的连接。如果验证失败,浏览器会显示一个警告或错误信息,提示用户存在潜在的安全风险。
参考链接:juejin.cn/post/684490…
-
输入 URL 后解析出协议、主机、端口、路径等信息,并构造一个 HTTP 请求。
-
缓存判断: 浏览器会判断所请求的资源是否在缓存里,如果请求的资源在缓存里并且没有失效,那么就直接使用,否则向服务器发起新的请求。
- 强制缓存:通过Cache-Control: max-age=3600的字段去设置强制缓存,当浏览器第一次发送请求到服务器时,服务器会在响应头中设置一个缓存有效期,下次再次请求资源时,游览器会先判断缓存有效期是否过期,如果没有过期,则直接使用本地缓存,否则在去浏览器中请求资源。
- 协商缓存:通过If-Modified-Since或If-None-Match字段来实现,当浏览器第一次发送请求给服务器时,服务器会在响应头设置资源的Last-Modified时间或Etag唯一标识。当再次请求资源时,浏览器还是会发送请求到服务器,服务器会根据If-Modified-Since(值为上次响应的Last-Modified时间)或If-None-Match(值为上次响应的ETage)去判断资源是否更新,若未更新,则返回304 Not Modified状态码,此时,游览器就从本地获取资源。若更新,则会返回最新的资源文件。
-
DNS 域名解析。(字节面试被虐后,是时候搞懂 DNS 了)
-
TCP 连接。
总是要问:为什么需要三次握手,两次不行吗?其实这是由 TCP 的自身特点可靠传输决定的。客户端和服务端要进行可靠传输,那么就需要确认双方的接收和发送能力。第一次握手可以确认客服端的发送能力,第二次握手,确认了服务端的发送能力和接收能力,所以第三次握手才可以确认客户端的接收能力。不然容易出现丢包的现象。
-
http 请求。
-
服务器处理请求并返回 HTTP 报文。
-
浏览器渲染页面。
-
- 解析HTML,构建DOM树
-
- 解析CSS,生成CSS规则树
-
- 合并DOM树和CSS规则,生成render树
-
- 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
-
- 绘制render树(paint),绘制页面像素信息 。
- 绘制render树(paint),绘制页面像素信息 。
- 断开 TCP 连接。(TCP四次回收)
答: keep-alive是HTTP协议中的一种机制,用于在同一TCP连接上进行多个HTTP请求和响应,以减少连接建立的开销和提高网络性能。设置Connection: keep-alive。他的连接保持时间很短,往往只有几秒。
答:
- 在HTTP 1中,浏览器对一个域名下最大TCP连接数是6,所以会多次请求。
- 在HTTP 2中,支持多路复用,一个TCP连接中可以发送多个HTTP请求,所以可以一瞬间加载出来。
以下面的URL为例:www.aspxfans.com:8080/news/index.…
从上面的URL可以看出,一个完整的URL包括以下几部分:
-
协议部分:该URL的协议部分为“http:”,这代表网页使用的是HTTP协议。在Internet中可以使用多种协议,如HTTP,FTP等等本例中使用的是HTTP协议。在"HTTP"后面的“//”为分隔符;
-
域名部分:该URL的域名部分为
-
端口部分:跟在域名后面的是端口,域名和端口之间使用“:”作为分隔符。端口不是一个URL必须的部分,如果省略端口部分,将采用默认端口(HTTP协议默认端口是80,HTTPS协议默认端口是443);
-
虚拟目录部分:从域名后的第一个“/”开始到最后一个“/”为止,是虚拟目录部分。虚拟目录也不是一个URL必须的部分。本例中的虚拟目录是“/news/”;
-
文件名部分:从域名后的最后一个“/”开始到“?”为止,是文件名部分,如果没有“?”,则是从域名后的最后一个“/”开始到“#”为止,是文件部分,如果没有“?”和“#”,那么从域名后的最后一个“/”开始到结束,都是文件名部分。本例中的文件名是“index.asp”。文件名部分也不是一个URL必须的部分,如果省略该部分,则使用默认的文件名;
-
锚部分:从“#”开始到最后,都是锚部分。使用锚部分可以实现页面内部的定位和导航效果。当用户点击包含锚部分的链接时,浏览器会自动滚动到对应的锚点位置。
-
参数部分:从“?”开始到“#”为止之间的部分为参数部分,又称搜索部分、查询部分。本例中的参数部分为“boardID=5&ID=24618&page=1”。参数可以允许有多个参数,参数与参数之间用“&”作为分隔符。
答: 超文本传输安全协议(Hypertext Transfer Protocol Secure,简称HTTPS)是一种通过计算机网络安全通信的传输协议。HTTPS经由HTTP进行通信,利用SSL/TLS来加密数据包。
主要是新增了安全层,主要职责就是对发起的HTTP请求的数据进行加密操作和对接收到的HTTP的内容进行解密操作。
答:
1.散列函数hash
答:常见的散列函数有MD5、SHA1、SHA256。该函数的特点是单向不可逆的,对输入数据非常敏感,输出的长度固定,任何数据的修改都会改变散列函数的结果,可以用于防止信息篡改并验证数据的完整性。
2.对称加密
答:对称加密的特点,双方使用同一个秘钥对数据进行加密和解密,但是对称加密存在一个问题,就是如何保证秘钥传输的安全性,因为秘钥还是会通过网络传输,一旦秘钥被其他人获取,那么加密过程就没有作用了。这时候就需要使用非对称加密方法传输密钥。
常见的对称加密算法有AES-CBC、DES、3DES等。
3.非对称加密
答:非对称加密特点,我们拥有两个秘钥,一个是公钥、一个是私钥,公钥是公开的,私钥是保密的,用私钥加密的数据,只有对应的公钥可以解密,同理,用公钥加密的数据,只有对应的私钥才能解密。我们将公钥公布出去,任何想和我们通信的客户,都可以使用我们提供的公钥对数据进行加密,这样我们在服务器端,就可以使用对应的私钥进行解密了,这样可以保证传输数据的安全。
缺点:加密的过程很慢,如果每次通信使用非对称加密方式的话,会造成等待时间过长问题。
总结: TLS/SSL的工作方式是客户端使用非对称加密与服务器进行通信,实现身份的验证并协商对称加密使用的密钥,对称加密算法采用协商秘钥对信息进行加密通信,不同节点之间采用的对称秘钥不同,从而保证信息只能通信双方获取。
补充:数字证书是什么?
答:首先使用一种 Hash 算法来对公钥和其他信息进行加密,生成一个信息摘要,然后让有公信力的认证中心(简称 CA )用它的私钥对消息摘要加密,形成签名。最后将原始的信息和签名合在一起,称为数字证书。当接收方收到数字证书的时候,先根据原始信息使用同样的 Hash 算法生成一个摘要,然后使用公证处的公钥来对数字证书中的摘要进行解密,最后将解密的摘要和生成的摘要进行对比,就能发现得到的信息是否被更改了。
补充:数字签名是什么?
答:数字签名就是⽤CA⾃带的HASH算法对证书的内容进⾏HASH得到⼀个摘要,再⽤CA的私钥加密,最终组成数字签名。当别⼈把他的证书发过来的时候,我再⽤同样的Hash算法,再次⽣成消息摘要,然后⽤CA的公钥对数字签名解密,得到CA创建的消息摘要,两者⼀⽐,就知道中间有没有被⼈篡改了。这个时候就能最⼤程度保证通信的安全了。
答:结合两种加密方式,将对称加密的秘钥使用非对称加密的公钥进行加密,然后发送出去,接受方使用私钥进行解密,得到对称加密的秘钥,然后双方可以使用对称加密进行沟通。
1、2XX(Success成功状态码)
(1)200 ok
表示客户端发来的请求被服务器正常处理。
(2)204 No Content
该状态码表示客户端发送的请求已经在服务器端正常处理了,但是没有返回的内容,响应报文中不包含实体的主体部分,一般在只需要从客户端往服务器端发送消息,而服务器端不需要往客户端发送内容时使用。
(3)206 Partial Content
该状态码表示客户端进行了范围请求,而服务器段执行了这部分的GET请求,响应报文中包含由Content-Range指定范围实体内容。
2、3XX(Redirection重定向状态码)
3XX响应结果表明浏览器需要执行某些特殊的处理以正确处理请求。
(1)301 Moved Permanently
永久重定向。该状态码表示请求的资源已经被分配了新的URI,以后应使用资源指定的URI,新的URI会在HTTP响应头中的Location首部字段指定。若用户已经旧的网址保存为了书签,则会去更新书签到新的网址。
使用场景:当我们想换域名的时候,旧的域名不再使用时,用户访问旧域名时用301就重定向到新的域名。
(2)302 Found
临时重定向。 该状态码表示请求的资源被分配到了新的URI,希望用户本次能使用新的URI访问资源。但是302代表的资源不是被永久重定向,只是临时性质的,如果用户把URI保存过书签,它就不会像301一样去更新书签。
使用场景:
- 当我们在做活动时,登陆到首页自动重定向,进入活动页面。
- 未登陆的用户访问用户中心重定向到登录页面。
- 访问404页面重定向到首页。
(3)303 See Other
状态码303 See Other是一种HTTP响应状态码,表示客户端应该向另一个URL发送新的请求,并且应该使用GET方法发送此请求。 与301和302状态码不同,303状态码明确要求客户端使用GET方法发送新的请求,而不是原来的请求方法。
(4)304 Not Modified
游览器缓存相关, 表示服务器端的数据未发生更新,直接可以使用客户端缓存的数据。304返回只有头部信息,没有内容部分,一定程度上提高了网页的性能。
(5) 307 Temporary Redurect
307表示临时重定向,该状态码与302有着相同的含义,虽然302标准禁止post变成get,但是实际上还是这样做了。
307会遵循浏览器标准,不会从post变成get,规范要求浏览器继续向localtion的地址post内容,规范要求浏览器继续向location的地址Post内容。
3、4XX(Client Error客户端错误状态码)
4xx的响应结果表明客户端是发生错误的原因所在。
(1)400 Bad Request
该状态码表示请求报文中存在语法错误。当错误发生时,需要修改请求的内容再次发送请求。
(2)401 Unauthorized
表示请求需要身份验证或缺乏有效的身份验证信息。当客户端发送的请求需要访问某个资源,但没有提供有效的身份验证信息或提供的凭据不正确时,服务器将返回401 Unauthorized状态码。
(3)403 Forbidden
表示服务器理解请求,但拒绝执行该请求,表示客户端请求的资源已经存在,但是服务器拒绝提供对该资源的访问权限,可能是用户没有足够的权限或者是服务器设置了防火墙之类的导致。
(4)404 Not Found
该状态码表明服务器上无法找到请求的资源。
(5)405 Method Not Allowed
该状态码表示客户端请求的方法虽然能被服务器识别,但是服务器禁止使用该方法。客户端可以通过OPTIONS(预检)来查看服务器允许访问的方法。
4、5XX(Server Error服务器错误状态码)
5XX 的响应结果表明服务器本身发生错误. (1)500 Internal Server Error
该状态码表明服务器端在执行请求时发生了错误。也有可能是 Web 应用存在的 bug 或某些临时的故障。
(2)502 Bad Gateway
该状态码表明扮演网关或代理角色的服务器,从上游服务器中接收到的响应是无效的。注意,502 错误通常不是客户端能够修复的,而是需要由途经的 Web 服务器或者代理服务器对其进行修复。以下情况会出现502:
- 502.1 - CGI (通用网关接口)应用程序超时。
- 502.2 - CGI (通用网关接口)应用程序出错。
(3)503 Service Unavailable
该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
使用场景:
- 服务器停机维护时,主动用503响应请求;
- nginx 设置限速,超过限速,会返回503。
(4)504 Gateway Timeout
该状态码表示网关或者代理的服务器无法在规定的时间内获得想要的响应。他是HTTP 1.1中新加入的。
使用场景:代码执行时间超时,或者发生了死循环。
总结:
(1)2XX 成功
- 200 OK,表示从客户端发来的请求在服务器端被正确处理
- 204 No content,表示请求成功,但响应报文不含实体的主体部分
- 205 Reset Content,表示请求成功,但响应报文不含实体的主体部分,但是与 204 响应不同在于要求请求方重置内容
- 206 Partial Content,进行范围请求
(2)3XX 重定向
- 301 moved permanently,永久性重定向,表示资源已被分配了新的 URL
- 302 found,临时性重定向,表示资源临时被分配了新的 URL
- 303 see other,表示资源存在着另一个 URL,应使用 GET 方法获取资源
- 304 not modified,表示服务器允许访问资源,但因发生请求未满足条件的情况
- 307 temporary redirect,临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求
(3)4XX 客户端错误
- 400 bad request,请求报文存在语法错误
- 401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息
- 403 forbidden,表示对请求资源的访问被服务器拒绝
- 404 not found,表示在服务器上没有找到请求的资源
(4)5XX 服务器错误
-
500 internal sever error,表示服务器端在执行请求时发生了错误
-
501 Not Implemented,表示服务器不支持当前请求所需要的某个功能
-
503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求
补充: 同样是重定向,307,303,302的区别?
302是http1.0的协议状态码,在http1.1版本的时候为了细化302状态码⼜出来了两个303和307。 303明确表示客户端应当采⽤get⽅法获取资源,他会把POST请求变为GET请求进⾏重定向。 307会遵照浏览器标准,不会从post变为get。
答;
- TCP是可靠的、面向连接的协议,提供了可靠的数据传输机制,保证了数据的完整性和有序性,TCP使用三次握手建立连接,通过流控制、拥塞控制、错误检测和重传机制来确保数据的可靠传输,TCP适合传输可靠性比较高的场景,比如电子邮件、网页游览
- UDP是一种不可靠、无连接的协议,UDP不提供数据的可靠性保证,不进行数据重传和流量控制。适合实时性比较高的场景,比如视频通话,直播等。
答:
- 1.**第一次握手:**客户端向服务端发送连接请求报文段。
- 2.第二次握手: 服务端收到连接请求报文段后,如果同意连接,则会发送一个应答。
- 3.第三次握手: 当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。此时连接建立成功。
为什么不是两次?
答: 为了确认双方的接收能力和发送能力都正常。如果使用两次的话,
-
- 假设主机A(客户端)向主机B(服务器)发送一个连接请求报文段,但在网络传输中延迟了很长时间。
-
- 主机A第二次重新发起连接请求,这次能够到达主机B。
-
- 主机B接收到连接请求报文段后向主机A发送确认报文段,建立连接。
-
- 但是延迟的第一个连接请求报文段在之前到达了主机B,并继续建立了连接。
答:
- 第一次挥手: 若客户端认为数据发送完成,则它需要向服务端发送连接释放请求。
- 第二次挥手:服务端收到连接释放请求后,此时表明客户端到服务端的连接已经释放,不再接收客户端发的数据了。但是因为 TCP 连接是双向的,所以服务端仍旧可以发送数据给客户端。
- 第三次挥手:服务端如果此时还有没发完的数据会继续发送,完毕后会向客户端发送连接释放请求
- 第四次挥手: 客户端收到释放请求后,向服务端发送确认应答,此时客户端进入 TIME-WAIT 状态。
websocket是浏览器和服务器进行全双工通讯的技术,浏览器和服务器只需要一次握手,两者就直接可以创建持久性的连接,并进行双向数据传输。
最大的特点: 服务器可以向客户端主动推送消息,客户端也可以主动向服务器推送消息。性能要求很大。
答:
- 1.短轮询的基本思路: 浏览器每隔一段时间向浏览器发送http请求,服务器端在收到请求后,不论是否有数据更新,都直接进行响应。
- 2.长轮询的基本思路: 首先客户端向服务器发起请求,当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否更新,如果有更新,则进行响应,如果没有更新,则达到一定时间的限制以后,才返回。
- 3.使用流信息向客户端推送信息: http协议无法做到服务器主动推送消息,但是有一种变动的方法,就是服务器向客户端表明,接下来发的是流信息,也就是说,发送的不是一次性的数据包,而是一个数据流,这时,客户端就不会关闭连接,会一直等着服务器发送新的数据流。
- 4.WebSocket的基本思想: WebSocket是一个全双工的协议,也就是通信双方是平等的,
可以相互发送消息。适合于聊天室这种场景。 对于这四种即时通信协议,从性能的角度来看: WebSocket > 长连接(SEE) > 长轮询 > 短轮询 但是,我们如果考虑浏览器的兼容性问题,顺序就恰恰相反了: 短轮询 > 长轮询 > 长连接(SEE) > WebSocket 所以,还是要根据具体的使用场景来判断使用哪种方式。
答:
1.为什么会产生跨域?
跨域问题是由于浏览器的同源策略(same origin policy)所导致的,同源策略是一种安全策略,用来限制一个源的脚本如何与另一个源的资源进行交互。以下情况会导致跨域问题:
- 1.协议不同:如http和https。
- 2.域名不同:如www.example.com 和 api.example.com。
- 3.端口不同:如 www.example.com:8080 和 www.example.com:9090。 任何两个页面的协议、域名、端口号中只要有一个不同,就会产生跨域问题。
2.跨域解决的方法有哪些?
答:
(1)前后端协商jsonp
用解决跨域的原理也比较简单,因为js中script标签的src是不受同源策略的限制的,也就是说可以跨域请求数据。但是这个script标签的src请求的数据有个缺点,就是只能发送get请求。使用jsonp时,对于后端来说它返回的是一个函数对,而不是一个字符串也不是一个json,所以说在前端时,要提前把这个函数给定义好,等返回响应体注入到函数的参数里面。此时就实现了跨域请求。
前端实例代码:
后端实例代码,以nodejs为例:
(2)使用代理服务器转发
通过设计一个代理服务器将前端的请求转发到目标服务器上,绕过浏览器的同源策略限制,从而解决跨域问题。 一般项目中,使用工具如来快速设置代理服务器。
配置示例(webpack.config.js):
配置完成后,当前端代码中发送的请求路径匹配到了路径时,就会将请求通过代理服务器转发到目标服务器上,避免了浏览器的跨域限制。目标服务器处理完请求后,将响应返回给代理服务器,再由代理服务器将响应返回给前端。
(3)后端设置请求头
在解决跨域问题时,后端可以通过设置响应头来允许特定的跨域请求。通过在响应中添加特定的头部信息,告知浏览器该请求是可以被跨域访问的。
后端代码示例(以Node.js和Express框架为例):
(4)通过nginx反向代理
在生产环境中,常通过Nginx作为反向代理服务器来解决跨域问题,通过在Nginx的配置中添加相应的反向代理规则,可以实现跨域请求的转发。
配置示例(nginx.conf):
答: http/1.0:如需要发送多个请求必须创建多个 TCP 连接,并且浏览器对于单域名请求有数量限制(一般6个),其连接无法被复用
http/1.1:引入流水线(Pipelining)技术,但先天 FIFO(先进先出)机制导致当前请求的执行依赖于上一个请求执行的完成,容易引起报头阻塞,并没有从根本上解决问题
http/2:重新定义底层 http 语义映射,允许同一个连接上使用请求和响应双向数据流。同一域名只需占用一个 TCP 连接,通过数据流(Stream)以帧为基本协议单位,从根本上解决了问题,避免了因频繁创建连接产生的延迟,减少了内存消耗,提升了使用性能。
答:
- 客户端使用https的url访问web服务器,要求与服务器建立ssl连接。
- web服务器收到客户端请求后,会将网站的证书(包含公钥)传送一份给客户端。
- 客户端收到网站证书后会检查证书的颁发机构以及过期时间,如果没有问题就随机产生一个秘钥。
- 客户端利用公钥将会话秘钥加密,并传送给服务端,服务端利用自己的私钥解密出会话秘钥。
- 之后服务器与客户端使用秘钥加密传输。
答: 1.验证证书的办法机构是否受客户端信任。 2.通过CRL或OCSP的方式校验证书是否被吊销。 3.对比系统时间,校验证书是否在有效期内。 4.通过校验对方是否存在证书的私钥,判断证书的网站域名是否与证书颁发的域名一致。
答:
传统身份验证的方法
HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用。这里我们把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过下回这个客户端再发送请求时候,还得再验证一下。
解决的方法就是,当用户请求登录的时候,如果没有问题,我们在服务端生成一条记录,这个记录里可以说明一下登录的用户是谁,然后把这条记录的 ID 号发送给客户端,客户端收到以后把这个 ID 号存储在 Cookie 里,下次这个用户再向服务端发送请求的时候,可以带着这个 Cookie ,这样服务端会验证一个这个 Cookie 里的信息,看看能不能在服务端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。
上面说的就是 Session,我们需要在服务端存储为登录的用户生成的 Session ,这些 Session 可能会存储在内存,磁盘,或者数据库里。我们可能需要在服务端定期的去清理过期的 Session 。
基于 Token 的身份验证方法
使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
- 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
- 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
- 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
答:React的事件机制是W3C的事件模型以及现代浏览器的一种封装,这个事件机制的主要目标是让你的应用程序在不同的浏览器中提供一致的行为。比如React并不是将click事件绑定到div的真实DOM上,而是在document处监听了所有事件,当事件发生并且冒泡到document处的时候,Re将事件内容封装并交由真正的处理函数运行,这样的方式不仅仅减少了内存的消耗,还能在组件挂载和销毁时统一订阅和移除事件。
注意: 冒泡到document上的事件不是原生游览器事件,而是React自己实现的合成事件(SyntheticEvent),因此我们如果不想要事件冒泡的话,需要调用的是event.preventDefault,而不是event.stopPropagation。
答:
- 事件命名上,在HTML中,事件名称全部小写,如'onclick',而React中,事件名称是驼峰命名的,如'onClick'
- 事件处理函数语法上,在HTML中,采用的是字符串,比如Click me,而React中传入的是一个函数Click me
- react事件不能采用return false的方式来阻止浏览器的默认行为,必须使用event.preventDefault()
- react使用一种称为事件委托的技术来处理事件,所有的事件处理函数都会被附加到document的根节点,当事件触发时,React利用事件冒泡到根节点的特性,在调用相应事件处理函数,而普通HTML直接在目标元素上处理函数。
答:React基于虚拟DOM实现了一个合成事件层(SyntheticEvent层),定义的事件处理器会接受到一个合成事件对象的实例,且与原生浏览器事件拥有同样的接口,支持冒泡机制,所有的事件都自动绑定在最外层上。 在React底层,主要对合成事件做了两件事,
事件委派: React会把所有的事件绑定到结构的最外层,使用统一的事件监听器。
自动绑定: React组件中,每个方法的上下文都会自动指向该组件的实例,即自动绑定this魏当前组件。
答:在React v15在渲染时,会递归比对虚拟DOM树,找出需要变动的节点,然后同步更新他们,在这个过程期间,React会占据浏览器资源,这会导致用户触发的事件得不到响应,并会导致掉帧,导致用户感觉到卡顿。 React-Fiber通过引入一种新的可中断的和继续的渲染流程,将渲染任务分割成一个个任务单元,每个任务单元具有优先级,并且可以随时中断和恢复,解决了旧的协调机制中遇到的性能问题,提高React应用的流程度和性能,并且支持更多的交互,使得React能够更好地应对大型应用。
答: PureComponent表示一个纯组件,可以用来优化React程序,减少Render函数执行次数。
- PureComponent继承自Component,继承了Component的所有功能和生命周期方法。
- PureComponent中的shoudComponentUpdate()进行的是浅比较,也就是说,如果是引用类型的props,只会比较是不是同一个地址,而不是比较这个地址里面的数据是否变化。如果属性没有发生实质性变化,render不会执行渲染。
答:
- Component(组件):Component是用于定义和描述React组件的类或函数,组件可以包含其他组件,组成复杂的UI界面,同时拥有自己的属性props和状态state。
- Element(元素):Element可以看做React组件的实例化描述,它是无状态的,不具备生命周期和方法。Element可以通过React的渲染方式转换为真实的DOM元素,渲染到页面中。
- Instance(实例):instance实例是你所写的组件类中使用关键字this所指向的东西。
答:React高阶组件(HOC)是一种用于重用组件逻辑的高级技术,HOC自身不是React组件,而是将现有组件作为输入,并返回一个新的包装组件作为输出。 HOC的目的是用于在组件之间共享功能和逻辑,通过将通用的逻辑从组件中抽象出来,并将其封装在HOC中,可以避免在多个组件中重复编写相同的代码,减少代码冗余,提高代码复用性。
HOC和普通组件的区别在于,HOC不是React组件本身,而是一个函数,接受一个组件作为参数,并返回一个新的组件。HOC输出的组件与输入组件具有相同的props。
答:在React组件中,当父组件的props改变时,子组件也会重新渲染,componentWillReceiveProps在子组件即将重新渲染之前被调用,它的作用是比较新props和当前props之间的差异,并根据这些差异做出相应的响应。 常用来更新组件内部的状态,可以通过比较新的props和当前props来确定是否更新组件的状态,也可以用来执行一些副作用操作,例如发送网络请求、计时器操作等。
答: 两种情况下会触发React重新渲染:
- 第一种是setState()方法被调用,setState()内部是通过Object.is(first, second)来原数据和新数据是否一致,若不一致则触发render,同时传入null,也是不会触发render的。
- 第二种情况是,组件的props发生变化,组件就会进行重新渲染。
- 第三种情况是,父组件发生重新渲染,子组件也将会重新渲染。
重新渲染render会做些什么?
- 1.调用组件的render方法,用于生成组件的虚拟DOM
- 2.虚拟DOM的diff算法:React使用虚拟DOM来提高渲染效率,通过比较前后两次渲染生成的虚拟DOM树,找出需要更新的部分。
- 3.根据虚拟DOM的变化,更新实际的DOM:React将虚拟DOM与实际DOM进行比较,找出需要更新的部分,并将更新应用到实际的DOM上。
- 4.更新子组件,如果该组件拥有子组件,在父组件的重新渲染过程中,也会依次触发子组件的重新渲染过程。
答:在子组件中,可以通过重写shouldComponentUpdate来控制组件是否重新渲染。
答:React中props和state的改变,都会触发页面的重新渲染事件,当React将要渲染组件时,会执行shouldComponentUpdate方法来看它是否返回true(默认返回true),所以需要重写shouldComponentUpdate方法让他根据情况,返回true或false,来告诉react什么时候需要重新渲染,什么时候跳过重新渲染。
答:三种方式:
- 无状态函数式组件:组件不会被实例化,整体渲染性能好,不能访问this对象,不能访问生命周期的方法。
- ES5原生方法React.createClass定义的组件
- ES6形式的类组件,通过extends React.Component定义的组件,组件需要被实例化,可以访问组件的生命周期方法,拥有this。
答:
有状态组件:
特点:可以使用react的生命周期,可以使用this,内部使用state,维护自身状态的变化,有状态组件根据外部传入的props和自身的state进行渲染。有状态更新时,就会重新渲染。性能较差。
无状态组件:
特点:可以完全避免使用this关键字,组件内部不维护state,只根据外部组件传入的props进行渲染的组件,当props改变时,组件重新渲染。有更高的性能,当不需要使用生命周期钩子时,应该首先使用无状态函数组件。
答:在React中,组件返回的元素只能有一个根元素,为了不添加多余的DOM节点,我们可以使用Fragment标签来包裹所有的元素,Fragment标签不会渲染出任何元素。
答:四种方式: 类组件的话有:
- 字符串格式:React16之前使用的比较多,
- 函数格式:ref对应的是一个方法,该方法有个参数,也就是对应的节点实例,
- React.createRef()方法
函数式组件:
- 使用useRef()
答:不可以,render阶段DOM和Ref还没有形成,ref是在render后componentDidMount阶段之前更新的。 比如:
答:
- 使用shouldComponentUpdate()方法: 可以在shouldComponentUpdate里面来决定组件是否需要重新渲染,如果不希望组件重新渲染,返回false即可。
- 使用PureComponent纯组件: 纯组件内部重写了shouldComponentUpdate方法,对新旧prop和state进行浅比较,如果没有变动,则不更新。
- 使用高阶组件React.memo方法:用来缓存组件的渲染,避免不必要的更新。只能用于函数式组件。默认情况下其只会对复杂对象做浅层对比。如果props变动,才会去重新渲染。 例子:
答:
- 简单来说就是,当你不想在组件树中通过逐层传递props或state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。
- Context API的使用基于生产者消费者模式,生产者一方,通过组件静态属性childContextTypes声明,然后通过实例方法getChildContext()创建Context对象,消费者一方,通过组件静态属性contextTypes申请用到的context属性,通过实例context访问context属性。
答:
相同点:受控组件和非受控组件都是指表单元素(如input,select和textarea)的不同使用方式。
不同点:
- 受控组件通过props接受初始值,并在state中保存它们,然后通过onChange的方式去更新组件状态,并根据状态的变化重新去渲染组件。
- 而非受控组件,表单元素的值由DOM维护和管理,通过ref的方式去获取表单的值。 案例:
答:refs提供了一种访问React组件实例或DOM元素的方法。通过refs,可以在React组件中直接访问和操作DOM元素,或者访问组件实例的方法和属性。
- 对于获取类组件的ref的话,我们可以使用React.createRef(),然后绑定在类组件上即可。
- 由于函数式组件没有ref,只能通过闭包的方式,在函数组件内部获取并保存ref。
答: 构造函数不是必须的。如果没有定义构造函数,React会默认生成一个空的构造函数,即。 构造函数主要有两个目的:
- 通过将对象分配给this.state来初始化本地状态。
- 将事件处理程序方法绑定在实例上
答:React中的setState是用于更新组件状态的方法,调用setState()会触发组件的重新渲染。
调用原理如下:
- 1.React会将setState()的新状态合并到组件的当前状态中。
- 2.React会检测到状态的变化,并通知组件需要重新渲染。
- 3.最后组件会重新调用render()方法来更新DOM,并根据新的状态进行渲染。
答:setState()本身是一种同步的方法,但是一旦走了react内部的合并逻辑,放入了updateQueue队列中就变成了异步了。
异步情况: 由React控制的事件处理函数,以及生命周期函数调用setState时表现为异步。 大部分开发中用到都是React封装的事件,比如onChange、onClick、onTouchMove等,这些事件处理函数中setState都是异步处理。
同步情况: React控制之外的事件中调用setState是同步更新的。 还有就是原生js绑定的事件,setTimeout/setInterval,ajax,promise.then等setState也是同步的。
原理: 在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates判断是直接更新 this.state 还是放到一个updateQueue中延时更新。 而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state。 但是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改为 true。 而当 React 在调用事件处理函数之前就会先调用这个 batchedUpdates将isBatchingUpdates修改为true。 这样由 React 控制的事件处理过程 setState 不会同步更新 this.state,而是异步的。
答:在React控制的事件处理函数中,调用setState时,组件的state并不会立即改变,setState只是把要修改的state放入一个队列,React出于性能的原因,会将React事件处理程序会将多次setSate的状态合并一次状态进行修改,最终只产生一次组件及其子组件的重新渲染。
对于相同属性的设置,React只会为其保留最后一次的更新。
答: setState的第二个参数是一个可选的回调函数,这个函数会在组件渲染后执行,等价于在componentDidMount生命周期内执行。
答: 区别:
- this.state主要是用来初始化使用的,如果在初始化以后,再使用this.state会覆盖掉之前的state,相当于赋值。
- this.setState()相当于Object.assign()方法,只替换掉了需要更新的值。并且更新完以后,会触发render。
答:
- props是父组件传递给子组件的,类似于函数的形参,而state是在组件内被组件自己管理的,类似于一个函数内声明的变量。
- props是只读,不可修改的,而state可以修改的。
答:
纯函数的特点:
- 给定相同的输入,总是返回相同的输出。
- 过程没有副作用
- 不依赖外部的状态。
一些常见的副作用操作包括:
- 数据获取:从服务器、数据库或本地存储中获取数据。
- DOM操作:直接操作dom元素,例如修改元素的样式、添加/删除元素等。
- 事件监听/订阅:在组件内监听或订阅事件,例如窗口大小改变、滚动事件等。
- 定时器:设置计时器来执行某些操作。
this.props就是采用了纯函数的思想。props被设计为只读的,是为了维护组件的可预测性和稳定性,以及提高组件的可维护性。如果props是可变的,即子组件可以修改props,会导致组件之间的数据流变的混乱,难以维护。
答:
- 1.使用componentDidUpdate,检查前后props是否发生变化,并相应更新组件。以通过比较和来判断是否需要进行更新操作。
- 2.使用useEffect()钩子函数,将props作为依赖源
- 3.使用shoudComponentUpdate()。在函数中比较前后props,如果发生变化则返回true,否则返回false.
- 4.使用React.mome高阶组件(函数式组件):使用React.memo()可以将组件包裹起来,使得组件只在props发生变化时才重新渲染。会对props进行浅比较,如果发现props发生变化,则重新渲染组件,否则使用缓存的结果。
- 5.使用getDerivedStateFromProps
React16之前:
初始化阶段: 发生在constructor中的内容,在constructor中进行state、props的初始化,在这个阶段修改state,不会执行更新阶段的生命周期,可以直接对state赋值。
挂载阶段: componentWillMount: 发生在render函数之前,还没有挂载Dom render componentDidMount:发生在render函数之后,已经挂载Dom
更新阶段: 更新分别是由state更新引起和props更新引起。 props更新时:
- 1.componentWillReceiveProps(nextProps, nextState) 这个生命周期主要为我们提供对props发生改变的监听,如果你需要在props发生改变后,相应改变组件的一些state.在这个方法中改变 state 不会二次渲染,而是直接合并 state。
- 2.shouldComponentUpdate(nextProps, nextState) 这个生命周期需要返回一个Boolean类型的值,判断是否需要更新渲染组件,优化react应用的主要手段之一,当返回false就不会向下执行生命周期了,在这个阶段不可以setState(),会导致循环引用。
- 3.componentWillUpdate(nextProps, nextState) 这个生命周期主要给我们一个世纪能够处理一些在Dom发生更新之前的事情,如获得Dom更新前某些元素的坐标,大小等。
到目前为止,this.props和this.state都未发生更新。
-
4.render
-
5.componentDidUpdate(prevProps, prevState)
在此时已经完成渲染,Dom已经发生变化,state已经发生更新,prevProps、prevState均为上一个状态的值。
state更新时(具体同上) 1.shouldComponentUpdate 2.componentWillUpdate 3.render 4.componetDidupdate
卸载阶段: 在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount 中创建的订阅等。componentWillUnmount 中不应调用 setState,因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。
React16之后:
参考链接:zhuanlan.zhihu.com/p/95865701
答: 每次setState都会会触发render, 可以通过shouldComponent去返回false或者使用pureComponent组件进行浅比较。从而去优化性能,不触发render,但是真实DOM树不会变,dom diff会比较前后未发生变化,从而不渲染。
答: 如果父组件重新渲染的话,不管传入的props有没有变化,都会引起子组件的重新渲染。
解决办法: 使用shouldComponentUpdate函数,可以比较this.props和nextProps,this.state和nextState的值是否发生变化,来确认返回true还是false,当返回false的时候,后续更新过程停止。后续的render和componentDidMount也不会调用。
答:主要由于这三个生命周期函数可能会被误用,例如在componentWillReceiveProps函数中更新state,这可能导致死循环等问题。
- 父组件向子组件通信:父组件通过props向子组件传递需要的信息。
- 子组件向父组件通信: props+回调的方式
答:父组件向子组件的子组件通信,向更深层的子组件通信:
- 1.使用props,利用中间组件层层传递。
- 2.使用context,context相当于一个大容器,可以把要通信的内容放在这个容器中
答:对于没有包含关系的组件。
- 可以使用发布订阅模式进行通信
- 可以使用redux进行全局状态管理
答:
- 使用context进行传递
- 使用redux等状态库
答:Redux提供了一个叫store的统一仓库,组件通过dispatch将state直接传入store,不用通过其他组件,并且组件通过subscribe从store获取state的改变。使用redux,所有的组件都可以从store中获取到所需的state,也能从store获取到state的改变。这比组件之间互相传递数据清晰的多。
解决了什么问题?
由于在react中组件间通信的数据流是单向的,顶层组件通过props属性向下层传递数据,而下层组件不能向上层组件传递数据,兄弟组件之间同样不能,Redex使用store仓库统一管理数据,减少了数据管理难度。
答:
- 首先用户(通过view)发出Action,发出方式就用dispatch方法
- 然后Store自动调用Reducer,并且传入两个参数,当前State和收到的Action,Reducer会返回新的State
- state一旦有变化,Store就会调用监听函数,来更新View
答:区别如下:
- 类组件需要继承class,函数组件不需要。
- 类组件可以访问生命周期方法,函数组件不能;
- 类组件可以获取到实例化后的this,而函数组件不可以
- 类组件中可以定义并维护state(状态),函数组件不需要维护state
答:
- 1.使函数式组件具有状态管理的能力:在React-Hook出现之前,函数式组件没有内置的状态管理机制,只能通过类组件来管理状态,而hooks之后,函数式组件可以使用useState等钩子函数来管理内部的状态。
- 2.降低组件之间逻辑复用的难度:Hook可以将组件的逻辑提取为可复用的自定义钩子函数,从而使不同组件之间逻辑复用更加方便。
- 3.更加简洁处理副作用:以前在类组件中需要在复杂的生命周期函数中处理副作用,hook只需要使用useEffect钩子函数处理即可。
总结:react-hook的出现,补全了函数式组件之前没有生命周期,不能维护自己状态的缺陷。
例子:
答: 如果用对象的话
总结:useState返回的是array而不是object的原因就是为了降低使用的复杂度,返回数组的话可以直接根据顺序解构,而返回对象的话要想使用多次就需要定义别名了。
答:
- 组件之间难以复用状态逻辑,过去常见的解决方案是高阶组件、状态管理框架redux等。
- 复杂的组件变得难以理解,生命周期函数与业务逻辑耦合太深,导致关联部分难以拆分
- this的使用太复杂,使用难度高。
答:主要有两条:
- 不要在循环、条件或者嵌套函数中调用hook
- 在React的函数组件中调用Hook
主要原因:
参考链接:blog.csdn.net/weixin_4348…
在React的源码中,hook的设计是基于类似于数组的链表实现的,在调用时按照顺序加入数组中,如果使用循环、条件或嵌套函数很有可能导致数据取值错误,执行错误的hook。
答:
共同点: 两者使用方式是一样的,都用来处理副作用,这些副作用包括改变DOM、操作定时器等。 区别:
- useEffect是按照顺序执行代码的,是在改变屏幕像素之后执行,也就是改变了真实dom以后执行,这时候在useEffect中如果再改变屏幕内容的话,可能会产生闪屏。
- useLayoutEffect是在改变屏幕真实dom之前就执行了,如果此时在useLayoutEffect改变屏幕内容,不会产生闪烁。
答: 我们打印出来虚拟DOM以后,实际上可以发现虚拟DOM是一个JS对象,对象内有type、props、children等属性。type是表示标签类型,props是标签内部的属性,children为节点中子节点的内容。
- 1.可以减少DOM操作:
- a.虚拟DOM可以将多次操作合并为一次操作,比如一个页面如果有500次变化,没有虚拟DOM的就会渲染500次,而虚拟DOM只需要渲染一次,从这点上来看,页面越复杂,虚拟DOM的优势越大
- b.虚拟DOM可以借助DOM diff算法,对比新旧虚拟DOM树的差异,只将差异更新到DOM中,以最小化成本更新。
- 2.支持跨平台:虚拟DOM本质上是一个对象,可以在兼容不同平台,抹平平台之间的差异,实现支持跨平台使用。
为什么要用虚拟DOM?
- 保证性能下限,在不进行手动优化的情况下,提供过得去的性能
- 虚拟DOM可以提供跨平台使用。
答:
原理: diff算法就是通过对比新旧两株虚拟DOM树的变更差异,将更新补丁作用于真实DOM,以最小成本完成视图更新。
具体流程如下: 我们需要把每一种节点类型抽象成对象,每一种节点类型有自己的属性,也就是prop,每次进行diff的时候,react会先比较该节点类型,假如节点类型不一样,那么react会直接删除该节点,然后直接创建新的节点插入到其中,假如节点类型一样,那么会比较prop是否有更新,假如有prop不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下,直到没有子节点。
答:不一定,虚拟DOM渲染是先将真实DOM转换为JS对象,然后通过DOM diff算法处理完毕以后,再去转换为真实DOM,也就是说他始终会创建JS对象。对于首次加载页面的情况,需要先创建所有的虚拟DOM,然后再把虚拟DOM转换为真实DOM,这样会多一层计算,不会比直接渲染真实DOM更快。
答:因为浏览器无法识别react,也不会识别jsx,甚至连ES6一些语法都无法识别,要想让浏览器识别,就需要借助babel,通过babel对jsx进行转换为对应的js对象,才能让浏览器识别,此时就会有个依据去判断是原生DOM标签还是React组件,而这个依据就是标签的首字母。如果首字母是小写,则是原生标签,反之,则是React组件。
答:
- 1.虚拟DOM树节点比较时,只比较同一层的节点,不跨层进行比较。
- 2.若发现虚拟DOM树当前比较的节点类型不一样,直接判断为脏组件,从而直接删除该节点以及它的所有子节点。
- 3.同一层级的一组子节点,可以通过设置唯一的key来进行区分。
补充: React key是干嘛用的?为什么要加?key主要解决哪一类问题
答:为了方便react内部进行优化,我们必须给每一个react节点添加key,这个key是prop在设计值之初不是给开发者用的,而是给react用的,大概的作用就是给每一个react节点添加一个身份标识,方便react进行识别,在重渲染过程中,如果key一样,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新,如果key不一样,则react先销毁该组件,然后重新创建该组件。
补充: 能使用数组的index作为虚拟DOM的节点的key吗? 答:不能,key是用于渲染对象的排序,所以必须是用唯一标记的值,假如使用数组的index的话,如果删除数组中间任意一个数,后面的数补上来,导致index发生变化,无法找到对应的虚拟DOM节点。可以使用UUID作为唯一的key。
答: 相似之处:
- 都是用了虚拟DOM,提高性能
- 都有props的概念,允许组件间的数据传递。 区别:
- Vue默认支持数据双向绑定的,React一直都是单向的数据流。
- React每次组件更新,全部子组件会重新渲染。而Vue不会渲染整个组件树。
答:
- 使用Redux-persist: redux-persist提供了一个简单的方法来将Redux存储状态持久化到本地存储(如localStorage或AsyncStorage
- 使用webpack构建项目时,新建一个data.js文件,专门用来做数据存储。
- 使用sessionStorage、localStorage:每次在进入页面,调用componenntWillUnMount的时候,将数据存储到sessionStorage中。
答:JSX实际上是React.createElement(component, props, ...children)的语法糖。 使用JSX可以完成的任何事情都可以通过纯JavaScript完成。
JSX写法:
原生写法:
答: 高阶组件(Higher-Order Component, HOC)是React中的一种模式,用于增加组件的功能和复用性,他本质是一个函数,接受一个组件作为参数,并返回一个新的增强后的组件。
作用:
- 1.属性代理:高阶组件可以通过包裹组件,将额外的属性传递给被包裹的组件。
- 2.渲染劫持:高阶组件可以通过修改被包裹组件的渲染流程,提供额外的渲染内容。
- 3.State增强:高阶组件可以通过自身去维护一些状态,将其传递给被包裹的组件。
答:key的作用就是更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的。使用key能更快的查找到旧的结点,否则只能通过遍历去寻找。
解析:
1、第一次和第二次都是在 react 自身生命周期内,触发时 isBatchingUpdates 为 true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,所以打印时获取的都是更新前的状态 0。
2、两次 setState 时,获取到 this.state.val 都是 0,所以执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次。设置完成后 state.val 值为 1。
3、setTimeout 中的代码,触发时 isBatchingUpdates 为 false,所以能够直接进行更新,所以连着输出 2,3。
答:XSS攻击指的是跨脚本攻击,是一种代码注入攻击,攻击者通过在网站注入恶意脚本,使得恶意脚本在浏览器上运行,从而盗取用户信息如cookie等
攻击者可以通过这种攻击方式进行以下操作:
- 1.获取页面数据,如DOM、cookie、localstorage等
- 2.进行DOS攻击,发送合理请求,占用服务器资源。造成其他用户无法访问服务器。
- 3.流量劫持,将链接指向某网站。
如何防御XSS攻击?
- 1.使用CSP(内容安全策略),本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击。
- 2.对一些敏感信息进行保护,比如cookie使用http-only,使脚本无法获取。
答:CSRF攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后通过该网站向被攻击网站发送跨站请求,如果用户在攻击网站保存了登陆信息,那攻击者就可以利用这个登陆状态,绕过后台用户验证,冒充用户向服务器执行一些操作。
CSRF攻击的本质是利用cookie会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。
攻击过程:
- 1.攻击者在一个网站上创建一个恶意网页,并在其中包含一些恶意代码。
- 2.受害者在已认证的网站上登陆,获取一个有效的会话凭证(例如:cookie)
- 3.受害者访问攻击者创建的恶意网页,恶意代码会自动向目标网站发送一些请求,这些请求利用了受害者的cookie。
- 4.目标网站无法区别是受害者自己发送的请求还是恶意请求,所以会执行这些请求,并对攻击者指令进行响应。
- 5.攻击者通过这种方式可以实现一些非法操作,如发送恶意邮件、购买商品等。
防御方式:
-
- 使用CSRF令牌,在每个表单请求中添加一个独特的令牌,服务器在接收到请求时验证该令牌的有效性。
- 2.使用SameSite属性,通过设置cookie的SameSite属性为Strict,限制cookie只能在相同的站点上进行交互,从而防止跨站点请求。
- 3.敏感操作,使用验证码来进行验证。
答:
cookie:登陆后后端生成一个sessionid放在cookie中返回给客户端,并且服务端一直记录着这个sessionid,客户端以后每次请求都会带上这个sessionid,服务端通过这个sessionid来验证身份之类的操作。所以别人拿到了cookie拿到了sessionid后,就可以完全替代你。
token:登陆后后端不返回一个token给客户端,客户端将这个token存储起来,然后每次客户端请求都需要开发者手动将token放在header中带过去,服务端每次只需要对这个token进行验证就能使用token中的信息来进行下一步操作了。
xss:用户通过各种方式将恶意代码注入到其他用户的页面中。就可以通过脚本获取信息,发起请求之类的操作。
csrf:跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。csrf并不能够拿到用户的任何信息,它只是欺骗用户浏览器,让其以用户的名义进行操作。
csrf例子:假如一家银行用以运行转账操作的URL地址如下: www.examplebank.com/withdraw?ac…
那么,一个恶意攻击者可以在另一个网站上放置如下代码:
如果有账户名为Alice的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失1000资金。
上面的两种攻击方式,如果被xss攻击了,不管是token还是cookie,都能被拿到,所以对于xss攻击来说,cookie和token没有什么区别。但是对于csrf来说就有区别了。
以上面的csrf攻击为例:
- cookie:用户点击了链接,cookie未失效,导致发起请求后后端以为是用户正常操作,于是进行扣款操作。
- token:用户点击链接,由于浏览器不会自动带上token,所以即使发了请求,后端的token验证不会通过,所以不会进行扣款操作。
这些进程的功能:
-
浏览器进程:主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
-
渲染进程:核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
-
GPU 进程:其实, GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
-
网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
-
插件进程:主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。
(1)GUI渲染线程
负责渲染浏览器页面,解析HTML、CSS,构建DOM树、构建CSSOM树、构建渲染树和绘制页面;当界面需要重绘或由于某种操作引发回流时,该线程就会执行。
注意:GUI渲染线程和JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
(2)JS引擎线程
JS引擎线程也称为JS内核,负责处理Javascript脚本程序,解析Javascript脚本,运行代码;JS引擎线程一直等待着任务队列中任务的到来,然后加以处理,一个Tab页中无论什么时候都只有一个JS引擎线程在运行JS程序;
注意:GUI渲染线程与JS引擎线程的互斥关系,所以如果JS执行的时间过长,会造成页面的渲染不连贯,导致页面渲染加载阻塞。
(3)事件触发线程
事件触发线程属于浏览器而不是JS引擎,用来控制事件循环;当JS引擎执行代码块如setTimeOut时(也可是来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件触发线程中;当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理;
注意:由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行);
(4)定时器触发进程
定时器触发进程即setInterval与setTimeout所在线程;浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确性;因此使用单独线程来计时并触发定时器,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行,所以定时器中的任务在设定的时间点不一定能够准时执行,定时器只是在指定时间点将任务添加到事件队列中;
注意:W3C在HTML标准中规定,定时器的定时时间不能小于4ms,如果是小于4ms,则默认为4ms。
(5)异步http请求线程
- XMLHttpRequest连接后通过浏览器新开一个线程请求;
- 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,等待JS引擎空闲后执行;
所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
系统中的资源可以分为两类:
- 可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺,CPU和主存均属于可剥夺性资源;
- 不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。
产生死锁的原因:
(1)竞争资源
- 产生死锁中的竞争资源之一指的是竞争不可剥夺资源(例如:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞)
- 产生死锁中的竞争资源另外一种资源指的是竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息等),通常消息通信顺序进行不当,则会产生死锁
(2)进程间推进顺序非法
若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁。例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁
产生死锁的必要条件:
- 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
- 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
- 环路等待条件:在发生死锁时,必然存在一个进程——资源的环形链。
预防死锁的方法:
-
资源一次性分配:一次性分配所有资源,这样就不会再有请求了(破坏请求条件)
-
只要有一个资源得不到分配,也不给这个进程分配其他的资源(破坏请保持条件)
-
可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
-
资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
实现多个标签页之间的通信,本质上都是通过中介者模式来实现的。因为标签页之间没有办法直接通信,因此我们可以找一个中介者,让标签页和中介者进行通信,然后让这个中介者来进行消息的转发。通信方法如下:
-
使用 websocket 协议,因为 websocket 协议可以实现服务器推送,所以服务器就可以用来当做这个中介者。标签页通过向服务器发送数据,然后由服务器向其他标签页推送转发。
-
使用 ShareWorker 的方式,shareWorker 会在页面存在的生命周期内创建一个唯一的线程,并且开启多个页面也只会使用同一个线程。这个时候共享线程就可以充当中介者的角色。标签页间通过共享一个线程,然后通过这个共享的线程来实现数据的交换。
-
使用 localStorage 的方式,我们可以在一个标签页对 localStorage 的变化事件进行监听,然后当另一个标签页修改数据的时候,我们就可以通过这个监听事件来获取到数据。这个时候 localStorage 对象就是充当的中介者的角色。
-
使用 postMessage 方法,如果我们能够获得对应标签页的引用,就可以使用postMessage 方法,进行通信。
答:web Worker是html5以后提出来的,由于js是单线程,当JS在页面中运行耗时任务的时候就会导致页面假死影响用户体验,web Worker的提出,可以解决这个问题,web Worker可以在浏览器的运行环境中,单独开启一个后台线程,去执行耗时任务,任务执行完毕以后,通过postMessage的方式,传递数据给主线程。web Worker线程中不能操作DOM元素。
-
点击刷新按钮或者按 F5: 浏览器直接对本地的缓存文件过期,但是会带上If-Modifed-Since,If-None-Match,这就意味着服务器会对文件检查新鲜度,返回结果可能是 304,也有可能是 200。
-
用户按 Ctrl+F5(强制刷新): 浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前从来没有请求过,返回结果是 200。
-
地址栏回车: 浏览器发起请求,按照正常流程,本地检查是否过期,然后服务器检查新鲜度,最后返回内容。
JavaScript 的加载、解析与执行会阻塞文档的解析,也就是说,在构建 DOM 时,HTML 解析器若遇到了 JavaScript,那么它会暂停文档的解析,将控制权移交给 JavaScript 引擎,等 JavaScript 引擎运行完毕,浏览器再从中断的地方恢复继续解析文档。也就是说,如果想要首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。
如果浏览器尚未完成 CSSOM 的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟 JavaScript 脚本执行和文档的解析,直至其完成 CSSOM 的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建 CSSOM,然后再执行 JavaScript,最后再继续文档的解析。
IndexedDB 具有以下特点:
-
键值对储存:IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
-
异步:IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
-
支持事务:IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
-
同源限制: IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
-
储存空间大:IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
-
支持二进制储存:IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
答:
- 1.是一种静态类型语言,提供了类型注解,在代码编译阶段就可以检查数据类型的错误。
- 2.是js的类型超集,支持es6语法,支持面向对象编程的概念,如类、接口、继承、泛型等。
答:增加了静态类型,可以在开发人员编写脚本时检测错误,使代码质量更好,更健壮。
答:
- const用于声明常量并阻止对变量本身的重新赋值,但是对于对象和数组等引用类型,其属性的值是可以被修改的。
- readonly用于声明只读的属性或元素,无论是基本类型,还是引用类型,或者类和接口的成员,在运行和编译时都不能修改。
答:
- 内置的:数字、字符串、布尔值、void(无效值)、空值null和未定义undefined
- 用户定义:枚举、类、接口、数组、元祖。
答:any类型是一种弱类型,它放宽了类型检查,而unknown类型是一种强类型,它需要进行类型检查才能进行操作,尽量避免使用any类型,而是优先使用unknown类型来增加类型安全性。
答:any是表示,我们不希望类型检查器对这些值进行检查,而直接让他们通过编译阶段的检查。
答:
- 1.使用typeof进行类型判断
- 2.对unknown类型使用类型断言。注意:断言错了时,语法能通过检测,但是运行时候就会报错。
答:
- 表示没有返回值的函数类型或变量类型。它通常用于表示函数没有返回任何值。在变量类型中,可以将其用作一种占位符,表示没有指定具体的类型。
- 表示永远不会发生的类型。它通常用于表示函数抛出异常、进入无限循环或永远不会返回的情况。如果一个函数永远不会返回(例如,总是抛出异常),那么该函数的返回类型应为 。
答:
相同点:
- 1.都可以描述一个对象或者函数
- 2.都允许拓展,都可以合并。
不同点:
- 1.type类型使用范围更广,如基本类型(原始值)、联合类型、元组。接口类型只能用来声明对象。
但是使用interface就不可以定义非对象类型(语法错误)
- 2.在声明对象时,interface可以多次声明,而type定义的别名,是不能重复的;
- 3.interface支持继承的,但是type不支持继承,只能用&合并
- 4.interface可以被类实现
答:
工具类型用于从一个类型中排除与另一个类型可赋值的部分。
的作用是忽略中的某些属性。
是将两个对象的属性合并。
用于计算一个新类型,该新类型是通过对另一个类型进行映射转换得到的。
的作用是取的属性,此属性同样也存在与。
是用的属性覆盖的相同属性。
:将指定类型 T 中的所有属性变为只读。
:从指定类型 T 中提取出部分属性。
:从类型 T 中排除掉 null 和 undefined 类型。
答:类型断言是一种方式,用于告诉编译器某个值的具体类型,它允许开发者手动指定变量或表达式的类型,以便在编译时进行类型检查。
两种方式:
- 1.尖括号语法
2.as语法
- 1.typeof:用于获取一个变量或表达式的类型,例如:type x返回x的类型。
- 2.keyof:用于获取一个类型的所有属性名组成的联合类型。例如keyof T返回类型T的所有属性名称的联合类型。
遇到 null 和 undefined 可以立即停止表达式的运行。
当左侧操作数为 null 或 undefined 时,其返回右侧的操作数,否则返回左侧的操作数。
x! 将从 x 值域中排除 null 和 undefined
在变量名后添加,可以断言排除undefined和null类型
分隔符不会改变数值字面量的值,使人更容易读懂数字 .e.g 1_101_324。
求幂
答:假设有一个导入语句import {a} from "moduleA"
- 1.首先,编译器会尝试定位需要导入的模块文件,通过绝对或者相对的路径查找方式。
- 2.如果上面的解析失败了,没有查找到对应的模块,编译器会尝试定位一个外部模块声明(.d.ts)
- 最后,如果编译器还是不能解析这个模块,则会抛出一个错误.
答: 可以合并,同名的interface会自动合并,同名的interface和class也会自动合并。
答:
- public:成员默认为public,被此限定符修饰的成员是可以被外部访问。
- private:被此限定符修饰的成员只可以被类的内部访问。
- protected:被此限定符修饰的成员是可以被类的内部以及类的子类访问。
- readonly:关键词将属性设置为只读。只读属性必须在声明时或构造函数里被初始化。
答: 泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候在指定类型的一种特性。
补充:泛型约束是什么?
答:在函数中使用泛型的时候,由于预先并不知道具体的类型,所以不能访问相应类型的方法。
上例中,泛型T不一定包含属性length,所以编译的时候会报错。 这时候,我们可以对泛型进行约束,只允许这个函数传入那些包含length属性的变量,这就是泛型约束。
们使用了 约束了泛型 必须符合接口 的形状,也就是必须包含 属性。
此时如果调用 的时候,传入的 不包含 ,那么在编译阶段就会报错了:
参考链接:juejin.cn/post/713820…
webpack是一个用于js应用程序的静态模块打包工具。主要作用如下:
- 1.模块打包:可以将不同模块的文件打包整合在一起,并且保证他们之间的引用正确,执行有序。
- 2.编译兼容。通过webpack的Loader机制,可以将代码转换成兼容性更强的版本的代码。
- 3.能力拓展:通过社区丰富的plugin可以实现多种强大的功能,比如代码分割、代码混淆、代码压缩、按需加载.....等等
- babel-loader:将es6转译es5
- image-webpack-loader: 加载并压缩图片资源
- css-loader/style-loader/less-loader:因为webpack默认的状态下只能识别js,不能识别css,所以就需要安装loader来帮助我们去解析css文件进行打包,style-loader把解析后的css代码从js中抽离,放到头部的style标签中(在运行时做的)
- 当我们使用类似于 或者 等预处理器的时候,通常需要多个 loader 的配合使用如
- eslint-loader:通过eslint检查js代码
- threada-loader:多线程打包,加快打包速度
插件(Plugins) :
- : 简化了 HTML 文件的创建,为你的 bundle 提供服务。
- : 在每次构建之前清理 /dist 文件夹。
- : 将 CSS 提取到单独的文件中。它为每个包含 CSS 的 JS 文件创建一个 CSS 文件。
- : 优化和最小化 CSS 资源。
- : 压缩 JavaScript。
- : 用于在运行时动态更新各种模块,无需完全刷新。
答: Loader主要负责将代码转译为webpack可以处理的JS代码,而plugin更多负责通过接入webpack的构建过程来改变输出结果。Loader的职责比较单一,而Plugin更为丰富。
Webpack 热更新(Hot Module Replacement,简称 HMR)是一种替换更新模块而无需刷新整个页面的技术。它可以在不刷新浏览器的情况下更新页面,从而提高开发效率和用户体验。
实现 HMR 的原理主要有以下几个步骤:
- 监听文件变化:Webpack-dev-server 监听文件变化,当文件发生变化时,会触发重新编译和打包。
- 更新模块:Webpack-dev-server 会将编译后的模块发送给浏览器端,浏览器端通过 WebSocket 接收到新的模块代码。
- 替换更新模块:浏览器端通过 Webpack-dev-server 提供的 HMR Runtime API,替换掉原有的模块。
- 更新页面:当模块被替换后,浏览器会重新渲染页面,但只会更新被替换的模块部分,从而实现页面无刷新更新。
需要注意的是,HMR 需要在代码中进行特殊的标记,以便在编译时生成对应的 HMR Runtime 代码,使得浏览器端能够正确地接收和替换更新的模块。同时,由于 HMR 只替换更新模块,因此对于一些全局状态的修改,仍然需要手动刷新页面才能生效。
-
初始化一个仓库:
-
查看分支:
-
将已修改或未跟踪的文件添加到暂存区:
-
提交至本地仓库:
-
本地分支推送至远程分支:
-
查看当前工作目录和暂存区的状态:
-
查看提交的日志记录:
-
从远程分支拉取代码:
-
合并某分支(xxx)到当前分支:
-
切换到分支xxx:
-
创建分支xxx并切换到该分支:
-
删除分支xxx:
-
将当前分支到改动保存到堆栈中:
-
恢复堆栈中缓存的改动内容:
答: 三个组成部分:
- 工作区(Working Directory)
- 暂存区(Stage)
- 历史记录区: git commit后的记录区
这三个区的转换关系以及转换所使用的命令:
答:git add和git stage命令是用来将工作目录中修改或新增的文件添加到暂存区,它们的作用是相同的,只是名称不同。
答:
- git reset --mixed(默认)
将指定commit id撤回之后所有内容,全部放进工作区。
- git reset --soft
将指定commit id撤回之后所有内容全部放进暂存区。
- git reset --hard
将指定commit id撤回并清空工作目录及暂停区所有修改。
答: 在commit层面上:
- git reset可以将一个分支的末端指向之前一个新commit,新commit之后的提交都丢掉。支持使用mixed、soft、hard三种方式。
- git checkout可以将head移到一个新的分支,并更新工作目录。可能会覆盖本地的修改,所以执行这个指令之前,需要stash或者commit暂存区和工作区的更改。
- git revert与git reset 目的是一样的,它会创建新的commit的方式来撤销commit,这样能保留之前的commit历史。
从文件层面来说:
- git reset 可以把文件从历史记录区拿到暂存区,不影响工作区的内容,不支持mixed、soft、hard
- git checkout则是把文件从历史记录拿到工作区,不影响暂存区的内容。
- git revert 不支持文件层面的操作。
答:
- 1.记录文件所有历史变化,这是版本控制系统的基本能力
- 2.随时恢复到任意时间点,历史记录功能让我们不怕改错代码
- 3.支持多功能并行开发,支持多分支管理。
- 4.支持多人协同开发。
版本控制有:
1.svn(集中式)
-
优点:适合多人团队协作开发,代码集中化管理
-
缺点:单点故障,必须联网,无法单机工作。
2.git(分布式版本控制系统)
- 优点:适合多人团队协作开发,代码集中管理,可以离线工作,每个计算机都是完整的仓库。
答:
- git merge:合并分支时,git merge会将两个分支的历史记录合并为一个新的提交节点,并创建一个新的合并提交。这个合并提交记录了将两个分支合并的结果。即,它将两个分支的更改集成到一个新的提交中,并保留了原始分支的历史记录。
- git rebase:合并分支时,git rebase会将当前分支上的提交应用到目标分支的最新提交之后。整个分支记录看起来会更加线性。
答: 冲突是在合并分支或进行代码拉取时发生的,当git无法自动合并两个不同的修改时产生冲突,冲突通常发生在同一个文件的同一部分被多个提交修改过后。
解决冲突过程:
- 当发生冲突时,git会在文件中标记哪些部分发生了冲突,通常用,和进行标记。
- 打开冲突文件,根据实际需要决定是选择保留哪些修改。手动修改冲突部分。
- 解决完冲突以后,保存文件。
- 使用git add命令将已解决的冲突文件添加到暂存区
- 使用git commit命令提交冲突的代码。
- 最后使用git push命令,推送到远程仓库。到此,冲突解决。
答:
- git fetch: 将远程仓库的最新代码更新到本地的远程分支,但是不会直接合并到当前工作分支。
- git pull: 相当于git fetch和git merge的组合操作,自动从远程仓库获取最新代码,并且合并到当前工作分支,可能会产生冲突。
答:git stash可以保存当前工作进度,会把暂存区和工作区的改动进行保存,这些修改会保存在一个栈上,后续你可以在任何时候任何分支重新将某次的修改推出来,重新应用这些更改的代码。
应用场景:
- 1.当代码开发到一半,不想commit的提交,但是这时候想同步远程仓库的代码,在这种可能发生冲突的情况下,git pull会拒绝覆盖当前的修改,这时候就可以使用。
- 2.当开发到一半,现在要修改别的分支问题的时候,可以使用git stash缓存当前区域的代码。
GitFlow重点解决的是由于源代码在开发过程中的各种冲突导致开发活动混乱的问题。重点是对各个分支的理解。
-
:主分支。
-
:主开发分支,平行于分支。
-
:功能分支,必须从分支建立,开发完成后合并到分支。
-
:发布分支,发布的时候用,一般测试时候发现的 bug 在该分支进行修复。从分支建立,完成后合并回与分支。
-
:紧急修复线上bug使用,必须从分支建立,完成后合并回与分支。
参考链接: juejin.cn/post/702151…
以常用的轮询方式获取二维码状态为例:
- 访问PC端二维码生成页面,PC端请求服务端获取
- 服务端生成相应的,设置二维码的过期时间,状态等。
- PC获取,生成相应的二维码。
- 手机端扫描二维码,获取。
- 手机端将和发送给服务端,确认登录。
- 服务端校验,根据和生成
- PC端通过轮询方式请求服务端,通过获取二维码状态,如果已成功,返回,登录成功。
答: 参考链接:juejin.cn/post/724151…
-
1.短链接生成流程:
- (1)用户输入原始长链接
- (2)调用短链接生成服务
- (3)选择合适的生成方法(Hash算法、自增ID法),生成短链接
- (4)将短链接与长链接进行映射,并将其存储到数据库或缓存中。
- (5)返回短链接给用户,并将短链接保存在数据库中。
-
2.短链接访问流程
- (1)用户访问短链接地址
- (2)服务器根据短链接查找映射表获取长链接
- (3)服务器进行3xx的重定向
- (4)浏览器根据重定向的地址进行跳转。
-
3.短链接优点:
- 方便用户操作,更方便分享URL地址
- 更加安全,访问者无法根据URL字符串的信息推断出原始URL的结构。
答: 实现过一个存储LocalStorage的钩子函数。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ri-ji/21403.html