回调函数到promise再到理解async/await

推特上有人发了个”在 7 秒内理解 async/await“ 的视频,地址看这里

拆分成就是写法的变化:回调函数 -> promise -> async/await

js 天生支持异步,如果你的数据依赖于异步请求,那么需要在它的回调中获取,一旦写的多了,就形成了回调地狱,如下图所示

回调函数模式

后来,ES6 出了 promise,promise 的意思是承诺,情景如下:

未婚妻:你一定要回来!

出去打战的士兵:I promise

这样写法上就有个先后顺序,不用再嵌套,而是串联(但换行之后看起来也很清晰)

promise模式

promise 的问题在于,它的语境还是异步,当 getDate 拿到数据后做事情(then)

人的惯性思维是同步,即写就写了,开心就开心了,怒就怒了,没有说等五秒后再笑

所以 promise 是虽好,但可以再换个写法——async/await

async/await模式

async/awiat 是绑定在一起的,缺一不可

const a = await getData() 其中的 a 就是请求数据拿到的结果,从理解上更符合人的思维

芝麻开门,显示全文!

闲人闲谈之执行力

最近一年来,开始将博客公开,为的是在在简历上给人一个能持续输出的印象。但本身却不爱在网上更多公开自己的信息,以至于当同事知道自己的 Github 后

主要是执行力不行。春夏天还行,但今年记录了一下,十月份过后(也许是十一综合征),状态就不行了

想挽回,即使执行了计划,还是不能很好的完成它

怎么能做到很好的执行自己的计划

迷茫有三:

为什么哪些大牛能一直持续更新?

  • 勤奋。强迫自己做
  • 兴趣。因为热爱不在乎
  • 生活。不得已

为什么我做不到?

  • 自身不够勤奋
  • 做了之后没有成就感,即使我写了文章,写了知识地图,但是成就感获得不多。但自己又不是那种特别爱成就感的人

设计属于自己的课程?

兴趣很廉价,专注力才可贵

芝麻开门,显示全文!

scss不能用除法?

之前在项目中就遇到过,scss 不是用除法的问题,当时项目忙,没有及时处理,但心里一直有一根刺,像张爱玲的红玫瑰一样闹的我心烦,现在有时间就想拔掉这根刺

错误定位

因为运行项目后,用到除法,提示错误为

scss警告不能除法

点进官方的解决方案

有两种,一种是引用 @use "sass:math";,使用 math.div(100%, 24) 这类写法,另一种是全局下载sass-migrator 对目标文件进行转换

网上查了一番, bootstrap 鸡贼,换了个思路,用乘法代替,具体可看代码。但也应该会遇到必须使用除法的情况,从 PR 上看,没有看到必须用除法的场景,可惜一番

算了,再去查一番,发现用指定绑定的 sass 可以,是个日本佬写的方案 。所以大概三种方法解决

先用第一种方法解决,确实,在开发环境上不报错了,但是在 build 时,报 Error: Invalid CSS after "...ion-delay: math"

报错信息

第二种、第三种都不好使

PS:这里要批评一下自己,看报错信息就知道使用到了 node-sass,node-sass 不支持这种写法,但是当时自己没认真看报错信息,直接去 Google 了

换种思路

我的项目是基于 umi 开发,用 scss 是因为安装了它的插件:@umijs/plugin-sass

umijs/plugin-sass文档

我的 @umijs/plugin-sass 已经升级到最新版本,也就是说使用到了 Dart Sass,难道 Dart Sass 的问题?

在胡乱找的时候,发现了这篇文章 ,解惑了

This is because you need to use sass instead of node-sass. Remove node-sass and use sass instead and this error should go away

翻译过来就是

这是因为您需要使用sass而不是node-sass. 删除node-sass并使用sass,此错误应该消失。

我的做法

直接升级整个项目,粗暴

npm update

接着删掉 package.json 中的 node-sass(当初的自己害了自己)

再接着删掉整个 node_modules,再重新下载

rm -rf node_modules
yarn

结果通了,这样解决了一个心头刺

@umijs/plugin-sass 源码解读

在找问题的时候看了下 @umijs/plugin-sass 的源码,贴出来看一下

import { IApi, utils } from "umi";

export default (api: IApi) => {
  api.describe({
    config: {
      schema(Joi) {
        return Joi.object({
          implementation: Joi.any(),
          sassOptions: Joi.object(),
          prependData: Joi.alternatives(Joi.string(), Joi.func()),
          sourceMap: Joi.boolean(),
          webpackImporter: Joi.boolean(),
        });
      },
    },
  });

  api.chainWebpack((memo, { createCSSRule }) => {
    createCSSRule({
      lang: "sass",
      test: /\.(sass|scss)(\?.*)?$/,
      loader: require.resolve("sass-loader"),
      options: utils.deepmerge(
        {
          implementation: require("sass"),
        },
        api.config.sass || {}
      ),
    });
    return memo;
  });
};

介于对 webpack 的不熟悉,下面的说法不带有参考性

  • api.describe 不懂
  • api.chainWebpack 这段大概是对 webpack 的规则的一些写入,即是用 sass 写法

后续学习前端工程化系列的时候再对其做补充

扩展阅读

Sass 是采用 Ruby 语言编写的一款 CSS 预处理语言

Sass 和 Scss 其实就是同一种东西,我们平时都称之为 Sass,两者不同之处主要有两点:

  1. 文件扩展名不同,Sass 是以“.sass”后缀为扩展名,而 Scss 是以“.scss”后缀为扩展名。
  2. 语法书写方式不同,Sass 是以严格的缩进式语法规则来书写,不带大括号({})和分号(;),而 Scss 的语法书写和我们的 CSS 语法书写方式非常类似。

芝麻开门,显示全文!

css定位小技巧

最近在忙着做组件,发现之前的组件做的不好看,其关键在于我们没有 UI 标准,所有的标准由产品定,设计就出一个看上去大差不差的效果图(尺寸没有按标准来做,多少会差个几像素)。然后开发按照原型标准来做

这倒是的我们前端出的产品处于没有标准但看上去是一回事的状况

这不,在重构过程中,我发现之前的组件的不合理之处

在做 Tag 标签组件时,发现加 border 的标签比其他的都长了

image-20211111142921445

查看 CSS,发现是因为 border 的长度导致,因为这里有三个标签,加起来就有 6 个像素,所以看起来就很明显

使用 box-sizing: border-box 更改盒模型,回顾一下,盒模型有哪几种,

标准盒模型

  • width= content

IE 盒模型(怪异盒子)

  • width=content+border+padding

但问题是我所有的盒模型都已经格式化为 border-box。所以这个方法没有用

参考了一下同行的做法,这里给有赞一个赞

他们的做法是对主 UI 做相对定位,再在 before 中添加绝对定位,即

.Tag {
  position: relative;
  &::before {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    border: 1px solid;
    border-color: inherit;
    border-radius: inherit;
    content: "";
    pointer-events: none;
  }
}

这样就做到了在 主容器中(Tag 组件)实现边框的内嵌

芝麻开门,显示全文!

我的历史梳理

前言

在微博写周末闲谈,写着写着就超过了一千字,想着干脆写完整了,放在博客上,记录一下 27 岁时我的历史观

正文

最近这两周来,一直在看温铁军教授的视频,很受感触,从三农问题说到国家战略;从国际政治说到马列主义,再到中国特色社会主义;从东西方文明接触的始点说到现在的贸易战,再到中国的应对。总之,我的历史观有被重塑。强烈推荐朋友们去看一看

这里我抛砖引玉把我的所知所记写下来,东西在脑子里,想到哪写什么

秦始皇统一中国,二世被灭。起源于不在于秦的暴政,而是他“车同轨,书同文,统一度量尺”等一些列措施得罪了各种利益阶级。像车同轨,得罪的就是贵族,为什么呢?各个国家的车轨尺寸不同,你一统一,尺寸不同意味着身份不同;同理书同文得罪的是文人;统一度量尺得罪的是商人(商人可在各个国家套利)。秦统一为了管理整个天下,必须要中央集权,搞郡县制,不再搞分封,毕竟周朝的分封已经证明失败,但要把不统一改为统一,必然招到反噬

像现在 200 多个国家和地区,要是有强人想统一整个地球,即靠武力统一地球,实现统一语言、文字、标准,阻力肯定很大,也许根本推行不下去。秦那个时候就处于那样的状况,结果是屁股还没坐稳几年,就被灭了

到了汉朝,前几代皇帝休养生息,文景之治就在这时发生的,安稳了几年,国库里有了存款。汉武帝也是千古一帝,他想打匈奴了。再此之前,汉朝是要每年给匈奴钱,交不起就侵略,汉武帝觉得国库有钱了,不想给钱了,就硬气起来和他们干起来了

