AWebMan

Menu

组织css代码

前言

本文主要描述的组织css的问题,文章内容整理自《前端架构设计》的读书笔记。

继承与特性之争


h2{
}
#sidebar h2{  
background:red;
}
#sidebar .calendar h2{
background:green;
}

上面这样写样式有很大的问题,主要是我们在样式书写时默认的继承到了之前的样式,但在特殊化的样式时,为了让自己的样式生效,又要新写样式进行覆盖。

所以列举一下可能的问题是下面的:

  • 选择器的优先级:选择合适的优先级,查询效率高,方便其他人样式覆盖的需求
  • 颜色重置:要恢复到原来的颜色,需要进行不断的重置或者赋值
  • 位置依赖:如果我们的样式移到其他位置,那么样式代码就会失效,因为严格依赖dom的结构和顺序,不能很好的解耦
  • 多重继承:最终元素的样式可能是层层嵌套得到的最终样式代码,非常不可复用,当改变主体的样式时,子元素都会受到影响
  • 深层嵌套:效率非常低

现代化的模块方案

在之前的章节中,我们讲到了css不同的模块化方案,那么对于上面描述的问题,其实可以很简化的去解决。

案例代码

你根据以上的需求按照模块的方案和命名,去除各种依赖,最大程度使用class命名方式,减少继承,更大程度的使用独立样式代码块,轻松解决了以上的问题。

<h2 class="content_title"></h2>
<div class="sidebar">
    <h2 class="content_title--reversed"></h2>
    <div class="calendar">
         <h2 class="calendar_title"></h2>
    </div>
</div>
// 组件文件夹
.content_title{}
// 不用恢复重置样式
.contitle_title--reverse{}
// 特殊组件的title定义
.calendar_title{
}

单一职责原则

比如我们在定义标题的时候,可能有两个模块都用到了标题。

<div class="calendar">
 <h2 class="primary-title"></h2>
</div>
<div class="blog">
 <h2 class="primary-title"></h2>
</div>
<style>
.primary-title{
}
</style>

可能的问题,是我们需要在日历或者博客的时候需要去修改这个样式,于是你的样式可能变成这样的。

.primary-title{
}
.blog .primary-title{}

这样的问题是会导致选择器过多,于是我们按照bem进一步优化。

.calendar_header{}
.blog_header{}

这样维护之后,每个样式块都只负责自己的内容,除了导致代码的重复之外没有任何坏处。好处是如果项目严格执行单一职责方式,当我们改动任何代码的时候,不会担心对全局造成的影响。那么重复的代码怎么解决,这个其实webpack的部分和gzip的部分已经能把我们的代码进行优化了。

延伸思考:如果压缩打包的角度可以去掉重复代码的部分,那么更多角度考虑代码的可持续、可维护就好了。

单一样式来源

简单来讲,就是你的样式代码应该来源于尽可能少的组件,最好是维护在一个class中,保证整个样式代码是可追溯的,也可以预想效果的。

案例

<div class="blog">
  <div class="blog-header"></div>
</div>
<div class="calendar">
  <div class="calendar-header"></div>
</div>
/* calendar css*/
.calendar-header{
}
/* blog css*/
.blog-header{
}
.blog .blog-header{}

以blog进行title的限定,主要是限制让字号小一点,这种的主要问题是当项目积累下来,会有很多样式代码散落在各个组件,这样导致不可追溯。(补充个常识,以父元素为什么进行修饰子元素,这个称为样式上下文)

那么建议的方式是将散落在各处的代码完全控制在一个组件内。

组件修饰符

虽然单一组件的原则让我们对组件的样式代码封装的很好,但还是有一些特殊的需求,那我们如何设计这方面的代码呢?通过皮肤或者子模块来实现,也可以称为组件修饰符。

比如我们针对日历的组件,我们需要追加一个特殊的样式,可以这样实现。

.calendar-header{
}
.calendar--nested .calendar-header{
}

通过这样的方式,我们不但可以实现皮肤的需求,也可以实现对上下文的解耦,我们只要关注组件需要什么样的特殊样式,进行组件修饰即可,而不用依赖耦合在父容器里。

element-ui的css

以下以element的2.4.11版本为例,根据其api文档以及源码的角度为大家分析element-ui是如何解决这些问题的。

