Better

Ethan的博客,欢迎访问交流

CSS的进化史

这里主要讨论 CSS 发展至今的几个节点,CSS 的最佳实践,CSS 的安全性和最新 CSS Grid 布局

CSS一直被web开发者认为是最简单也是最难的一门奇葩语言。它的入门确实非常简单——你只需为元素定义好样式属性和值,看起来似乎需要做的工作也就这样嘛!然而在一些大型工程中CSS的组织是一件复杂和凌乱的事情,你更改页面上任意一个元素的一行CSS样式都有可能影响到其他页面上的元素。

通过回顾CSS的历史背景,介绍下时至2018年的今天CSS发展过程中的一些设计模式和工具的演变。通过对这些背景的理解,你将会更轻松的理解每个设计思想并且学以致用。

CSS布局

早期的时候,页面布局通常使用的是HTML表格,在行和列中组织内容,这种方式虽然有效,但是把内容和表现杂糅在一块了,如果你想改变网页的布局就得需要修改大量的HTML代码。

CSS的出现推动了内容(写在HTML中)和表现(写在CSS中)的分离,人们开始把所有的布局代码从HTML中移除放入到CSS中,需要注意的是,和HTML一样CSS的设计也不是用来做网页内容布局的,所以早期的时候试图解决这种分离设计是很困难的。

CSS布局的发展主要三个阶段

  • 基于浮动的布局
    • CSS浮动属性最初是为了将图片浮动在一列文本的左侧或者右侧(报纸上经常看到)
    • 2006年,热门文章概述了实现圣杯布局的详细方法——一个头部、三列内容和一个底部
    • hack方式(负边距、clear: both、硬编码的宽度计算等)
    • 问题:所有的浮动只是将内容放在某一区块的左边或者右边,但是没法知道其他区块的高度。这个问题一直没有个好的解决方案,直到Flexbox布局的出现。
  • 基于Flexbox的布局
    • Flexbox CSS属性实在2009年第一次提出来的,但直到2015年才得到浏览器的广泛支持。
    • Flexbox被设计为定义一个空间在行或者列上如何分布的,这让它比浮动更适合用来做布局,这意味在使用浮动布局十多年后,web开发者终于不再使用带有hack的浮动布局方式了。
    • 特别需要注意的是,flexbox被设计用来在单行或者单列中分割元素的——它不是设计用来给整个页面做布局的!尽管它能很好的实现(相对于浮动布局好很多)。
    • 另一种不同的规范是用来处理多行或者多列布局的,我们称之为CSS 网格。
  • 基于Grid的布局
    • CSS网格最早在2011年提出的(比flexbox提案晚不了多久),但是花了好长时间才在浏览器上普及起来。
    • 缺点:浏览器支持

CSS预处理器

CSS预处理器允许你使用不同的语言来定义样式,最终会帮你转换为浏览器可以解释的CSS,这一点在当今浏览器对新特性支持缓慢的情况下很有价值。接来看看预处理器发展

  • 第一个主流的CSS预处理器是2006年发布的Sass,它提供了一个新的更简洁的语法
  • 2009年的时候,Less CSS预编译器发布。它也是Ruby写的,并且提供了类似于Sass的功能,关键不同点是它的语法设计上更接近CSS。这意味着任何CSS代码都是合法的Less代码
    • 2012年,Less使用了JavaScript(Node.js)重写了替换了Ruby,性能上比Ruby编译更快了,并且很多在工作中使用了Node.js的人更容易上手了。
  • 汲取了Less的经验,Sass开发者在2010年发布了一个新的语法叫SCSS(与Less类似的一个CSS超集),同时发布了LibSass,一个基于C++扩展的Ruby引擎,让编译更快并且适配于多种语言。
  • 另外一个CSS预处理器是2010年发布的Stylus,使用Node.js编写,和Sass或者Less相比更注重于清晰的语法。

CSS后处理器

CSS后处理器使用JavaScript分析并转换你的CSS为合法CSS,从这方面来看和CSS预处理器很相似,你可以认为是解决同一个问题的不同方式。关键的不同点是CSS预处理器使用特殊的语法来标记需要转换的地方,而CSS后处理器可以解析转换标准的CSS,并不需要任何特殊的语法。

最常用的场景:自动添加各浏览器厂商的兼容性前缀

使用 PostCSS 和 autoprefixer 插件,你就可以直接写正常的CSS并不需要指定浏览器厂商前缀,剩下的工作全交给后置处理器去处理。

除了PostCSS的autoprefixer插件还有很多有意思的插件,cssnext 插件可以让你体验下一些实验性质的CSS新功能,CSS modules 可以自动改变class的名字避免名称冲突,stylelint 能检查出你CSS代码中一些定义错误和不符合规范的写法。这些工具在过去一两年里开始流行起来,给开发者提供了从未有过的工程化流程。

设计模式

CSS预处理器和CSS后处理器让CSS开发体验有了巨大的提升,但是单靠这些工具还不足以解决维护大型项目CSS代码的问题。为了解决这个问题,人们编写了一些关于如何写CSS的指导方针,通常被称为CSS规范。

