来写一个CSS工程实例感受一下前端工程化的便利。顺便也复习一下CSS。

  1. 项目文件结构
  2. 分离CSS文件与导入CSS文件
  3. BEM理念与拼接类名
  4. 编写CSS样式

项目文件结构

这个项目的例子采用了Udemy课程第五部分的例子,使用WebStorm创建新的Node.js项目,然后把项目的源代码复制进来。打开/app/index.html,之后就可以来编写CSS文件了。

想象我们在一个公司里工作,设计人员给了我们要达到的网页目标(在大的psd文件里),就以这个目标设计样式。

项目的CSS源文件的路径是:app/assets/styles/styles.cssindex.html位于app/index.html中,我们的CSS目标路径是app/temp/styles/styles.css

所以先要更新一下gulp监听任务的路径:

gulp.task('watchcss', done => {
    watch('app/assets/styles/styles.css', function () {
        gulp.src('app/assets/styles/styles.css')
            .pipe(postcss([cssvars, nestedcss, autoprefixer]))
            .pipe(gulp.dest('app/temp/styles/'));
    });
    done();
});

然后在index.html中添加上最终CSS文件的link标签:

<link rel="stylesheet" href="temp/styles/styles.css">

之后就可以编写具体的CSS样式,然后自动编译到目录路径,刷新页面即可看到加载后的样式。

导入其他CSS文件

开始编写CSS样式,先是一些基础的全局样式,英文现在用Roboto比较漂亮。

body {
    font-family: 'Roboto', sans-serif;
    color: #333;
}

img {
    max-width: 100%;
    height: auto;
}

之后要开始处理顶部标题栏以及大图片和居中的文字。在开始处理之前,发现大图片的类是large-hero,当然可以继续往下编写,但为了简便和解耦,可以将这部分的CSS代码单独放在一个文件里

app/assets/styles下创建modules目录,在其中创建_large-hero.css文件。这个文件的完整路径是app/assets/styles/modules/_large-hero.css,名字可以自己起,没有影响。

在编写large-hero类之前,先解决的问题是,如何将多个CSS文件拼合成一个呢?只需要使用postcss-import库:

npm install postcss-import --save-dev

然后修改gulpfile.js

var gulp = require('gulp');
var watch = require('gulp-watch');
var postcss = require("gulp-postcss");
var autoprefixer = require("autoprefixer");
var cssvars = require('postcss-simple-vars');
var nestedcss = require('postcss-nested');
var cssImport = require('postcss-import');
gulp.task('watchcss', done => {
    watch('app/assets/styles/styles.css', function () {
        gulp.src('app/assets/styles/styles.css')
            .pipe(postcss([cssImport, cssvars, nestedcss, autoprefixer]))
            .pipe(gulp.dest('app/temp/styles/'));
    });
    done();
});

重新启动gulp任务。之后在CSS文件中,就可以使用@Import导入其他CSS文件,在style.css源文件里,编写large-hero类的地方添加:

@import "modules/_large-hero.css";

当然一般可以把导入语句都放入CSS文件的顶部。

以后就可以按此方法导入多个CSS文件,这些文件最后会拼合成一个完整的CSS文件并输出到目标目录下。

现在来重新整理一下CSS工程文件,之前的全局设置也单独用一个文件,放在app/assets/styles/base/_global.css中,这样目前的style.css源文件只有如下两行:

@import "base/_global.css";
@import "modules/_large-hero.css";

这样就初步搭建起互相分离但能最后结合在一起的CSS样式表文件。之后就可以按照HTML里元素对应的类来编写CSS文件了。

注意之前我们添加了normalize.css的依赖,这个依赖目前是在项目的node_modules/normalize.css/normalize.css路径内,要怎么把这个东西加进来呢?如果NPM包是一个CSS样式包,可以直接导入这个包的名称:

@import "normalize.css";

在编译后的style.css中,可以看到已经添加上了normalize.css的内容。

BEM理念与拼接类名

让文字在图片居中,子绝父相老套路,div为相对定位,把子元素设置为绝对定位即可。

