图解 CSS - 梳理与巩固

2020-04-22· 20min

#CSS

#计量单位

  • 相对长度单位:vw、vh、rem、em、%、...
    • vw、vh
      相对浏览器的可视区域
    • rem
      相对根节点html的字体大小
    • em
      相对父节点字体的大小
  • 绝对长度单位:px、pt、...
    • px
      精确像素
  • 扩展
    • 物理像素:设备像素(DP,DevicePixels)代表屏幕上有多少个点
    • 逻辑像素:设备独立像素(DIP,DeviceIndependentPixels)如 CSS 中的单位 px
    • 设备像素比:DPR (DevicePixelRatio),公式:设备像素 / 设备独立像素
    • 像素密度:PPI (PixelsPerInch) 表示每英寸的像素个数
    • 打印分辨率:DPI (DotsPerInch) 表示每英寸的点数
    • 屏幕分辨率:屏幕显示的像素点数,如1080 * 1920
    • 图像分辨率:每英寸图像内所拥有的像素点数,如 320 * 640

#盒模型

  • 盒模型:由 content、border、margin、paddding 四个部分组成
    • W3C 标准盒模型 (content-box),width/height 只包含 content
    • IE 盒模型 (border-box),width/height 包含 content、border、padding
  • 可通过 box-sizing 改变元素的盒模型:border-box、content-box
  • 声明 <!DOCTYPE html>
    • 可以让 IE6 及其以上的版本遵循标准兼容模式的方式工作
    • 所有的浏览器都会把盒模型解释为标准盒模型

#BFC

  • 块级格式化上下文(Block Formatting Context),规定了处于 BFC 内部的块级元素如何布局
  • 特性
    • BFC 内的子元素不会影响外部元素,反之同
    • 当计算其区域的高度时,浮动元素也可以参与计算
    • BFC 内的子元素的 margin-top 不会和 BFC 这个父容器发生重叠
  • 启用BFC
    • float:left|right
    • position:absoulte|fixed
    • display:flex|grid|table|inline-block|table-cell|flow-root|...
      • flow-root:元素块状化,同时包含BCF
    • overflow:auto|scroll|hidden
    • 根元素(html)、包含body的元素
  • 作用
    • 防止外边距重叠
      • 相邻元素
      • 父子元素
    • 解决高度塌陷
      • 父元素
    • 清除浮动
      • 相邻元素
  • 扩展
    • IFC(行级格式化上下文)
    • GFC(网格布局格式化上下文)
      display: grid
    • FFC(自适应格式化上下文)
      display: flex、display: inline-flex

#伪类和伪元素

  • 伪类:以冒号
    :
    开头,用于选择处于特定状态的元素。如::hover、:active
  • 伪元素:以双冒号
    ::
    开头,用于在文档中插入虚构的元素(并不存在于 DOM 之中)。如:::before、::after

#选择器

  • 优先级:!important > 内联样式 > id选择器 > 类选择器、伪类选择器、属性选择器 > 标签选择器、伪元素选择器 > 通配符选择器
  • id选择器、类选择器、标签选择器
  • 后代选择器(div span):选择 div 里所有 span 元素
  • 子选择器(ul > li):选择 ul 直接子元素,如
    # Yes
    <ul> <li></li> </ul>
    
    # No
    <ul> <div><li></li></div> </ul>
    
  • 兄弟选择器(h1 ~ div):选择和 h1 元素有相同父元素的所有 div 兄弟元素(在 h1 之后出现的兄弟)
    # Yes
    <h1></h1> <div></div> <div></div>
    
    # No
    <h1></h1> <span></span> <div></div>
    
  • 相邻兄弟选择器(h1 + div):选择和 h1 元素相邻的下一个兄弟元素 div
    # 仅选择第一个 div
    <h1></h1> <div></div> <div></div>
    
  • 奇数行/偶数行
    /* 奇数行 */
    div:nth-child(odd) {
    }
    /* 偶数行 */
    div:nth-child(even) {
    }
    

