14种CSS实现水平或垂直居中的技巧

点击上图,查看教学大纲

前言

css水平和垂直居中是一个亘古不变的话题,它常常出现在优美的网页上以及各大前端面试当中。

说来惭愧,在两年前面试的时候,我完全不知道如何做到水平和垂直均居中的方法,那场面别提有多尴尬了(ps:特想找个地洞钻进去)。。。时隔两年,对于这个问题算是有一些了解了,现做个小小的整理,也算是对自己学习的总结。

注:文中例子没写明html代码时,使用的是下面结构:

class="example example14"> <div class="con"> 超级好用超级放心 <a href="https://tinypng.com/" target="_blank">在线压缩图片a> <span>压缩完可以 span> div></div>

只是类名会有所不同。

1、Line-height

适用情景:单行文字(垂直居中)

原理:将单行文字的行高设定后,文字会位于行高的垂直中间位置。

html:

class="example">Lorem ipsam.</div>

css:

.example{    width: 400px;    background: #afddf3;    line-height: 50px;}

2、Line-height + inline-block

原理:将多个元素或多行元素当成一个行元素来看待,所以我们必须要将这些数据多包一层,并将其设定为inline-block。

由于inline-block在不同浏览器会有空隙产生,因此设定父元素font-size:0来消除,从而达到完美的垂直居中。

css:

.example2{    width: 400px;    border: 1px solid #dcdcdc;    line-height: 100px;    font-size: 0;}.example2 .con {    display: inline-block;    line-height: 2;    vertical-align: middle;    width: 300px;    font-size: 15px;    background: #afddf3;}

3、:before + inline-block

原理::before 伪类元素搭配 inline-block 属性的写法应该是很传统的垂直居中的技巧了,此方式的好处在于子元素居中可以不需要特别设定高度。

我们将利用:before伪类元素设定为100%高的inline-block,再搭配上将需要居中的子元素同样设置成inline-block性质后,就能使用vertical-align: middle来达到垂直居中的目的了。

此方式在以往其实是个非常棒的垂直居中解决方案,唯独需要特别处理掉inline-block元素之间的4-5px空间这个小缺陷,不用怕,设置父元素font-size: 0,子元素font-size: 15px即可。

CSS:

.example3 {    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example3::before {    content: '';    display: inline-block;    height: 100%;    width: 0;    vertical-align: middle;}.example .con {    width: 300px;    font-size: 15px;    background: #afddf3;    display: inline-block;    vertical-align: middle;}

4、table + margin

适用情景:单对象(水平居中)

原理:将子元素设置块级表格,再设置水平居中。

CSS:

.example4 {    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example .con {    display: table;    margin: 0 auto;    width: 300px;    font-size: 15px;    background: #afddf3;}

5、table + table-cell + vertical-align: middle

适用情景:多对象(垂直居中)

html:

<div class="example example5">    <div class="con">        超级好用超级放心            <a href="https://tinypng.com/" target="_blank">在线压缩图片a>        <span>压缩完可以打包下载哦 span>     div>    <div class="con">        CSS-TRICKS    div>div>

css:

.example5 {    display: table;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example .con {    display: table-cell;    vertical-align: middle;    width: 300px;    font-size: 15px;    background: #afddf3;}

6、absolute + margin 负值

原理:设置绝对定位,top: 50%;后,再设置高度一半的负值实现。说来说去,这就是一道简单的数学题而已。

缺陷:需要设置居中元素的高度。

优势:无浏览器兼容性问题

css:

.example6 {    position: relative;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example6 .con {    position: absolute;    top: 50%;    height: 80px;    margin-top: -40px;    width: 300px;    font-size: 15px;    background: #afddf3;}

7、absolute + margin auto

原理:当元素设置为绝对定位后,假设它是抓不到整体可运用的空间范围,所以margin: auto会失效,但当你设置了top:0;bottom:0;时,绝对定位元素就抓到了可运用的空间了,这时你的margin:auto就生效了。

缺陷:定位元素必须有固定的宽高(百分比也算)。

css:

.example7 {    position: relative;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example7 .con {    position: absolute;    top: 0;    bottom: 0;    left: 0;    right: 0;    margin: auto;    height: 80px;    width: 300px;    font-size: 15px;    background: #afddf3;}

8、absolute + translate

原理:利用绝对定位时的top 与right设置元素的上方跟左方各为50%,再利用transform: translate(-50%, -50%);位移居中元素自身宽与高的50%就能达成居中的目的了。

缺陷:translate是css3属性,低版本浏览器不支持。

显著优势:无需固定定位元素的宽高。

css:

.example8 {    position: relative;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example8 .con {    position: absolute;    left: 50%;    top: 50%;    transform: translate(-50%, -50%);    font-size: 15px;    background: #afddf3;}

9、Flex + align-items

适用情景:多行文字(垂直居中)

原理:弹性布局,align-items定义flex子项在flex容器的当前行的侧轴(纵轴)方向上的对齐方式,参考CSS-TRICKS。

缺陷:css3属性,低版本浏览器不支持。

显著优势:无需固定定位元素的宽高,代码干净利索。

css:

.example9 {    display: flex;    align-items: center;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example9 .con {    font-size: 15px;    background: #afddf3;}

10、Flex + justify-content

适用情景:多行文字(水平居中)

原理:弹性布局,justify-content设置或检索弹性盒子元素在主轴(横轴)方向上的对齐方式,参考CSS-TRICKS。

缺陷:css3属性,低版本浏览器不支持。

显著优势:无需固定定位元素的宽高,代码干净利索。

css:

.example10 {    display: flex;    justify-content: center;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example .con {    height: 80px;    font-size: 15px;    background: #afddf3;}

11、Flex + :before + flex-grow

适用情景:多行文字(垂直居中)

原理:弹性布局,Flex-direction:column;将项目垂直显示,正如一个列一样。flex-grow: [number];规定项目将相对于其他灵活的项目进行扩展的量,参考CSS-TRICKS。

缺陷:css3属性,低版本浏览器不支持,并且难度稍大,一般不会想到这种方法。

显著优势:无需固定定位元素的宽高

css:

.example11 {    display: flex;    flex-direction: column;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example11:before {    content: '';    flex-grow: .5;}.example11 .con {    font-size: 15px;    background: #afddf3;}

12、Flex + margin

缺陷:css3属性,低版本浏览器不支持。

css:

.example12 {    display: flex;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example12 .con {    margin: auto;    width: 300px;    font-size: 15px;    background: #afddf3;}

13、Flex + align-self

原理:align-self定义flex子项单独在侧轴(纵轴)方向上的对齐方式。

缺陷:css3属性,低版本浏览器不支持。

css:

.example13 {    display: flex;    justify-content: center;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example13 .con {    align-self: center;    width: 300px;    font-size: 15px;    background: #afddf3;}

14、Flex + align-content

原理:align-content在弹性容器内的各项没有占用交叉轴上所有可用的空间时对齐容器内的各项(垂直),弹性项目有多项此属性才会发挥作用。

缺陷:css3属性,低版本浏览器不支持。

css:

.example14 {    display: flex;    align-content: center;    flex-wrap: wrap;    margin-top: 10px;    width: 400px;    height: 150px;    font-size: 0;    border: 1px solid #dcdcdc;}.example14:before, .example14:after {    content: "";    display: block;    width: 100%;}.example14 .con {    height: 80px;    width: 300px;    font-size: 15px;    background: #afddf3;}

下面是一个比较常见的例子,往往是不想让图片发生变形并且不管尺寸大小均会显示在容器的正中央(以下例子应用的是第8条)。

html:

<div class="imgbox-box">   <div class="imgbox">       <img src="imgs/head.jpeg" alt="">   div>   <div class="imgbox">       <img src="imgs/head.jpeg" alt="">   div>   <div class="imgbox">       <img src="imgs/head.jpeg" alt="">   div>div>

css:

.imgbox-box {    display: flex;    justify-content: center;    margin-bottom: 40px;}.imgbox {    width: 200px;    height: 200px;    position: relative;    background: #ebf8ed;    overflow: hidden;}.imgbox img {    position: absolute;    left: 50%;    top: 50%;    transform: translate(-50%, -50%);    max-width: 100%;    max-height: 100%;}

结语

有些是水平居中,有些是垂直居中,将它们某两个合在一起就能实现水平和垂直均居中。

感谢你的阅读,希望这些小技巧对你有用。

阿里 P7二面:Redis 执行 Lua,能保证原子性吗?

大家下午好!

Redis 和 Lua,两个看似风流马不相及的技术点,为何能产生“爱”的火花,成为工作开发中的黄金搭档?技术面试中更是高频出现,Redis 执行 Lua 到底能不能保证原子性?今天就来聊一聊。

要想弄清楚这个问题,需要对“原子性”这个概念有一个清晰的认识,因此,首先要分析的是原子性的概念。

一、原子性通常意义的原子性

通常意义上,我们说的原子性是指关系型数据库 RDBMS(比如 MySQL)的原子性,也就是 ACID(Atomicity、Consistency、Isolation、Durability)中 Atomicity这一项特性。

ACID 中的原子性指:事务中的所有操作要么全部执行,要么全部不执行。

这里以银行转账,账户A 给账户B 转账100元为例来解释原子性:

账户A 减去100元;账户B 增加100元;

原子性是指上面两个过程,要么全部执行,要么全部不执行。也就是说,账户A 减去 100元的同时,账户B 必须增加100元,否则,该操作就不具备原子性。Java代码简要实现如下图:

Lua 原子性

在分析 Lua的原子性之前,我们先看看 Lua是什么,下图摘自 Lua官方描述:

从官方描述可以得知:Lua 是一种功能强大、高效、轻量级、可嵌入的脚本语言。它支持过程编程、面向对象编程、函数式编程、数据驱动编程和数据描述。 Lua 将简单的过程语法与基于关联数组和可扩展语义的强大数据描述结构相结合。Lua 是动态类型的,通过使用基于寄存器的虚拟机解释字节码来运行,并具有自动内存管理和增量垃圾回收功能,使其成为配置、脚本编写和快速原型设计的理想选择。

Lua 本身并没有提供对于原子性的直接支持,它只是一种脚本语言,通常是嵌入到其他宿主程序中运行,比如 Redis。

在 Redis中执行 Lua的原子性是指:整个 Lua脚本在执行期间,会被当作一个整体,不会被其他客户端的命令打断。

为了对 Redis执行 Lua的原子性有一个感官上的认识,这里以 Lua脚本中需要完成 SET key1 value1 和 INCRBY key2 value2 和 SET key3 value3 三个命令为例:

上述例子,整个 luaScript 字符串脚本作为一个整体被执行且不被其他事务打断,这就是一个原子性的操作。

好了,总结下 ACID的原子性和 Redis执行 Lua脚本原子性在概念上的差异:

在分析原子性概念时,我们可以发现“原子性”其实是事务中的一项特性,因此,接下来分析 Redis的事务。

二、Redis 事务

下图是 Redis官方对事务描述的摘要:

文档看起来很长,总结成一句话:Redis 事务允许执行一批命令,通过执行 MULTI命令开启事务,执行 EXEC命令结束事务,WATCH 和 DISCARD 配合事务一起使用,提供了一种 CAS(check-and-set) 乐观锁的机制。WATCH 用于监听 Key,如果被监听的 Key有任何一个发生变化,则中止事务(被动关闭事务),而 DISCARD 用于主动中止事务。

MULTI/EXEC

用一个示例来理解 MULTI/EXEC:

通过执行的结果可以看出:Redis的事务是以 MULTI命令开启,以 EXEC命令结束,期间所有的命令都是先进入队列,只有执行 EXEC命令时,才会把队列中的所有命令顺序串行执行,并且返回一个所有命令执行结果的数组,包括命令执行的错误信息。

需要注意的是:在 EXEC 执行后,即使事务队列中有命令执行失败,队列中的所有其他命令也会被处理,Redis 不会停止执行这些命令。

DISCARD 和 WATCH 也是 Redis 中用于事务的两个命令,它们与 MULTI 和 EXEC 一起使用,提供更复杂的事务处理机制。

WATCH

WATCH 命令用于监听一个或多个 Key,如果在执行事务期间这些 Key中任何一个Key的 value被其他事务修改,当前整个事务将会被中止。(需要注意:低于 6.0.9 的 Redis 版本,Key过期不会中止事务)

如下示例:事务1 watch key1 key2,事务2在事务1执行期间修改 key2 = 10,当事务1执行 exec命令时,因为 watch监听到 key2被其他事务(事务2)修改了(value=10) , 因此事务1被取消,事务队列中的所有命令被清除,即 set key1 value1 和 incrby key 2两条命令都不执行,key2的 value还是10;

事务1

事务2

watch key1 key2

multi

set key1 value1

incrby key2 2

set key2 10

exec

keys * // 只有key2=10

keys * // 只有key2=10

DISCARD

DISCARD 命令用于中止事务。

如下示例,执行 DISCARD命令后,当前事务被中止,因此,执行 EXEC 时会报“ERR EXEC without MULTI”错误。

事务中的错误

事务中主要会出现两种类型的错误:

事务命令进入事务队列之前出错。例如,命令语法错误(参数错误、命令名称错误等),或者可能存在一些关键情况,比如内存不足。如下示例,命令incr key2 1/0 在进入事务队列之前报错,所以,当前事务被中止,执行 EXEC命令会报错:

调用 EXEC 命令后,事务队列中的命令执行失败。例如,对字符串值进行加1操作。如下示例,key的 value是字符串,当对 key 执行incr key 操作时报错,因此,该条命令执行失败:

事务回滚

Redis的事务不支持回滚。 官方说明如下:

Redis 不支持事务回滚,因为支持回滚会对 Redis 的简单性和性能产生重大影响。

官方说明简明扼要,其实,多加思考也能理解:"Redis" 是 "REmote DIctionary Server" 的缩写,翻译为“远程字典服务”,设计的初衷是用于缓存,追求快速高效。而了解过 ACID事务的小伙伴应该能明白事务回滚的复杂度,因此,Redis不支持事务回滚似乎也合情合理。

到此,我们也对 Redis事务做个小结:Redis的事务由 MULTI/EXEC 两个命令完成,WATCH/DISCARD 两个命令的加持,给 Redis事务提供了 CAS 乐观锁机制。Redis 事务不支持回滚,它和关系型数据库(比如 MySQL)的事务(ACID)是不一样的。

三、Redis 如何执行 Lua?

分析完原子性和 Redis事务这些理论知识后,我们就得动手实操,看看 Redis是如何执行 Lua的。

一般情况下,Redis执行 Lua常用的方法有 2种:

原生命令,比如 EVAL/EVALSHA命令等;编程工具,比如编程语言中提供的三方工具包或类库;

在编写 Lua脚本时,需要注意区分 redis.call() 和 redis.pcall() 两个命令的使用。

EVAL

语法:

EVAL script numkeys [key [key ...]] [arg [arg ...]]

EVAL语法很简单,EVAL script numkeys 是必填项,[key [key ...]] [arg [arg ...]]是选填项。

如下示例截图,分别展示了不传Key,传 1个key 和 2个 key 3种场景:

下图示例展示了 [key [key ...]] [arg [arg ...]] 和 numkeys 匹配错误时报错的场景:

redis.call()

redis.call() 用于执行 Redis的命令。当命令执行出错时,会阻断整个脚本执行,并将错误信息返回给客户端。

如下示例:当执行INCRBY key2 1/0 失败时,会抛异常,后续流程被阻断,即SET key3 value3没有被执行。

Redis原生命令执行示例如下:

EVAL "redis.call('SET', 'key1', 'value1'); redis.call('INCRBY', 'key2', 1/0); redis.call('SET', 'key3', 'value3')" 0

使用 Jedis框架执行 Lua示例如下:

查看 Lua执行后各个key的值,截图如下:

redis.pcall()

redis.pcall() 也用于执行 Redis的命令。当命令执行出错时,不会阻断脚本的执行,而是内部捕获错误,并继续执行后续的命令。

如下示例:当执行INCRBY key2 1/0 失败时,不会抛异常,后续流程继续执行,即SET key3 value3 也被执行。

Redis原生命令执行示例:

EVAL "redis.pcall('SET', 'key1', 'value1'); redis.pcall('INCRBY', 'key2', 1/0); redis.pcall('SET', 'key3', 'value3')" 0

使用 Jedis框架执行 Lua示例:

对于 Lua中 redis.call() 和 redis.pcall() 如何选择,需要根据实际业务来判断,标准是:当 Lua脚本中某条命令执行出错时,是否需要阻断后续的命令执行。

四、如何保证原子性?

首先,可以肯定的是:Redis执行 Lua脚本可以保证原子性,不过这和 Redis Server的部署方式密不可分。

Redis是典型的 C/S(Client/Server) 模型,如下图:

因此,Redis 通常有 3种不同的部署方式,部署方式不同,原子性的保证也不一样。

单机部署

不管 Lua脚本中操作的 key是不是同一个,都能保证原子性;

主从部署

Redis 主从复制是用于将主节点的数据同步到从节点,以保持数据的一致性。而Redis的所有写操作都在主节点上,所以,不管 Lua脚本中操作的 key是不是同一个,都能保证原子性;

需要注意:当主节点执行写命令时,从节点会异步地复制这些写操作。在这个复制的过程中,从节点的数据可能与主节点存在一定的延迟。因此,如果在 Lua 脚本中包含读操作,并且该脚本在主节点上执行,可能会读到最新的数据,但如果在从节点上执行,可能会读到稍有延迟的数据。

Cluster集群部署

如果 Lua脚本操作的 key是同一个,能保证原子性;

如果操作的 Key不相同,可能被 hash 到不同的 slot,也可能 hash 到相同的 slot,所以不一定能保证原子性;

因此,在 Cluster集群部署的环境下使用 Lua脚本时一定要注意:Lua脚本中操作的是同一个 Key;

原子性保证

这里以 Redis单机部署为例:当客户端向服务器发送一个带有 Lua脚本的请求时,Redis会把该脚本当作一个整体,然后加载到一个脚本缓存中,因为 Redis读写命令是单线程操作(关于 Redis的单线程模型和多路复用线程模型会在其他的文章中讲解),最终,Lua脚本的读写在 Redis服务器上可以简单地抽象成下图,所有的 Lua脚本会按照进入顺序放入队列中,然后串行进行读写,这样就保证每个 Lua不会被其他的客户端打断,从而保证了原子性:

五、面试该如何回答?

在面试中,Redis 执行 Lua脚本时,能否保证原子性?这个问题如何作答?

第一步,需要解释这里的原子性是什么?它和关系数据事务 ACID中的一致性的差异是什么?消除原子性在具体载体(RDBMS/NoSQL)上概念的差异;第二步,需要解释 Redis的事务,说明 RDBMS/NoSQL 在事务上的差异点;第三步,需要解释 Redis在不同部署方式下原子性能否保证。Redis部署方式有3种:单机部署,主从部署,Cluster集群部署,需要说明在哪些部署方式下能保证原子性,哪些不能保证原子性;第四步,解释 Redis 执行 Lua脚本是如何保证原子性;第五步,分析下 Redis的单线程模型 和 IO多路复用模型(加分项),这步是可选项;六、Why Lua?

既然 Redis事务能保证原子性,为什么还需要 Lua脚本呢?

Lua 是一种嵌入式语言,是 Redis官方推荐的脚本语言;Lua 脚本一般比 MULTI/EXEC 更快、更简单;Redis 事务中,事务队列中的所有命令都必须在 EXEC命令执行才会被执行,对于多个命令之间存在依赖关系,比如后面的命令需要依赖上一个命令结果的场景,Redis事务无法满足,因此 Lua 脚本更适合复杂的场景;Redis 事务能做的 Lua能做,Redis事务做不到的 Lua也能做;七、Lua注意事项

Redis执行 Lua脚本时,Lua的编写需要注意以下几个点:

不要在 Lua脚本中使用阻塞命令(如BLPOP、BRPOP等)。因此这些命令可能会导致 Redis服务器在执行脚本期间被阻塞,无法处理其他请求;不要编写过长的 Lua脚本。因为 Redis读写命令是单线程,过长的脚本,加载,解析,运行会比较耗时,导致其他命令的延迟延迟增加;不要在 Lua脚本中进行复杂耗时的逻辑;因为 Redis读写命令是单线程的,长时间运行脚本可能导致其他命令的延迟增加;Lua脚本中,需要注意区分 redis.call() 和 redis.pcall() 命令;Lua 索引表从索引 1 开始,而不是 0;八、总结九、参考资料

Scripting with Lua:redis.io/docs/intera…

Atomicity with Lua:developer.redis.com/develop/jav…

Redis Transactions:redis.io/docs/intera…

The Programming Language Lua:

十、温馨提示

如果你发现文章中存在缺点和错误,欢迎批评指正。如果你觉得文章对你有帮助,欢迎关注,点赞,评论,或者转发给更多的小伙伴,获取更多干货资料私信我。

本站内容来自用户投稿,如果侵犯了您的权利,请与我们联系删除。联系邮箱:835971066@qq.com

本文链接:http://news.xiuzhanwang.com/post/2427.html

友情链接: