flex深度剖析-解决移动端适配问题!

18,255 阅读12分钟

前言

上回说到,移动端适配,推荐了,使用px为主,vw,百分比为辅助,再搭配flex的布局方式,于是有人就开始问我了,这个flex搭配布局应该怎么用,梳理一遍,巩固一下

flex前世今生

在前端刚刚兴起html,css,js,还停留在初级阶段是的时候,前端工程化还不存在的时候,jqery还在统治江湖的时候,以及这张图还在大火的时候,额!我又盗图了

我么们想要实现两个div一排显示除了行内块元素以外,只能用这让人又爱又恨的float

float

float 属性定义元素在哪个方向浮动。以往这个属性总应用于图像,使文本围绕在图像周围,不过在 CSS 中,任何元素都可以浮动。浮动元素会生成一个块级框,而不论它本身是何种元素。

其实float的设计初衷仅仅是为了实现:文字环绕效果,只不过后来在前端的迅速发展中,float被尝试用来了布局,于是,后来flaot就约定熟成的变成一种布局属性,虽然很好用,但是,既然设计初衷不是为了布局,那么强行安上一个布局的帽子,当然会有好多副作用!

副作用

1、当子元素都设置了浮动时,就会导致父元素的塌陷,即父元素撑不开,如下图所示:

2、无法实现动态实现自适应布局,

举个例子,如果想要实现二等分一排布局,width要为50%,但是如果突然来个三等分呢,width设置50%显然是已经不行了

3、margin padding设置值不能正确显示

由于浮动导致父级子级之间设置了css padding、css margin属性的值不能正确表达。特别是上下边的padding和margin不能正确显示。

4、如果前面的元素设置了浮动,那后面的元素就有可能产生异位的现象。

如图所示,由于元素1浮动了,脱离文档流,导致元素2上去了

这些问题让众多开发这相当苦恼,尤其是在相对复杂的项目,各种float会搞得页面相当复杂(特别是移动端),出现不好解决的诡异bug,稍微欠点火候的web开发者,有可能被搞得晕头转向。

而后随着移动互联网兴起,移动端的h5页面铺天盖地,布局的传统解决方案,基于盒状模型,依赖 display 属性 + position属性 + float属性。它对移动端布局非常不方便(引用阮老师),于是flex横空出世,终于,这个万能的float终于要退出历史舞台了!

flex初探

2009年,W3C 提出了一种新的方案----Flex(弹性盒模型) 布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。

在2020的今天,flex由于在移动端的天然兼容性,已经成为布局的首选方案,实现更改好的效果

flex到底是个什么东西

引用老罗名言,少废话先看东西,其实就是给容器指定一个display属性为flex

//比如这样以后div就具有弹性了
div{
    display:flex;
}

采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。

flex有什么属性

flex 的属性,容器上有,项目上同样也有,且听慢慢道来!

flex容器属性

  • flex-direction
  • flex-wrap
  • flex-flow
  • justify-content
  • align-items
  • align-content

1、flex-direction属性

flex-direction属性决定主轴的方向(即项目的排列方向)。

.box {
  flex-direction: row | row-reverse | column | column-reverse;
}
//依次表示主轴为水平方向,起点在左端。(默认)
//主轴为水平方向,起点在右端。
//主轴为垂直方向,起点在上沿。
//主轴为垂直方向,起点在下沿。

2、flex-wrap属性

默认情况下,项目都排在一条线(又称"轴线")上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

.box{
  flex-wrap: nowrap | wrap | wrap-reverse;
  //依次是不换行
  //换行,第一行在上方。
  //换行,第一行在下方。
}

3、flex-flow属性

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

.box {
  flex-flow: <flex-direction> || <flex-wrap>;
}

4、justify-content属性(这个很重要,经常用)

justify-content属性定义了项目在主轴上的对齐方式。

.box {
  justify-content: flex-start | flex-end | center | space-between | space-around;
  //依次是左对齐(默认值)
  //右对齐
  //居中
  //两端对齐,项目之间的间隔都相等。
  //每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
}

5、align-items属性(这个也很重要。也常用)

align-items属性定义项目在交叉轴上如何对齐。