其实匈奴来犯也是有原因的,因为那个时候正处于小冰河时期,气候正好变冷,他们必须南下生存,也是迫不得已

汉武帝做到了每户人家没儿子的必须养马,有男丁的必须出一人,去打战时是一个人牵着两匹马上的,霍去病、卫青、李广就是那个时候的出来的英雄,因为他们打的是外来侵略者,所以在历史上的名气大,而秦国时期大将白起、蒙恬知道的人就少了(对老百姓而言)

匈奴被汉武帝打败后,还是要生活啊,怎么办,往西走,翻过喀尔巴阡山脉,进入东欧,跟欧洲的原始部落冲突,最终进入中欧、北欧一代,把这一代的日耳曼蛮族南压,罗马人打不过,允许日耳曼南下,进入意大利,最终是日耳曼蛮族灭了西罗马

简单来说,东方把匈奴(游牧民族)打到大漠以北,匈奴进入欧洲,引起欧洲动乱,使得西罗马灭亡(外因)。这次天气变化引起的打仗、让东西方文明都倒退了。东方倒退了 400 年,期间经历了东汉、三国、魏晋、十六国、南北朝,直到隋唐又一大一统;西方进入中世纪,直接倒退 1000 年,抗打击能力属实不行。

而在唐朝时期,听说又是因为天气原因。不过面对的是突厥。突厥分东西突厥。唐朝李世民照样打,打败了突厥后,吸纳了一部分,即新疆地区同胞,而另一部分突厥人往西跑,经过突厥走廊,并南下,到了今天的土耳其(安纳托利亚半岛),导致了东罗马(拜占庭)灭亡的一个外因

这就是两次文明冲突的历史梳理

温老讲课讲的极好,我一个对历史没多大兴趣的人都听得津津有趣,推荐大家可以去B站看一下他的课

芝麻开门,显示全文!

搞轮子:皮肤概念的几种方案

而且我还希望能有灰度模式和暗黑模式,皮肤概念孕育而生

大厂的做法

vant

ant-design

zarm

考察下来,zarm 最符合我的预期

按 zarm 的来

简单来说,使用 context 来做主题色,因为 context 能传递到任意组件,接下来就看怎么写

皮肤解决方案

https://segmentfault.com/a/1190000041195585?_ea=195391016

CSS 变量

:root {
  --bg-color: brown; // 定义颜色变量
}
.btn {
  // 直接使用颜色预定义的颜色变量
  background-color: var(--bg-color);
}

https://css-tricks.com/switch-font-color-for-different-backgrounds-with-css/

https://zhuanlan.zhihu.com/p/494460951

https://mp.weixin.qq.com/s/6bmqki5IPDlD4H7a7C1HXw

张鑫旭的换肤方案

https://www.bilibili.com/video/BV1kU4y1X7a8?vd_source=55c655c3b4aed7bb7a250da7eea13eb8

https://juejin.cn/post/7117911005841063944

芝麻开门,显示全文!

项目实战:服务器中的nginx和docker起的nginx冲突怎么办

今天,后端过来说有个需求需要前端帮忙,我立即摆谱:“哼,什么问题?”

他连忙跑到我的座位前,窜着手机跟我说:“我发你一个链接,能不能把这些代码放到 hosts 文件里。”

我说:“这是什么?为什么要放。”

经过他的解释,大致是

前端点击一个按钮,调用 API ,后端返回一个 url,url 是第三方链接(后端对接第三方公司返回的链接),因为第三方链接的测试环境需要内网环境才能测试,所以要测试这个产品需要配置 hosts 文件

我的理解是,这个只需要在本机上添加 hosts 文件,让测试添加即可,管我前端什么事!

后来经过拉锯战,他找来技术负责人,负责人亲自讲解,希望我把这些代码放到前端服务器的 hosts 文件中

没办法,说了不听,听了不悦,要我做。官大一级压死人。这里也很有意思,技术人员中,如果你技术真的过硬,听你的,确实没问题,到后面下级还能学到东西。但是如果上级技术一般,这个知识点不是很懂,那么只能试,试就需要成本,这次就是一次成本代价

我在服务器上的 hosts 中添加代码后,网站就访问不了了,nginx 502

我感觉删掉添加的代码,再重启 DNS 服务,结果还是不行

继续搞,搞半天才想起来我的站点部署在 docker 中,启动服务器的 nginx 没用

搞 docker

常用命令

  • docker ps 查看运行的容器
  • docker exec -it containerID /bin/bash 进入指定容器的 docker 服务

