前端知识汇总
浏览器原理
1、Chrome有多少个线程和进程,做什么用?渲染进程中有什么线程?
- 主进程(Browser Process)
- 用户界面线程:处理浏览器的用户界面,如地址栏、书签、后退和前进按钮等。
- 网络线程:处理网络活动,如发送和接收数据。
- 存储线程:负责文件读写和本地数据存储。
- 渲染进程(Renderer Process)
- 主线程:处理HTML、CSS的解析,构建DOM树,执行JavaScript代码,处理用户的交互事件。
- 合成线程:负责页面的合成,准备图层的合成和页面部分的重绘。
- 栅格化线程:将图层转换成像素,即栅格化过程。
- GPU线程:管理GPU渲染任务。
- 插件进程(Plugin Process)
- 插件线程:用于独立运行第三方插件,如AdobeFlash
- GPU进程(GPU Process)
- GPU命令缓冲线程:负责处理和转发到GPU的绘制指令。
- 扩进程(Extension Process)
- 背景线程:用于运行Chrome扩展的背景脚本。
2、浏览器渲染流程,分层之后在什么时候合成
- 解析HTML构建DOM树:
- 浏览器解析HTML文件,创建DOM树。
- 解析CSS构建CSSOM树:
- 解析CSS文件和样式,构建CSS对象模型(CSSOM)树。
- 合并DOM和CSSOM构建染树:
- 将DOM和CSSOM合并,创建渲染树,这一步确定每个节点的样式。
- 布局(Layout):
- 计算渲染树中每个节点的准确位置和大小。
- 分层(Layering):
- 一些元素(如具有复杂效果的元素,使用3D变换或wil1-change的元素)会被分到单独的合成层。
- 绘制(Paint):
- 在各个层上填充像素,包括颜色、图片、文字等。
- 合成(Compositing):
- 分层之后,在屏幕显示之前,浏览器会将所有层合井。这个过程通常由GPU加速,特别是当使用CSS3D变换等技术时。
3、重排、重绘和避免重拍重绘
- 重排(Reflow):
- 当DOM的变化影响元素的几何属性(如宽度、高度、位置等),浏览器需要重新计算元素的位置和大小。这可能导致整个页面或页面的一部分重新布局。
- 重绘(Repaint):
- 当元素的外观被改变,但不影响其布局时(如更改颜色、阴影等),浏览器会重新绘制这些元素。
- 如何避免重排和重绘
- 最小化DOM操作:使用DocumentFragment或隐元素进行批修。
- 批量修改样式:批量读取CSS样式让浏览器进行合并,然后使用rerequestldleCallback批量修改。即读取和修改分离
- 使用CSS的transform和opacity进行动画:这些属性不会触发重排和重绘。
- 避免触发同步布局事件:避免频繁读会触发重排的属性。
- 使用绝对定位:对于频繁重排的元素,使用绝对定位。
- 优化CSS选择器:优化CSS选择器,避免过度复杂的选择器。浏览器解析CSS选择器是从右向左的,因此选择器的最右边是关键。
- 避免使用表格布局:因为它可能导致更多的重排。
- 使用CSS3DTransform:触发硬件加速,将元素提升到单独的合成层。
- 利用will-change属性:提前通知浏览器元素可能的变化,以便优化。
4、CDN优化静态资源加载速度的机制
- 网络传输快
- 地理位置优化
- 边缘缓存:CDN在全球范围内部署多个边缘服务器,将静态内容(如图片、CSS/JS文件、视频等)缓存在这些服务器上,让用户能从地理位置上最近的服务器获取 资源,显著减少了数据传输距离和延迟。
- 缓存策略
- 缓存过期控制:通过设置合理的缓存过期时间(如Cache-Control和Expires头),CDN确保用户设备能够缓存内容,减少重复下载。
- 内容更新和失效:当原始内容更新时,CDN可以快速失效旧内容,并更新新的内容,确保用户总是获取最新的资源。
- 内容优化
- 压缩:CDN可以自动压缩静态资源,如使用gzip或Brotli压缩HTML、CSS和JavaScript文件,减小文件大小,加快传输速度。
- 图片优化:对图像进行格式转换(如WebP)、压缩和大小调整,根据用户设备和浏览器能力提供最优化的图像。
- HTTP/2和HTTP/3支持
- 多路复用:通过支持HTTP/2和HTTP/3,CDN允许在单个连接上同时传输多个请求和响应,减少了连接建立的开销和队头阻塞问题。
- 服务器推送:HTTP/2的服务器推送功能允许服务器主动发送额外资源,减少了额外的往返延迟。
- TCP优化
- 长连接:通过使用TCP长连接,减少了频繁的连接建立和关闭所需的时间和资源。
- TCP加速:一些CDN还使用特定的算法来优化TCP传输,如更快的拥塞控制和恢复机制。
- 地理位置优化
- 支持大并发
- 负载均衡,智能路由:CDN通过实时监测网络条件和服务器性能,动态地将用户请求路由到最佳的服务器,保证高效的内容分发。
- 安全
- DDoS保护:通过分散流量负载,CDN能够帮助抵御大规模的分布式拒绝服务(DDoS)攻击,保证资源的可用性。
- TLS/SSL:CDN通常提供TLS/SSL加密,确保数据传输的安全性。
5、从输入URL到页面加载全过程
- 客户端先处理请求内容
- 用户在浏览器的地址栏输入网址,浏览器解析出
协议
、地址
、端口
等信息,并发送到操作系统 - 操作系统再次对数据封装,封入本地ip、MAC地址等系统信息并通过网卡发出请求
- 用户在浏览器的地址栏输入网址,浏览器解析出
- 网络运营商寻找后台服务器地址
- DNS查询:
- 查找IP地址:浏览器需要找到该URL的IP地址。首先在本地缓存中查找,如果未找到,会向ISP的DNS服务器发起查询。
- 递归查询:如果ISP的DNS服务器也没有缓存,则进行递归查询,直到找到域名的授权DNS服务器,并获取到网站的IP地址。
- DNS查询:
- 后端服务器处理
- 根据
端口
判断给指定应用去处理 - 建立TCP链接
- HTTP三次握手:浏览器与服务器之间建立TCP连接,这通常涉及一个称为“三次握手的过程,确保双方都准备好进行数据传输。
- HTTPS握手:会进行TLS握手,以建立加密的数据传输通道。
- 依据链接通道,前端发送HTTP请求,后端接收处理并返回网页资源
- 根据
- 浏览器解析HTML:
- 构建DOM树:浏览器开始解析HTML文档,构建文档对象模型(DOM)。
- CSS解析:同时解析CSS,构建CSS对象模型(CSSOM)。
- 如果HTML引用了JavaScript文件,浏览器会下载并执行它们,这可能会修改DOM和CSSOM。
- 渲染页面:
- 生成渲染树:浏览器结合DOM和CSSOM生成渲染树。
layout
出页面的布局
,每个节点的位置和大小。- 对需要分层的元素(比如3D变换)的进行分层(Layering)
paint
绘制阴影、字体、边框
。- 最后
合并图层
展示到页面上
6、DNS解析过程
- 用户输入域名:
- 当用户在浏览器中输入一个网址(例如www.example.com),DNS解析过程就开始了。
- 检查本地DNS缓存:
- 浏览器首先检查它自己的缓存中是否有这个域名的记录。如果没有找到,操作系统的DNS缓存会被检查。
- 查询递归DNS服务器:
- 如果本地缓存中没有找到记录,浏览器会向配置的递归DNS服务器(通常是用户的互联网服务提供商ISP提供的)发送查询请求。
- 这个递归DNS服务器也有自己的缓存。如果找到了对应的记录,就会返回结果给用户的浏览器。
- 如果递归DNS服务器的缓存中也没有找到记录,它会代表用户向互联网上其他DNS服务器查询。
- 根DNS服务器查询:
- 如果递归服务器没有缓存该记录,它会向根DNS服务器查询。根DNS服务器是顶级的DNS服务器,负责管理顶级域名(如.com、.net、.org 等)。
- 根服务器不会直接解析域名,而是告诉递归服务器哪个顶级域名服务器(TLD服务器)负责这个域名的扩展名(例如.com)。
- 顶级域名服务器(TLD)查询:
- 接着,递归DNS服务器向相应的TLD服务器发送查询请求。例如,对于www.example.com,它会查询负责.com域的TLD服务器。
- TLD服务器会返回负责该特定域名(在本例中为example.com)的授权DNS服务器的地址。
- 授权DNS服务器查询:
- 然后,递归DNS服务器向该授权DNS服务器发送请求,该服务器具有特定域名的具体记录。
- 授权DNS服务器返回请求的域名的IP地址。
- 返回IP地址:
- 递归服务器将IP地址返回给浏览器。
- 浏览器接收到IP地址后,就可以发起到该地址的网络请求,开始加载网页内容。
- 缓存DNS记录:
- 为了加快未来的查询,递归服务器和用户的浏览器会缓存DNS记录。每个记录都有一个生存时间”(TTL),决定了它应该缓存多长时间。
7、http
- 状态码
- 1xx服务器收到请求
- 2xx请求成功
- 200 成功
- 3xx重定向
- 301 永久重定向,换域名时
- 302 临时重定向,临时域名维护和短链接,百度的搜索结果
- 304 资源未修改,资源使用了缓存
- 4xx 客户端错误
- 403 没有权限
- 404 未找到资源
- 5xx 服务端错误
- 504 网关超时
- method
- get
- post
- patch/put
- delete
- 请求头
- 请求服务器相关
- host信息
- 说明:指定请求的服务器的域名。
- 作用:在多域名共享同一个IP地址的服务器上区分不同的域名请求。
- cookie
- Connection:keep-alive 一次TCP连接重复使用
- 说明:指明当前的连接管理策略,通常值为keep-alive或close。
- 作用:控制连接是保持还是在响应后关闭。
- host信息
- 发送的浏览器基本信息
- 浏览器UA信息
- 说明:包含了关于客户端(如浏览器或移动应用)的信息,例如浏览器类型和版本。
- 作用:允许服务器根据客户端的类型进行优化处理,如提供不同的响应格式
- Content-type 发送数据的格式,application/json
- 说明:在POST或PUT请求中使用,指明发送的数据类型。
- 作用:告诉服务器请求体的媒体类型,如application/x-www-form-urlencoded、application/json等。
- Content-Length 发送数据长度
- 说明:在POST或PUT请求中,指出请求体的长度。
- 作用:告诉服务器请求体的大小,以便于正确接收数据。
- Accept-Languange 浏览器可接收的语言,zh-CN
- 说明:指明客户端优先接受的语言,如en-US、zh-CN等。
- 作用:用于国际化,服务器可以根据此返回特定语言的内容。
- Accept 浏览器可接收的数据格式
- 说明:指明客户端能够处理的媒体类型,如text/html、application/json等。
- 作用:服务器可以根据这个头信息提供适当格式的内容。
- Accept-Encoding 浏览器可接收的压缩算法,gzip
- 说明:指明客户端支持的内容编码方式,如gzip、deflate等。
- 作用:服务器可以选择一种压缩方法,减少响应体的大小,加快传输速度。
- gzip:
- 说明:GNUzip压缩格式,是一种广泛使用的压缩方法。
- 特点:提供了良好的压缩比,是目前最流行的HTTP压缩方法之一。
- deflate:
- 说明:另一种常见的压缩方法,它结合了LZ77算法和Huffman编码。
- 特点:通常比gzip更快,但压缩率可能略低。
- br(Brotli):
- 说明:由Google开发的较新的压缩格式。
- 特点:提供比gzip更好的压缩比,尤其适合文本数据的压缩。
- compress:
- 说明:一种较旧的UNIX文件压缩程序,现在较少使用。
- 特点:由于其较差的性能和兼容性问题,已经被其他方法所取代。
- identity:
- 说明:表示没有应用任何编码。
- 特点:即使明确指定 Content-Encoding: identity,也等同于没有指定 Content-Encoding。
- gzip:
- 浏览器UA信息
- 服务器缓存相关:
- Cache-Control:max-age:1111(秒)
- 说明:指定请求的缓存机制。
- 作用:告诉服务器客户端希望如何处理缓存,如no-cache表示不希望从缓存中获取数据。
- If-Modified-Since:Stu,30 Dec日期(modified)
- If-None-Matche:"432dfdFdds"(etag毫秒级)
- Cache-Control:max-age:1111(秒)
- 服务器安全性相关
- Referer帮助服务器了解请求来源,防止CSRF攻击
- 说明:指明当前请求是从哪个页面的链接中发起的。
- 作用:帮助服务器了解流量来源,也用于防止CSRF攻击。
- Authorization token凭证
- 说明:包含用于证明客户端有权请求特定资源的凭据。
- 作用:用于身份验证,如基本认证、令牌(Token)认证
- Referer帮助服务器了解请求来源,防止CSRF攻击
- 请求服务器相关
- 响应头
- CSP,后端设置前端加载的资源地址
- server:nginx
- last-modified(modified)
- connection(keep-alive)
- 交互流程
- 三次握手
- 图片
网络七层模型
- TCP三次握手(建立连接):
- SYN:
- 客户端发送一个SYN(同步序列编号)报文到服务器,并进入SYN-SENT状态。
- SYN报文中包含一个客户端的初始序列号(ISNc)。
- SYN-ACK:
- 服务器收到SYN后,会回应一个SYN-ACK(同步和确认)报文。
- 该报文中包含服务器的初始序列号(ISNs)和对客户端ISNc的确认号(ISNc+1)。
- 服务器进入SYN-RECEIVED状态。
- ACK:
- 客户端收到SYN-ACK后发送一个确认报文(ACK)。
- ACK报文中的确认号为ISNs+1,同时自己的序列号为ISNc+1。
- 此后,客户端和服务器都进入ESTABLISHED状态,完成连接建立。
- SYN:
- 图片
- 四次挥手
- FIN from Initiator:
- 当一个端点(如客户端)完成数据发送任务后,它会发送一个FIN(结束)报文。
- 客户端进入FIN-WAT-1状态。
- ACK to FIN:
- 服务器接收到FIN后,发送一个ACK(确认)报文,并进入CLOSE-WAIT状态。
- 客户端收到ACK后,进入FIN-WAIT-2状态。
- FIN from Receiver:
- 服务器准备好关闭连接时,发送一个FIN到客户端。
- 服务器进入LAST-ACK状态。
- ACK to FIN:
- 客户端收到FIN后,进入TIME-WAIT状态,并发送一个ACK到服务器。
- 服务器收到ACK后,关闭连接。
- 客户端等待一段时间以确保服务器接收到ACK后,也关闭连接。
- FIN from Initiator:
- 加密握手
- 客户端问候(Client Hello)
- 客户端发起连接:客户端开始握手过程,发送一个"CientHeo"消息,其中包含客户端支持的TLS版本、加密套件列表、一个随机数(ClientRandom)和可能的其 他会话特定数据。
- 服务器响应(ServerHello)
- 服务器选择参数:
- 服务器回复"ServerHello"消息,选择客户端提议的参数中的一套来使用,包括TLS版本和加密套件,并发送自己的随机数(ServerRandom)。
- 证书和密钥交换:
- 服务器发送其数字证书(如果是匿名加密则除外),证书中包含了服务器的公钥,对于某些密钥交换方法,服务器还会发送一个服务器密钥交换消 息
- 服务器选择参数:
- 服务器完成消息(ServerHelloDone)
- 请求客户端响应:服务器发送ServerHelloDone"消息,通知客户端已完成其握手消息的初步部分。
- 客户端密钥交换
- 密钥交换:客户端根据服务器的公钥和选定的密钥交换方法生成预备主密钥(Pre-MasterSecret),并加密后发送给服务器。
- 证书和密钥验证:如果服务器请求客户端证书,客户端还将发送其证书。客户端也可能发送一个密钥验证消息来证明它拥有与其证书中的公钥匹配的私钥。
- 客户端完成消息(Client Hello Done)
- 完成振手:客户端发送一个"ChangeCipher Spec"消息,表明接下来的消息将使用协商的参数和密钥进行加密,然后客户端发送“Finished"消息,该消息包含前面提手 消息的加密和完整性检查。
- 服务器完成握手
- 加密通信开始:服务器回复一个"ChangeCipher Spec"消息和加密的"Finished"消息,此时,握手完成,客户端和服务器开始加密通信。
- 客户端问候(Client Hello)
- 三次握手
- http和https区别,配置
- HTTPS简介
- HTTPS是HTTP的安全版本,它在HTTP的基础上通过SSL/TLS协议提供了数据加 密、数据完整性和认证。
- HTTP和HTTPS的区别
- 加密:
- HTTP:数据以明文形式传输,没有加密,容易被窃听和篡改。
- HTTPS:所有数据传输都经过加密,保护数据不被窃听和改。
- 端口:
- HTTP:认使用端口80。
- HTTPS:默认使用端口443。
- 加密:
- URL前:
- HTTP:URLhttp://头。
- HTTPS: URL https://。
- 安全证书:
- HTTP:不需要安全证书。
- HTTPS:需要SSL/TLS证书来建立安全连接。
- 为什么HTTPS比HTTP安全
- 加密通信:HTTPS使用SSL/TLS协议对数据进行加密,即使数据被截获,也无法轻易解读其内容。
- 数据完整性:HTTPS可以验证数据在传输过程中是否被篡改,确保数据的完整性。
- 认证:HTTPS提供了一种方式来验证网站的身份,减少了遭受“中间人攻击”的风险。用户可以通过查看证书来确认网站的真实性。
- 如何配置HTTPS
- 获取SSL/TLS证书:
- 可以从证书颁发机构(CA)购买,或者通过Let'sEncrypt等服务免费获得。
- 安装和配置证书:
- 在服务器上安装SSL/TLS证书,并配置你的Web服务器(如Apache、Nginx)使用这个证书。
- 配置服务器:
- 修改Web服务器的配置,以启用SSL/TLS加密。
- 确保服务器配置了正确的端口(通常是443)。
- 重定向HTTP到HTTPS:
- 配置Web服务器,将所有HTTP流量重定向到HTTPS,确保所有请求都经过加密。
- 测试配置:
- 使用在线工具检查你的HTTPS配置,确保它安全且有效。
- 保持更新:
- 定期更新你的SSL/TLS证书,以及服务器上的加密和安全配置。
- 获取SSL/TLS证书:
- HTTPS简介
- HTTP1、HTTP2、HTTP3分别有什么最大的区别,Http2怎么确保文件同时传输不会报错?
- HTTP/1.1
- 无并行请求:HTTP/1.1不支持一个TCP连接上的多个并行请求。虽然可以通过多个连接实现并行,但这增加了延迟和资源消耗。
- 头部未压缩:HTTP1.1的头部没有压缩,这可能导致不必要的数据传输,尤其是在头部信息冗长的情况下。
- 顺序和阻塞:HTTP/1.1存在"队头阻塞"问题,即前一个请求的延迟会影响后续请求的处理。
- HTTP/2
- 多路复用:HTTP/2引入了多路复用,允许在单个TCP连接上并行传输多个请求和响应,极大提高了效率和页面加载速度。
- 头部压缩:HTTP/2使用HPACK压缩算法压缩头部信息,减少了传输的数据量。
- 服务器推送:HTTP/2支持服务器推送技术,服务器可以主动发送客户端可能需要的资源,减少了往返延迟。
- 优先级和流控制:HTTP2允许设置请求的优先级,并进行流控制,有效管理带宽和服务器资源。
- HTTP/3
- 基于QUIC协议:HTTP/3是在新的QUIC传输协议之上构建的,而QUIC是基于UDP的,不同于前两者基于TCP。
- 更快的连接建立和恢复:QUIC减少了连接建立的延迟,特别是在丢包情况下的性能更优。
- 内置加密:QUIC包含了TLS加密,提供了更好的安全性。
- 改进的阻塞控制:HTTP/3通过QUIC改进了阻塞控制,减少了队头阻塞问题。
- HTTP/2如何确保文件同时传输不会报错
- 多路复用:HTTP/2的多路复用允许在单个TCP连接上同时发送多个请求和响应。每个请求或响应都分成帧,并在同一个连接上交错发送,从而提高了效率。
- 帧排序和流控制:HTTP/2中的数据传输是以帧为单位进行的。每个帧都属于某个特定的流,并且有一个独立的流标识符。这样可以确保即使在并行传输的情况下,帧 的顺序和完整性也得到了保障。
- 错误检测和重传机制:虽然HTTP/2本身不直接处理错误检测和重传(这是TCP层面的职责),但它通过流控制和优先级机制确保了资源的有效管理,减少了因资源 竞争和网络条件变化导致的错误。
- HTTP/1.1
- 缓存
- 概念和用处
第一次请求
资源时,将资源缓存到本地
- 用处是
下次
请求时可直接读取缓存
,不用再次请求资源
- 强制缓存Cache-Control
前端无法参与
- cache-control:max-age=1000
- 可以缓存
react
、vue
等很少改动的库和文件
- 协商缓存
第一次
请求缓存后
,第二次
请求与服务器协商
是否读取缓存
秒级
的last-Modified
和毫秒级
的Etag
- 概念和用处
9、tcp和udp概念,tcp确保数据正确性的流程,tcp头内容、tcp属于的层级
- 连接方式:
- TCP:是一种面向连接的协议。在数据传输之前,它需要在发送方和接收方之间建立连接。
- UDP:是一种无连接协议。它发送数据时不需要建立连接,直接发送。
- 可靠性:
- TCP:提供高可靠性的数据传输。它通过确认响应和重传丢失的数据包来确保数据正确性和完整性。
- UDP:不保证数据包的顺序、完整性或正确性。它不会对丢失的数据包进行重传。
- 顺序和完整性:
- TCP:确保数据包按照正确的顺序到达接收方。
- UDP:不能保证数据包的顺序。
- 速度和效率:
- TCP:由于建立连接、确认响应等过程,比UDP慢。
- UDP:由于缺乏确认机制,通常比TCP快,适用于对实时性要求高的应用,如视频流和在线游戏。
- 流量控制和拥塞控制:
- TCP:提供流量控制和拥塞控制机制。
- UDP:没有这些机制。
- TCP确保数据正确性的方式:
- 数据包确认:接收方会对接收到的数据包发送确认。如果发送方没有收到确认,它会重传数据包。
- 顺序控制:TCP数据包包含序列号,确保数据的顺序性。
- 校验和:每个TCP数据包都包含校验和,用于检测数据在传输过程中是否出现错误。
- 重传机制:丢失的数据包会被重新发送。
- 流量控制:通过窗口大小调整,控制数据的发送速率,防止接收方被快速发送的数据淹没。
- 拥塞控制:当网络拥堵时,TCP会减少数据的发送量。
- TCP头包含的内容:
- 源端口号和目标端口号:用于识别发送和接收的应用程序。
- 序列号:标识从TCP源到目标的字节流的顺序。
- 确认号:发送方期望收到的下一个字节的序列号。
- 数据偏移(头部长度):指示TCP头的大小。
- 标志位:如SYN(建立连接)、ACK(确认)、FIN(结束连接)等。
- 窗口大小:用于流量控制,指示接收方还能接收的字节数。
- 校验和:用于错误检测。
- 紧急指针:仅在紧急标志被设置时有效。
- TCP属于哪一层:
- TCP属于OSI模型的传输层(第四层),也是TCPIP模型中的传输层。传输层负责在网络连接的两端之间提供可靠的数据传输。
- TCP和UDP每种协议都有其优势和适用场景,选择使用哪一种取决于应用程序的具体需求。TCP适用于需要高可靠性和数据完整性的应用,如网页浏览、文件传输等; 而UDP适用于需要快速传输且可以容忍一定丢包的应用,如视频会议、实时游戏等。
10、安全
- 网页安全
- xss跨站脚本攻击
- 攻击者在网站上注入恶意的JavaScript代码。
- 当其他用户浏览该网站时,注入的脚本在用户的浏览器上执行,腾讯的QQ空间。
- 攻击者可以通过这种方式窃取用户的Cookie、会话令牌、甚至重写网页内容。
- 注入点有
浏览器地址栏
、html的节点和属性
、富文本
、js代码
- 防御
- 浏览器
自动防御
地址栏输入 html节点和属性
可通过转义单双引号
、尖括号
和空格
js
对单双引号
,正反斜杠
进行转义富文本
设置白名单
- 通过设置HTTP响应头设置CSP,指定前端的js、img灯资源加载来源`
- react和vue都有一个直接渲染传入的html方法,这个方法要使用第三方库进行转义,不要直接使用
- 浏览器
- 攻击者在网站上注入恶意的JavaScript代码。
- CSRF攻击
登录本网站拿到cookie
去使用- 攻击原理
- 攻击者创建恶意网站或链接,诱导已登录目标网站(如银行网站)的用户点击或访问。
- 用户访问恶意网站时,恶意网站发起请求到目标网站,由于用户已登录,请求会被认为是合法的。
- 目标网站执行请求,如转账、改密等操作,无法辨识请求的真实来源。
- 防御措施
- Token验证:在表单中使用隐藏的CSRF令牌,仅服务器知道,每次用户提交表单时,服务器验证令牌。
- 检查Referer头:后端验证HTTP请求的Referer头,确保请求来自合法的来源。
- 双重验证:对敏感操作使用验证码或二次确认。
- 使用SameSite Cookie性:设置严格模式strict或Lax,限制第三方网站发送Cookie
- 设置cookie的属性
http-only
和https的secure为true,让cookie无法被js读取
- 攻击原理
- 点击劫持,将被攻击的网页
嵌入到iframe
上,并将它透明化
,再用一个页面覆盖
,引导用户点击。- 前端
判断
top.location和window.location是否一致
- 前端
- React和Vue的XSS如何防范与解决?
- React和vue默认会转义所有要渲染到DOM的字符串
- 避免使用react的dangerouslySetInnerHTML和vue的v-html,或者通过第三方库来清洁HTML内容
- 使用CSP通过白名单机制来限制网页可以加载和执行的资源
- 小心处理URL和链接
- 当创建链接或处理URL时,确保它们不会被注入恶意代码。例如,使用encodeURIComponent函数来处理URL参数。
- 更新依赖
- 定期更新你的所有依赖,包括React和其他npm包。安全漏洞时常被发现,而依赖的维护者通常会发布修复版本来解决这些问题。
- 安全的第三方库使用 2. 当引入第三方组件或库时,确保它们是可信赖的,并且没有已知的安全漏洞。
- 教育和代码审查
- 对开发团队进行安全教育,并实施代码审查流程,确保所有代码符合安全标准。
- xss跨站脚本攻击
- 服务器安全保障
- DDOS攻击
- 购买服务器供应商的
防火墙和流量清洗服务
,或者高防IP
- 前端
降低接口延迟
,快速失败快速返回 测出项目瓶颈
,当并发到瓶颈的时候拦截流量
,防止服务器雪崩
,保证核心系统可用。
- 购买服务器供应商的
- DNS攻击
买
供应商的防护功能,或者自己加固
DNS解析服务器 - 挖矿
买
供应商的防护功能,或者自己修改复杂密码
- 网络窃听和篡改 可以通过使用
https传输数据
,核心内容再用签名和加密
,http有很多工具可以监听到请求数据 - 防止盗链
nginx
,使用referer验证是否是自己网站的请求
- DDOS攻击
- 代码安全
- github查询公司代码是否被上传,开发人员必须提供自己的github和码云账号,定期检查是否将公司源码上传;
- 使用可靠的人工智能辅助编码,禁止使用第三方人工智能插件编写和优化代码;
- 需要可控的人工智能
- 代码上传至外网的人工智能要慎重使用。
- cookie
- 基本作用
- 1)可做
前端存储
- 2)遵守同源策略下,http请求
自动带上
cookie - 3)遵守同源策略下,
后端
可通过请求设置cookie
- 1)可做
- 与安全相关
- 1)防止
xss
和crsf
攻击可以 设置cookie的属性http-only和https的secure为true,让cookie无法被js读取 - 2)设置cookie的same-sit属性设置为严格模式,完全禁止第三方cookie
- 3)
防止窃听
需要使用对cookie加密
- 1)防止
- 基本作用
11、GraphQL、RESTFUL、TRPC
- GraphQL
- 概述:GraphQL是由Facebook开发的一种数据查询和操作语言,以及与之配套的运行时。它允许客户端精确地指定所需数据的结构。
- 特点
- 精确查询:客户端可以指定所需的数据结构,避免过度获取或不足获取数据。
- 单一端点:所有数据查询和操作通过一个单一的端点进行。
- 类型系统:GraphQL有一个强类型系统,可以在查询执行前进行验证。
- 实时数据(通过订阅):支持实时数据更新功能。
- 适用场景:适合需要高度灵活性和精确控制数据交互的复杂应用,尤其是在多变的产品需求和多端数据需求频繁的场景。
- RESTful API
- 概述:RESTful API是基于 REST (Representational State Transfer,表现层状态转移)原则的 Web API。它使用标准的HTTP方法和状态码,操作资源的表述
- 特点:
- 无状态:每个请求独立,不依赖之前的请求。
- 资源导向:通过URI定位资源,使用HTTP方法(GET、POST、PUT、DELETE)进行操作。
- 多种数据格式:支持JSON、XML等多种数据格式。
- 可浏览性:API的可浏览性较强,易于理解和使用。
- 适用场景:适合资源导向的应用,尤其是那些需要清晰的资源管理和简单直观API的应用。
- tRPC
- 概述:tRPC (Type-safe Remote Procedure Call)是一个为 TypeScript构建的端到端类型安全的 RPC框架。
- 特点:
- 端到端类型安全:提供类型安全的API调用体验。
- 不需要编写API模式:不需要单独定义API接口和数据模式。
- 简化数据获取:客户端直接调用服务器端的函数,减少传统API设计的复杂性。
- 适用场景:适合使用TypeScript的项目,特别是那些追求类型安全和希望减少前后端间数据交互复杂性的项目。
- 总结
- GraphQL提供了灵活的数据获取方式,适合于数据需求频繁变动的场景。
- RESTfulAPI是更传统的方法,以其简单性和可扩展性为主要特点。
- tRPC则是一个新兴的框架,强调类型安全和简化的API设计,适合TypeScript项目。 在选择这些技术时,需要考虑项目的具体需求、团队的熟悉度和技术栈的适配性。
12、浏览器兼容
- 原因
- 因为浏览器有chrom、ie、firefox、edge等型号
- 各个型号又有多个发行版本,支持的js和css版本又各不相同,各个浏览器又都有默认的样式。
- 显示的硬件有台式电脑、笔记本电脑、手机、平板,这些硬件的大小引发出样式兼容问题。
- 解决方法
- html兼容 尽量使用类库样式和基础的语义化标签,div、span、ul等
- js兼容
- 使用bable进行语义降级,将各种class、箭头函数转为浏览器可运行的function
- core.js转义新方法,将数组字符等新的操作方法进行降级转义
- 使用generator补充新语法,比如promise
- css兼容
- 使用normal.css进行浏览器样式统一,或者自己将所有元素默认样式
- 使用兼容性好的css样式
- 使用postcss打包出兼容各大浏览器的css前缀
- 屏幕大小兼容
- 使用media查询,根据可视窗口的宽度预设元素的宽高
- 使用rem单位,根据可视窗口的宽度设置font-size,动态设置大小
- 使用vw和vh根据可视窗口的宽高和百分比来布局
- 开发和设计师按照ipone6的750px宽度为基础进行整体缩放,使用px2rem库,代码里写px,编译的时候该库会根据可视窗口宽度自动编译成rem缩放单位,这也是小程序的rpx的来源。
13、V8工作原理
词法分析
出变量名、关键字、运算符等词法单元
语法分析器
根据词法单元转换为抽象语法树(AST)
- 根据语法树
生成字节码
- 最后根据字节码生成计算机可
执行的机器码
垃圾回收
基本有两种
方法,一种是标记清除
,对变量打标
,当变量离开环境时标记为可清除。缺点
是清楚后会导致内存碎片
- 另一种
引用计数
清除,记录
变量被引用
的次数
,引用次数为0
就会被清除
,缺点
是回收机制是周期性运行
的,每隔几百毫秒执行一次,且需要大量空间
来计算引用次数 局部变量
,函数执行完
,则标记回收
全局变量
和闭包引用的变量
直至浏览器卸载页面
才释放- V8的垃圾回收策略主要是基于分代式垃圾回收机制,其根据对象的存活时间将内存的垃圾回收进行不同的分代,然后对不同的分代采用不同的垃圾回收算法
- 在新生代的垃圾回收过程中主要采用了Scavenge算法将存活的对象复制到老生代空间;在老生代采用Mark-Sweep(标记清除)和Mark-Compact(标记整理)算法
14、跨域
- 跨域是浏览器的一种安全保护机制,浏览器的同源策略规定协议、域名、端口都要相同,否则就产生跨域,
- 但是浏览器加载图片、css、js不会促发同源策略,加载图片基本用于统计打点或者第三方服务,图片一般用于CDN
- jsonp是基于加载js实现的,在js路径上面加请求,再掉个callback函数,后端用jsonp返回,前端就能通过window.callback函数获取值
性能优化
19、你了解前端指标有哪些?
- 首次内容绘制(FCP)
- 优化服务器响应时间。
- 减少关键渲染路径资源的数量和大小。
- 最大内容绘制(LCP)
- 优化图片大小和格式。
- 懒加载非关键图片。
- 优化服务器端渲染。
- 交互时间(TTI),点击没反应
- 减少JavaScript执行时间。
- 拆分长任务。可通过监控得到长任务时间
- 异步加载非关键脚本。
- 首次输入延迟(FID),输入没反应
- 优化JavaScript执行。
- 减少主线程的工作量。
- 累积布局偏移(CLS)
- 为图片和视频指定尺寸。
- 避免在加载过程中插入广告或动态内容。
- 速度指数
- 优化页面上方内容的加载速度。
- 使用骨架屏(Skeleton Screens)
- 首字节时间(TTFB)
- 优化服务器响应速度。
- 使用CDN。
- 确保服务器和数据库的优化
20、获取首次内容绘制FCP,指标是什么,如何提升FCP?
- FCP指标
- 优秀:低于1秒
- 良好:1-2.5秒
- 需改进:2.5-4秒
- 较差:大于4秒
- 提升FCP
- 优化服务器响应时间:
- 使用更快的托管服务。
- 优化后端逻辑和数据库查询。
- 减少关键渲染路径长度:
- 识别和优化影响首次渲染的关键资源。
- 使用异步或延迟加载阻塞JavaScript和CSS。
- 减少资源大小:
- 压缩CSS、JavaScript和HTML。
- 优化图像大小和格式。
- 利用缓存策略:
- 使用浏览器缓存来存储重复使用的资源。
- 使用内容分发网络(CDN):
- 通过CDN分发内容,减少地理位置对加载时间的影响。
- 移除或优化重量级第三方脚本:
- 分析和移除增加显著加载时间的第三方脚本。
- 预加载关键资源:
- 使用<linkrel="preload">预加载首屏关键资源。
- 优化字体加载:
- 避免使用过多的字体变体,使用font-display:swap属性来减少字体阻塞渲染。
- 优化服务器响应时间:
- 代码
代码
jsconst longtime = window.performance.timing.domInteractive if ('PerformanceObserver' in window) { const observer = new PerformanceObserver((list) => { const entries = list.getEntriesByName('first-contentful-paint'); if (entries.length) { console.log('FCP:', entries[0].startTime, 'ms'); } }); observer.observe({ type: 'paint', buffered: true }); }
21、获取最大内容绘制LCP,指标是什么?如何提升LCP?
- LCP指标
- 好:小于2.5秒
- 需改进:2.5到4秒
- 差:超过4秒
- 提升LCP
- 优化图像:
- 使用更小的、优化过的图像。
- 使用现代格式,如WebP
- 对于较大的图像,考虑懒加载。
- 优化服务器/CDN:
- 使用CDN加速内容分发。
- 优化服务器响应时间。
- 优化CSS和JavaScript:
- 减少关键染路径上的CSS和JavaScript
- 异步加载非关键JavaScript
- 内联关键CSS.
- 使用预加载:
- 使用<linkrel-prelad">预加载关键资源。
- 优化Web字体:
- 优化字体加载并考虑使用font-display:swap.
- 优化客户端渲染:
- 如果使用客户端渲染框架,考虑服务器端渲染或静态生成。
- 优化DOM结构:
- 简化DOM,减少深层嵌套。
- 缓存策略:
- 为静态资源实施有效的缓存策略。
- 优化图像:
22、获取首次内容交互TTI,指标是什么,如何提升TTI?
- TTI指标
- 好:小于3.8秒
- 需改进:3.8到7.3秒
- 差:大于7.3秒
- 提升TTI
- 优化JavaScript加载和执行:
- 减少JavaScript文件大小
- 使用代码分割以异步加载非关键JavaScript
- 避免长任务阻塞主线程。
- 最小化关键请求深度:
- 优化关键渲染路径。
- 减少关键资源数量。
- 服务器端渲染(SSR)或静态生成:
- 使用服务器端渲染或静态生成以减少客户端渲染的工作量。
- 使用预加载和预连接技术:
- 使用<linkrel="preload">预加载关键资源
- 使用<linkrel="preconnect">提前建立必要的连接。
- 优化字体加载:
- 使用font-display:swap等策优化字体的可见加载。
- 优化图片和媒体资源:
- 对图片和视频进行压缩和格式优化。
- 使用懒加载策略。
- 避免大型DOM结构:
- 简化页面的DOM结构。
- 使用高效的CSS:
- 避免复杂的CSS选择器和布局重排。
- 优化JavaScript加载和执行:
- 代码
代码
jsconst longtime = window.performance.timing.domInteractive
23、获取首次输入延迟FID,指标是什么?如何提升FID?
- 指标
- 好:小于100毫秒
- 需改进:100-300毫秒
- 差:超过300毫秒
- 提升FID
- 减少JavaScript执行时间:
- 减少长任务,尽量将长的JavaScript任务拆分成更小的任务。
- 使用代码分(Code Splitting)来减少初始加载的JavaScript量。
- 延迟非关键JavaScript的加载,例如使用defer属性。
- 优化页面加载:
- 使用预加载技术预加载关键资源。
- 优化服务器响应时间和资源压缩。
- 避免不必要的第三方脚本:
- 减少第三方脚本的使用,或推迟其加载。
- 使WebWorkers:
- 对于复杂的计算任务,考虑使用WebWorkers。
- 减少JavaScript执行时间:
- React和Vue对于提升FID的贡献
- React和Vue是现代前端框架,它们本身对于直接提升FID没有特定优势,但它们的架构和生态提供了优化的可能性:
- 虚拟DOM:React和Vue使用虚拟DOM来优化DOM更新,减少实际的DOM操作成本,从而可以在一定程度上减少渲染阻塞时间。
- 异步组件:React和Vue支持异步组件和代码分割,使得开发者可以更灵活地控制资源加载和渲染时机。
- 生态工具:React和Vue的生态系统中有许多性能优化工具和库,如react-lazy、vue-router的懒加载等,有助于优化页面加载和交互性。
- React和Vue是现代前端框架,它们本身对于直接提升FID没有特定优势,但它们的架构和生态提供了优化的可能性:
- 在实际项目中,无论使用哪个框架,重点在于如何合理利用它们提供的特性和生态工具来优化FID。例如,合理利用代码分割、加载组件和资源、优化事件处理器的实 现等。同时,应当持续监控页面性能,针对具体瓶颈进行优化。
24、获取累计布局偏移CLS,指标是什么?如何提升CLS?
- 指标
- 好:小于等于0.1s
- 需改进:大于0.1s小于等于0.25
- 坏:大于0.25s
- 如何提升CLS
- 稳定尺寸的媒体内容:
- 为图像和视频元素设置显式的宽度和高度,以防止加载时的布局变化。
- 使用CSS的aspect-ratio属性为无尺寸媒体留空间。
- 避免插入动态内容:
- 尽量避免在页面主要内容上方动态添加内容,这会导致下方内容突然下移。
- 优化字体加载:
- 使用font-display: optional或font-display: swap属性减少字体加载对布局的响。
- 预留广告和嵌入框架空间:
- 为广告和嵌入内容预留足够的空间,避免加载时导致内容移动。
- 动画和过渡优化:
- 在实现动画和过渡时使用transform属性,这种变化不会影响页面布局。
- 使用容器包裹异步加载的内容:
- 异步加载的内容(如评论区、推荐列表)应该在固定尺寸的容器中加载,避免内容渲染导致的布局偏移。
- 稳定尺寸的媒体内容:
25、如何获取服务器响应速度TTFB,指标是什么?如何提升TTFB
- 指标
- 好:小于200毫秒
- 需要改进:200到500毫秒
- 差:大于500毫秒
- 如何提升TTFB
- 优化服务器性能:
- 使用更快的服务器或升级服务器硬件。
- 优化服务器配置和代码,如数据库查询、服务器端脚本优化等。
- 优化服务器性能:
- 使用CDN:
- 使用内容分发网络(CDN)可以将内容缓存到离用户更近的地点,从而降低响应时间。
- 减少网络延迟:
- 优化网络路径,确保服务器与用户之间的网络延迟最小。
- 选择靠近用户的的服务器位置
- 优化应用程序:
- 减少服务器端渲染所需的资源和时间。
- 使用异步操作和后台处理来提高效率。
- 数据库优化:
- 对数据库进行索引和查询优化,
- 避免复杂的数据库操作和大量的数据库请求。
- 减少HTTP请求:
- 减少页面加载所需的HTTP请求数量。
- 会并资文件,如CSS和JayaScript
- 启用压缩:
- 使用GZIP或Br等技术来小发送到客户的数量。
- 总结:
- 提升TTFB主要涉及到服务器性能、网络优化和应用程序优化。需要注息的是,TTFB也受到用户的网络环境影响,因此在全球范围内提供一致的TTFB性能可能需要一 些额外的工作,如使用全球CDN。
26、前端如何性能优化
- 性能检测
- 加载时检测
- 哪里性能交互需要优化的,可以使用谷歌浏览器的lighthouse、performance功能进行性能分析。
- (moreTools->Performance monitor->(CPU usage+js heap size+DOM nodes))
- network调出Priority优先级
- 再调成3G网络再测一遍。netWork->wifi图标->network conditions->newwork throttling设置
- 关闭页面第二次打开看是否有缓存优化
- 多场景测试可以用webtest测试
- 哪里性能交互需要优化的,可以使用谷歌浏览器的lighthouse、performance功能进行性能分析。
- 运行时检测复合谷歌测量模型RAIL
- 操作页面50ms反馈,输入框100ms反馈,(R:Response)
- Animation动画每秒60帧,(A:Animation)moreTools->rendering->(Frame Rendering Stats+ Paint fashing)
- 保持空闲,处理少量长任务(I:Idle空闲)
- 5秒内加载完成并可交互(L:Load加载)
- 用谷歌浏览器查看接口,主要看四点:
- DNS解析时间
- 与服务器建立链接时间
- 后端返回时间
- 资源下载时间
- 测量代码
测量代码
js<script> window.addEventListener('load',(e)=>{ let timing = performance.getEntriesByType('navigation')[0] console.log('DNS 解析耗时',timing.domainLookupEnd-timing.domainLookupStart) console.log('TCP 连接耗时',timing.connectEnd-timing.connectStart) console.log('SSL 安全连接耗时',timing.connectEnd-timing.secureConnectionStart) console.log('网络请求耗时 (TTFB)',timing.responseStart-timing.requestStart) console.log('数据传输耗时',timing.responseEnd-timing.responseStart) console.log('DOM 解析耗时',timing.domInteractive-timing.responseEnd) console.log('资源加载耗时',timing.loadEventStart-timing.domContentLoadedEventEnd) console.log('First Byte时间',timing.responseStart-timing.domainLookupStart) console.log('白屏时间',timing.responseEnd-timing.fetchStart) console.log('首次可交互时间',timing.domInteractive-timing.fetchStart) console.log('DOM Ready 时间',timing.domContentLoadEventEnd-timing.fetchStart) console.log('页面完全加载时间',timing.loadEventStart-timing.fetchStart) console.log('http 头部大小',timing.transferSize-timing.encodedBodySize) console.log('重定向次数',timing.redirectCount) console.log('重定向耗时',timing.redirectEnd-timing.redirectStart) }) </script>
- 加载时检测
- 代码优化
- 资源加载优化
- 代码压缩:压缩HTML、CSS和JavaScript文件,减少文件体积。
- 代码分割:使用代码分割(特别是对于单页应用),仅加载用户所需的代码。
- 资源加载:
- 懒加载:对图片、视频和非关键脚本实施懒加载,只有当它们出现在视口中时才加载。
- 预加载:使用优先级最高的<1ink rel="preload">预加载关键资源,确保它们尽早可用。prefetch空闲加载
- 类库选择按需加载
- 避免阻塞渲染的CSS和JS:将CSS放在中,将非关键JS延迟加载
- js的defer延迟加载和async异步加载。
- 使用ServiceWorkers缓存资源和提高加载速度,实现离线体验
- 使用react-snap预渲染出路由页面
- 长列表渲染react-window
- 图像优化
- 格式和压缩:使用现代图像格式(如WebP),并对图像进行适当压缩。
- 响应式图像:使用不同尺寸的图像以适应不同设备和屏幕尺寸(css中使用imgset自动决策1x2x),img标签的srcset属性
- SVG优化:对于简单矢量图形,使用SVG格式。
- 雪碧图减少http请求次数
- 大图使用渐进式库:progressive-image、imageMagick、libjpeg、jpegtran、jpeg-recompress、imagemin
- react-lazy-load-image-component组件图片懒加载
- 图片对比
- jpeg 不需要透明图片的场景,有锯齿模糊,不适合logo
- png 需要透明业务场景
- imagemin-pngquant
- png8--256色+支持透明
- png24--2^24+不支持透明
- png32--2^24+支持透明
- webp 只有安卓支持
- iconFont简单图标
- svg 移动端图标,代码内嵌,体积小、只能做简单的图片
- PNG到IconFont
- 多个png可以一套字体解决,价绍请求数量和体积
- 矢量图形
- 可通过css修改颜色,大小
- 颜色单调,无法实现彩色
- iconFont到svg
- 支持多色彩
- 矢量图形
- xml语法SEO可阅读
- 渲染优化
- 减少DOM操作:优化JavaScript以减少对DOM的频繁操作。
- 避免长任务:避免执行时间超过50毫秒的长任务。
- CSS优化:优化CSS选择器,减少布局重排和重绘。
- 使用骨架屏
- Progressive Web App(PWA)
- 服务工作者:
- 离线体验:提供基本的离线浏览体验。
- 使用https:/guess-js.github.io/
- 基于AI的方式分析落地页然后配合quicklink进行preload
- 资源加载优化
- 服务器优化
- 硬件优化
- 扩充内存
- 使用固态硬盘
- 购买DNS解析服务
- 使用CDN
- 软件优化
- 减少HTTP请求:合并资源(如CSS和JavaScript文件)减少请求数量。
- nginx优化方式
- 设置nginx把CPU内核占满
- 节约
内存
和磁盘
,nginx只记录必要的日志
,像请求转发的时候不要缓存请求头,请求体
。 - 限制每分钟
最大请求数
- 快速失败,快速返回
- 负载均衡
- 启用GZIP或Brotli压缩
- 开启https
- serviceWorker
- 数据库优化:优化数据库查询,减少查询时间。
- 硬件优化
架构
1、技术共同点
- 创建项目,配置别名、代码格式化、配置接口请求
- 页面逻辑处理,变量、判断、循环+方法
- 样式处理,内联样式和动态class
- 生命周期,单组件,父子组件
- 父子组件交互,互相传值,互相调用方法
- 表单处理
- 路由处理,跳转传值、路由过滤器
- 全局变量store
2、提升效率的手段
- 硬件
- 双屏显示器(代码、设计稿、浏览器、沟通工具)
- 支持宏定义鼠标(前进、后退、自定义宏)
- 软件
- css架构
- 浏览器插件,webstorm和vscode插件
- 代码块
- 低代码生成工具
- 设计稿生成代码工具
- 公用组件
3、开发遇到过哪些问题
- 技术更新
git
代码丢失- 安全类,手机app被劫持
4、首屏优化
- 服务器硬件:
带宽
、磁盘吞吐速度
、内存大小
、cpu计算速度
、CDN
- 服务器软件:使用纯净版
Linux
,比如centos和redhat、nginx
- 设置nginx占满
CPU资源
- 节约
内存
和磁盘
,nginx只记录必要的日志
,像请求转发的时候不要缓存请求头,请求体
。 - 限制每分钟
最大请求数
- 设置nginx占满
- 前端代码上的优化
- 一个是使用
SSR
,服务器生成出html文件传给前端,然后再由react
或者vue接管
- 另一个就是前端
根据路由
打包资源 - 再通过
webpack
或vite
资源进行一个压缩
优化 - 最后把首页的资源放入
CDN
- 一个是使用
4、你做项目经理,你会怎么做?
先捋清楚手头上工作,排出优先级,重要的事情先做起来,然后了解公司的规章制度、考核指标等,不能犯原则上的错误 接着是提高团队战斗力和凝聚力,我有4个标准可以做参考
- 第一个硬件标准
两块显示器
是标配,护颈
、护腰
、午休枕头
。
- 软件标准
翻墙工具
- 浏览器收藏夹
常用网站
,常用插件
开发工具
和好用的插件
- 提升开发质量
- 该
注释
的要注释,vue的data和react的state css
用架构- 常用
utils工具
- 该
- 提升开发速度
代码块
和生成代码
的插件低代码
人工智能
5、介绍自己
- 面试官你好,我叫曹征前,“
计算机专业
”毕业,工作了14年,从事
了13年的软件开发,使用过java、php、vue、react等技术。 - 刚开始在一家公司外派到
上海农商银行
,开发柜面业务,比如存款
、对账单
、二手房
等系统。 - 后来去了一家小公司,
- 用php为公司
从0到1
建设官网
、公众号
、运营平台
, - 对接
外包供应商
,为公司开发移动APP
。
- 用php为公司
- 接着又去了
一家车联网企业
,- 使用
react
、reactnative
和安卓原生
开发车机和PC端
应用 - 公司申请了
两个车辆
相关的专利
。
- 使用
- 接着又去了一家外包公司做
前端开发+管理
,- 带领团队开发
太平洋保险
、友邦保险
、大地车险
、同方人寿
等项目 - 技术涉及比较广
VUE
、react
、react-native
、jquery
都有涉及 - 也涉及
手机移动端
、平板
、PC浏览器
多终端开发。
- 带领团队开发
4年前入职德邦物流
,- 负责公司
核心业务
系统的界面开发与优化 - 实现可复用的
前端组件库
- 构建并维护
低代码
开发平台
- 负责公司
- 我具备
独立
实施前端架构
、开发
、安全保障
、浏览器兼容
、性能优化
以及部署
的能力 - 熟练
掌握前端技术栈
,深入理解浏览器
、服务器
·以及
主流框架`如Vue和React的技术内核 - 拥有
跨平台
前端项目经验,包括PC端
、移动端(Android/iOS/H5)
、微信公众号
及车载系统
等。
6、为什么要离职?
前年京东收购德邦
,德邦
的系统
与京东物流
的系统
是重叠
的,然后京东
开始对德邦进行了人员优化
,剩下人都要身兼数职
,我是前端
兼职测试
,白天
开会评需求
,写用例
,搞测试
、晚上发行版本
,验证上线功能
,- 测试的活太耗时间和精力了,我
无法实现
我作为一个前端的价值
,
7、遇到过哪些问题,如何解决的?
技术发展太快
,一年一小变,两年一大变,三年推翻重来- 我总结了一套学习方法
- 新技术
学习成本越来越大
、 老技术停止更新,维护成本
变大 - 前端实现一个功能的
写法太多
,没有之前的mvvm
或者类似后端的MVC
等规范约束
8、你如何理解低代码?
低代码其实是我个人的一个研究方向,我觉得低代码按照范围来分类的话有8种
webstrom插件
,专门给程序员使用的低代码,用来辅助生成页面。字段属性
低代码配置,比如我在德邦写的一个项目,经常批量调整输入范围,直接开发一个低代码平台给业务配置,省去了我们频繁上版本的烦恼字段
低代码配置,给出页面,完成下拉框和下拉框的值的配置,比如我在德邦写的标签管理项目,管理员自己配置标签类型和属性,下拉框和选择值,然后各个系统页面加一个字段就行了,不需要所有产品线每次按照业务需求去添加标签。组件
低代码配置,范围就更大一点,比如一般官网配置,涉及轮播图模块、底部导航栏模块,还有后台管理的权限管理模块,创建一个角色,给角色添加权限,然后把角色分配给对应的员工或部门表单页面
和设计稿生成代码
低代码配置,比如拖拉拽,把图片和文字拖过来合成页面,或者拖拉拽文本、按钮生成表单页面,我之前在银行就用的这种表单生成低代码的工具业务页面
是根据成熟业务,选择页面进行低代码配置,比如业务要出一个摇奖页面,只需要配置一等奖是什么有多少个名额,依次设置二等奖和三等奖,再选择样式,使用老虎机,还是大转盘,最后自动生成营销摇奖页面项目
低代码配置,类似php的商城网站,微信公众号网站、等等,只需要将文件放在服务器,执行指定脚本,自动数据库建表,生成网站,并且后续更新按照模块插件进行下载,我在昆山那家公司就是用这类php低代码平台为公司搭建了微信公众号、官网和运营网站。- 还有一个方案就是
人工智能
,比如Copilot
9、对前端工程化的理解
- 前端工程化包含多个方面,旨在通过系统化、标准化、自动化 来提升前端的开发效率、开发质量和可维护性。 它不仅涉及技术和工具、还包含团队协作、开发流程和最佳实践的整合
- 代码模块化和UI组件化
- 将复杂代码拆分为独立,可重用的模块
- UI组件化将页面元素抽取为可重用的组件
- 自动化,
- 构建工具自动化:包括构建工具自动化使用webpack、Vite
- 代码质量工具:使用eslint、pretter
- 测试工具使用jest,Cypress等
- 标准化
- 组件标准化:定义组件的命名、文档等
- 流程标准化:代码审查、合并和发布的流程标准化
- 性能优化
- 加载时性能优化:代码拆分、懒加载、预加载
- 运行时性能优化:包括优化渲染过程、减少重绘回流等
- 持续集成CICD
- 跨平台开发
- 响应式开发,确保兼容不同设备和不同分辨率
- 框架使用react-native和flutter进行跨平台开发
- 代码模块化和UI组件化
Js
1、闭包
- js变量属于本地或全局作用域,全局变量能够通过闭包实现局部私有变量,而且闭包调用多次产生多个作用域数据互相不影响,我们的组件都是闭包
- 表现形式是一个函数方法作为变量被返回,这样可以实现私有变量,只能通过返回的方法去修改。
- 闭包也有个缺点,常驻内存得不到释放
2、原型和原型链
- 每个class都有一个显示原型property,如果new一个该class的实例,那么这个实例的隐式原型_proto_指向class的显示原型property,
- 当实例执行一个方法的时候会先在当前实例寻找该方法,如果找不到就会通过隐式原型去class对象的显示原型里寻找
3、微任务宏任务
- 因为JS是单线程一行行的执行代码 所以异步需要实现
- 遇到异步,比如(定时、网络请求)会基于even loop将任务移动到callback队列中
- 当同步代码执行完毕,执行微任务,比如promise
- 微任务执行完毕尝试渲染DOM
- 这时候evenloop开始执行宏任务,将callback队列中的异步移过来执行
- 代码
代码
jsasync function async1(){ console.log('async1 start') await async2() console.log('async1 end') } async function async2(){ console.log('async2 start') } console.log('script start') setTimeout(()=>{ console.log('setTimeOut') }) async1() new Promise(resolve=>{ console.log('promise1') resolve() }).then(()=>{ console.log('promise2') }) console.log('script end')
4、函数call和apply、bind的区别
- 都是修改this指向,都不会修改原先函数的this指向
- call和apply临时修改一次,bind是返回一个新函数 传参不同,call和bind是一个个传入,apply支持数组
5、this的四个场景
- 普通函数,没有对象去调用,this指向window严格模式为undefined
- 事件函数和自定义对象,谁调用,this就指向谁,比如document.onclick=function(){},这个function里的this就指向document
- 箭头函数里的this指向他父作用域的this
- this在new出来的class对象实例中,基本是指向new出来的对象实例
TS
初始化生成
基本类型
- 直接写法
- ts
const putong:boolean = true //// string number boolean symbol bigint null undefined
- type写法
- ts
type putongType=string const putong:putongType = 'abc'
[]
- 限制类型
- 直接写法
- ts
const arr22:number[] = [1,2] const arr2: Array<number> = [1,'hello'];
- type写法
- ts
type arrT =(number|string)[] const arr:arrT=[1, 'hello'] type arrT1 = number|string const arr2: Array<arrT1> = [1,'hello'];
- 限制类型和个数
- 直接写法
- ts
let arr3: [number,number, string] = [1, 2, 'hello',];
- type写法
- ts
type arrT = [number,number, string]; const arr4: arrT = [1, 2, 'hello',];
- [{}]
- 直接写法
- ts
const obj: {username?: string, age?: number}[] = [{username:'zhangsan',age:12}];
- type写法
- ts
type arrT = {username?: string, age?: number}[] ; const arr4: arrT = [{username:'zhangsan',age:12}];
{}
- 约束每个选项类型
- type对象ts
type A = { username?:string, age?:number } let a:A ={} as A //初始化空对象 a={ username:'zhangsan', age:12 }
- type对象
- '{[]}'
- type对象ts
type objT = {username?:[string,number],age:number} let a:objT = {} as objT//初始化空对象 a={ username:['abc',12], age:12 }
- type对象
- 交叉类型
- type交叉ts
type A = { username: string } type B = { age: number } let a: A&B ={} as A&B a= { username: 'xiaoming', age: 20 }
- type交叉
function
有返回值
- 箭头函数ts
//第一种写法
const foo: (n: number, m: number) => string = (n, m) => { return n + m + '' } foo(1,2) 2. 普通function
ts //第一种写法 const foo1 = function (n: number, m: number): string{ return n+m+''; } //第二种写法 function foo1 (n: number, m: number): string{ return n+m+''; } foo1(1,2) ```
无返回值
- 箭头函数ts
//第一种写法 const foo: (n: number, m: number) => void = function(n, m){ console.log(m,n); } foo(1,2) //第二种写法 const s = (n: number, m: number)=>{ console.log(m,n); } s(1,2)
- 普通functionts
//第一种写法 function foo1 (n: number, m: number): void{ console.log(m,n); } foo1(1,2) //第二种写法 function foo1 (n: number, m: number){ console.log(m,n); }
重载待完善
interface和type区别
- interface只能对象,type不仅能对象,还能基本类型
- 合并对象类型的时候,type只能交叉,interface能通过继承和合并实现
- 代码ts
// type的交叉 type A = {username: string} type B = {age: number} let a: A&B ={} as A&B a= { username: 'xiaoming', age: 20 } // interface合并 interface A {username: string;} interface A {age: number;} const a:A = { username:'zhangsan', age:12 } // interface继承 interface A {username: string} interface B extends A {age: number} const b: B = { username: 'xiaoming', age: 20 }
- 代码
CSS
2、注释
2、 ios元素fixed在底部,有什么影响?
3、 BFC
4、常用的选择器
5、伪类和伪元素的区别
React
1、前端的状态库的原理和区别
- Redux
- 原理:使用单一不可变状态树(store)来管理状态。它通过纯函数(reducers)来描述状态的变化。
- 特点:高度可预测、透明且易于测试。有强大的中间件支持。
- 适用场景:适合大型应用,特别是当需要高度可预测的状态管理时。
- MobX
- 原理:MobX基于可观察(observable)对象和自动追踪依赖(autorun)的机制来管理状态。
- 特点:简单易用,自动追踪状态变化。代码量相对较少。
- 适用场景:适合追求简洁代码和灵活状态管理的应用。
- Vuex (Vue.js)
- 原理:Vuex专为Vue.js设计,基于单一状态树和严格的同步更新。
- 特点:与Vue.js深度集成,易于理解和使用。
- 适用场景:适用于中大型Vue.js应用。
- Context API (React)
- 原理:React的Context API允许组件共享状态,而无需显式地通过组件树传递。
- 特点:简单,无需额外库。适用于轻量级的状态共享。
- 适用场景:适合React应用中的简单状态管理。
- Zustand
- 原理:Zustand是一个轻量级的状态管理库,使用原生的React hooks。
- 特点:简洁、无样板代码,灵活性高。
- 适用场景:适合需要快速开发且状态管理不过于复杂的React应用。
- Recoil,jotai
- 原理:Recoil是为React应用设计的状态管理库,使用原子(atoms)和选择器(selectors)来管理状态。
- 特点:可以更细粒度地控制状态,与React的ConcurrentMode兼容。
- 适用场景:适用于需要更细致状态控制的React应用。
- XState
- 原理:XState是基于有限状态机(Finite State Machines, FSM)和状态图(Statecharts)的状态管理库。它提供了一种形式化的方法来建模应用状态。
- 特点:显式状态管理:状态及其转换在状态机中被显式定义,这使得状态的管理更加可预测和可理解。
- 可视化工具:XState提供了可视化工具来帮助开发者设计和理解状态机。
2、高阶组件(HOC)原理,业务中解决问题。
- HOC本质上是一个函数,它接收一个组件作为参数,然后返回一个新的组件,返回的新组件将拥有被包裹的组件的所有props,并且可以添加额外的props或状态。
- 可用业务:
- 授权和权限管理:例如,你可能有一些组件只允许认证过的用户访问。你可以创建一个HOC,它接收一个组件并返回一个新的组件,新的组件在渲染之前会检查 用户是否已经认证。
- 数据获取:另一个常见的HOC用例是用于数据获取。例如,你可以创建一个HOC,它接收一个组件并返回一个新的组件,新的组件在挂载时获取数据,并将数据 通过props传递给被包裹的组件。
- 错误处理:你可以创建一个HOC,它接收一个组件并返回一个新的组件,新的组件包裹原组件的渲染,并在发生错误时显示错误信息或其他备用内容。
3、什么时候应该使用类组件而不是函数组件?React组件错误捕获怎么做?
- 在早期的React版本中,类组件和函数组件有明显的区别。类组件提供了生命周期和state, 而函数组件则 是无状态的,并且没有生命周期方法。 因此,如果你的组件需要维护状态或者需要使用生命周期方法,那么你需要使用类组件。
- 从React16.8版本开始,引入了Hooks特性,允许在函数组件中使用状态(useState)和生命周期方法(useEffect)。 所以,目前你几乎在所有场景下都可以使用 函数组件替代类组件。
- 有一个情况下,你可能还需要使用类组件,那就是错误边界(Error Boundaries)。 错误边界是React中的一种特性,它允许你在子组件树中捕获JavaScript错误, 并在发生错误时显示备用内容,而不是让整个组件树崩溃。 错误边界在React中只能通过类组件来实现。
- 代码
代码
jsclass ErrorBoundary extends React.Component{ constructor(props) { super(props); this.state = { hasError: false }; static getDerivedStateFromError(error) { //当发生错误时,更新状态使下一次渲染显示备用UI return { hasError: true }; } componentDidCatch(error, info) { //你也可以将错误日志上报给一个错误报告服务 logErrorToMyService(error, info); ) render(){ if (this.state.hasError) { //你可以自定义备用UI return <h1>Something went wrong.</hl>; return this.props.children; }}
这样子使用
js<ErrorBoundary> <MyWidget /> </ErrorBoundary>
4、React中如何创建Refs?创建Refs的方式有什么区别?
- Refs主要用于获取和操作DOM元素或者React组件的实例,是一种逃脱props传递的方法, 函数组件中,使用React.useRef()和回调Refs来创建Refs。Compents中使用React.createRef() 和回调Refs。
- React.useRef()创建的Ref更简洁,而且Ref的值在组件的整个生命周期中保持不变。 回调Refs更灵活,它允许你在组件挂载和卸载时执行一些额外的逻辑。 但是,如果回调函数是在 render 方法中定义的,那么每次render时都会创建一个新的函数实例,可能会导致一些性能问题。
5、createContext解决了什么问题?React父组件如何与子组件通信?子组件如何改变父组件的状态?
- React的 createContext API主要解决了props"穿透"的问题,就是需要把一个prop层层传递给深层嵌套的子组件。 这在应用中有一些全局可用的数据时(如主题,用户 信息等)非常有用。 通过创建一个context,你可以让组件直接访问这些数据,无需通过props层层传递。
- 父组件通过props向子组件传递数据和函数。子组件可以通过props获取到这些数据和函数。
- 子组件无法直接改变父组件的状态。但是,父组件可以通过props向子组件传递一个函数,子组件调用这个函数就能间接地改变父组件的状态。
6、memo有什么用途,useMemo和memo区别是什么?useCallback和useMemo有什么区别?
- React中的memo是一个高阶组件,它用于优化组件的渲染性能。memo可以将一个纯函数组件(无状态组件)包装起来,以避免不必要的重新渲染。
- useMemo专用的hooks,缓存值。useCallback一般缓存我们的函数。
- React.memo是一个高阶组件,它类似于React.PureComponent,但只适用于函数组件,不适用于类组件。 React.memo对一个组件进行封装,使其仅在props 改变时进行重新渲染,而不是每次父组件重新渲染时都进行渲染。 这样可以避免不必要的渲染,提高性能。
- useMemo 和 memo 的区别:
- useMemo 是一个Hook, 它用于避免执行昂贵的计算操作。当依赖项改变时, useMemo 将重新计算缓存的值。
- React.memo是一个高阶组件,它用于免函数组件进行不必要的重新染。
- 简单来说,useMemo是用于优化计算操作,而React.memo是用于优化渲染。
- usecallback和useMemo区:
- useCallbackuseMemo优化的Hook,但它们用于优化的对象不同。
- usecallback返回一个memoized记忆过的回调函数
- 用于返回一个memoized记忆过的值
- 如果你有一个依赖于某些值的函数,并且你想防止这个函数在这些值未改变时被重新创建,那么你应该使用useCallback
- 如果你有一个依赖于某些值的昂贵计算,你想防止这个计算在这些值未改变时被重新执行,那么你应该使用useMemo
7、React中的状态管理库你如何选择?什么是状态撕裂?useState同步还是异步?
- Redux作者的加入useStae底层也被彻底重写都是基于useReducer
- 在react18版本以前在同步环境中异步,在异步环境中同步。在react18版本以后,setState()不论在同步环境还是异步环境都是异步的。
- setState本身并不具备绝对的同步/异步概念。比如:在promise的then()方法中、setTimeOut()、setinterVal(),ajax的回调等异步环境中,setState就是同步的。同步环 境下就是异步的。
- react会有一个上下文环境,在同步环境中,setState处于react的上下文中,react会监控动作合并,所以这个时候setState()是异步的。 而在异步环境中,比如promise的then()方法中、setTimeOut()、setInterVal()中,react实际上已经脱离了react的上下文环境。所以setState()是同步执行的
- Redux:这是最常见的状态管理库,适用于大型应用和需要跨组件共享状态的情况。Redux提供了一个集中式的状态存储,可以让你在任何地方访问和修改状态。 如果你的应用状态逻辑比较复杂,或者你需要在应用的不同部分共享大量的状态,那么Redux可能是一个好选择。
- MobX:这是另一个常见的状态管理库,它采用更灵活的、响应式的状态管理模型。与Redux的"单一数据源,不可变状态"的原则不同,MobX允许有多个状态 源,并且状态可以是可变的。 如果你对Redux的严格性有些不满,或者你需要一个更加适应快速原型开发的状态管理库,那么MobX可能是一个好选择。
- Context API和useState/useReducer Hooks:对于一些小型应用或组件,你可能不需要一个完整的状态管理库。 React的Context API和Hooks(例如 useState和useReducer)可能已经足够满足你的需求。 使用这些React内置的功能,你可以在组件间共享状态,而无需添加额外的依赖。
- 当然,还有Jotai,Jotai是一个较新的状态管理库,旨在提供一个原子状态管理的解决方案。其核心理念在于将状态分解为最小的、可组合的单元,即原子。 Jotai的主要目标是提供一个简单且轻量的全局状态管理工具,它对Concurrent Mode完全友好,并尝试解决React状态共享的问题。 Jotai的API极其简 单,它将状态分解为"atom"(即最小的状态单元)。 这个库的优势在于其原子状态可以被细粒度地订阅,因此,只有当状态改变时,依赖这个状态的组件才会重新渲染,这避免了无效的组件重新渲染。 如果你的应用有许多独立但又需要共享的状态,Jotai可能是一个很好的选择。
- "状态撕裂"是指在并发渲染(Concurrent Mode)中,由于渲染的优先级不同,可能导致应用中的不同部分看到的同一份共享状态不一致的问题。 这是因为在 Concurrent Mode下,React可以选择暂停、中断或延迟某些更新,以优先处理更重要的更新。如果你的状态更新和组件的渲染不是同步的,那么就可能出现状态撕裂的问题。 React团队正在开发一个新的特性(React Server Components)和新的 Hook(如useTransition和useDeferredValue)来帮助开发者解决这个问题。
8、React新老生命周期的区别是什么?合并老生命周期的理由是什么?
- react 生命周期
- 创建的时候: constructor、getDerivedStateFromProps、ComponentDidMount
- 更新的时候: getDerivedStateFromProps、shouldComponentUpdate、BeforeUpdate、componentDidUpdate
- 销毁的时候: componetWillUnmount
- ·React16.3版本以后, 对组件的生命周期函数进行了一些修改和增强, 主要出于优化异步渲染和性能的考虑。
- 移除的生命周期方法: componentWillNount、componentWillReceiveProps和 componentWillUpdate。 这些生命周期方法在新的版本中被认为是 不安全的。 因为在异步渲染(React16.3引入的新特性)中,它们可能会被意外地多次调用。使用它们可能会导致一些难以调试的问题。
- 新增的生命周期方法:getDerivedStateFromProps和getSnapshotBeforeUpdate。 这两个方法是为了替代被移除的生命周期方法,同时提供更好的异 步渲染支持。
- 修改的生命周期方法:componentDidUpdate和componentDidCatch,它们的功能没有改变, 但是它们现在会在提交阶段被调用,这是异步渲染引入的新 阶段。
- 被认为是安全的生命周期方法: componentDidMount、shouldComponentUpdate和componentWillUnmount。 这些生命周期方法并没有改变,而且在 异步渲染中被认为是安全的。
- 合并老生命周期的理由:
- 老生命周期函数在异步渲染模式下存在一些潜在的问题, 比如可能会多次调用componentWillUpdate和componentWillReceiveProps,这可能会导致状态的不一致。 为了解决这些问题,React团队提出了新的生命周期函数,以更好地支持异步渲染和性能优化。
- 新的getDerivedStateFromProps生命周期函数在每次渲染前都会被调用,包括初始化渲染和后续更新,这使得组件可以在渲染前更新状态,以此来替换 componentWillReceiveProps的部分功能。 getSnapshotBeforeUpdate则在DOM更新前被调用,能够在可能的情况下捕获一些DOM信息,以此来替代 componentwillupdate的部分功能 这些改变使得React的生命周期函数更易于理解,同时提供更好的性能,并更适合未来React的发展,包括异步渲染等新特性。
9、在React中什么是Portal?
- 在React中,Portal提供了一种将子节点渲染到存在于父组件DOM层次结构之外的DOM节点的方式。
- 在很多场景下,当父组件有一些样式(比如:overflow:hidden或z-index)时,这些样式可能会影响或剪裁其子组件的布局表现。 常见的应用场景如模态框 (Modal)、提示框(Tooltips)等,我们期望这些组件能够“跳出"其父组件的容器, 渲染到DOM树的顶层,从而避免这些问题。这就是Portal的作用。
- 你可以使用ReactDOM.createPortal方法创建一个portal,这个方法有两个参数: 第一个参数(child)是任何可渲染的React子元素,比如一个元素,字符串或 者fragment; 第二个参数(container)是一个DOM元素。
10、自己实现一个Hooks的关键点在哪里?
- 创建自定义Hook本质上就是创建一个JavaScript函数, 但这个函数需要遵循两个主要的规则:
- 函数名称必须以use开头:这是一个命名约定,React依赖于此约定来自动检查你的Hook是否遵循Hook的规则。
- 只能在函数组件或者其他Hook中调用Hook:这是因为React需要保持Hook调用的顺序一致以正确地维护内部状态。
- 如果在TypeScript中编写自定义Hook, 需要指定一些类型信息以确保代码的正确性和利于代码的理解:
- 为Hook的参数指定类型:你应该为Hook的输入参数指定类型。
- 为Hook的返回值指定类型:对于Hook返回的对象或其他值,
- 为内部状态和函数指定类型:使用useState 或 useReducer 数和返回值提供类型注解。
- 注意useCallback、和return元类型的结构。
11、React和其他框架对比优缺点是什么?你们团队选择React的理由是什么?
- 优点
- React是一个用于构建用户界面的JavaScript库,由Facebook大团队维护,已经在很多知名项目和公司中得到广泛使用。
- 丝滑函数组件和hooks:React通过组件化的方式帮助开发者构建复杂的用户界面。每个组件都有自己的状态和逻辑,可以单独测试和重用。
- 性能:并发更新模式、FID的提前
- 强大的社区支持:由于其广泛的使用,React拥有大量的开源库,丰富的学习资源和活跃的社区。
- 方便的状态管理:React提供了Context API和Hooks,简化了状态管理。另外,也可以使用Redux、MobX、Jotai等状态管理库。
- 缺点:
- React只是视图展:相对于像Angular这样的完整框架,React只关注视图层,这意味着开发者需要选择其他库来处理路由和状态管理等功能,这可能会增加项目 的复杂性。
- 频繁更新:React社区非常活跃,经常会有新的特性和改进,然而,这也意味着开发者需要不断学习新的概念和最佳实践。
- 学习曲线相对陡峭:想维护一个高性能的React应用需要你掌握非常详细的React使用技巧,比如配合WhyDidYouRender
- 至于为什么选择React,这通常取决于团队的具体需求和偏好。 以下是一些常见的理由:
- 技术栈的一致性:如果团队已经在其他项目中使用了React,那么选择React可以保持技术栈的一致性,提高开发效率。
- 团队的技能和经验:如果团队成员已经熟悉React,那么选择React可以减少学习新框架的时间。
- 项目需求:如果项目需要构建复杂的用户界面,并且希望有更高的灵活性,那么React可能是一个好选择。
12、React16/17/18都有哪些新变化?useTransition是啥提解决了什么?
- React16的主要更新是引入了新的核心算法Fiber架构,它提供了如下一些新特性和更新:
- 错误边界(Error Boundaries):错误边界是React16中引入的一种新的错误处理机制,让你可以捕获和打印发生在子组件树任何地方的JavaScript错误,防止 整个应用崩溃。
- Fragments和Strings: React 16允许组件可以返回多个元素(Fragments)或者字符串。
- Portals:Portals提供了一种将子节点渲染到存在于父组件DOM层次结构之外的DOM节点的方式。
- 更好的服务器端渲染:包括新的SSRAPI,支持流式渲染和组件缓存。
- React17没有引入新的特性,但它做了许多更改来使React更容易升级, 并且支持在同一个应用中运行多个版本的React。
- Event Delegation的改变:在React17中,React不再将事件处理程序附加到document,而是附加到根DOM容器。
- Gradual Upgrades:React17的一个主要目标是使React的升级更加平滑,允许在同一应用中使用不同版本的React。
- React18做了很大的改变将如并行和模式和用户自定义控制优先级等进行了正式发版:
- 并发模式(Concurrent Mode):并发模式是一个让React能在渲染过程中让出控制权给浏览器,以便浏览器能及时处理用户交互的新模式。
- React Server Components:服务器组件是一种只在服务器上运行,无需发送到客户端的新组件类型,它旨在提升渲染性能和减少客户端代码。
- useTransition是在并发模式下使用的一个新Hook,它可以让你避免在UI中创建阻塞的视觉更新。例如,在数据加载时,你可能希望先显示一个加载的指示器, 等数据加载完再更新UI。在这种情况下,useTransition可以让你的应用保持响应,并且让视觉更新的过程看起来更加平滑。
13、react渲染原理
- 流程展示
图片
- 初始化阶段:
- 创建根Fiber节点,代表整个React应用的根组件。
- 调用根组件的render方法,创建初始的虚拟DOM树。
- 调度阶段(Scheduler):
- 使用调度器(Scheduler)调度更新,决定何时执行更新任务。
- 检查是否有高优先级任务需要执行,如用户交互事件或优先级较高的异步操作。
- 根据优先级确定任务执行顺序,并根据任务的优先级将任务添加到不同的任务队列(lane)中。
- 协调阶段(Reconciliation):
- 从任务队列中取出下一个任务。
- 对任务中涉及的组件进行协调,比较前后两个虚拟DOM树的差异,找出需要更新的部分。
- 使用DOMdiff算法进行差异计算,生成需要更新的操作指令。
- 生命周期阶段(Lifecycle):
- 在协调阶段和提交阶段,React会根据组件的生命周期方法调用相应的钩子函数。
- 生命周期方法包括componentDidMount、componentDidUpdate等,用于处理组件的生命周期事件。
- 渲染阶段(Render):
- 在Render阶段,React会根据组件的状态变化、props的更新或者父组件的重新渲染等触发条件, 重新执行组件的函数体(函数组件)或者render方法(类组件)。 当React执行函数组件或render方法时,它会检测组件中是否包含了Hooks, 如果包含了Hooks,那么React会根据Hooks的顺序依次调用它们。
- Hooks执行:
- 在Render阶段,React会根据组件中Hooks的顺序,依次执行每个Hooks函数。
- Hooks函数可能包括useState、useEffect、useContext等等,这些Hooks函数会在组件每次更新时被调用,让你能够在函数组件中使用状态、副作用和上下文等特性。
- (批处理setState合并一次setTimeout 16阶段不支持了React18自动化批处理)
- Commit(提交)阶段:
- 在Commit阶段,React将Render阶段生成的更新应用到真实的DOM中,完成页面的渲染。
- 在Commit阶段,React可能会执行一些其他操作,比如调用生命周期方法(如componentDidMount、componentDidUpdate等)或执行其他副作用。
- 重复步骤2-7:根据应用程序的交互和状态变化,React会重复执行调度、协调、渲染、提交的步骤,实现更新的循环流程。
14、Fiber架构原理你能细致描述下么?
- Fiber是React 16中引入的新的调和(reconciliation)引擎。在这个新的架构下,React能够做到调度和优先级, 使得React可以在执行过程中暂停、中断和恢复工 作, 从而实现了时间切片(timeslicing)和并发模式(ConcurrentMode)等特性。
- Fiber的核心原理可以用以下几个关键概念来理解:
- FiberNode:在Fiber架构中,每一个React组件都有一个对应的Fiber节点,它是一个保存了组件状态、组件类型和其他信息的对象。 每一个Fiber节点都链 接到一个父节点、第一个子节点、兄弟节点,形成了一个Fiber树。
- 双缓冲技术:Fiber架构中采用了双缓冲技术,即有两棵Fiber树:当前在屏幕上显示的Fiber树(current Fiber tree)和下一帧要显示的Fiber树(work-in- progress Fiber tree)。 这种方式避免了在渲染过程中直接修改DOM,提升了性能,并能在出现错误时回退到稳定状态。
- 工作循环:React的渲染过程可以看作是一个工作循环。在这个循环中,React会遍历Fiber树,为每个节点调用对应的生命周期方法,并生成对应的DOM更新。 如果在遍历过程中,有更高优先级的更新出现,React可以将当前的工作暂停,去处理更高优先级的更新。
- 时间切片和暂停(React18去掉了时间切片,改成了微任务+宏任务):在Fiber架构中,React将渲染工作分解为多个小任务,每个任务的执行时间不超过一个 值, 这使得React可以在长时间的渲染任务中,让出控制权给浏览器,处理其他更重要的工作, 如用户的输入和动画。这就是所谓的“时间切片”。
- 优先级和并发:在工作循环中,不同类型的更新可以有不同的优先级。 例如,用户的交互会有更高的优先级,因为它们需要立即响应。 这使得React能在需要的时候,打断当前的工作,去处理更紧急的任务。这就是所谓的“并发"。
- 错误边界:在Fiber架构中,如果一个组件在渲染过程中发生错误, React会寻找最近的错误边界组件,并将错误传递给它,错误边界组件可以捕获这个错误, 并显示一个备用的UI,防止整个应用崩溃。
15、React Scheduler核心原理React 16/17/18变化都有哪些?Batching在这个阶段里么,解决了什么原理是什么?
- React Scheduler是一个React内部的任务调度库。 它主要用于在长期执行的渲染任务中切分任务,让浏览器在执行长期任务的空闲时间内有机会处理其他的任务, 比 如用户输入和动画,以提高应用的响应性。这也是React中时间切片(TimeSlicing)的核心实现。
- 在React16中,Scheduler作为一个实验性的库被引入,用于实现新的Fiber架构和时间切片。
- 在React17中,Scheduler并没有明显的变化,React17主要在于更改了事件系统,使得React能和其他JavaScript库更好的共存。
- 在React 18中Scheduler将被更完整的利用,以实现并发模式(Concurrent Mode)和新的Suspense特性。
- Batching是React的一个重要特性,它允许React将多个状态更新合并为一次渲染,以减少不必要的渲染次数和DOM更新,从而提高性能。 在React的历史版本 中,Batching主要在React的事件处理函数和生命周期方法中生效。 在其他的异步代码中,Batching不生效,每个状态更新都会导致一次渲染。
- 然而,在React18中,引入了一个新的特性叫做automatic batching。 这个特性使得在任何地方,只要是连续的多个状态更新,都会被自动合并为一次渲染。 这使得 性能优化变得更简单,开发者无需考虑是否在一个batch中。
- 代码
新旧batchin1g
js//新旧都合并 handleclick() { this.setState((count: this.state.count+1}); this.setState((count: this.state.count +1}); ) //新的合并,旧的不合并 setTimeout(() => { setCount(count + 1); setCount(count + 1); },1000);
16、Hooks为什么不能写在条件判断、函数体里。我现在有业务场景就需要在if里写怎么办呢?
- React Hooks使用一个单向链表来保存组件的状态和副作用。 在每次组件渲染时,React会遍历这个链表,按照定义的顺序依次执行每个Hook对应的状态更新和副作用函数。 通过链表的形式,React可以保持Hook的调用顺序一致,并正确地跟踪每个Hook的状态和更新。
- 非要卸载if里写hooks,可使用组件外状态zustand等
17、DomDiff细节请详细描述一下?Vue使用了双指针,React为什么没采用呢?
- React的更新过程包括新旧虚拟DOM树的对比过程和更新DOM过程。
- 16版本之前,是对比、更新同时进行,对比的过程采用递归的方式, 技术实现方式是不断的将各个节点、各个节点的子节点压入栈中,采用深度遍历的方式不断的 访问子节点,回溯直到diff完整棵树。 整个过程由于是递归实现的,中间不能中断、中断后必须要重新开始, 如果树的层级较深,会导致整个更新过程(s执行)时间 过长, 阻碍页面渲染和造成用户交互卡顿等问题,体验较差。
- 由于递归算法、栈本身的局限性,16之后将递归改成选代, 而且只Dif同层节点(div->span就不要了), 并将栈结构改进成fiber链表结构,实现了更新过程可以随时中断的功能。
18、React如何实现自身的事件系统?什么叫合成事件?
- React合成事件(SyntheticEvent)是React框架自己实现的一套事件系统。 这套系统模拟了原生的DOM事件, 但同时提供了一些额外的优点:
- 跨浏览器兼容性:不同浏览器的原生事件行为可能会存在差异。 React的合成事件为所有浏览器提供了一致的API和行为,从而消除了这种差异。
- 性能优化:React使用了事件委托(eventdelegation)机制, 同一类型的事件,React并不会直接将事件处理器绑定到DOM节点上,而是将一个统 一的事件监听器绑定到文档的根节点上, 当事件发生时,React会根据其内部映射确定真正的事件处理器。 这样做可以有效减少事件监听器的数量,节省内存,提 高性能。
- 集成到React的状态系统:React合成事件系统与其组件生命周期和状态系统紧密集成, 可以在事件处理函数中调用setState,React会正确地批处理更新和重新渲染。
- 提供更多的信息:React的合成事件提供了比原生事件更多的信息,例如event.target。
- 合成事件的名称(例如onClick,onChange等)和它们在组件中的使用方式, 都与你在JavaScript中使用DOM事件的方式非常相似。 这意味着对于实际上,大多数情况下,你可 以把它们当作标准的DOM事件来使用。
- 在React组件中,对大多数事件来说,React实际上并不会将它们附加到DOM节点上, 相反,React会直接在document节点上为每种事件类型附加一个处理器, 除了在大型应用程序上具有性能优势外,它还使添加类似于replayingevents这样的新特性变得更加容易。 但是如果页面上有多个React版本,他们都将在顶层注册事件处理器。 这会破坏e.stoppropagation():如果嵌套树结构中阻止了事件冒泡,但外部树依然能接收到 它。这会使不同版本React嵌套变得困难重重。
- 在React17中,React将不再向document附加事件处理器。而会将事件处理器附加到渲染React树的根DOM容器中:
19、React Concurrent Mode是什么?React18是怎么实现的?他和useTransition有联系么?
- React的Concurrent Mode是一种新的渲染模式,它使React能够在多个状态更新中进行时间切片", 从而使得长时间运行的渲染任务不会阻塞浏览器的主线程。 这种模式可以提高应用的响应性,特别是在复杂的用户界面和/或设备性能较低的情况下。
- 在传统的同步渲染模式中,React会在一个状态更新发生时阻塞主线程,直到所有的组件都渲染完成。 在一些情况下,这可能会导致应用变得不响应,因为主线程在渲 染过程中无法处理其他任务,比如用户输入和动画。
- 而在Concurrent Mode中,React会把渲染任务分解成多个小任务,每个任务的执行时间都很短,在这些任务之间,React会给出一些空闲的时间,让浏览器有机会处 理其他的任务。这就是所谓的"时间切片"。
- useTransition 是一个在 React 18中引入的新的Hook,它与 Concurrent Mode紧密相关, useTransition 使你可以告诉 React你的状态更新可能需要一些时 间来准备数据, 例如发起一个网络请求,在这个状态更新的数据准备好之前,React会继续显示旧的UI,而不是立即渲染一个加载的状态,这可以避免界面的抖动,提 高用户体验。
- 在ConcurrentMode中,useTransition可以让你的应用在等待新的数据时保持响应,同时在数据准备好之后再平滑的过渡到新的状态。 需要注意的是,Concurrent Mode和useTransition都是 React18中的新特性。
20、将Vue换成React能提高FPS么?请给出理由
- 将Vue替换为React,不一定会提高应用的率(FPS)。实际的性能表现取决于许多因素, 包括但不限于你如何使用这些框架,你的应用的具体需求,以及 用户的设备性能。
- React和Vue在设计上有一些关键的不同,这些不同可能会影响它们在特定场景下的性能:
- 虚拟DOM实现:React和Vue都使用虚拟DOM来提高渲染性能,但它们的实现方式略有不同。 Vue在一些情况下可以跟踪依赖关系,只更新改变的部分,而不是重新渲染整个组件树。 React从另一方面提供了一些优化技巧,如shouldComponentUpdate和React.memo,开发者可以用它们来避免不必要的渲染。
- 异步渲染:React的Concurrent Mode和新的 useTransition Hook支持异步渲染, 这可以让React在处理大量更新时保持界面的响应。这可能有助于提高帧 率,特别是在处理复杂交互和动画时。 虽然Vue在此时没有类似的特性,但它也在寻求实现类似的优化。
- 框架的大小:React和Vue的大小相近,但Vue通常稍微小一些。较小的框架可以更快地加载和解析, 这可能对首次渲染时间有所帮助,但对帧率的影响可能较 小。
- 总的来说,从一个框架迁移到另一个框架通常需要大量的工作,并且可能带来不确定的结果。 如果你在使用Vue的应用中遇到性能问题,我会建议首先寻找优化现有代码的机会, 例如使用Vue的异步组件,优化依赖追踪,或者使用Webpack的代码分割等。
21、Lane是什么?解决了React什么问题。原理是什么?
- React 17的 lanes模型和 Concurrent Mode都是为了更好地支持React Suspense, 它们在一起可以更好地处理复杂的异步更新和任务调度。
- 在React16中,即使是在启用了Concurrent Mode的情况下,Suspense也可能在一些情况下表现得不够理想。 当多个Suspense组件同时进行数据加载时,它们可能会阻塞其他的更新,甚至阻塞整个应用,直到所有的数据都加载完成。 这可能会导致不必要的渲染延迟和用户体验下降。
- 在React17中,通过引入lanes模型,React可以更智能地处理并调度各种不同的更新。 Suspense组件现在可以被分配到不同的lane上,这使得React能够更好地 管理和调度Suspense组件的加载和渲染, 对于那些被Suspense捕获的异步更新,React可以暂时将它们推迟,而去优先处理其他更高优先级的更新, 从而改善应用 的响应速度和性能。
22、react的diff原理
- react中diff算法主要遵循三个层级的策略,tree、conponent和element 层级
- tree层级,DOM节点跨层级的操作不做优化,只会对相同层级的节点进行比较,只有删除、创建操作,没有移动操作
- conponent层级同级比较是否是一个类的组件,进行新增和删除操作
- element层级,每个节点在对应的层级用唯一的key作为标识,reactdiff提供了三种节点操作:插入、删除、移动。
- 新节点不存在于老集合当中,即全新的节点,就会执行插入操作 移动
- 新节点在老集合中存在,并且只做了位置上的更新,就会复用之前的节点,做移动操作(依赖于Key)
- 新节点在老集合中存在,但节点做出了更改不能直接复用,做出删除操作
17、函数式编程解释redux
- store
- 函数式概念:Store可以看作是一个存储应用状态的容器。在函数式编程中这相当于一个不可变的数据源
- 实现:store通过createStore函数创建。
- reducer
- 函数式概念:Reducer是一个纯函数,它接收当前的状态和一个action动作,返回新的状态。
- action
- 函数式概念:Action是一个描述状态变化的普通JavaScript对象。在函数式编程中,这相当于一个函数的参数,指示函数(即reducer)应该如何改变其返回值。
- Dispatch
- 函数式概念:Dispatch是一个函数,用于将action发送到store。在函数式编程中,这可以被视为触发状态变化的函数调用。
18、react和reacthooks区别在哪里
- hooks解决了class组件的
大型组件难拆分
公用逻辑难提取
编写逻辑比较分散
react性能优化
- 渲染列表时加key
- 及时销毁自定义事件,定时器等
- 合理使用异步组件,路由懒加载、图片懒加载
- data层级不能太深,使用Immutable.js
- 合理使用SCU PureComponent和memo
React和Vue
1、react和vue区别
- 相同点
- 都支持
组件化
- 都是
数据驱动试图
- 都是使用
vdom操作DOM
,减少页面的回流重绘,达到性能优化的目的
- 都支持
- 不同点
- 背景方面,react比vue更好,react依赖facebook,有人力和资金去支持react16、18的大改动和reactnative对多平台的支持,蚂蚁金服开发了antd; vue是个人开发,常用的UI库之前是饿了么开发,后面也停止维护了。
- 写法上React使用JSX
拥抱js
函数式编程, Vue类似模板语言,跟之前的jsp、php很像,语法和指令较多,但是对后端开发很友好。 - 原理方面,react采用用户主动setState触发更新,使用fiber进行渲染优化,vue使用obect.defineProperty和proxy进行全局监听变量从而触发更新。vue3的proxy的ie兼容性不是太好。
- 跨平台方面,react做的比较好,包括将事件封装不依赖浏览器的事件, 推出了reactnative开发各个平台原生应用。vue用的是浏览器的原生事件,靠uniapp等手段实现跨平台应用开发
2、vue的composition API与reacthooks对比
- vue3的
setup
只会调用一次
,reacthooks
会被多次调用
- vue3
无需
usememo,useCallback,因为
setup只会调
用一次
- vue3
无需
关心调用顺序
,reacthooks要保证顺序一致
- vue3的
reactive
和ref
比reacthooks难理解
3、vue2和vue3区别
- 更好的
组织代码
和抽取公共逻辑
- 支持
ts
,更好的类型推导
- 替换了
diff
算法的核心
方法:proxy
代替了object.defineProperty
,代价是浏览器的兼容性降低,不再支持ie11
及其之前 - 性能更好
区分
动态和静态
文件,diff
算法的时候区分静态和动态,提升性能- 静态文件会
合并
,减少dom节点 SSR
的时候静态
节点绕过虚拟dom
直接输出- 做了
事件缓存
- 还有vue3自身增加了
treeshaking
,没用到的语法
都删除了,减少体积
4、vue原理
- 之前技术都是使用js操作真实DOM,
- 当业务复杂的时候,用户每次操作,页面就会有多个节点更新,每个节点更新的时候都会更改真实DOM,并且发生一次回流重绘,这样很耗性能。
- vue的原理是虚拟dom模拟真实DOM,用户每次操作,会把所有更改通过虚拟dom和diff算法算出最新的虚拟dom,然后只需一次回流重绘就可更新真实DOM。
- vue使用模板解析方法将元素转为虚拟dom,然后将虚拟dom进行页面渲染,并使用object.defineProperty或proxy实行全局监听
- 当用户操作页面的时候都会被vue监听,并生成新的虚拟dom,
- 然后通过diff算法算出最新的虚拟dom,最后进行页面渲染
5、vue2的diff算法
- 主要先使用实现监听方法
- 当页面第一次加载的时候就使用object.defineProperty创建监听方法,监听变量的变动
- 变量变动的时候,会生成新的虚拟dom,使用diff算法将前后虚拟dom进行对比,
- 显示两个虚拟dom树先同级比较,不同就删掉重建,不再深度比较
- 碰到循环就比较标签和key,相同的话,也不在深度比较。
- 最后把算出来的虚拟dom渲染到页面上。
- 但是这个实现监听的方法有局限性,无法监听新增和删除属性,无法监听原生数组,这些都要特殊处理,所有vue3把他替换了
6、vue性能优化
- 渲染列表时加key
- 及时销毁自定义事件,定时器等
- 合理使用异步组件、路由懒加载、图片懒加载
- data层级不能太深
7、vue 生命周期
- 创建的时候: beforeCreate、created、beforeMount、mounted
- 更新的时候: beforeUpdate、updated
- 销毁的时候: beforeDestroy、destroyed
8、vue2和vue3比较
- 主要有5点:vue3的性能更好、体积更小 更好的组织代码,逻辑抽离 更好的ts支持来实现类型推导
- 使用proxy替代vue2的diff算法object.defineProperty,深度监听性能更好,但是不能兼容ie
- 编译上做了优化,做了静态标记,比如这个节点是静态文本,那虚拟dom对比的时候就忽略对比了,多个静态文本还会合并
- vue自身的tree-shaking优化,比如只引用了v-if指令,那就只把v-if的库打包进去。
- 对事件做了缓存
打包工具
webpack
基本编译配置
- 公共配置
- html编译(
HtmlWebpackPlugin
) - css编译
- less编译(
less-loader
) - scss编译(
scss-loader
) - vue样式编译(
vue-style-loader
) - css编译(
css-loader
、style-loader
) - 多浏览器兼容(
postcss-loader
)
- less编译(
- js编译
- vue编译(
vue-loader
) - react编译(
babel-loader
) - babel-loader
- 预设,比如箭头函数转为function(
@babel/preset-env
、@babel/preset-react
) - 新语法补丁polyfill,比如(
core.js
,generator
) - 按需引入polyfill(
useBuiltIns
、corejs
) - 是否是第三方库,写页面无需配置(
run-time
)
- 预设,比如箭头函数转为function(
- vue编译(
- img编译(
file-loader
)
- html编译(
- 开发环境编译配置
- 热更新,热部署(
HotModuleReplacementPlugin
) - 加速编译dll(
webpack/lib/DllPlugin
、webpack/lib/DllReferencePlugin
) - html、css、js、img按照基本配置
- 热更新,热部署(
- 生产环境编译配置
- html编译压缩+hash(
HtmlWebpackPlugin
) - css编译压缩+hash
- 抽离并hash(
MiniCssExtractPlugin
) - 压缩css(
TerserJSPlugin
,OptimizeCSSAssetsPlugin
)
- 抽离并hash(
- js编译压缩+hash
- 抽离公共代码(
splitChunks
) - 忽略不常更新的包,比如react.min.js(
noParse
) - 删除包内不需要的,比如只留moment的中文包(
IgnorePlugin
) - 多进程打包、压缩(
HappyPack
、UglifyJS
) - 删除无用js(
treeshaking
自带) - 减少闭包scopeHosting(
ModuleConcatenationPlugin
)
- 抽离公共代码(
- img编译压缩(
url-loader
)
- html编译压缩+hash(
webpack优化
- 优化构建速度:
- 优化babel-loader 开启缓存,明确范围、
- IgnorePlugin忽略包内部分文件,比如moment的/locale目录,只要手动导入中文包即可
- noParse忽略对完整的min包,比如已有react.min.js,不需要重复打包、
- happyPack开启多进程打包,提高构建速度、
- ParallelUgifyPlugin开启多进程压缩js、
- 自动刷新watchOptions、热更新devServe、不能用于生产环境
- DllPlugin动态链接库插件,vue和react体积大但是不常升级版本,一个版本构建一次就可
- 优化产出代码可以通过:
- 使用生产环境pro命令,vue和react会自动去除警告,启动代码压缩,启动tree-shaking、
- 小图片base64编码、
- bundle和hash、
- 使用CDN
- 提取公共代码、
- 懒加载配合动态组件、
- scope hosting减少闭包
- IgnorePlugin忽略包内部分文件
小程序
小程序生命周期
- 组件
- 组件创建时: created、attached、ready
- 销毁的时候: detached
- 能监听到页面的: show、hide、resize、routeDone
- 页面
- 页面创建时 onLoad、onShow、onReady
- 销毁、切后台的时候 onHide或者onUnload
- 应用
- 冷启动
- 创建时:onLaunch、onShow
- 切后台:onHide
- 热启动 onShow