现代WEB开发,遵循BEM理念:

  1. B-Block,指可重复利用样式片段,即一个CSS样式类
  2. E-Element,指属于一个Block的元素,不能在这个Block以外使用,依赖于这个Block,即依赖于一个CSS类的样式类
  3. M-Modifier,修饰符,用于一个Block或者一个Element,改变这个对象的默认状态,比如让一个菜单显示与否,经常就是用一个CSS样式类存在与否来控制

我的理解,BEM其实指的是存在于元素上的CSS样式类的功能类别,所以不能直接去选择元素,而是要给元素添加类,然后再编写对应的CSS样式。

.large-hero {
    position: relative;
}

.large-hero__text-content {
    position: absolute;
    left: 0;
    /*定位之后由于是块级,直接伸展到父元素100%,然后居中*/
    width: 100%;
    text-align: center;
    /*垂直居中:下移动50%父元素距离,然后向上移动自身50%距离*/
    top: 50%;
    transform: translateY(-50%);
}

.large-hero__title {
    font-weight: 300;
    color:#2f5572;
    font-size: 4.8rem;
}

.large-hero__subtitle {
    font-weight: 300;
    color:#2f5572;
    font-size: 2.8rem;
}

这里实际上为了特定选择,最好把父类加上去,但是相比写成:

.large-hero .large-hero__text-content

可以采用一种拼接的方式:

.large-hero {
    position: relative;

    &__text-content {
    position: absolute;
    left: 0;
    /*定位之后由于是块级,直接伸展到父元素100%,然后居中*/
    width: 100%;
    text-align: center;
    /*垂直居中:下移动50%父元素距离,然后向上移动自身50%距离*/
    top: 50%;
    transform: translateY(-50%);
    }

    &__title {
        margin: 0;
        font-weight: 300;
        color:#2f5572;
        font-size: 4.8rem;
    }

    &__subtitle {
        font-weight: 300;
        color:#2f5572;
        font-size: 2.8rem;
    }
}

实际上这是把嵌套的类名当做变量,然后在嵌套中拼合,最终生成的样式和原来一样。

编写CSS样式

继续给large-hero类中的p元素设置类和编写样式:

&__description {
    color: #FFF;
    font-size: 1.875rem;
    font-weight: 100;
    text-shadow: 2px 2px 0 rgba(0,0,0,.1);
    max-width: 30rem;
    margin-left: auto;
    margin-right: auto;
}

这里解释一下rem,表示相对HTML元素的字体比例大小,1rem就说明和HTML元素的字体大小相等。大部分浏览器的默认HTML元素的字体大小是16px,1.5rem就是24像素。

rem尺寸可以方便的根据浏览器和用户实际设置的字体来调整,不光是字体大小,间距,宽度都可以,在响应式页面中运用的非常广泛。

之后是当中部分的按钮,观察整个页面,发现右上方,右下方,下方都还有按钮,蓝色按钮应该是一般样式的,橙色按钮只要在基础样式上添加modifier即可。在modules目录下新建一个btn样式表文件,名字随便起,导入到主style.css中,然后编写样式:

$mainBlue: #2f5572;

.btn {
    background-color: $mainBlue;
    color: #FFF;
}

这里为了进一步解耦,在base目录下创建一个专门用来存储变量的文件:_variables.css,把这个变量提取走。现在的style.css源文件如下:

@import "normalize.css";
@import "base/_global.css";
@import "base/_variables.css";
@import "modules/_large-hero.css";
@import "modules/_btn.css";

相比没有工程化之前,写在一个CSS文件里,或者写在多个CSS文件里需要导入多次,简直是又解耦又方便啊。

剩下就可以继续编写样式了,以后的变量就自动抽离到_variables.css中。

.btn {
    background-color: $mainBlue;
    color: #FFF;
    text-decoration: none;
/*    设置了padding,要更改为块级才有效*/
    padding: .75rem 1.2em;
    display: inline-block;

    &--orange {
        background-color: $mainOrange;
    }

    &--large {
        font-size: 1.25rem;
        padding: 1.1rem 1.9rem;
    }
}

$mainOrange: #d59541;

之后在页脚和中央的按钮上,应用btn样式类和btn–orange,btn–large修饰符样式类即可。