先删掉原本的 docker,在手动启动 docker 命令(同事写好的 bash 文件)

直接报错,反向代理不能用字母

host not found in upstream "XXX.com" in /etc/nginx/conf.d/default.conf:18
nginx: [emerg] host not found in upstream "XXX.com" in /etc/nginx/conf.d/default.conf:18

暂时找不到原因,先改,改成 ip 后,启动成功,使用

docker exec -it containerID /bin/bash 进入容器中,查看 docker 中的 nginx 和静态文件是否有问题,发现都是最新的,理论上是没问题的

回到服务中,再查看端口占用

ps -ef | grep nginx

有好几个 nginx 的服务是启动的

所以猜想是不是 hosts 文件添加之后,docker 自动被弄坏了,然后我再启动了 nginx,nginx 和 docker 启动的 nginx 冲突,所以即使把 hosts 文件恢复了,因为 nginx 一直启动着,所以 docker 启动的容器一直不能访问

解决方案

简单来说,把服务器中的 nginx 关掉,再重启 docker 容器即可

一、查找 nginx 所占的端口

ps -ef | grep nginx

二、杀掉所有的与 nginx 相关的端口

kill -9 12782

三、重启 docker 相关容器

docker restart f4d

总结

到最后解决方案很简单,但主要是排查能力和对命令的熟悉

这次排查唤起了我对很多 nginx 和 docker 的知识点,明年要对这两块做重新的知识梳理

芝麻开门,显示全文!

左边固定宽,右边自适应的6种方法

这是一道面试题,你有多少种办法呢?

这里我们假设左边名为 left,宽度为 200 px,右边名为 right。即默认

.left {
  width: 200px;
}

我的理解分四大类

  • flex 布局
    • 需设置父元素高度
  • grid 布局
    • 需设置父元素高度
  • 绝对定位
    • 双子元素 absolute
      • 不需要设置父元素高度
      • 子元素都设置高度,右边子元素 left:200px + width: 100%
    • 左元素 absolute + 右元素 margin-left
      • 不需要设置父元素高度
      • 子元素都设置高度,右边子元素 margin-left: 200px + width: 100%
  • float 浮动
    • 左元素左浮动,右元素不动
      • 无需父元素
      • 左元素需设置宽高和浮动,右元素设置高度即可
    • 左元素左浮动,右元素右浮动
      • 无需父元素
      • 左元素设置宽高和左浮动,右元素设置右浮动以及高和宽(width: calc(100% - 200px)

flex 布局

需要一个父元素做 flex 布局,且需要给它一个高度(撑开容器)

.father {
  display: flex;
  height: 200px;
}
.right {
  flex: 1;
}

grid 布局

高级的布局方式,子元素不需要设置宽度,单单设置父元素属性即可。

.grid {
  display: grid;
  grid-template-columns: 200px 100%;
  height: 200px;
}

双子元素 + absolute

需要给子元素设置宽高,不然撑不起来。右元素设置left: 200px

.father {
  position: relative;
  height: 200px;
}
.left {
  position: absolute;
  height: 200px;
}
.right {
  position: absolute;
  left: 200px;
  height: 200px;
  width: 100%;
}

左元素 absolute + 右元素 margin-left

.father {
  position: relative;
  height: 200px;
}
.left {
  position: absolute;
  width: 200px;
  height: 200px;
}
.right {
  width: 100%;
  height: 200px;
  margin-left: 200px;
}

无父元素 + 左元素左浮动,右元素不动

前两种都需要有个父元素,但浮动不需要

左边浮动,下一个元素独占位置,并排一行

同样,需要设置高度,子元素才能撑开

.left {
  float: left;
  height: 200px;
}
.right {
  height: 200px;
}

无父元素 + 左边左浮动,右边有浮动

浮动不需要父元素,浮动就区别于正常文档流

我的理解是正常文档流是二维层面,而浮动相当于成了三维,区别于 Z 轴

右边元素有浮动不够,还需要设置宽度

.left {
  float: left;
  height: 200px;
}

.right {
  float: right;
  height: 200px;
  width: calc(100% - 200px);
}

只要是 float 实现此功能的,都不需要父元素,以及自身都需要设置高度

总结

简单来说,实现布局最好的方式是 flex,简单兼容现代浏览器和机型。当然,我是因为还没有学 grid(但 grid 要记得参数比较多)。绝对定位和浮动各有优缺点

各大方法优缺点需要什么
flex布局简单需要父元素、高度。子项 flex:1
grid布局最简单,但兼容性更现代只需要父元素设置属性就好
绝对定位兼容性更高需要父元素做相对定位、高度
浮动兼容性更高不需要父元素,子项都需要宽高

float 区别于其他三种,不需要父元素做容器

grid 区别于其他三种,不需要设置子元素(左元素的)宽

绝对定位区别于其他三种,它的方法不仅要父元素有高,其子元素也要有高

flex 最简单

附上线上 demo

芝麻开门,显示全文!

水平垂直居中的17种方法

面试的时候,绝不能只说一种,绝不能说一种解决方案,绝不能停下你吞吞吐吐的嘴

CSS 方面问的最多的问题之一,我想分三种情况,水平居中、垂直居中和水平垂直居中来分析

单单就水平垂直居中而言,大概有以下几种方案:

居中元素不定宽高

  • absolute + transform
  • flex 属性居中
  • flex + 子项 margin auto
  • grid 属性居中
  • grid + 子项 margin auto
  • grid + 子项属性居中
  • -webkit-box 属性居中
  • table-cell + text-align
  • line-height + text-align
  • writing-mode
  • table

仅居中元素定宽高适用:

  • 须知宽高 + absolute + 负 margin
  • 须知宽高 + absolute + calc
  • 须知宽高 + absolute + margin auto

局限性较大的全局居中

  • 须知宽高 + fixed + transform
  • 须知宽高 + fixed + 负 margin
  • 须知宽高 + fixed + calc
  • 须知宽高 + fixed + margin auto

水平居中

text-align: center

text-align: center;

需设置 display: inline-block 行内块元素

绝对定位 + transform 位移

position: absolute;
left: 50%;
transform: translateX(-50%);

脱离文档流

宽度+ margin: 0 auto

width: 100px;
margin: 0 auto;

这里说明下,width:100px 必须是具体的数字,且这个居中是外层居中,宽度中的内容没有居中

垂直居中

绝对定位 + transform 位移

position: absolute;
top: 50%;
transform: translateY(-50%);

与水平方向的居中一样,都是脱离文档流的做法

table-cell + vertical-align

display: table-cell;
vertical-align: middle;

display: table-cell ,让其标签元素以表格单元格的形式呈现,类似于 td 标签,

vertical-align: middle,用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直居中

水平垂直居中

绝对居中 + transform 位移

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  position: relative;
}
.son {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

flex 属性居中

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  display: flex;
  justify-content: center;
  align-items: center;
}

