Spring 支持哪几种事务管理类型?
大家好呀,今天是编程导航 30 天面试题挑战的第二十四天,一起来看看今天有哪些优质面试题吧。
后端
题目一
Spring 支持哪几种事务管理类型,Spring 的事务实现方式和实现原理是?
官方解析
Spring 支持的事务管理类型包括:
编程式事务管理:在代码中显式地编写事务管理相关代码,如开启事务、提交事务、回滚事务等。 声明式事务管理:使用 AOP 技术,在代码中通过配置进行声明,从而实现对事务管理的控制。 注解式事务管理:基于声明式事务管理,使用注解的方式进行事务的管理。
Spring 的事务实现方式采用了模板模式,通过模板模式实现了事务的封装。Spring 事务管理实现原理主要是通过 AOP 和代理模式来实现的。在使用声明式事务管理时,Spring 通过拦截器和代理对象来实现对方法调用的拦截和控制。当方法被调用时,Spring 会在方法调用前开启一个新的事务,如果方法执行成功,Spring 会提交事务,否则回滚事务。在使用注解式事务管理时,Spring 会扫描带有事务注解的方法,并在运行时使用动态代理为这些方法生成代理对象,在代理对象的方法调用前后进行事务管理的操作。
鱼友的精彩回答
猫十二懿的回答
Spring 支持的事务管理有
编程式事务管理:使用编程的方式来管理事务,包括事务的开始、提交或回滚等操作,可以精细地控制事务的边界。 声明式事务管理:使用注解或 XML 配置来声明事务的属性,例如事务的隔离级别、传播行为、超时时间等,Spring 会自动为这些方法添加事务管理的支持,无需手动编写事务管理代码。 注解式事务管理:通过在方法上添加 @Transactional 注解,开发人员可以非常方便地声明事务的属性,例如隔离级别、传播行为、超时时间等
Spring 的事务实现方式和实现原理是
Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring 是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过 binlog 或者 redo log 实现的。
事务管理器(Transaction Manager):Spring 事务管理器是事务的核心组件,它负责管理事务的生命周期和事务的状态。Spring 事务管理器提供了多种实现,包括 JDBC、Hibernate、JPA、JTA 等,开发人员可以根据自己的需求来选择适合的事务管理器。 事务代理(Transaction Proxy):Spring 使用代理模式来实现事务管理,即通过代理对象来拦截被代理对象的方法调用,以实现事务的开启、提交或回滚等操作。Spring 事务代理有两种实现方式:基于 AOP 的代理和基于动态代理的代理。 事务切面(Transaction Aspect):Spring 事务切面是一组通知(Advice)和切点(Pointcut)的组合,用于定义事务的行为,例如事务的开启、提交或回滚等操作。Spring 事务切面可以基于注解或 XML 配置来实现,开发人员可以根据自己的需求来选择适合的方式。 事务同步器(Transaction Synchronization):Spring 事务同步器用于管理事务的提交或回滚时需要执行的一些操作,例如清理缓存、释放资源等。Spring 事务同步器通过 Synchronization 接口和 TransactionSynchronizationManager 类来实现,可以实现对事务的后置处理。
通过以上四种实现方式提高了事务管理的效率和灵活性。
HeiHei 的回答
Spring 支持的事务管理类型:
编程式事务: transactionTemplate:此种方式是自动的事务管理,无需手动开启、提交 、回滚 PlatformTransactionTemplate:此方式,可手动开启、提交、回滚事务。 声明式事务: 基于Aspectj AOP开启事务 注解式事务管理: @Transactional 的声明式事务管理
Spring 的事务实现原理:
Spring 对事务的管理底层实现方式是基于 AOP 实现的,采用 AOP 的方式进行了封装,核心接口是 PlatformTransactionManager: 当在某个类或者方法上使用 @Transactional 注解后,spring 会基于该类生成一个代理对象,并将这个代理对象作为 bean。当调用这个代理对象的方法时,如果有事务处理,则会先关闭事务的自动功能,然后执行方法的具体业务逻辑,如果业务逻辑没有异常,那么代理逻辑就会直接提交,如果出现任何异常,那么直接进行回滚操作。当然我们也可以控制对哪些异常进行回滚操作。
题目二
Redis 为什么快?
官方解析
Redis 之所以快,主要有以下几个方面的原因:
内存存储:Redis 的数据都是存储在内存中的,相比于硬盘存储的数据库,内存存储速度更快。 单线程模型:Redis 使用单线程模型处理所有的请求,避免了多线程之间的线程切换和竞争等开销,提高了处理请求的效率。 非阻塞 I/O:Redis 使用非阻塞 I/O 处理网络通信,当一个客户端请求到来时,Redis 不会一直等待客户端的响应,而是会先处理其它的请求,这样就避免了 I/O 阻塞带来的性能问题。 精简高效的数据结构:Redis 内置了多种高效的数据结构,如哈希表、跳表等,这些数据结构的实现非常精简高效,减少了 Redis 对内存和 CPU 的占用,从而提高了 Redis 的性能。 持久化策略:Redis 支持多种持久化策略,如 RDB(快照)和 AOF(追加式文件)等,这些策略可以将内存中的数据保存到硬盘中,以保证数据的持久性和安全性。同时,Redis 可以将数据以压缩的方式存储在硬盘中,减少了硬盘的占用,提高了数据的读写速度。
综上所述,Redis 之所以快,主要得益于其内存存储、单线程模型、非阻塞 I/O、高效的数据结构和灵活的持久化策略等方面的设计和实现。
鱼友的精彩回答
Starr y的回答
1、纯内存操作
Redis 是基于内存的数据存储系统,绝大部分请求是纯粹的内存操作。
2、单线程操作,避免了频繁的上下文切换
Redis 的单线程操作是指,Redis 使用一个主线程来处理所有的客户端请求和数据操作,不会创建新的线程来处理请求。这种单线程模型的优点是可以避免多线程并发访问共享数据时的竞争和死锁问题,从而提高了 Redis 的性能和稳定性。此外,由于 Redis 的内存访问速度非常快,因此单线程处理请求也能够保证足够的性能。
3、采用了非阻塞 I/O 多路复用机制
为了实现单线程模型,Redis 使用了 IO 多路复用技术。IO 多路复用是指操作系统提供的一种 IO 模型,可以让一个进程同时监听多个 IO 事件(如读写事件),并在有事件发生时通知进程,从而实现并发处理 IO 事件。
具体来说,在 Redis 中,客户端的请求是由一个单线程来处理的,而 IO 操作却是通过 epoll 多路复用技术实现的。
Redis 单线程情况下,内核会一直监听 socket 上的连接请求或者数据请求,一旦有请求到达就交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。
select/epoll 提供了基于事件的回调机制,即针对不同事件的发生,调用相应的事件处理器。所以 Redis 一直在处理事件,提升 Redis 的响应性能。
HeiHei 的回答
Redis
核心业务部分(处理命令)使用的是单线程 抛开持久化不谈,Redis 是纯内存操作,执行速度非常快,数据存在内存中,绝大部分请求是纯粹的内存操作,非常快速,跟传统的磁盘文件数据存储相比,避免了通过磁盘 IO 读取到内存这部分的开销。 数据结构简单,对数据操作也简单。Redis 中的数据结构是专门进行设计的,每种数据结构都有一种或多种数据结构来支持。Redis 正是依赖这些灵活的数据结构,来提升读取和写入的性能。 使用基于 IO 多路复用机制的线程模型,可以处理并发的链接。 redis6.0 后引入多线程,因为 Redis 的瓶颈不在内存,而是在网络 I/O 模块带来 CPU 的耗时,所以 Redis6.0 的多线程是用来处理网络 I/O 这部分,充分利用 CPU 资源,减少网络 I/O 阻塞带来的性能损耗。
题目三
简述 TCP 三次握手、四次挥手的流程?为什么需要三次握手?为什么需要四次挥手?
官方解析
TCP 三次握手和四次挥手是 TCP 协议中建立和终止连接的过程。
三次握手的流程如下:
客户端向服务器发送 SYN 包,表示请求建立连接,此时客户端进入 SYN_SENT 状态。 服务器接收到 SYN 包后,回应一个 SYN-ACK 包,表示同意建立连接,此时服务器进入 SYN-RECEIVED 状态。 客户端接收到 SYN-ACK 包后,向服务器回应一个 ACK 包,表示确认建立连接,此时客户端进入 ESTABLISHED 状态。服务器收到 ACK 包后也进入 ESTABLISHED 状态。
四次挥手的流程如下:
客户端向服务器发送 FIN 包,表示希望关闭连接,此时客户端进入 FIN-WAIT-1 状态。 服务器接收到 FIN 包后,向客户端回应一个 ACK 包,表示已经收到了关闭请求,此时服务器进入 CLOSE-WAIT 状态,客户端收到 ACK 包后进入 FIN-WAIT-2 状态。 服务器关闭连接后,向客户端发送一个 FIN 包,表示可以关闭连接,此时服务器进入 LAST-ACK 状态。 客户端接收到服务器的 FIN 包后,向服务器回应一个 ACK 包,表示已经收到关闭请求,此时客户端进入 TIME-WAIT 状态,等待 2MSL(最大报文生存时间)后,关闭连接。服务器收到 ACK 包后也进入 CLOSED 状态。
三次握手是为了确保客户端和服务器都能够收到对方的请求和回应,防止因为网络原因导致请求或回应丢失而建立不了连接。四次挥手则是为了确保连接的正常关闭,防止因为网络原因导致连接无法关闭或者出现连接断开后还能够接收到数据的情况。
鱼友的精彩回答
Starry的回答
TCP(Transmission Control Protocol)是一种面向连接的协议,为了保证数据传输的可靠性,TCP 使用了三次握手和四次挥手的过程。
三次握手的过程如下:
第一次握手:客户端向服务器发送 SYN 报文,请求建立连接。 第二次握手:服务器收到客户端的 SYN 报文,向客户端发送 SYN+ACK 报文,表示可以建立连接。 第三次握手:客户端收到服务器的 SYN+ACK 报文,向服务器发送 ACK 报文,表示连接已经建立。
如图所示:
为什么需要三次握手?
三次握手的目的是为了确认双方的收发能力和同步初始序列号。
四次挥手的过程如下:
第一次挥手:客户端向服务器发送 FIN 报文,请求关闭连接。第二次挥手:服务器收到客户端的 FIN 报文,向客户端发送 ACK 报文,表示收到关闭请求。第三次挥手:服务器向客户端发送 FIN 报文,请求关闭连接。第四次挥手:客户端收到服务器的 FIN 报文,向服务器发送 ACK 报文,表示收到关闭请求。如图所示:
为什么需要四次挥手?
四次挥手的目的是为了保证数据的完整性和可靠性。在关闭连接之前,双方需要确保所有数据都已经传输完毕,因此需要通过四次挥手的过程进行确认和处理。
总结:三次握手的本质是确认通信双方收发数据的能力 ,四次挥手的目的是关闭一个连接。
行云的回答
TCP 三次握手
目的:
建立连接 使每一方确认对方的存在 允许双方进行参数的协商 进行资源的分配
流程
A 的 TCP 向 B 发送 连接请求报文段,其首部中的同步位 SYN = 1 ,并随机选择一个序号 seq = x ,表明传送数据时的第一个数据字节序号为 x。
B 的 TCP 收到连接请求报文段后,如果同意,则发送连接同意报文。
B 在连接同意报文段中应使 SYN = 1 ,使 ACK = 1 其确认号ack = x + 1 ,自己随机选择一个序号seq = y
A 收到此报文后向 B 给出确认,其 ACK = 1 ,确认号 ack = y + 1,seq = x + 1。
A 的TCP通知上层应用进程,连接已经建立
B 的 TCP 收到主机A的确认后,也通知其上层应用进程:TCP连接已经建立
为什么需要三次握手
不是两次的主要原因使为了防止多次连接导致连接混乱。 比如A主机的网络较差,连续发送了多个连接请求,B收到请求后发送连接同意报文,但是B不知道A是否收到了同意连接请求,就只能重复同意,这些过期的请求可能回导致网络的混乱 设计成三次握手,客户端在接收到服务端的同意连接报文之后,就不需要再重复发送请求连接报文,并且第三次握手客户端会发送报文给服务端,告诉服务端我已经知道你同意连接了,就不需要再同意我重复发的其他请求。所以三次握手的原因就是避免多次建立重复连接,三次握手少了不能保证建立TCP连接成功,多了会造成资源浪费。
TCP 四次挥手
目的:释放 TCP 连接
流程:
数据传输结束后,通信双方都可以释放连接 现在假设 A 向 B 已经发送完数据,A 就可以发出连接释放报文段,并停止在发送数据,主动关闭 TCP 连接 B 收到后。发出确认,意思我收到了,从 A 到 B 这个方向的连接就释放了(此时 A 就不能再发送数据给 B 了),TCP 连接处于半关闭状态。B 若发送数据,A 仍需要接收 当 B 发送完数据后,就可以释放连接。B 发出的连接释放报文 A 收到连接释放报文后,必须发出确认。
为什么需要四次挥手:
TCP 是全双工通信,可以双向传输数据。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。
举个例子:A 和 B 打电话,通话即将结束后。
第一次挥手 :A 说“我没啥要说的了” 第二次挥手 :B 回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话 第三次挥手 :于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了” 第四次挥手 :A 回答“知道了”,这样通话才算结束。
Gianhing 的回答
TCP 三次握手:用于建立连接
客户端向服务器发送 SYN 包
客户端发送一个 SYN 数据包给服务器,请求建立连接,其中包含一个随机生成的序列号 seq = x服务器回复 SYN-ACK 确认
服务器收到 SYN 包后,如果可以建立连接,就会回复一个 SYN-ACK 数据包,其中会包含一个随机生成的序列号 seq = y 和一个确认序列号 ack = x + 1,表示收到客户端的请求,并准备好接收数据客户端发送 ACK 确认
客户端收到服务器的 SYN-ACK 后,会发送一个 ACK 数据包给服务器,其中会包含一个确认序列号 ack = y + 1,并且自己的序列号 seq = x + 1,表示确认收到,并准备发送数据
为什么需要三次握手?
三次握手为了客户端和服务端都能够确认对方的身份,并确保数据的发送和接收都正常。
防止服务端开启一些无用的连接增加服务端的开销 防止重复连接造成的连接错误
TCP 四次挥手:用于释放连接
客户端向服务器发送 FIN
当客户端不需要数据传输的时候,会向服务器发送一个 FIN 数据包,请求关闭连接服务器回复 ACK 确认
服务器收到 FIN 包之后回复 ACK 数据包,表示同意关闭连接,但可能还有数据要传输,并不会立即关闭服务器发送 FIN
当服务器也没有数据要传输的时候,会向发送客户端发送一个 FIN 数据包,表示也准备好关闭连接客户端发送 ACK 确认
客户端收到 FIN 包之后回复 ACK 数据包,表示收到关闭请求,此时客户端处于 TIME-WAIT 状态,等待 2MSL(最大报文存活时间)后,若没有收到数据则说明服务端已关闭,自己也可以关闭连接了
为什么需要四次挥手?
四次挥手为了确保数据都能传输完成并正常关闭连接。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,再发出连接释放通知,对方确认后就完全关闭了 TCP 连接。
前端
题目一
说说你对 JS 模块化方案的理解,比如 CommonJS、AMD、CMD、ES Module 分别是什么?
官方解析
JS 模块化是指将一个 JS 应用拆分成多个模块或文件,并通过一定的规范或语法定义它们之间的依赖关系,从而达到复用、维护、扩展的目的。常见的 JS 模块化方案有以下几种:
CommonJS:一种用于服务器端的模块化规范,主要用于 Node.js,它通过 require 来引入模块,通过 module.exports 或 exports 来导出模块。 AMD:一种用于浏览器端异步加载模块的规范,主要用于 RequireJS,它通过 define 来定义模块,通过 require 来异步加载模块。 CMD:一种用于浏览器端延迟执行的模块化规范,主要用于 SeaJS,它通过 define 来定义模块,通过 require 来延迟执行模块。 ES Module:一种官方规范,用于浏览器端和 Node.js 中,通过 import 来引入模块,通过 export 来导出模块。
这些模块化方案在实现上有不同的语法和规范,但它们都是为了实现 JS 的模块化而产生的,都可以使 JS 应用更易于维护和扩展。
其中,ES Module 是官方规范,具有更好的兼容性和可扩展性,而 CommonJS 和 AMD/CMD 则更多地用于 Node.js 和浏览器端的异步加载,各有优缺点。
鱼友的精彩回答
你还费解吗的回答
模块化是一种管理代码的方案,它的思想是将一个复杂的程序按功能的不同划分成不同的模块(文件),各个模块存储自己的私有数据,外部不可访问,但同时模块也会向外暴露一些公共接口,使得多个模块之间可以互相通信。采用模块化方案能让开发者对程序的各项功能有更加清晰的认识,从而方便对代码进行维护和管理,提高开发效率。JS 的模块化在经历了多年发展后,最终形成了统一的规范,如今比较常见的有:
CommonJS:
Node.js 实现了 CommonJS 规范,在 Node.js 中,每个文件就是一个模块,有自己的作用域,也就是说,在一个文件里定义的变量或函数,都是私有的,对其他文件不可见。如果想提供给其它文件使用,可将它们挂载到 exports 或 module.exports 接口对象上,比如:
exports.a = 1;
// 等价于
module.exports.a = 1;
console.log(exports === module.exports); // true
其中,module 是一个对象,代表当前模块,它有一个属性 exports ,是对外的接口,所以 module.exports 和 exports 保存的是同一个引用。
如果要获取另一个模块暴露的接口,则使用 require(xxx) ,如果是第三方模块,xxx 为模块名;如果是自定义模块,xxx 为模块文件路径。require() 会读入并执行一个 JS 文件,然后返回该模块的module.exports 对象,如果没有发现指定模块,会报错。
AMD:AMD 是一种异步模块化方案,主要用于浏览器端。它采用异步加载模块的方式,可以在页面加载时并行加载多个模块,提高了页面的加载速度。AMD 的模块化语法主要是通过 define 和 require 来实现的,最著名的实现是 RequireJS。
CMD:CMD 是另一种异步模块化方案,也是主要用于浏览器端的。它采用异步加载模块的方式,但是与 AMD 不同的是,CMD 是按需加载模块,只有在需要使用某个模块时才会加载。CMD 的模块化语法主要是通过 define 和 require 来实现的,最著名的实现是 SeaJS。
ES6 Module:ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,极大可能成为浏览器和服务器通用的模块解决方案。其模块功能主要由两个命令构成:export 和 import。export 命令用于规定模块的对外接口,import 命令用于输入其他模块提供的功能。
ES6 Module 与 CommonJS 的差异:
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
编译时加载: ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。
CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
题目二
如果使用 Math.random() 来计算中奖概率,会有什么问题吗?
官方解析
使用 Math.random() 来计算中奖概率是不可靠的。因为 Math.random() 函数的随机性并不是真正的随机,它是基于一个种子值(seed)生成的伪随机数序列。如果你在相同的环境中使用相同的种子值,那么生成的随机数序列是相同的。因此,如果不使用合适的种子值生成伪随机数,就有可能出现概率计算错误的情况。
为了准确地计算中奖概率,可以使用其他的随机数生成算法,比如 crypto 模块中的 randomBytes() 方法,它可以生成真正的随机数。例如,如果要计算 1/1000 的中奖概率,可以使用如下代码:
const crypto = require('crypto');
function isWinner() {
const rand = crypto.randomBytes(4).readUInt32BE();
return rand < 429496729; // 429496729 = Math.pow(2, 32) / 1000
}
在上面的代码中,使用 crypto.randomBytes() 生成 4 个字节的随机数,然后将它转换为无符号的 32 位整数,判断该随机数是否小于 429496729,如果小于,则认为中奖了。这样计算中奖概率是比较准确的。
鱼友的精彩回答
codexgh的回答
Math.random() 函数返回一个 0~1 之间的伪随机浮点数,其在 V8 中的实现原理式这样的:为了能够保证足够的性能,Math.random() 随机数并不是实时生成的,而是直接生成一组随机数(64 个),并放到缓存中,当这一组随机数取完之后会再重新生成一批新的随机数,放到缓存中。由于 Math.random() 的底层算法是公开的(xorshift128+ 算法),V8 源码可见,因此,是可以使用其他语言模拟的,这就导致,如果攻击者知道了当前随机生成器的状态,那就可以知道缓存中的所有随机数,那就很容易匹配与破解。所以说使用 Math.random() 来计算中奖概率是不可靠的。
Et cetera的回答
如果使用 Math.random() 计算中奖概率,可能会存在以下问题:
不够随机:虽然 Math.random() 方法可以生成一个 0 到 1 之间的随机数,但是这个随机数并不是真正的随机数,而是伪随机数。在一些特定的情况下,比如连续生成多个随机数,可能会出现重复的情况。
容易被猜测:由于 Math.random() 方法本质上是一个伪随机数生成器,因此可以通过分析和猜测算法,来推测下一个数是什么。在安全性要求较高的场合,使用 Math.random() 生成随机数可能会暴露系统的安全漏洞。
难以控制精度:如果仅仅使用 Math.random() 方法生成随机数,很难精确控制中奖概率。例如,如果要控制中奖概率为 1%,那么就需要根据随机数生成的概率来调整算法,可能需要多次尝试才能得到合适的结果
题目三
浏览器的本地存储方式有哪些,有什么区别,分别有哪些应用场景?
官方解析
浏览器的本地存储方式主要有以下几种:
Cookie:Cookie 是浏览器中最古老的本地存储方式,它可以存储少量的文本数据,并在之后的 HTTP 请求中自动携带发送给服务器。Cookie 可以设置过期时间,也可以设置作用域(只在特定域名或路径下有效)。应用场景:一般用于存储会话信息、用户偏好设置等少量的文本数据。 LocalStorage:LocalStorage 是 HTML5 新增的本地存储方式,可以存储较大量的数据,数据保存在浏览器本地且不会过期,除非手动删除或清除缓存。应用场景:适用于存储用户个性化数据、本地数据缓存等。 SessionStorage:SessionStorage 也是 HTML5 新增的本地存储方式,与 LocalStorage 类似,但是数据只在会话期间有效,会话结束或关闭浏览器后数据会被清除。应用场景:适用于需要短期保存数据的场景,如表单数据暂存、页面数据缓存等。 IndexedDB:IndexedDB 是 HTML5 中的一个本地数据库存储方案,可以存储大量结构化数据,支持事务处理和索引查找,功能比较强大。应用场景:适用于需要离线存储数据、本地数据库操作等。 Web SQL:Web SQL 是 HTML5 中的另一种本地数据库存储方案,采用 SQL 语句进行数据存储和查询,但是目前已经被弃用,不建议使用。应用场景:类似于 IndexedDB,适用于需要离线存储数据、本地数据库操作等。
不同的本地存储方式适用于不同的场景,选择合适的方式可以提高用户体验和网站性能。
鱼友的精彩回答
codexgh的回答
ES6 中的 Reflect 对象有什么用?
我么来看看常见的浏览器的本地存储方式:localStorage、sessionStorage、indexedDB、Cookies
webStorage:
localStorage:
用于存储持久数据,除非用户手动将其从浏览器中删除,否则数据经终身存储,即使用户关闭窗口后选项卡,他也不会过期
sessionStorage:
用户存储临时会话数据,页面重新加载后数据仍然存在,当关闭浏览器或选项卡时数据就会丢失;
方法和属性:
● setItem() :用于存储数据,它有两个参数,即 key 和 value。使用形式:localStorage.setItem(key, value);
● getItem():用于获取数据,它接受一个参数 key,即需要访问其值的键。使用形式:localStorage.getItem(key);
● removeItem():用于删除数据,它接受一个参数 key,即需要删除其值的键。使用形式:localStorage.removeItem(key);
● clear() :用于清除其中存储的所有数据,使用形式:localStorage.clear();
● key():该方法用于获取 localStorage 中数据的所有 key,它接受一个数字作为参数,该数字可以是 localStorage 项的索引位置。localStorage 和 sessionStorage 都非常适合缓存非敏感应用数据,可以再需要存储少量简单值,它们本质是同步的,并且会阻塞主 UI 线程,所以需要谨慎使用。
Cookie:
Cookie 主要用于身份验证和用户数据持久性。Cookie 与请求一起发送到服务器,并在响应时发送到客户端;因此,cookies 数据在每次请求时都会与服务器交换。服务器可以使用 cookie 数据向用户发送个性化内容。严格来说,cookie 并不是客户端存储方式,因为服务器和浏览器都可以修改数据。它是唯一可以在一段时间后自动使数据过期的方式。每个 HTTP 请求和响应都会发送 cookie 数据。存储过多的数据会使 HTTP 请求更加冗长,从而使应用比预期更慢:
● 浏览器限制 cookie 的大小最大为4kb,特定域允许的 cookie 数量为 20 个,并且只能包含字符串;
● cookie 的操作是同步的;
● 不能通过 web workers 来访问,但可以通过全局 window 对象访问。Cookie 通常用于会话管理、个性化以及跨网站跟踪用户行为。我们可以通过服务端和客户端设置和访问 cookie。Cookie 还具有各种属性,这些属性决定了在何处以及如何访问和修改它们,
Cookie 分为两种类型:
● 会话 Cookie:没有指定 Expires 或 Max-Age 等属性,因此在关闭浏览器时会被删除;
● 持久性 Cookie:指定 Expires 或 Max-Age 属性。这些 cookie 在关闭浏览器时不会过期,但会在特定日期 (Expires) 或时间长度 (Max-Age) 后过期。
IndexedDB:
IndexedDB 提供了一个类似 NoSQL 的 key/value 数据库,它可以存储大量结构化数据,甚至是文件和 blob。每个域至少有 1GB 的可用空间,并且最多可以达到剩余磁盘空间的 60%。
indexedDB 特点如下:
● 可以将任何 JavaScript 类型的数据存储为键值对,例如对象(blob、文件)或数组等。
● IndexedDB API 是异步的,不会在数据加载时停止页面的渲染。
● 可以存储结构化数据,例如 Date、视频、图像对象等。
● 支持数据库事务和版本控制。
● 可以存储大量数据。
● 可以在大量数据中快速定位/搜索数据。
● 数据库是域专用的,因此任何其他站点都无法访问其他网站的 IndexedDB 存储,这也称为同源策略。
IndexedDB 使用场景:
● 存储用户生成的内容:例如表单,在填写表单的过程中,用户可以离开并稍后再回来完成表单,存储之后就不会丢失初始输入的数据。
● 存储应用状态:当用户首次加载网站或应用时,可以使用 IndexedDB 存储这些初始状态。可以是登录身份验证、API 请求或呈现 UI 之前所需的任何其他状态。因此,当用户下次访问该站点时,加载速度会增加,因为应用已经存储了状态,这意味着它可以更快地呈现 UI。
● 对于离线工作的应用:用户可以在应用离线时编辑和添加数据。当应用程序来连接时,IndexedDB 将处理并清空同步队列中的这些操作。
星球活动
1.欢迎参与 30 天面试题挑战活动 ,搞定高频面试题,斩杀面试官!
2.欢迎已加入星球的同学 免费申请一年编程导航网站会员 !
3.欢迎学习 鱼皮最新原创项目教程,手把手教你做出项目、写出高分简历!
加入我们
欢迎加入鱼皮的编程导航知识星球,鱼皮会 1 对 1 回答您的问题、直播带你做出项目、为你定制学习计划和求职指导,还能获取海量编程学习资源,和上万名学编程的同学共享知识、交流进步。
💎 加入星球后,您可以:
1)添加鱼皮本人微信,向他 1 对 1 提问,帮您解决问题、告别迷茫!点击了解详情
2)获取海量编程知识和资源,包括:3000+ 鱼皮的编程答疑和求职指导、原创编程学习路线、几十万字的编程学习知识库、几十 T 编程学习资源、500+ 精华帖等!点击了解详情
3)找鱼皮咨询求职建议和优化简历,次数不限!点击了解详情
4)鱼皮直播从 0 到 1 带大家做出项目,已有 50+ 直播、完结 3 套项目、10+ 项目分享,帮您掌握独立开发项目的能力、丰富简历!点击了解详情
外面一套项目课就上千元了,而星球内所有项目都有指导答疑,轻松解决问题
星球提供的所有服务,都是为了帮您更好地学编程、找到理想的工作。诚挚地欢迎您的加入,这可能是最好的学习机会,也是最值得的一笔投资!
长按扫码领优惠券加入,也可以添加微信 yupi1085 咨询星球(备注“想加星球”):
往期推荐