.box {
  align-items: flex-start | flex-end | center | baseline | stretch;
  //依次是交叉轴的起点对齐。
  //交叉轴的终点对齐。
  //交叉轴的中点对齐。
  //项目的第一行文字的基线对齐。
  //如果项目未设置高度或设为auto,将占满整个容器的高度(默认值)
}

6、align-content属性

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

.box {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
  //依次是与交叉轴的起点对齐。
  //与交叉轴的终点对齐。
  //与交叉轴的中点对齐。
  //与交叉轴两端对齐,轴线之间的间隔平均分布。
  //每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
  //轴线占满整个交叉轴。(默认值)
}

项目的属性

  • order
  • flex-grow
  • flex-shrink
  • flex-basis
  • flex
  • align-self

1、order属性

order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

.item {
  order: <integer>;
}

2、flex-grow属性(很重要)

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。

.item {
  flex-grow: <number>; /* default 0 */
}

3、flex-shrink属性

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。

如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。

注意:负值对该属性无效。

.item {
  flex-shrink: <number>; /* default 1 */
}

4、flex-basis属性(这个是重点,常用)

flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。

5、flex属性(这个最重要)

flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。

.item {
//该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
//建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。
  flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}

align-self属性

align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
  //这个其实就是在项目里用align-items 注:auto为默认值
}

ok,阮大佬的文章算是抄完了(阮大佬的文章实在太好了,一激动就没有自己写,感谢大佬)

实战

基础的东西咱是抄完了,怎么也得有点自己的东西啊,废话少说,实战开始

1、常用的flex 1到底是啥意思?

上文说到。flex 是 flex-grow、flex-shrink、flex-basis的缩写,那咋晕的忽的由来个1呢,其实他又如下规则(mdn上的相当难记请看这里大佬们整理好的):

1、当 flex 取值为 none,则计算值为 0 0 auto

//此处为预处理语法
div{
diaplay:flex
 .div {
    flex-grow: 0;
    flex-shrink: 0;
    flex-basis: auto;
}   
}

2、当 flex 取值为 auto,则计算值为 1 1 auto

//此处为预处理语法
div{
diaplay:flex
 .div {
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: auto;
}   
}

3、当 flex 取值为一个非负数字,则 flex-grow 数字,flex-shrink 取 1,flex-basis 取 0%(最常用)

//此处为预处理语法
div{
diaplay:flex
 .div {
    flex-grow: 1;//要取的值
    flex-shrink: 1;//默认
    flex-basis: 0%;//默认
}   
}

4、当 flex 取值为一个长度或百分比,则视为 flex-basis 值,flex-grow 取 1,flex-shrink 取 1

//此处为预处理语法
div{
diaplay:flex
 .div {
    flex-grow: 1;//默认
    flex-shrink: 1;//默认
    flex-basis: 0%;//要取得值
}   
}

当 flex 取值为两个非负数字,则分别视为 flex-grow 和 flex-shrink 的值,flex-basis 取 0%

//此处为预处理语法
div{
diaplay:flex
 .div {
    flex-grow: 1;//要取的第一个值
    flex-shrink: 1;//要取的第二个值
    flex-basis: 0%;//默认
}   
}

当 flex 取值为一个非负数字和一个长度或百分比,则分别视为 flex-grow 和 flex-basis 的值,flex-shrink 取 1

div{
diaplay:flex
 .div {
    flex-grow: 1;//要取的第一个值
    flex-shrink: 1;//默认
    flex-basis: 0%;//要取的第二个值
}   
}

如何用flex实现等分布局?

//css
   .box{
            height: 500px;
            display: flex;
        }
        .box div{
             height: 300px;
             border: 1px solid #000000;
                flex: 1;//这就是flex:1的妙用
                text-align: center;
            }
    //html
    div class="box">
        <div>等分效果</div>
        <div>等分效果</div>
    </div>

左边固定右边自适应

//css
   .box {
            height: 500px;
            display: flex;
        }

        .box div {
            height: 300px;


            text-align: center;
        }

        .box div.right {
            flex: 1;
            border: 1px solid #000000;
        }

        .box div.left {
            border: 1px solid #000000;
            flex-basis: 100px;
        }
    //html
    <div class="box">
        <div class="left">左边固定效果</div>
        <div class="right">右边自适应效果</div>
    </div>

垂直水平居中对齐

//css
 .box {
            width: 100%;
            height: 300px;
            border: 1px solid purple;
            display: flex;
            justify-content: center;
            align-items: center;
        }
//html
<div class="box"><p>看看剧中了吗</p>
    </div>

有了以上三种基础款,我们便可以扩展出各种布局

1、常见搜索框

//css
  .box {
            width: 100%;
            height: 50px;
            border: 1px solid purple;
            display: flex;
        }
        .input{
            flex:1;
            border:1px solid #000000
        }
        .bottom{
            flex-basis: 60px;
            line-height: 50px;
            text-align: center;
            font-size: 16px;
        }
    //html
     <div class="box">
        <div class="input"></div>
        <div class="bottom">搜索</div>
    </div>

2、自适应导航栏布局

//css
 ul{
            display: flex;
            height: 50px;
            list-style: none;
            padding: 0;
        }
        li{
            flex:1;
            text-align: center;
        }
        .active{
           border-bottom: 1px solid rebeccapurple;
        }
    //html
    <ul>
       <li class="active">好好学习</li>
       <li>上学</li>
       <li>天天向上</li>
   </ul>

3、常见的左图右文的list列表布局

随便写的样式有点丑,担待

//css
 .box {
            display: flex;
            width: 100%;
            height: 150px;
        }

        .left {
            flex-basis: 100px;
            background: #000;
            color: #ffffff;
        }

        .right {
            flex: 1;
            position: relative;
            text-align: right;
        }

        .btn {
            border: 1px solid rebeccapurple;
            width: 100px;
            display: inline-block;
            overflow: hidden;
            margin-top: 20px;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
    //html,
     <div class="box">
        <div class="left">代表是个图</div>
          <div class="right">
                  <p>这个个大大的标题,</p>
                  <span>下边的烂七八糟</span>
                  <div class="btn">搞个按钮让你点点</div>
              </div>
    </div>

这里面有个坑,如果右边自适应的内容如果超过范围了,那么就会形成如下效果,解决办法也很简单,在这个自适应的地方价格min-width:0,即可,但是具体为啥,现在也没有在官方文档里面找到(有知道的大佬,请告知)

4、栅格布局

这个布局有点小小的瑕疵,会有除不尽的情况,但是无伤大雅,

//css
  ul {
            width: 100%;
            display: flex;
            flex-wrap: wrap;
            align-content: flex-start;
            list-style: none;
            padding: 0;
        }

        li {
            flex: 0 0 33.333333%;
            height: 80px;
            box-sizing: border-box;
            border:1px solid #000
        }
    //html
     <ul>
        <li>
            方框
        </li>
        <li>
            方框
        </li>
        <li>
            方框
        </li>
        <li>
            方框
        </li>
        <li>
            方框
        </li>
        <li>
            方框
        </li>
        <li>
            方框
        </li>

    </ul>

5、底部固定,上部自适应

有人就会说了这个用定位来解决不久完事了,多一个选择啊,对吧?也许就是这个选择让你惊艳面试官

//css
 .box {
            display: flex;
            flex-direction: column;
            height: 100vh;
        }

        .header {
            flex: 1;

        }

        .footer {
            flex-basis: 100px;
            background: red;
        }
    //html
     <div class="box">
        <div class="header">
            这是头部,
        </div>
        <div class="footer">
            这是底部
        </div>
    </div>

6、圣杯布局,双飞翼布局

这种布局,其实就是两层的flex 在第一层布局用了 flex-direction: column;而已,不在赘述

总结

乘着上期的文章还有热乎劲头,再来总结一些flex布局,夯实我推荐的移动端适配的写法 基础部分,借鉴阮一峰大佬:www.ruanyifeng.com/blog/2015/0…

作为一个曾被移动端困扰的人,痛定思痛总结如上:愿大家以后不被移动端布局困扰。

上期移动端适配:面试官:你了解过移动端适配吗?