在我们深入分析CSS规范前,首先要搞清楚是什么让CSS随着时间推移变得更加难维护,关键点是CSS是全局性的——你定义的每个样式都会全局应用到页面的每个部分,用一个命名约定来保证class名称的唯一性或者有特殊的规则来决定指定样式应用到指定元素。让我们按照时间顺序来看看主流的一些规范吧

  • OOCSS(面向对象的CSS)是在2009年首次提出的,它是围绕两个原则建立的规范。
    • 第一个原则是结构和样式分离,这意味着定义结构(布局)的CSS不应该和定义样式(颜色、字体等)的CSS混杂在一起
    • 第二个原则是容器和内容分离,把元素看成是一个可重用的对象
    • OOCSS提供了成熟的指导规范,但是对于具体的执行规范并没有明确指出。
  • SMACSS
    • SMACSS(可扩展模块化架构的CSS)是在2011年出现的一种设计模式
    • 它将CSS分为5个不同的类别——基本规范、布局规范、模块、状态规范和样式规范。
  • BEM
    • BEM (块, 元素, 修饰符)是在2010年出现的规范,它的思想主要是围绕把用户界面切分成独立的块。
    • BEM的规范很容易理解,对于新手来说命名规则上也很友好,缺点就是可能会导致class名字非常长,并且没有遵循传统的命名规范。
  • Atomic CSS
    • (也称为 功能性CSS)是2014年出现的一个规范,它的思想是基于可视化的方法创建小而功能单一化的class。
    • 这种规范与OOCSS、SMACSS和BEM完全相反——它并不是把页面上的元素看做是可重用的对象
    • 传统的分离思想是CSS依赖于HTML创建(即使使用像BEM这类的规范),而Atomic的方式是HTML依赖于CSS创建
  • CSS in JS
    • CSS in JS 是2014年推出的一种设计模式,它的核心思想是把CSS直接写到各自组件中,而不是单独的样式文件里
    • 这种方式在React框架中引入的,最早是使用内联样式,后来又进化成了使用JavaScript生成CSS然后插入到页面的style标签中的方式。
    • CSS in JS设计的目标是定义边界清晰包含自己HTML/CSS/JS的独立组件,并且不受其他组件的影响。React是最早采用这种思想的框架,后续也影响到了其他框架像Angular、Ember和Vue.js。

为什么我们需要在JS中写CSS呢,主要是日常编写CSS代码时有如下痛点

  • 全局污染
  • 命名混乱
  • 样式重用困难
  • 代码冗余

Grid 布局

CSS网格布局是CSS中非常强大的布局系统。这是一个二维系统,意味着它可以同时处理列和行,不像Flex Box,是一维系统。 使用网格布局您可以通过将CSS规则应用于父元素(成为网格容器)和该元素的子元素(成为网格物体)。

网格容器:display:grid

网格轨道:通过 grid-template-columns 和 grid-template-rows 属性来定义网格中的行和列。

  • fr 单位
    • 网格轨道可以使用任何长度单位进行定义。 网格还引入了一个额外的长度单位来帮助我们创建灵活的网格轨道。新的fr单位代表网格容器中可用空间的一等份。
    • fr 同样可以和普通数值混合使用
  • 在网格轨道中使用repeat()
    • 有着多轨道的大型网格可使用 repeat() 标记来重复部分或整个轨道列表。
    • 还可以创建一个多轨道模式的重复轨道列表:
  • 关于显式和隐式网格
    • 显式网格就是我们通过 grid-template-columns 和 grid-template-rows 属性中明确定义的行和列。
    • 隐式网格就是网格根据内容自动创建的行和列,默认地,这些轨道将自动定义尺寸,会根据它里面的内容改变尺寸。
    • 我们可以通过grid-auto-rows 和 grid-auto-columns 属性来定义隐式网格的尺寸
    • minmax() 用来控制网格的最大最小尺寸。

网格线

  • 当我们创建网格时,网格会为网格线进行编号,以便我们通过网格线来定位每一个网格项目。
  • 跨轨道放置网格项目
    • 当放置元素时,我们使用 网格线 定位,而非 网格轨道。
    • grid-column-start, grid-column-end, grid-row-start 和 grid-row-end 属性

网格线的编号顺序取决于文章的书写模式。在从左至右书写的语言中,编号为 1 的网格线位于最左边。在从右至左书写的语言中,编号为 1 的网格线位于最右边。

网格间距

  • grid-column-gap(网格纵向间距) 和 grid-row-gap(网格横向间距) 属性来设置
  • 直接使用两个合并的缩写形式 grid-gap。

网格顺序:在网格项目发生堆积时使用 z-index 属性控制堆积的顺序。

示例

.grid {
    display: grid;
    /* grid-template-columns: 33.33% 33.33% 33.33%; */
    /* grid-template-columns: 2fr 3fr 4fr; */
    /* grid-template-columns: 70px 2fr 3fr; */
    /* grid-template-columns: 1fr 1fr 1fr; */
    /* grid-template-columns: repeat(3,1fr); */
    /* grid-template-columns: 70px repeat(3, 100px) 20px; */
    /* grid-template-columns: repeat(3, 70px 2fr); */
    grid-template-columns: 2fr 3fr 4fr;
    /*默认创建的网格行高度将为100px*/
    /* grid-auto-rows: 100px; */
    grid-auto-rows: minmax(100px, auto);
    /*grid-column-gap: 10px;*/
    /*grid-row-gap: 20px;*/
    grid-gap: 10px 20px;
}

div {
    border: 2px solid #8dd59c;
    border-radius: 5px;
    background-color: #68ae76;
    padding: 1em;
    color: #14dc3c;
}

.one {
    grid-column-start: 1;
    grid-column-end: 4;
    grid-row-start: 1;
    grid-row-end: 3;
    z-index: 2;
    background-color: #1e4c27;
    opacity: 0.8;
}

.two {
    grid-column-start: 1;
    grid-row-start: 2;
    grid-row-end: 5;
    z-index: 1;
}

资料



留言