flex + margin auto

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  display: flex;
}
.son {
  margin: auto;
}

grid 属性居中

<div class="father">123123</div>
// 或者
<div class="father">
  <div class="son">123123</div>
</div>
.father {
  display: grid;
  justify-content: center;
  align-items: center;
}

grid 子项属性居中

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  display: grid;
}
.son {
  align-self: center;
  justify-self: center;
}

grid + margin auto

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  display: grid;
}
.son {
  margin: auto;
}

grid 和 flex 很像,是 flex 的升级版,所以 grid 能做的事情更多

以上绝对定位、flex、grid 关于水平垂直居中的做法,剩下再说居中比较老的布局方法

-webkit-box 属性居中

这是一个已经过时的布局,可以看看这篇文章 CSS3 display: flex 和 display: box 有什么区别?

网友一丝说:

flex 是 2012 年的语法,是以后的标准

box 是 2009 年的语法,已经过时,需要加上对应前缀

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  display: -webkit-box;
  -webkit-box-pack: center;
  -webkit-box-align: center;
}

table-cell + text-align

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}
.son {
  display: inline-block;
}

line-height + text-align

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  height: 200px;
  line-height: 200px;
  text-align: center;
}

line-heightheight ,行高和高度一样高,自然就垂直方向居中了

writing-mode

<div class="father">
  <div class="“son”">
    <div class="“sonson”">123123</div>
  </div>
</div>
.father {
  writing-mode: tb-lr;
  writing-mode: vertical-lr;
  text-align: center;
}

.father .son {
  writing-mode: lr-tb;
  writing-mode: horizontal-tb;
  text-align: center;
  width: 100%;
  display: inline-block;
}
.father9 .son .sonson {
  display: inline-block;
  text-align: initial;
}

这个很冷闷,有人介绍过这种情况

table

<table>
  <tbody>
    <tr>
      <td class="father">
        <div class="son">123123</div>
      </td>
    </tr>
  </tbody>
</table>
.father {
  text-align: center;
}

table 标签自己将它垂直居中了,text-align:center 后就是水平居中了

可以看 demo

元素有宽高的情况,又多了三种方案

须知宽高 + 绝对居中 + margin 负边距

<div class="father">
    <div class="son">
        123123
    </div>
</div>
.father {
  position: relative;
  height: 200px;
}
.son {
  width: 100px;
  height: 100px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -50px 0 0 -50px;
}

父元素必须要有个高度,这样才能撑开容器。子元素必须要有个宽高,才能计算出 margin 值

须知宽高 + 绝对定位 + calc

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  position: relative;
  height: 200px;
}

.son {
  width: 100px;
  height: 100px;
  position: absolute;
  top: calc(50% - 50px);
  left: calc(50% - 50px);
}

与 margin 负边距一个道理,父元素需要设置一个高度。子元素必须要有高度,不用 margin,而用 CSS3 中的 calc,计算出要居中位移,兼容性需要支持 CSS3 属性

须知宽高 + 绝对居中 + margin: auto

<div class="father">
  <div class="son">123123</div>
</div>
.father {
  position: relative;
  height: 300px;
}

.son {
  width: 100px;
  height: 100px;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}

同以上两种情况。

这三种需要定位方式来实现水平垂直居中的方法,需要设置父元素的高度(一定要有,撑开画面),子元素需要设置宽高,前两种方法是为了算出它在父元素中的相对位置,后一种方法是为了说明子元素是个容器(如果不设置宽高,就是无)

其他方法

其实,水平垂直居中方面,如果面试官硬要问还有吗?还真的有,用 fixed 定位。但这个方法有缺陷,虽然能实现水平垂直居中,但它是相对于视口(viewport),而非父元素

方法就是以上用 absolute 实现的改成 fixed 即可

  • 须知宽高 + fixed + transform
  • 须知宽高 + fixed + 负 margin
  • 须知宽高 + fixed + calc
  • 须知宽高 + fixed + margin auto

这四种方法,都需要设置子元素的宽高

这里贴一下代码

<div class="father">
  <div class="son">123123</div>
</div>
/* transform */
.son {
  width: 100px;
  height: 100px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: blue;
}

/* 负 margin */
.son {
  width: 100px;
  height: 100px;
  position: fixed;
  top: 50%;
  left: 50%;
  margin-left: -50px;
  margin-top: -50px;
  background: blue;
}

/* calc */
.son {
  width: 100px;
  height: 100px;
  position: fixed;
  top: calc(50% - 50px);
  left: calc(50% - 50px);
  background: blue;
}

/* margin: auto */
.son {
  width: 100px;
  height: 100px;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  margin: auto;
  background: blue;
}

总结

随着微软宣布放弃 IE11,现实项目中完全可以使用 flex 布局,grid 部分还不适配,但是以后肯定会取代 flex。

虽然写了很多,但是自己工作中也不会使用 table 、writing-mode、-webkit-box 等过时的布局方式,写这篇文章,纯粹是为了面试时被问到这种问题。

收获是 absolute 的居中要父子同心(父元素设置高度,子元素设置宽高),fixed 的居中只需要设置子元素的宽高。

线上 demo 查看

参考资料

芝麻开门,显示全文!

flex从总结到了解

flex 是一种布局方式,在 CSS3 之后开始有。它主要由父容器和子项组成,父容器有六个属性,分别为:

  • 控制主轴轴向:flex-direction
    • row:横轴(默认)
    • row-reverse:倒过来的横轴
    • column:竖轴
    • column-reverse:倒过来的竖轴
  • 换行方式:flex-wrap
    • nowrap:不换行(默认)
    • wrap:换行
    • wrap-reverse:反着换行
  • 主轴排列:justify-content
  • 交叉轴排列:align-items
  • 轴向与换行组合设置:flex-flow(流向)
    • 一般很少用这个属性,即改变子项的布局顺序,正着来,倒着来

子项也有六个属性,分别为:

  • 弹性扩展:flex-grow
    • 指定容器剩余空间多余时的分配规则
    • 默认值为 0,多余空间不分配
  • 弹性收缩:flex-shrink
    • 指定容器剩余空间不足时的分配规则
    • 默认值为 1,空间不足要分配;如果为 0,表示不分配
  • 基础尺寸:flex-basis
    • 指定 flex 元素在主轴方向上的初始大小(基础尺寸)
    • 默认值为 auto,即项目本身大小
  • 缩写:flex
    • flex-grow、flex-shrink、flex-basis 的缩写
    • 默认值为 0 1 auto
  • 主轴顺序:order
  • 交叉轴对齐方式:align-self

总的来说,父容器控制整体布局,子项控制子项布局

在面试中,常常不会问怎么宽泛,最常见的 flex 面试题为:

  • flex: 0 1 auto 怎么理解?
  • flex: 1具体代表什么,有什么应用场景
  • flex: 0flex: 1flex: noneflex: auto,表示什么意思,并应用在什么场景下使用?

要想回答这些问题,我们必须了解子项中的 flex 属性

flex 语法

flex: none | auto | [< "flex-grow" > < "flex-shrink" >? || < "flex-basis" >];

单管道符 | ,表示排他。也就是这个符号前后的属性值都是支持的,且不能同时出现。因此,下面这些语法都是支持的:

flex: auto;
flex: none;

flex: [< "flex-grow" > < "flex-shrink" >? || < "flex-basis" >];

方括号 [...] 表示范围。支持的属性在这个范围内

其中 ? ,表示 0 个或者 1 个,也就是说 flex-shrink 属性可有可无。因为 flex 属性值也可以是 2 个值

flex: auto;
flex: none;
/* 2个值 */
flex: 1 100px;
/* 3个值 */
flex: 1 1 100px;

双管道 || ,表示”或者“的意思。表示前后可以分开独立使用,也就是 flex: flex-grow flex-shrink?flex-basis 都是合法的。于是我们又多了 2 种合法的写法:

/* 1个值,flex-basis */
flex: 100px;
/* 2个值,flex-grow 和 flex-shrink */
flex: 1 1;

转为文字表述

单值语法:

如果 flex 的属性值只有一个值,有三种情况

  • 一个无单位数,例如例如 flex: 1,表示 flex-shrink: 1,剩余空间扩展。此时,flex-shrinkflex-basis 的值分别是 1 和 0%。注意,这里的 flex-basis 的值是 0%,而不是默认值 auto

    • 只要改变 flex: 数字flex-basis 的值就为 0
  • 一个有效的宽度(width)值,表现形式为长度值,例如 flex: 100px,表示flex-basis: 100px,基础尺寸为 100px。此时,flex-growflex-shrink 的值都是 1,注意,这里的 flex-grow 的值是 1,而不是默认值 0

  • 关键字 noneautoinitial

双值语法:

如果 flex 的属性值有两个值,则第 1 个值一定是 flex-grow,第 2 个根据值的类型不同表示不同的 CSS 属性,具体规则如下:

  • 数值:例如flex: 1 2,则这个 2 表示 flex-shrink,此时 flex-basis 的值为 0%,而非默认值 auto
  • 长度值,例如flex: 1 100px,则这个 100pxflex-basis,此时 flex-shrink 默认值为 0

三值语法:

如果 flex 的属性值有 3 个值,则长度值表示 flex-basis,其余 2 个数值分别表示flex-growflex-shrink。下面两行 CSS 语句的语法都是合法的,且含义也是一样的:

flex: 1 2 50%;
flex: 50% 1 2;

flex 属性值场景应用

flex 默认值为 0 1 auto。除此之外,还有各种其他值

  • flex: none,等同于 flex: 0 0 auto;

  • flex: auto,等同于 flex: 1 1 auto;

  • flex: 1,等同于 flex: 1 1 0%;

  • flex: 0,等同于 flex 0 1 0%;

张鑫旭大神画过一张图:

单值语法等同于备注
flex: initialflex: 0 1 auto初始值,常用
flex: 0flex: 0 1 0%适用场景少
flex: noneflex: 0 0 auto推荐
flex: 1flex: 1 1 0%推荐
flex: autoflex: 1 1 auto适用场景少

默认值 flex: initial

它等同于 flex:0 1 auto,表示 flex 容器有剩余空间时尺寸不增长(flex-grow: 0),flex 容器尺寸不足时尺寸会收缩变小(flex-shrink:1),尺寸自适应于内容(flex-basis:auto)

我的理解:子项总长度小于总容器时,不会去撑满(flex-grow:0),而按实际宽高度存在(flex-basis:auto);当子项总长度大于总容器时,子项会相对于的收缩相对比例(flex-shrink:1)

适用场景

适用于子项总长度小于总容器的场景,例如按钮、标题、小图标等小部件的排版布局

