年荣誉之路活动已经开启了,和去年一样,没达到2级荣誉并没有被锁的玩家可以通过完成任务来恢复至2级荣誉,2级至4级的玩家可以提升一级荣誉,5级荣誉则可以领取魔法引擎。而恢复至2级荣誉并在单双或灵活排位达到黄金及以上玩家能获得胜利女神瑟庄妮,达到5级荣誉可以获得三大荣誉玛尔扎哈。
活动时间:2022年11月25日-2023年1月8日
活动链接:
?https://lol.qq.com/act/a20221125roadtohonor/index.html?e_code=508038&exchangeType=1
目前有三种情况:
①恢复2级荣誉,适用0级和1级的,这个恢复也是用来给刚刚结束的S12赛季单双或灵活排位达到黄金及以上段位的玩家领胜利女神瑟庄妮的。
②提升1档荣誉,给2-4级的玩家用的,能提升一档荣誉,因为每一级荣誉有三个里程碑点,我建议尽可能把等级升高点和刚升荣誉等级的时候用,比如刚升4级,再到这里申请就可以直达5级。可以用来领三大荣誉玛尔扎哈的皮肤。
③荣誉5级奖励。5级荣誉玩家可以领一个魔法引擎,这个引擎也没啥好东西,就是一些钥匙和守卫皮肤碎片等。
要注意的是,这三个只能选择一个参与。
你选择从3级升到4级,以后哪怕再达到5级也不能选择领取魔法引擎。或者你恢复到了2级,也不能再选择提升一档荣誉了。
所以这个活动可以晚点参与。
游戏任务:
?都是登录7天游戏并每天完成一场对局,由于时间比较长肯定做的完。
恢复2级荣誉需要手打“我承诺...”也就是“认罪书”。对皮肤无所谓的也不用参加这玩意。
关于奖励
?之前的赛季结算公告有提过会在11.17以后发放奖励,并在12月完成所有账号发放。现在基本都还没领到,慢慢等就行。
?当然玛尔扎哈的皮肤只要在1月8日23:59之前达到5级荣誉都能获得。
最后觉得有用可以点个关注、分享啥的,有关英雄联盟、云顶之弈的皮肤、资讯、活动、白嫖福利都会发。
章来源:https://www.cnblogs.com/jackson0714/p/TSQLFundamentals_08.html
作者:公众号:悟空聊架构
本篇主要是对SQL中事务和并发的详细讲解。
回到顶部
为单个工作单元而执行的一系列操作。如查询、修改数据、修改数据定义。
(1)显示定义事务的开始、提交
1 2 3 4 | BEGIN TRAN INSERT INTO b(t1) VALUES(1) INSERT INTO b(t1) VALUES(2) COMMIT TRAN |
(2)隐式定义
如果不显示定义事务的边界,则SQL Server会默认把每个单独的语句作为一个事务,即在执行完每个语句之后就会自动提交事务。
(1)原子性Atomicity
1.事务必须是原子工作单元。事务中进行的修改,要么全部执行,要么全都不执行;
2.在事务完成之前(提交指令被记录到事务日志之前),系统出现故障或重新启动,SQL Server将会撤销在事务中进行的所有修改;
3.事务在处理中遇到错误,SQL Server通常会自动回滚事务;
4.少数不太严重的错误不会引发事务的自动回滚,如主键冲突、锁超时等;
5.可以使用错误处理来捕获第4点提到的错误,并采取某种操作,如把错误记录在日志中,再回滚事务;
6.SELECT @@TRANCOUNT可用在代码的任何位置来判断当前使用SELECT @@TRANCOUNT的地方是否位于一个打开的事务当中,如果不在任何打开的事务范围内,则该函数返回0;如果在某个打开的事务返回范围内,则返回一个大于0的值。打开一个事务,@@TRANCOUNT=@@TRANCOUNT+1;提交一个事务,@@TRANCOUNT-1。
(2)一致性Consiitency
1.同时发生的事务在修改和查询数据时不发生冲突;
2.一致性取决于应用程序的需要。后面会讲到一致性级别,以及如何对一致性进行控制。
(3)隔离性Isolation
1.用于控制数据访问,确保事务只访问处于期望的一致性级别下的数据;
2.使用锁对各个事务之间正在修改和查询的数据进行隔离。
(4)持久性Durability
1.在将数据修改写入到磁盘上数据库的数据分区之前会把这些修改写入到磁盘上数据库的事务日志中,把提交指令记录到磁盘的事务日志中以后,及时数据修改还没有应用到磁盘的数据分区,也可以认为事务时持久化的。
2.系统重新启动(正常启动或在发生系统故障之后启动),SQL Server会每个数据库的事务日志,进行回复处理。
3.恢复处理包含两个阶段:重做阶段和撤销阶段。
4.前滚:在重做阶段,对于提交指令已经写入到日志的事务,但数据修改还没有应用到数据分区的事务,数据库引擎会重做这些食物所做的所有修改。
5.回滚:在撤销阶段,对于提交指令没有写入到日志中的事务,数据库引擎会撤销这些事务所做的修改。(这句话需要research,可能是不正确的。因为提交指令没有写入到数据分区,撤销修改是指撤销哪些修改呢???)
回到顶部
(1)SQL Server使用锁来实现事务的隔离。
(2)事务获取锁这种控制资源,用于保护数据资源,防止其他事务对数据进行冲突的或不兼容的访问。
(1)排他锁
a.当试图修改数据时,事务只能为所依赖的数据资源请求排他锁。
b.持有排他锁时间:一旦某个事务得到了排他锁,则这个事务将一直持有排他锁直到事务完成。
c.排他锁和其他任何类型的锁在多事务中不能在同一阶段作用于同一个资源。
如:当前事务获得了某个资源的排他锁,则其他事务不能获得该资源的任何其他类型的锁。其他事务获得了某个资源的任何其他类型的锁,则当前事务不能获得该资源的排他锁。
(2)共享锁
a.当试图读取数据时,事务默认会为所依赖的数据资源请求共享锁。
b.持有共享锁时间:从事务得到共享锁到读操作完成。
c.多个事务可以在同一阶段用共享锁作用于同一数据资源。
d.在读取数据时,可以对如何处理锁定进行控制。后面隔离级别会讲到如何对锁定进行控制。
(1)如果数据正在由一个事务进行修改,则其他事务既不能修改该数据,也不能读取(至少默认不能)该数据,直到第一个事务完成。
(2)如果数据正在由一个事务读取,则其他事务不能修改该数据(至少默认不能)。
RID、KEY(行)、PAGE(页)、对象(例如表)、数据库、EXTENT(区)、分配单元(ALLOCATION_UNIT)、堆(HEAP)、以及B树(B-tree)。
RID: 标识页上的特定行
格式: fileid: pagenumber: rid (1:109:0 )
其中fileid标识包含页的文件, pagenumber标识包含行的页,rid标识页上的特定行。
fileid与sys.databases_files 目录视图中的file_id列相匹配
例子:
在查询视图sys.dm_tran_locks的时候有一行的resource_description列显示RID 是1:109:0 而status列显示wait,
表示第1个数据文件上的第109页上的第0行上的锁资源。
SQL Server可以先获得细粒度的锁(例如行或页),在某些情况下将细粒度锁升级为更粗粒度的锁(例如,表)。
例如单个语句获得至少5000个锁,就会触发锁升级,如果由于锁冲突而导致无法升级锁,则SQL Server每当获取1250个新锁时出发锁升级。
回到顶部
当多个事务都需要对某一资源进行锁定时,默认情况下会发生阻塞。被阻塞的请求会一直等待,直到原来的事务释放相关的锁。锁定超时期限可以限制,这样就可以限制被阻塞的请求在超时之前要等待的时间。
阶段1:事务A请求资源S1,事务不对资源S1进行操作
阶段2:事务A用锁A锁定资源S1,事务B请求对资源S1进行不兼容的锁定(锁B),锁B的请求被阻塞,事务B将进入等待状态
阶段3:事务A正在释放锁A,事务B等待锁A释放,
阶段4:事务A的锁A已释放,事务B用锁B锁定资源S1
例子:
(1)准备工作:
1.准备测试数据
1 2 3 4 5 6 7 8 | --先创建一张表Product作为测试。id为表的主键,price为product的价格 CREATE TABLE [dbo].[myProduct]( [id] [int] NOT NULL, [price] [money] NOT NULL ) ON [PRIMARY] GO --插入一条数据,id=1,price=10 INSERT INTO [TSQLFundamentals2008].[dbo].[myProduct]([id],[price])VALUES(1,10) |
2.模拟阻塞发生的情况
在SQL Server中打开三个查询窗口Connection1、Connection2、Connection3,分别按顺序执行表格中的执行语句。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | --Connection1 BEGIN TRAN UPDATE dbo.myProduct SET price=price + 1 WHERE id=1 --Connection2 SELECT * FROM dbo.myProduct WHERE id=1 --Connection3 SELECT request_session_id AS 会话id , resource_type AS 请求锁定的资源类型 , resource_description AS 描述 , request_mode AS 模式 , request_status AS 状态 FROM sys.dm_tran_locks |
查询窗口 | 服务器进程标识符SPID | 执行语句 | 结果 | 说明 | ||
Connection1 | 52 |
| 为了更新id=1这一行数据,会话必须先获得一个排他锁。事务处于一直打开状态,没有提交,所以事务一直持有排他锁,直到事务提交并完成。 | |||
Connection2 | 56 |
| 事务为了读取数据,需要请求一个共享锁,但是这一行已经被其他会话持有的排他锁锁定,而且共享锁和排他锁不是兼容的,所以会话被阻塞,进入等待状态 | |||
Connection3 | 57 |
| 会话56: |
(2)分析阻塞
★ 1.sys.dm_tran_locks 视图
(1)该动态视图可以查询出哪些资源被哪个进程ID锁了
(2)查询出对资源授予或正在等待的锁模式
(3)查询出被锁定资源的类型
上面的查询语句3已经用到了这个视图,可以参考上图中的分析说明。
★ 2.sys.dm_exec_connections 视图
(1)查询出该动态视图可以查询出进程相关的信息
(2)查询出最后一次发生读操作和写操作的时间last_read,last_write
(3)查询出进程执行的最后一个SQL批处理的二进制标记most_recent_sql_handle
查询窗口 | 服务器进程标识符SPID | 执行语句 | 结果 | 说明 | ||
Connection3 | 57 |
| 会话52: |
★ 3.sys.dm_exec_sql_text 表函数
(1)该函数可以将二进制标记most_recent_sql_handle作为参数,然后返回SQL代码。
(2)阻塞进程在不断地运行,所以在代码中看到的最后一个操作不一定是导致问题的语句。在本例中最后一条执行语句是导致阻塞的语句。
查询窗口 | 服务器进程标识符SPID | 执行语句 | 结果 | 说明 | ||||||
Connection3 | 57 |
| 会话52:
|
★ 4.sys.dm_exec_sessions 视图
(1)会话建立的时间login_time
(2)特定于会话的客户端工作站名称host_name
(3)初始化会话的客户端程序的名称program_name
(4)会话所使用的SQL Server登录名login_name
(5)最近一次会话请求的开始时间last_request_start_time
(6)最近一次会话请求的完成时间last_request_end_time
查询窗口 | 服务器进程标识符SPID | 执行语句 | 结果 | 说明 | ||
Connection3 | 57 |
|
★ 5.sys.dm_exec_requests 视图
(1)识别出阻塞链涉及到的会话、争用的资源、被阻塞会话等待了多长时间
查询窗口 | 服务器进程标识符SPID | 执行语句 | 结果 | 说明 | ||
Connection3 | 57 |
| 会话56: |
★ 6.Lock_TIMEOUT 选项
(1)设置会话等待锁释放的超时期限
(2)默认情况下会话不会设置等待锁释放的超时期限
(3)设置会话超时期限为5秒, SET Lock_TIMEOUT 5000
(4)锁定如果超时,不会引发事务回滚
(5)取消会话超时锁定的设置,SET LOCK_TIMEOUT -1
如果超时,将显示以下错误:
★7.KILL <spid> 命令
(1)杀掉会话52,KILL 52
(2)杀掉会话,会引起事务回滚,同时释放排他锁
回到顶部
(1)隔离级别用来做什么
a.隔离级别用于决定如何控制并发用户读写数据的操作
(2)写操作
a.任何对表做出修改的语句
b.使用排他锁
c.不能修改读操作获得的锁和锁的持续时间
(3)读操作:
a.任何检索数据的语句
b.默认使用共享锁
c.使用隔离级别来控制读操作的处理方式
(1)未提交读 (READ UNCOMMITTED)
(2)已提交读(READ COMMITTED)(默认值)
(3)可重复读(REPEATABLE READ)
(4)可序列化(SERIALIZABLE)
(5)快照(SNAPSHOT)
(6)已经提交读快照(READ_COMMITTED_SNAPSHOT)
(1)设置整个会话的隔离级别
1 | SET TRANSACTION ISOLATION LEVEL <isolation name>; |
1 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED; |
(2)用表提示设置查询的隔离级别
1 2 | SELECT ... FROM <table> WITH (<isolation name>);<br> SELECT * FROM dbo.myProduct WITH (READCOMMITTED); |
注意:
1.设置会话选项的隔离级别时,隔离级别中的每个单词之间需要用空格分隔
2.用表提示的隔离级别时,隔离级别中的每个单词之间不需要用空格分隔
3.表提示的隔离级别有同义词,如:NOLOCK->READUNCOMMITTED,HOLDLOCK->REPEATABLEREAD
4.隔离级别的严格性:1.未提交读<2.已提交读<3.可重复读<4.可序列化
5.隔离级别越高,一致性越高,并发性越低
6.基于快照的隔离级别,SQL Server将提交过的行保存到tempdb数据库中,当读操作发现行的当前版本和它们预期的不一致时,可以立即得到行的以前版本,从而不用请求共享锁也能取得预期的一致性。
★ 1.未提交读 (READ UNCOMMITTED)
打开两个查询窗口,Connetion1,connection2
Step1: 执行Connection1的阶段2的SQL 语句,然后执行connection2的SQL语句
Step2: 执行Connection1的阶段3的SQL 语句,执行connection2的SQL语句
Step3: 执行Connection1的阶段4的SQL 语句,执行connection2的SQL语句
查询窗口 | 事务 | 执行语句 | ||
Connetion1 | A |
| ||
Connection2 | B |
|
两个事务的流程图:
阶段1:Price=10,事务A对myProduct表请求排他锁
阶段2:事务A对myProduct表使用了排他锁,更新price=price + 1,然后事务A查询price的价格: price=11。事务B不请求任何锁,事务B在A更新Price之后进行查询,price=11
阶段3:事务A更新price=price + 5,然后事务A查询price的价格,price=16。事务B查询price的价格: price=16
阶段4:事务A释放排他锁
阶段5:事务A中查询price的价格:price=16。事务B查询price的价格: price=16
大家可以看到事务B有两种结果,这就是“未提交读 (READ UNCOMMITTED)”隔离级别的含义:
(1)读操作可以读取未提交的修改(也称为脏读)。
(2)读操作不会妨碍写操作请求排他锁,其他事务正在进行读操作时,写操作可以同时对这些数据进行修改。
(3)事务A进行了多次修改,事务B在不同阶段进行查询时可能会有不同的结果。
★ 2.已提交读(READ COMMITTED)(默认值)
打开两个查询窗口,Connetion1,connection2
Step1: 执行Connection1的SQL 语句
Step2: 执行Connection2的SQL 语句
执行语句 | 执行语句 | |||
Connetion1 | A |
| ||
Connection2 | B |
|
两个事务的流程图:
阶段1:Price=10,事务A对myProduct表请求排他锁
阶段2:事务A对myProduct表使用了排他锁,更新price=price + 1,然后事务A查询price的价格: price=11。然后事务B请求共享锁进行读操作,查询price,
由于在当前隔离级别下,事务A的排他锁和事务B的共享锁存在冲突,所以事务B需要等待事务A释放排他锁后才能读取数据。
阶段3:事务A提交事务(COMMIT TRAN)
阶段4:事务A提交完事务后,释放排他锁
阶段5:事务B获得了共享锁,进行读操作,price=11
“已提交读 (READ UNCOMMITTED)”隔离级别的含义:
(1)必须获得共享锁才能进行读操作,其他事务如果对该资源持有排他锁,则共享锁必须等待排他锁释放。
(2)读操作不能读取未提交的修改,读操作读取到的数据是提交过的修改。
(3)读操作不会在事务持续期间内保留共享锁,其他事务可以在两个读操作之间更改数据资源,读操作因而可能每次得到不同的取值。这种现象称为“不可重复读”
★ 3.可重复读(REPEATABLE READ)
打开两个查询窗口,Connetion1,connection2
Step1: 执行Connection1的SQL 语句
Step2: 执行Connection2的SQL 语句
执行语句 | 事务 | 执行语句 | ||
Connetion1 | A |
| ||
Connection2 | B |
|
两个事务的流程图:
阶段1:Price=10,事务A对myProduct表请求共享锁
阶段2:事务A对myProduct表使用了共享锁,事务A查询price的价格: price=10,事务A一直持有共享锁直到事务A完成为止。然后事务B请求排他锁进行写操作price=price+1,
由于在当前隔离级别下,事务A的共享锁和事务B请求的排他锁存在冲突,所以事务B需要等待事务A释放共享锁后才能修改数据。
阶段3:事务A查询price, price=10, 说明事务B的更新操作被阻塞了,更新操作没有被执行。然后事务A提交事务(COMMIT TRAN)
阶段4:事务A提交完事务后,释放共享锁
阶段5:事务B获得了排他锁,进行写操作,price=11
“可重复读 (REPEATABLE READ)”隔离级别的含义:
(1)必须获得共享锁才能进行读操作,获得的共享锁将一直保持直到事务完成之止。
(2)在获得共享锁的事务完成之前,没有其他事务能够获得排他锁修改这一数据资源,这样可以保证实现可重复的读取。
(3)两个事务在第一次读操作之后都将保留它们获得的共享锁,所以任何一个事务都不能获得为了更新数据而需要的排他锁,这种情况将会导致死锁(deadlock),不过却避免了更新冲突。
★ 4.可序列化(SERIALIZABLE)
打开两个查询窗口,Connetion1,connection2
Step1: 执行Connection1的SQL 语句
Step2: 执行Connection2的SQL 语句
执行语句 | 事务 |
| ||
Connetion1 | A |
| ||
Connection2 | B |
|
两个事务的流程图:
阶段1:Price=10,事务A对myProduct表请求共享锁
阶段2:事务A对myProduct表使用了共享锁,事务A查询id=1的price的价格:1行记录,price=10,事务A一直持有共享锁直到事务A完成为止。然后事务B请求排他锁进行插入操作id=1,price=20,
由于在当前隔离级别下,事务B试图增加能够满足事务A的读操作的查询搜索条件的新行,所以事务A的共享锁和事务B请求的排他锁存在冲突,事务B需要等待事务A释放共享锁后才能插入数据。
阶段3:事务A查询出id=1的数据只有1行,说明事务B的插入操作被阻塞了,插入操作没有被执行。然后事务A提交事务(COMMIT TRAN)
阶段4:事务A提交完事务后,释放共享锁
阶段5:事务B获得了排他锁,进行插入操作,插入成功,查询出id=1的数据有两条
“可序列化(SERIALIZABLE)”隔离级别的含义:
(1)必须获得共享锁才能进行读操作,获得的共享锁将一直保持直到事务完成之止。
(2)在获得共享锁的事务完成之前,没有其他事务能够获得排他锁修改这一数据资源,且当其他事务增加能够满足当前事务的读操作的查询搜索条件的新行时,其他事务将会被阻塞,直到当前事务完成然后释放共享锁,其他事务才能获得排他锁进行插入操作。
(3)事务中的读操作在任何情况下读取到的数据是一致的,不会出现幻影行(幻读)。
(4)范围锁:读操作锁定满足查询搜索条件范围的锁
脏读:读取未提交的更改。
不可重复读:读操作不会在事务持续期间内保留共享锁,其他事务可以在两个读操作之间更改数据资源,读操作因而可能每次得到不同的取值。
丢失更新:两个事务进行读操作,获得资源上的共享锁,读取完数据后,不再持有资源上的任何锁,两个事务都能更新这个值,
最后进行更新的事务将会覆盖其他事务做的更改,导致其他事务更改的数据丢失。
幻读:第一次和第二次读取到的数据行数不一致。
范围锁:读操作锁定满足查询搜索条件范围的锁
隔离级别 | 是否读取未提交的行 | 是否不可重复读 | 是否丢失更新 | 是否幻读 | 共享锁持续时间 | 是否持有范围锁 |
未提交读 READ UNCOMMITTED | Y | Y | Y | Y | 当前语句 | N |
已提交读 READ COMMITTED | N | Y | Y | Y | 当前语句 | N |
可重复读REPEATABLE READ | N | N | N | Y | 事务开始到事务完成 | N |
可序列化SERIALZABLE | N | N | N | N | 事务开始到事务完成 | Y |
死锁是指一种进程之间互相永久阻塞的状态,可能涉及两个或更多的进程。
打开两个查询窗口,Connetion1,connection2
Step1: 执行Connection1的SQL 语句
Step2: 执行Connection2的SQL 语句
执行语句 | 事务 | 执行语句 | ||
Connetion1 | A |
| ||
Connection2 | B |
|
两个事务的流程图:
阶段1:Price=10,事务A对myProduct表请求排他锁。Customer=aaa,事务B对myOrder请求排他锁
阶段2:事务A对myProduct表使用了排他锁,更新price=price + 1。然后事务B对myOrder表使用了排他锁,更新customer=ddd。
阶段3:事务A查询myOrder表,对myOrder表请求共享锁,因为事务A的请求的共享锁与事务B的排他锁冲突,所以事务A被阻塞。然后事务B查询myProduct表,对myProduct表请求共享锁,因为事务B的请求的共享锁与事务A的排他锁冲突,所以事务B被阻塞。
阶段4:事务A等待事务B的排他锁释放,事务B等待事务A的排他锁释放,导致死锁。事务A和事务B都被阻塞了。
阶段5:SQL Server在几秒之内检测到死锁,会选择一个事务作为死锁的牺牲品,终止这个事务,并回滚这个事务所做的操作。在这个例子中,事务A被终止,提示信息:事务(进程 ID 53)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。
“死锁 (Dead Lock)”的一些注意事项:
(1)如果两个事务没有设置死锁优先级,且两个事务进行的工作量也差不多一样时,任何一个事务都有可能被终止。
(2)解除死锁要付出一定的系统开销,因为这个过程会涉及撤销已经执行过的处理。
(3)事务处理的时间时间越长,持有锁的时间就越长,死锁的可能性也就越大,应该尽可能保持事务简短,把逻辑上可以不属于同一个工作单元的操作移到事务以外。
(4)上面的例子中,事务A和事务B以相反顺序访问资源,所以发生了死锁。如果两个事务按同样的顺序来访问资源,则不会发生这种类型的死锁。在不改变程序的逻辑情况下,可以通过交换顺序来解决死锁的问题。
/ 阿里淘系 F(x) Team - 大貘
前几天在 F(x) Team.午夜识堂 和大家聊了一下 CSS 方面的话题,即 CSS 新特性。
在这个话题中主要整理了有关于 CSS 方面的特性,并且尽可能的整理了一些大家现在能用或过不了多久就能用的属性。另外,虽然标题是“新特性”,但其中有蛮多特性并不是“新”,可能已经出现在你的项目中,或者你已经看过,只是不了解而以。接下来,就和大家一起来简单地回顾一下这些性,希望大家能喜欢,也希望对大家平时工作有所帮助。
CSS 等比缩放一般指的是 “容器高度按比例根据宽度改变”,很多时候也称为宽高比或纵宽比。 众所周知,我们开发 Web 页面要面对的终端更复杂的了,而这些终端的宽高比都不一样。常见的比例有:?
特别是在做媒体相关开发的同学,比如视频、图像等,这方面的需求会更多,比如 Facebook 上的图片,视频展示:?
CSS 在还没有 _aspect-ratio_ 之前,常使用一些 Hacck 手段来实现实类似的效果,即使用 padding-top 或 padding-bottom 来实现:?
<aspectratio-container>
<aspectratio-content></aspectratio-content>
</aspectratio-container>
<style>
.aspectratio-container {
width: 50vmin; /* 用户根据自己需要设置相应的值 */
/* 布局方案可以调整 */
display: flex;
justify-content: center;
align-items: center;
}
/* 用来撑开aspectratio-container高度 */
.aspectratio-container::after {
content: "";
width: 1px;
padding-bottom: 56.25%;
/*元素的宽高比*/
margin: -1px;
z-index: -1;
}
</style>
?
有了 CSS 自定义属性之后,可以结合 calc() 函数来实现容器等比缩放的效果:?
.container {
--ratio: 16/9;
height: calc(var(--width) * 1 / (var(--ratio)));
width: 100%;
}
?
虽然比padding-top 这样的Hack 手段简单,但相比原生的aspect-ratio还是要复杂的多。即:?
.container {
width: 100%;
aspect-ratio: 16 / 9;
}
下面这个示例演示了这三种不同方案实现宽比的效果:
Demo: https://codepen.io/airen/full/ExWjeZr
?
还可以通过 @media 让元素在不同的终端上按不同的比例进行缩放:?
.transition-it {
aspect-ratio: 1/1;
transition: aspect-ratio .5s ease;
@media (orientation: landscape) { & {
aspect-ratio: 16/9;
}}
}
在 Web 布局中,时常会碰到内容溢出容器的现状,如果 overflow 设置为 auto 或 scroll 时容器会出现水平或垂直滚动条:?
为了给用户提供更好的滚动体验,CSS 提供了一些优化滚动体验的 CSS 特性,其中滚动捕捉就是其中之一。CSS 的滚动捕捉有点类似于 Flexbox 和 Grid 布局的特性,分类可用于滚动容器的属性和滚动项目的属性:?
有了滚动捕捉特性,我们要实现类似下图的效果就可以不需要依赖任何 JavaScript 库或脚本:?
就是每次滚动时,图片的中心位置和容器中心位置对齐(想象一下 Swiper 的效果)。关键代码就下面这几行:?
.container {
scroll-behavior: smooth;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scroll-snap-type: x mandatory;
scroll-padding: 20px;
}
img {
scroll-snap-align: center;
scroll-snap-stop: always;
}
Demo: https://codepen.io/airen/full/mdRpboo
利用该特性,还可以实现类似 iOS的一些原生交互效果:
Demo: https://codepen.io/airen/full/PoWQPvN
要是再利用一点点JavaScript脚本,还可以实现沉浸式讲故事的交互效果:?
Demo: https://codepen.io/airen/full/qBRxNOo
CSS 的 gap 属性的出现,帮助我们解决了以前一直比较麻烦的布局效果:?
正如上图所示,设计师期望的一个效果是,紧邻容器边缘没有任何间距,但相邻项目之间(水平或垂直方向)都有一定的间距。在没有 gap 属性之前使用 margin 是很烦人的,特别是多行多列的时候更麻烦。有了 gap 仅需要一行代码即可。?
CSS 的 gap 属性是一个简写属性,分为 row-gap 和 column-gap :?
该属性 gap 到目前为止只能运用于多列布局,Flexbox布局和网格布局的容器上:?
// 多列布局
.multi__column {
gap: 5ch
}
// Flexbox布局
.flexbox {
display: flex;
gap: 20px
}
// Grid布局
.grid {
display: grid;
gap: 10vh 20%
}
gap 属性可以是一个值,也可以是两个值:?
.gap {
gap: 10px;
}
// 等同于
.gap {
row-gap: 10px;
column-gap: 10px
}
.gap {
gap: 20px 30px;
}
// 等同于
.gap {
row-gap: 20px;
column-gap: 30px;
}
如果 gap 仅有一个值时,表示 row-gap 和 column-gap 相同。?
国内大多数 Web 开发者面对的场景相对来说比较单一,这里所说的场景指的是书写模式或排版的阅读模式。一般都是 LTR (Left To Right)。但有开发过国际业务的,比如阿拉伯国家的业务,就会碰到 RTL (Right To Left) 的场景。比如你打开 Facebook ,查看中文和阿拉伯文两种语言下的 UI 效果:?
在没见有逻辑属性之前,一般都会在 <html> 或 <body> 上设置 dir 属性,中文是 ltr ,阿拉伯语是 rtl ,然后针对不同的场景运用不同的 CSS 样式:?
其实,阅读方式除了水平方向(ltr 或 trl)之外,还会有垂直方向的阅读方式:?
?
为了让 Web 开发者能更好的针对不同的阅读模式提供不同的排版效果,在CSS新增逻辑属性。有了逻辑属性之后,以前很多概念都有所变化了。比如我们以前熟悉的坐标轴,x 轴和 y 轴就变成了 inline 轴 和 block 轴,而且这两个轴也会随着书写模式做出调整:?
除此之外,我们熟悉的 CSS 盒模型也分物理盒模型和逻辑盒模型:?
你可能感知到了,只要是以前带有 top、right 、bottom 和 left 方向的物理属性都有了相应的 inline-start 、 inline-end 、block-start 和 block-end 的逻辑属性:?
我根据 W3C 规范,把物理属性和逻辑属性映射关系整了一份更详细的表:?
?
回到实际生产中来:?
如果不使用逻辑属性的话,要实现类似上图这样的效果,我们需要这样来编写 CSS:?
.avatar {
margin-right: 1rem;
}
html[dir="rtl"] .avatar {
margin-right: 0;
margin-left: 1rem;
}
有了 CSS 逻辑属性之后,仅一行 CSS 代码即可实现:?
.avatar {
margin-inline-end: 1rem;
}
简单多了吧,特别是有国际化需求的开发者,简直就是一种福音。
CSS 媒体查询 @media 又称为 CSS 条件查询。在 Level 5 版本中提供了一些新的媒体查询特性,可以查询到用户在设备上的喜好设置:
比如:?
?
使用的方式和以往我们熟悉的 @media 是相似。比如 prefers-color-scheme 实现暗黑查式的皮肤切换效果:?
// 代码源于: https://codepen.io/airen/full/ProgLL
// dark & light mode
:root {
/* Light theme */
--c-text: #333;
--c-background: #fff;
}
body {
color: var(--c-text);
background-color: var(--c-background);
}
@media (prefers-color-scheme: dark) {
:root {
/* Dark theme */
--c-text: #fff;
--c-background: #333;
}
}
?
还可以根据网格数据设置来控制资源的加载:?
@media (prefers-reduced-data: reduce) {
header {
background-image: url(/grunge.avif);
}
}
@media (prefers-reduced-data: no-preference) {
@font-face {
font-family: 'Radness';
src: url(megafile.woff2);
}
}
其他的使用方式和效果就不一一演示了。不过在未来,CSS 的 @media 在编写方式上会变得更简单:?
@media (width <=320px) {
body {
padding-block: var(--sm-space);
}
}
@custom-media --motionOK (prefers-reduced-motion: no-preference);
@media (--motionOK) {
.card {
transition: transform .2s ease;
}
}
.card {
@media (--motionOK) { & {
transition: transform .2s ease;
}}
}
@media (1024px >=width >=320px) {
body {
padding-block: 1rem;
}
}
?
特别声明,该示例代码来自于 @argyleink 的 PPT 。
?
自从折叠屏设备的出现,给 Web 开发者带来新的挑战:?
值得庆幸的是,微软和三星的团队就针对折叠屏幕设备提供了不同的 媒体查询判断。?
上图是带有物理分隔线的双屏幕设备:
main {
display: grid;
gap: env(fold-width);
grid-template-columns: env(fold-left) 1fr;
}
@media (spanning: single-fold-vertical) {
aside {
flex: 1 1 env(fold-left);
}
}
无缝的折叠设备:?
@media (screen-fold-posture: laptop){
body {
display: flex;
flex-flow: column nowrap;
}
.videocall-area,
.videocall-controls {
flex: 1 1 env(fold-bottom);
}
}
?
CSS 的比较函数是指 min() 、max() 和 clamp() ,我们可以给这几个函数传入值(多个)或表达式,它们会对传入的值进行比较,然后返回最合适的值。另外,这几个和我们熟悉的 calc() 类似,也可以帮助我们在 CSS 中做动态计算。?
先看 min() 和 max() ,它们之间的差异只是返回值的不同:?
下图展示了 min(50vw, 500px) 在浏览器视窗宽度改变时,返回的值的变化:?
Demo: https://codepen.io/airen/full/mdeLMoZ
?
把上面的示例的 min() 换成 max() 函数,即 max(50vw, 500px),它的返回值是:?
Demo: https://codepen.io/airen/full/oNjdGyv
clamp() 和 min() 以及 max()略有不同,它将返回一个区间值,即 在定义的最小值和最大值之间的数值范围内的一个中间值。该函数接受三个参数:?
?
clamp(MIN, VAL, MAX),这三个值之间的关系(或者说取值的方式):?
?
比如下面这个示例:?
.element {
/**
* MIN=100px
* VAL=50vw ? 根据视窗的宽度计算
* MAX=500px
**/
width: clamp(100px, 50vw, 500px);
}
?
就这个示例而言,clamp() 函数的计算会经历以下几个步骤:?
.element {
width: clamp(100px, 50vw, 500px);
/* 50vw相当于视窗宽度的一半,如果视窗宽度是760px的话,那么50vw相当等于380px*/
width: clamp(100px, 380px, 500px);
/* 用min()和max()描述*/
width: max(100px, min(380px, 500px))
/*min(380px, 500px)返回的值是380px*/
width: max(100px, 380px)
/*max(100px, 380px)返回的值是380px*/
width: 380px;
}
示例效果如下:
Demo: https://codepen.io/airen/full/pojVpJv
?
简单地说,clamp() 、min() 和 max() 函数都可以随着浏览器视窗宽度的缩放对值进行调整,但它们的计算的值取决于上下文。
我们来看一个比较函数中 clamp() 的典型案例。假设我们需要在不同的屏幕(或者说终端场景)运用不同大小的 font-size :?
在还没有 CSS 比较函数之前,使用了一个叫 CSS 锁(CSS Locks)的概念来实现类似的效果:?
需要做一些数学计算:?
Demo: https://codepen.io/airen/full/bGqdOma
?
使用 clamp() 之后,只需要一行代码就可以实现:?
/** minf: 20px (min font-size)
* maxf: 40px (max font-size)
* current vw: 100vw
* minw: 320px (min viewport's width)
* maxw: 960px (max viewport's width)
*/
h1 {
font-size: clamp(20px, 1rem + 3vw, 40px);
}
?
使用这方面的技术,我们就可以轻易实现类似下图这样的效果:?
注,上图来自《Use CSS Clamp to create a more flexible wrapper utility》一文。
CSS 内容可见性,说要是指 content-visibilit 和 contain-intrinsic-size 两个属性,目前隶属于 W3C 的 CSS Containment Module Level 2 模块,主要功能是可以用来提高页面的渲染性能。
一般来说,大多数Web应用都有复杂的UI元素,而且有的内容会在设备可视区域之外(内容超出了用户浏览器可视区域),比如下图中红色区域就在手机设备屏幕可视区域之外:?
在这种场合下,我们可以使用CSS的 content-visibility 来跳过屏幕外的内容渲染。也就是说,如果你有大量的离屏内容(Off-screen Content),这将会大幅减少页面渲染时间。?
Google Chrome 团队有工程师对 content-visibility 做过相关的测试:?
使用了 CSS 的 `content-visibility` 属性,浏览器的渲染过程就变得更加容易。本质上,这个属性 改变了一个元素的可见性,并管理其渲染状态。?
而 contain-intrinsic-size 属性控制由 content-visibility 指定的元素的自然尺寸。它的意思是,content-visibility 会将分配给它的元素的高度(height)视为 0,浏览器在渲染之前会将这个元素的高度变为 0,从而使我们的页面高度和滚动变得混乱。但如果已经为元素或其子元素显式设置了高度,那这种行为就会被覆盖。如果你的元素中没显式设置高度,并且因为显式设置 height可能会带来一定的副作用而没设置,那么我们可以使用contain-intrinsic-size来确保元素的正确渲染,同时也保留延迟渲染的好处。?
换句话说,contain-intrinsic-size 和 content-visibility 是一般是形影不离的出现:?
section {
content-visibility: auto;
contain-intrinsic-size: 1000px;
}
如果你使用浏览器开发者工具审查代码时,将鼠标放到一个 <img> 标签上,你会看到类似下图这样的:?
<img> 的 src 路径上浮出来的图片底下有一行对图像的尺寸的描述,即252 x 158 px (intrinsic: 800 x 533 px) ,其实现这表述图片尺寸中两个重要信息:?
?
其实在 CSS 中给一个元素框设置大小时,有的是根据元素框内在的内容来决定,有的是根据上下文来决定的。根据该特性,CSS的尺寸也分为内部(内在)尺寸和外部(外在)尺寸。?
?
通过一个简单的示例来向大家演示 CSS 内在尺寸的特性,即 min-content 、max-content 和 fit-content 的特性。?
<h1>CSS is Awesome</h1>
<style>
h1 {
width: auto;
}
</style>
?
先来看 h1 的 width 取值为 auto 和 min-content 的差异:?
// 外在尺寸
h1 {
width: auto; // 根据容器变化
}
// 内在尺寸
h1 {
width: min-content; // 根据内容变化
}
Demo: https://codepen.io/airen/full/zYZvGrY
?
从上图中不难发现,width 取值为 min-content 时,h1 的宽度始终是单词“Awesome”长度(大约是144.52px)。它的宽度和容器宽度变化并无任何关系,但它受排版内相关的属性的影响,比如font-size、font-family 等。?
再来看max-content :?
Demo: https://codepen.io/airen/full/zYZvGrY
?
当h1 的 width 取值为 max-content 时,它的宽度是h1 所在行所有内容的宽度。最后来看 fit-content :?
?
Demo: https://codepen.io/airen/full/zYZvGrY
?
相对而言,fit-content 要比 min-content 和 max-content 复杂地多:?
h1 {
width: fit-content;
}
// 等同于
h1 {
width: auto;
min-width: min-content;
max-width: max-content;
}
简单地说,fit-content 相当于 min-content 和 max-content,其 取值:?
?
使用下图来描述它们之间的关系:?
min-content、max-content 和 fit-content 被称之个内在尺寸,它可以运用于设置容器尺寸的属性上,比如width 、height 以及 inline-size 和 block-size 等。但有些属性上使用的话则会无效:?
?
在布局上使用 min-content 、max-content 或 fit-content 可以帮助我们设计内在布局,另外在构建一些自适应的布局也非常灵活。特别是和 CSS 的 Grid 和 Shapes 相关特性结合,还能构建一些具有创意的布局。?
最后有一点需要特别声明,fit-content 和 fit-content()函数不是相同的两个东东,使用的时候需要区别对待。?
display 对于大家而言并不陌生,主要用来格式化上下文,这里特别拿出来和大家说的是因为 display 也有一些变化。其中之一就是 display 未来可以支持多个值:?
据最新的消息,Sarafi 浏览器已经把display 设置两个值已进入实验性属性。display 取两个值的含义大致如下:?
另外单独要说的是,display 新增了 contennts 属性值,** W3C规范是这样描述的**:?
大致意思是说:?
设置了 display: contents 的元素自身将不会产生任何盒子,但是它的子元素能正常展示。
?
比如:?
<div class="outer">
I'm, some content
<div class="inner">I'm some inner content </div>
</div>
<style>
.outer {
border: 2px solid lightcoral;
background-color: lightpink;
padding: 20px;
}
.innter {
background-color: #ffdb3a;
padding: 20px;
}
</style>
?
上面这个简单地示例代码,你将看到的效果如下:?
如果我们在.outer 元素上显式设置 display: contents ,该元素本身不会被渲染,但子元素能够正常渲染:?
Demo: https://codepen.io/airen/full/abJvyoj
?
在某些布局中,特别是不希望调整 HTML 的结构的时候,我们就可以使用该特性。比如在 Flexbox 或 Grid 中,希望把其他后代元素变成网格项目或 Flex项目,那就可以这样做:?
Demo: https://codepen.io/airen/full/zYZvdJb
?
display: contents 在规范讨论阶段和 display: subgrid 的讨论中是非常的激烈,最终是 display: contents 获胜了。你现在在Grid的布局中,也没有单独的display: subgrid ,而是将subgrid 移入到 grid-template-columns 和 grid-template-rows 中。?
另外还有一个比较大的争执就是 display: contents 和 Web 可访问性方面的。有关于这方面的讨论,你要是感兴趣的话,可以阅读:?
?
CSS 中的 @ 规则有很多种,但大家比较熟悉的应该是 @import 、@media 和 @supports 之类的。今天给大家简单的提几个不常见的,比如:?
?
使用过 CSS 处理器的同学,应该用过嵌套来组织自己的代码,比如 SCSS:?
// SCSS
foo {
color: red;
& bar {
color: green;
}
}
上面的代码经过编译之后:?
// CSS
foo {
color: red;
}
foo bar {
color: green;
}
庆幸的是,W3C 也在讨论和定义CSS中的嵌套规则。目前两种规则:?
foo {
color: red;
@nest bar {
color: green;
}
}
// 或者
foo {
color: red;
& bar {
color: green;
}
}
// 都等同于
foo {
color: red;
}
foo bar {
color: green;
}
也可以和媒体查询 @media 相互嵌套:?
article {
color: darkgray;
& > a {
color: var(--link-color);
}
}
code > pre {
@media (hover) {
&:hover {
color: hotpink;
}
}
}
code > pre {
@media (hover) {
@nest &:hover {
color: hotpink;
}
}
}
article {
@nest section:focus-within > & {
color: hotpink;
}
}
main {
padding: var(--space-sm);
@media (width >=540px) { & {
padding: var(--space-lg);
}}
}
除了 @nest 之外还有 @apply 。你可能在一些前端的框架或构建器中看到过 @apply:?
如果你在 Chrome Canary 浏览器“实验性属性” 就可以直接体验 @apply :?
?
简单地说,它有点类似于 SCSS 中的混合宏 @mixin 和 @extend :?
:root {
--brand-color: red;
--heading-style: {
color: var(--brand-color);
font-family: cursive;
font-weight: 700;
}
}
h1 {
--brand-color: green;
@apply --heading-style;
}
@property 是用来注册一个变量的,该变量是一个 CSS Houdini 中的变量,但它的使用和 CSS 中的自定义属性(CSS变量)是一样的,不同的是注册方式:?
// Chrome 78+
// 需要在 JavaScript脚本中注册
CSS.registerProperty({
'name': '--custom-property-name',
'syntax': '<color>',
'initialValue': 'black',
'inherits': false
})
// Chrome 85+
// 在CSS文件中注册
@property --custom-property-name {
'syntax': '<color>',
'initialValue': 'black',
'inherits': false
}
他的最大特色之一就是可以指定已注册的 CSS 变量的类型、初始值,是否可继承:?
上图截取于 Maxi 在推特上发的推文。
?
虽然它的使用方式和 CSS 的自定义属性相似,但它要更强大,特别是在动效方面的使用,能增强 CSS 的动效能力,甚至实现一些以前 CSS 无法实现的动效。比如?
@property --hue {
initial-value: 0;
inherits: false;
syntax: '<number>';
}
@keyframes rainbow {
to {
--hue: 360;
}
}
@property --milliseconds {
syntax: '<integer>';
initial-value: 0;
inherits: false;
}
.counter {
counter-reset: ms var(--milliseconds);
animation: count 1s steps(100) infinite;
}
@keyframes count { to {
--milliseconds: 100;
}}
把它和 CSS Houdini 的 Paint API 结合起来,可做的事情更多:?
更多这方向的效果可以在 houdini.how 网站上查阅:
Una Kravets 在 Google I/O 开发大会上就分享了容器查询 @container ,她把它称为新的响式布局所需特性之一:?
那么容器查询 @container 可以做什么呢?假设你的设计师给你提供了一份像下图这样的设计稿:?
你可能首先会想到的是 @media (在没有容器查询之前,似乎也只有这样的方式),而有了@container 之后,就可以换过一种姿势:?
这两张图上来自于 @shadeed9 的 《CSS Container Queries For Designers》一文,他的另一篇文章《Say Hello To CSS Container Queries》也是介绍容器查询的。
看上去非常强大,事实上也很强大,并且它的使用和 @meida 非常相似:?
// Demo: https://codepen.io/una/pen/mdOgyVL
.product {
contain: layout inline-size;
}
@container (min-width: 350px) {
.card-container {
padding: 0.5rem 0 0;
display: flex;
}
.card-container button {
/* ... */
}
}
Demo: https://codepen.io/una/pen/mdOgyVL
?
对于 @container 特性,有叫好的,也有不同的,比如 Kenton de Jong 在他的新博文《Why I am not a fan of CSS container queries》阐述了自己不喜欢该t特性:?
就我个人而言,我是很喜欢这个特性,后面会花一定的时间深入了解和学习 @container。当然有讨论是一件好事,这样会让该特性更成熟,如果你也想参与进来讨论的话,可以点击这里加入。?
我以前只看到过 @scope 规则,主要是用来处理 CSS 中样式规则作用域相关的,但并没有深入了解过。Una Kravets 在 Google I/O 开发大会分享上再次看到了 @scope :?
上图是 Miriam Suzanne 绘制的!
?
@scope 内的样式允许穿透和特定组件的样式,以避免命名冲突,许多框架和插件(如CSS模块)已经使我们能够在框架内做到这一点。现在,这个规范将允许我们为我们的组件编写具有可读性的CSS的本地封装样式,而不需要调整标记。?
/* @scope (<root>#) [to (<boundary>#)]? { … } */
@scope (.tabs) to (.panel) {
:scope { /* targeting the scope root */ }
.light-theme :scope .tab { /* contextual styles */ }
}
怎么看上去和 Web Componed中的 Scope 那么的相似呢??
对于 @layer ,我第一见:?
@layer reset {
* { box-sizing: border-box; }
body { margin: 0; }
}
// ...
@layer reset { /* add more later */ }
@import url(headings.css) layer(default);
@import url(links.css) layer(default);
@layer default;
@layer theme;
@layer components;
@import url(theme.css) layer(theme);
@layer default, theme, components;
@import url(theme.css) layer(theme);
@layer framework.theme {
p {
color: rebeccapurple;
}
}
@layer framework {
@layer theme {
p { color: cyan; }
}
}
上面代码表示啥意思,我也还没整明白,只知道 @layer 被称为层叠层(Cascade Layers)。该特性是 什么** W3C 层叠和继承规范 Level5** 中新提出来的。?
在我分享结束没多久,正在整理这篇文章的时候,发现我的偶像 @argyleink 也分享了一个相似的话题《Hover:CSS! What's New in 2021?》,分享了 31 个 CSS 相关的特性,并且按风险级别分为高、中、低三档:?
你会发现,和我整理的特性有很多吻合之处。如果你听过他去年在伦敦CSS大会分享的《London CSS: What‘s New in 2020?》,你会发现 2021 年的是 2020 年的升级版。?
在 2020 年听完 分享之后,我也整理了一份中文版本的《2020年你不应该错过的 CSS 新特性》,并在淘系前端团队 微信公众号发过。
?
文章很长,能阅读到这里,说明你也是 CSS 的真爱。感谢大家的阅读,在未来我将继续为大家服务。把 CSS 方面最新、最有意思的一面与大家共享!
*请认真填写需求信息,我们会在24小时内与您取得联系。