#浮动

  • 特性:脱离文档流、变成行内块级元素、浮动元素碰到父元素/浮动元素边就停止
  • 场景:文字环绕、灵活布局
  • 影响:
    • 浮动元素会盖在非浮动兄弟元素之上。(Playground
    • 父元素没有设置高度的话,浮动元素不会撑开父元素的高度(Playground
  • 清除浮动
    • CSS属性
      clear
      清除浮动
    • 父元素设置高度
    • 父元素创建 BFC(如:overflow:hidden、display: flow-root)
    • 在父元素内部最后添加一个没有高度的元素,并使用
      clear:both
    • 父元素添加伪元素
      ::after
      .parent-box::after {
        content: "";
        display: block;
        clear: both;
      }
      

#重绘与重排

  • 重绘(Repaint)
    • 定义:当页面中的元素样式发生改变时,浏览器会重新绘制这些元素的外观,但不会改变它们在页面中的位置和大小。(详见MDN
    • 触发:改变元素的 color、border-style、background...
  • 重排(Reflow)
    • 定义:当页面中的元素发生布局或几何属性发生变化时,浏览器需要重新计算这些元素的位置和大小,然后重新构建页面的渲染树。(详见MDN
    • 触发:改变元素位置/尺寸/内容/字体大小、改变浏览器窗口尺寸、页面初始渲染、添加/删除可见的 DOM 元素...
    • 优化建议:
      • 减少重排范围
        1. 尽可能局部布局
        2. 避免使用 table 布局(另:table-layout:auto、table-layout:fixed 可减少影响范围)
      • 减少重排次数
        1. 使用 className 修改 DOM 样式(避免一条条地修改)
        2. 动画元素采用 absolute、fixd 布局,避免影响到其它元素
        3. 分离 DOM 的读写操作
        /* 两次重排 + 重绘 */
        div.style.left = div.offsetLeft * 2
        div.style.right = div.offsetRight * 2
        
        /* 一次重排 + 重绘 */
        const left = div.offsetLeft
        const right = div.offsetLeft
        div.style.left = left * 2
        div.style.right = right * 2
        
        /* 以下操作,浏览器会强制刷新渲染队列 */
        getComputedStyle、offsetXXX、scrollXXX、clientXXX
        

#布局

#响应式布局(RWD,Responsive Web Design)

  • 特点:当屏幕尺寸变化时,布局方式等呈现效果会发生改变(一套代码)
  • 设计方法:媒体查询 + 弹式布局(Flexbox Layout)、网格布局(Grid Layout)或 rem、vh、vw 等
  • 注意:移动端的浏览器默认 viewport(layout viewport)为 980px/1024px/...
    • 布局视口(layout viewport)
      • 不同厂商出厂不一致
      • document.documentElement.clientWidth/Height(不包含滚动条)
    • 可视视口(visual viewport)
      • 默认是屏幕的宽度
      • window.innerWidth/Height(包含滚动条)
    • 理想视口(ideal viewport)
      • screen.width/height
  • meta 标签解析
    • name="viewport"
      仅对移动设备作用
    • width=device-width
      布局视口(layout viewport)= 理想视口(ideal viewport)
    • initial-scale=1.0
      页面的初始缩放值
    • maximum-scale=1.0
      用户最大缩放值
    • user-scalable=0
      不允许用户缩放
    <!-- 建议:允许缩放 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    
    <!-- 不建议 -->
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
    />
    

#自适应布局(AWD,Adaptive Web Design)

  • 特点:根据不同设备/尺寸,提供不同版本的网站(多套代码)
  • 设计方法:通过检测设备特性(如 UA),提供多个布局

#弹式布局(Flexbox Layout)

  • 父级容器
    • flex-direction: row
      row-reverse|column|column-reverse 元素排列方向
    • flex-wrap: nowrap
      wrap|wrap-reverse 元素是否换行
    • flex-flow
      <flex-direction> || <flex-wrap>的简写,默认 flex-flow: row nowrap;
    • justify-content: flex-start
      flex-end|center|space-between|space-around 元素在主轴上的对齐方式
    • align-items: stretch
      flex-start|flex-end|center|baseline 元素在交叉轴上的对齐方式
    • align-content: stretch
      flex-start|flex-end|center|baseline 多根轴线的对齐方式
  • 子级容器
    • order: 0
      元素排列顺序,值越小越靠前
    • flex-grow: 0
      元素放大比例(默认为 0,即如果存在剩余空间,也不放大)
    • flex-shrink: 1
      元素缩小比例(默认为 1,即如果空间不足,元素将缩小)
    • flex-basis: auto
      在分配多余空间之前,项目占据的主轴空间(默认值为 auto,即元素的本来大小)
    • align-self: auto
      可覆盖 align-items 属性。默认 auto,继承父元素的 align-items 属性,如果没有父元素,则等同于stretch
    • flex: 0 1 auto
      • flex-grow: 0; 元素不会放大(在 flex 容器中,不会占据剩余的空间),默认为 0
      • flex-shrink: 1; 元素会缩小(空间不足,元素将缩小)),默认为 1
      • flex-basis: auto; 元素的初始大小将基于其内容或定义的宽度/高度,默认为 0
    • flex: 1
      • 1 1 0

#网格布局(Grid Layout)

  • 关键字
    • auto: 自适应
    • fr: fraction 的缩写,片段,百分比属性
  • grid-template-columns
    grid 布局的每一列 列宽
  • grid-template-rows
    grid 布局的每一行 行高
/* 两行两列,每列列宽 60px,每行行高 60px */
.grid-container {
  display: grid;
  grid-template-columns: 60px 60px; /* repeat(2, 60px) */
  grid-template-rows: 60px 60px;
}
  • grid-row-gap
    行间距
  • grid-column-gap
    列间距
  • grid-gap
    行间距和列间距 的简写形式,如:grid-gap: <grid-row-gap> <grid-column-gap>;
    /* 九宫格 */
    .grid-container {
      display: grid;
      grid-template-columns: repeat(3, 60px);
      grid-template-rows: repeat(3, 60px);
      grid-gap: 10px 20px;
    }
    

#布局案例

#圣杯/三栏布局

<style>
  body,
  .content {
    display: flex;
    flex-direction: column;
  }

  .aside-left {
    order: -1;
  }

  @media (min-width: 768px) {
    .content {
      flex-direction: row;
      flex: 1;
    }
    main {
      flex: 1;
    }
    .aside-left,
    .aside-right {
      flex: 0 0 12em;
    }
  }
</style>

<body>
  <header>Header</header>
  <div class="content">
    <main>Main</main>
    <aside class="aside-left">side-left</aside>
    <aside class="aside-right">side-right</aside>
  </div>
  <footer>Footer</footer>
</body>
<style>
  html,
  body {
    height: 100%;
    /* reset def css */
    margin: 0;
  }

  .def-layout {
    height: fit-content;
    min-height: 100%;
    display: flex;
    flex-direction: column;
  }

  .slot-wrap {
    width: 100%;
    flex: 1 0 auto;
  }

  footer {
    width: 100%;
    flex: 0 0 auto;
  }
</>
<body>
  <div class="def-layout">
    <header>Header</header>
    <div class="slot-wrap">
      <main>Main</main>
    </div>
    <footer>Footer</footer>
  </div>
</body>

#自适应 & 换行

<style>
  .grid-container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    margin: -0.5rem;
  }

  .grid-item {
    height: 100px;
    background: green;
    margin: 0.5rem;
    color: white;
  }

  .flex-container {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
  }

  .flex-item {
    flex: 1 0 150px;
    height: 100px;
    background: purple;
    color: white;
  }
</style>

<h2>grid</h2>
<div class="grid-container">
  <div class="grid-item">1</div>
  <div class="grid-item">2</div>
  <div class="grid-item">3</div>
  <div class="grid-item">4</div>
  <div class="grid-item">5</div>
</div>
<h2>flex</h2>
<div class="flex-container">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
  <div class="flex-item">4</div>
  <div class="flex-item">5</div>
</div>

#场景案例

#元素水平垂直居中

  • 不知道元素宽高大小仍能实现水平垂直居中
    • flex + align-items、justify-content
    • grid + align-items、justify-content
    <style>
      .parent {
        display: flex;
        justify-content: center;
        align-items: center;
        /*  width: 200px;
        height: 200px; */
      }
      .child {
        /*  width: 100px;
        height: 100px; */
      }
    </style>
    
    <div class="parent">
      <div class="child"></div>
    </div>
    
    • position + transform
    <style>
      .parent {
        /* width: 200px;
          height: 200px; */
        position: relative;
      }
      .son {
        /* width: 100px;
          height: 100px; */
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
    </style>
    
    <div class="parent">
      <div class="child"></div>
    </div>
    
    • position + margin:auto

#字体小于 12px

  • transform:scale()
<style>
  .text-1 {
    display: inline-block;
    font-size: 12px;
    transform: scale(0.8);
    /*设置缩放中心*/
    transform-origin: 0 0;
    /* 视情况补上缩小的宽度 (1-0.8)+1 */
    /* width: 120%; */
  }
  .text-2 {
    display: inline-block;
    font-size: 12px;
  }
</style>

<span class="text-1">9.6px</span> <span class="text-2">12px</span>

#画一个 0.5px 的线

transform: scaleY(0.5);

#画一个三角形、梯形...

<style>
  .clippath-triangle {
    width: 100px;
    height: 100px;
    background: orange;
    clip-path: polygon(0px 0px, 100px 0px, 50px 50px);
  }

  .border-triangle {
    width: 0px;
    height: 0px;
    border: 50px solid;
    border-color: olivedrab transparent transparent transparent;
  }

  .border-trapezoid {
    width: 50px;
    height: 50px;
    border: 50px solid;
    border-color: olivedrab transparent transparent transparent;
  }

  .border-parallelogram {
    width: 100px;
    height: 100px;
    background: skyblue;
    transform: skew(30deg);
  }
</style>

<div class="clippath-triangle"></div>
<div class="border-triangle"></div>
<div class="border-trapezoid"></div>
<div class="border-parallelogram"></div>

#CSS工程化

#目的

  • 优化CSS代码的组织、拆分、模块化等设计、提高构建速度、解决各浏览器兼容性问题等

#实践

  • 预处理器:Less、Sass、Stylus,用以预编译 Sass、Less。
  • 后处理器:PostCSS,为 CSS 属性添加私有前缀,解决兼容性问题
  • CSS Modules:
    • 局部作用域和模块依赖,使组件样式隔离
    • 构建工具:css-loader 等
  • CSS-in-JS
  • 原子化CSS:一种 CSS 架构,对 class 极致的封装,最大程度提高复用性
  • 命名规范:BEM,块(block)、元素(element)、修饰符(modifier),由 Yandex 团队提出的一种命名方法论
    <div class="main">
      <h2 class="main__title">Title</h2>
      <div class="main__links">
        <a href="/" class="main__links--active">Active</a>
      </div>
    </div>