flex: 0 和 flex: none 的区别

flex: 0 等同于设置 flex: 0 1 0%flex:none 等同于 flex: 0 0 auto

flex: 0,因为是一个值且为数值,所以它表示 flex-grow,后续我发现只用设置了flex: 数字,那么 flex-basis 就自动成了 0%,所以,设置flex:0 的元素的最终尺寸表示为最小内容宽度;

注意:

flex: 1 === flex: 1 1 0%

flex: 0 === flex: 0 1 0%

flex 设置为数字后,虽然 flex-basis 为最小宽度,但是前者的 flex-grow 有值,可以把子项扩充满容器,后者为 0,不扩展

flex: none,既不是数值也不是长度值,none 关键字。flex: 0 0 auto 表示元素尺寸不会收缩也不会扩展,再加上 flex-basis: auto 表示固定尺寸由内容决定,由于元素不具有弹性,因为,元素内的元素不会换行,最终尺寸通常表现为最大内容宽度

适用使用 flex: 0 的场景

flex:0的应用场景

无论文字的内容给如何设置,左侧内容的宽度都是图像的宽度

适合使用 flex: none 的场景

当 flex 子项的宽度就是内容的宽度,且内容永远不会换行,则适合使用 flex:none,例如如下的场景,图片和按钮固定长度,内容弹性

flex:none适用场景

flex: 1 和 flex: auto 的区别和适用场景

flex:1 等同于设置 flex: 1 1 0%flex: auto 等同于 flex: 1 1 auto

可以看出两者的 flex-growflex-shrink 都是一样的,意味着它们都可以弹性扩展以及弹性收缩,区别在于 flex: 1flex-basis 为 0,即宽度为 0。flex:auto 中的 flex-basis为 auto,即宽度为自身宽度

表现的样子为:

flex:1

这里需要解释一下,因为我最开始也不理解,其公式为:

每个子项的宽度 = (总宽度 - flex-basis 的宽度)/ 3(以这个例子为例)

因为 flex:1flex-basis 的宽度为 0 ,所以最后它的总宽度扩张或者收缩时每个子项都能等分

适用于 flex: 1 的场景

当希望元素充分利用剩余空间,同时不会侵占其他元素应用的宽度的适用,适合适用 flex:1,例如所有的等分列表

之前适用 flex: none 的例子,同样设置文字部分flex: 1 也能实现类似的效果

flex:1

适用于 flex: auto 的场景

当希望元素充分利用剩余空间,但是各自的尺寸按照各自内容进行分配的时候,适用于 flex: auto

例如导航数量不固定,每个导航文字数量页不固定的导航效果就适合适用 flex: auto

flex-auto

回过头来看之前说的面试题

  1. flex: 0 1 auto 怎么理解?
  2. flex: 1具体代表什么,有什么应用场景
  3. flex: 0flex: 1flex: noneflex: auto,表示什么意思,并应用在什么场景下使用?

第一个问题回答

flex 的默认值为 0 1 auto,表示容器剩余空间有多余的时候不扩展,不足的时候收缩,子项的宽度根据自身的宽度来展示

第二个问题回答

脑子思考 flex 的值如果是一个值且为数字,说明是 flex-grow:1,当它为数字时,flex-basis 会自动变成 0,所以它具体表示为 flex:1 1 0%,表示容器剩余空间有多余的时候扩展,不足的时候收缩,子项的宽度为 0。它一般适用于充分利用剩余空间,又不侵占其他元素的宽度,例如等分布局

第三个问题回答

flex:0,表示 flex: 0 1 0%,表示容器剩余空间有多余的时候不扩展,不足的时候收缩,子项的宽度为 0,适用设置在替换元素的父元素上

flex:1,看第二个回答

flex: none,表示 flex: 0 0 auto,表示容器剩余空间有多余的时候不扩展,不足的时候也不收缩,子项的宽度为自身宽度,适用于不换行的内容或者较少的小控件元素上

flex: auto,表示 flex: 1 1 auto,表示容器剩余空间有多余的时候扩展,不足的时候收缩,子项的宽度为自身宽度,适用于基于内容动态适配的布局(例如导航数量文字长度不固定)

flex:initial,表示 flex: 0 1 auto,表示容器剩余空间有多余的时候不扩展,不足的时候收缩,子项的宽度为自身宽度,适用于小控件元素的分布布局,或者某一项内容动态变化的布局

参考资料

芝麻开门,显示全文!