el-button分析

  • el-button文档地址
  • api设计规范:可以看到button的暴露的api主要是基于属性的
    参数 说明 类型 可选值 默认值
    size 尺寸 string medium / small / mini
    type 类型 string primary / success / warning / danger / info / text
    plain 是否朴素按钮 boolean false
    round 是否圆角按钮 boolean false
    circle 是否圆形按钮 boolean false
    loading 是否加载中状态 boolean false
    disabled 是否禁用状态 boolean false
    icon 图标类名 string
    autofocus 是否默认聚焦 boolean false
    native-type 原生 type 属性 string button / submit / reset button
  • class的设计,虽然组件的设计是基于属性的,但实际不同的属性最终都是表现为class的。那么基于这样的角度去设计样式是element-ui的首创或者是vue组件的创新么?其实早在bootstrap里就是这样设计的。
    class 说明
    el-button-group 按钮组的样式,作为外部容器,不对el-button产生任何样式代码,但对其内含有的el-button会产生垂直居中的效果,还有向右的间距,.el-button-group .el-button--primary:first-child{}
    el-button el-button--default 基本样式,修饰符,生效规则el-button--default也是直接定义其样式规则,不依赖于el-button
    el-button el-button--medium el-button--medium 修饰符中直接定义好尺寸的全部代码
    el-button el-button--default is-circle 基本样式,smacss状态样式 ,其中is-circle包含这个原型的全部样式,其生效的条件是与el-button同时生效,is-disabled同理
    icon="el-icon-edit" 带icon的部分不在button里做特殊样式,其样式属于基本样式,但是dom结构是通过组件进行添加的,那么其图标的字号来源于哪里呢?来源于el-button的基本样式14px,所以这部分进行了解耦,不用单独设置
  • 组件内如何根据属性进行返回对应的class.
class="el-button"
// 可以看到其根据传入的各个type属性分别将class追加到class数组之中
:class="[type ? 'el-button--' + type : '',buttonSize ? 'el-button--' + buttonSize : '',{'is-disabled': buttonDisabled,'is-loading': loading,'is-plain': plain,'is-round': round,'is-circle': circle}]"
//根据为loading 显示出固定的loading图标
<i class="el-icon-loading" v-if="loading"></i>
// 根据传入的icon以及没有loading显示出对应type的icon
<i :class="icon" v-if="icon && !loading"></i>

轻度定制的element

经常有场景我们需要引入element-ui之后需要对其ui进行皮肤化的开发样式,虽然element-ui有暴露其对应的组件样式,我们也可以进行对应的详细的源码的fork并开发,但在大多数中小公司其实不用小题大做。我们只需要根据自己的情况,针对一样的样式进行全局样式覆盖即可。那么,我们项目中针对按钮的皮肤化改变会是这样的。

// 定义variables.scss的主题变量
// 主题色相关变量
$color-thin:rgba(60,191,196,0.1);
$color:rgba(60,191,196,1);

// customer-element.scss  主要定义已使用饿了么组件样式的修改
@import  './variables';
// 一个按钮样式的全局覆盖
.el-button--primary{
&:hover,&:focus{
background:$color;
border-color:$color;
}
background:$color;
border-color:$color;
}

bootstrap的css

我们同样也是分析bootstrap里的样式使用,基本也是使用class拼盘理论的。

<div class="btn-group" role="group" aria-label="...">
  <button type="button" class="btn btn-default">Left</button>
  <button type="button" class="btn btn-default">Middle</button>
  <button type="button" class="btn btn-default">Right</button>
</div>

button.less样式文件节选,可以看到其基本样式、嵌套内伪类的样式,button-size的方法类,修饰符的(子模块的)维护方式,这都是值得我们借鉴的科学规范思想。


// Base styles
// --------------------------------------------------

.btn {
  display: inline-block;
......
  .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);
  .user-select(none);
  &,
  &:active,
  &.active {
    &:focus,
    &.focus {
      .tab-focus();
    }
  }

  &:hover,
  &:focus,
  &.focus {
    color: @btn-default-color;
    text-decoration: none;
  }

  a& {
    &.disabled,
    fieldset[disabled] & {
      pointer-events: none; // Future-proof disabling of clicks on `<a>` elements
    }
  }
}

// Alternate buttons
// --------------------------------------------------
.btn-default {
  .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);
}
.btn-primary {
  .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);
}

小结

到此为止总结的技巧点如下:当然肯定还有更多的技巧等待你补充

  • 分离容器与内容
  • 单一样式来源
  • 区分布局与组件的角色
  • 在标记上使用单一的、扁平的标识
  • 组件修饰符

通过本文我们了解并总结了css代码的继承与特殊化的问题,并通过上面的技巧解决了这样的痛点,并用来显著提升自己的代码水平。

— 于 共写了5056个字
— 文内使用到的标签:

发表评论

%d 博主赞过: