# 现代化css方法论 CSS WorkFlow

# CSS预处理器

处理特定格式源文件到目标css的处理程序.

# CSS后处理器

  • CSS压缩 CLEAN-CSS
  • 自动添加浏览器前缀 Autoprefixer
  • CSS更加美观排序 CSScomb
  • Rework取代Stylus 后处理器发热
  • 前后通吃 PostCSS

用css-doodle实战一下css-next

先引用一下css-doodle的sdn

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/css-doodle/0.7.2/css-doodle.min.js"></script>
<style>

</style>

<body>
    
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

先使用css-doodle里的grid创建10*10的网格,因为css-doodle实际上就是web-components ,所以直接写在body里

<body>
    <css-doodle>
        :doodle{
            @grid: 1 x 10 / 85vmax;
        }
    </css-doodle>
</body>
1
2
3
4
5
6
7

接下来用cssnext定义一些样式

<style>
    /* 定义全局变量 */
    :root{
        --customUnit: 100%;
        --flag:0;
        --bgColor:#0a0c27;
    }
    /* 判断是否支持display:flex */
    @supports(display: flex){
        html,body{
            display: flex;
            align-items: center;
            justify-content: center;
        }
    }
    /* 使用变量 */
    html,body{
        width: var(--customUnit);
        height: var(--customUnit);
        background:var(--bgColor);
    }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

接下来给网格上色

<body>
    <css-doodle>
        :doodle{
            @grid: 1 x 10 / 85vmax;
        }
        @size:calc(@index()*10%);
        border-width:calc(@index()*1vmin);
        border-style:dashed;
        border-color:hsla(calc(20*@index()), 70%, 68%,calc(3/@index()*.8));
        border-radius:50%;
    </css-doodle>
</body>
1
2
3
4
5
6
7
8
9
10
11
12

现在看不出什么效果,一堆花里胡哨的东西,接下来再加一句居中瞬间爆炸

<body>
    <css-doodle>
        :doodle{
            @grid: 1 x 10 / 85vmax;
        }
        <!-- 这 -->
        @place-cell:center;
         <!-- 这 -->
        @size:calc(@index()*10%);
        border-width:calc(@index()*1vmin);
        border-style:dashed;
        border-color:hsla(calc(20*@index()), 70%, 68%,calc(3/@index()*.8));
        border-radius:50%;
    </css-doodle>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

可以看到现在是一个渐变的放射圆了

接下来再加上点动画效果

<body>
    <css-doodle>
        :doodle{
            @grid: 1 x 10 / 85vmax;
        }
        @place-cell:center;
        @size:calc(@index()*10%);
        border-width:calc(@index()*1vmin);
        border-style:dashed;
        border-color:hsla(calc(20*@index()), 70%, 68%,calc(3/@index()*.8));
        border-radius:50%;
        <!-- 动画 -->
        --d:@rand(20s,40s);
        --rf:@rand(360deg);
        --rt:calc(var(--rf) + @pick(1turn,-1turn));
        animation: spin var(--d) linear infinite;
        @keyframes spin {
            from{
                transform: rotate(var(--rf))
            }
            to{
                transform: rotate(var(--rt))
            }
        }
        <!-- 动画 -->
    </css-doodle>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# CSS分层与面向对象

# 为什什么要分层?

  • CSS有语义化的命名约定和CSS层的分离,将有助于它的可扩展性,性能的提高 和代码的组织管理。
  • 大量的样式,覆盖、权重和很多!important,分好层可以让团队命名统⼀一规范, ⽅方便便维护。
  • 有责任感地去命名你的选择器器。

# SMACSS

SMACSS(Scalable and Modular Architecture for CSS 可扩展的模块化架 构的CSS)像OOCSS⼀一样以减少重复样式为基础。然⽽而SMACSS使⽤用⼀一套五个层 次来划分CSS给项⽬目带来更更结构化的⽅方法:

  • Base -设定标签元素的预设值 PS:html {} input[type=text] {}
  • Layout -整个⽹网站的「⼤大架构」的外观 PS:#header { margin: 30px 0; }
  • Module -应⽤用在不不同⻚页⾯面公共模块 PS:.button{} + State -定义元素不不同的状态 PS:.nav--main { .active {} }
  • Theme - 画⾯面上所有「主视觉」的定义 PS: border-color、background-image
<div class="container">
        <div class="container-header">
            <div class="container-header__title">
                <h1 class="containe-header__title--home"></h1>
            </div>
        </div>
    </div>
1
2
3
4
5
6
7

# BEM

BEM和SMACCS⾮非常类似,主要⽤用来如何给项⽬目命名。⼀一个简单命名更更容易易让别 ⼈人⼀一起⼯工作。⽐比如选项卡导航是⼀一个块(Block),这个块⾥里里的元素的是其中标签之⼀一 (Element),⽽而当前选项卡是⼀一个修饰状态(Modifier):

  • block -代表了了更更⾼高级别的抽象或组件
  • block__element -代表.block的后代,⽤用于形成⼀一个完整的.block的整体。 + .block--modifier -代表.block的不不同状态或不不同版本。
  • 修饰符使⽤用的是_,⼦子模块使⽤用__符号。(不不⽤用⼀一个-的原因是CSS单词连接)
    <ul class="menu">
        <li class="menu__item">...</li>
        <li class="menu__item_state_current">...</li>
        <li class="menu__item">...</li>
    </ul>
1
2
3
4
5

# SUIT

Suit起源于BEM,但是它对组件名使⽤用驼峰式和连字号把组件从他们的修饰和⼦子孙 后代中区分出来:

  • 修饰符使⽤用的是—,⼦子模块使⽤用__符号。(不不⽤用⼀一个-的原因是CSS单词连接)

如果你不不想使⽤用如此严格或者复杂的命名规则,给每⼀一个选择器器加前缀同样可以达到这样的效果。 .s-product-details {} .t-product-details {} .js-product-details {} 结构属性将会被应⽤用到s-product-details选择器器中。主题属性将应⽤用于t-product-details选择器器。

# ACSS

考虑如何设计⼀一个系统的接⼝口。原⼦子(Atoms)是创建⼀一个区块的最基本的特质, ⽐比如说表单按钮。分⼦子(Molecules)是很多个原⼦子(Atoms)的组合,⽐比如说⼀一个表 单中包括了了⼀一个标签,输⼊入框和按钮。⽣生物(Organisms)是众多分⼦子(Molecules) 的组合物,⽐比如⼀一个⽹网站的顶部区域,他包括了了⽹网站的标题、导航等。⽽而模板 (Templates)⼜又是众多⽣生物(Organisms)的结合体。⽐比如⼀一个⽹网站⻚页⾯面的布局。⽽而 最后的⻚页⾯面就是特殊的模板。

.m-10{
    margin: 10px;
}
.w-50{
    width: 50%;
}
1
2
3
4
5
6

# ITCSS

  • Settings — 全局可⽤用配置,设置开关。$color-ui: #BADA55; $spacing-unit:10px
  • Tools —通⽤用⼯工具函数。@mixin font-color() {font-color: $color-ui;}+ Generic — 通⽤用基础样式。Normalize, resets, box-sizing: border-box; + Base — 未归类的HTML元素。ul {list-style: square outside;}
  • Objects —设计部分开始使⽤用专⽤用类。.ui-list__item {padding: $spacing-unit;}
  • Components — 设计符合你们的组件。.products-list {@include font-brand();border-top: 1px solid $color-ui;}
  • Trumps —重写,只影响⼀一块的DOM。(通常带上我们的!important)

# 实战

打开实战项目 src/web/components/banner/index.html

<div class="component-banner">
    <ul class="item">
        <li class="item__element item__element--active"><a href="/books/list">图书列表页</a></li>
        <li class="item__element"><a href="/books/create">创建图书</a></li>
    </ul>
</div>
1
2
3
4
5
6

banner/index.css

.component-banner{
    & .item {
        float:left;
    }
    & .item__element{
        color:aqua;
    }
    & .item__element--active{
        color:red;
    }  
}
1
2
3
4
5
6
7
8
9
10
11

banner/index.js

import('./index.css');
const banner = {
    init() {
        console.log('banner js 入口');
    }
}
export default banner;
1
2
3
4
5
6
7

src/web/components/list/index.html

<div class="component-list pjaxcontent">
    <h1 id="js-h1">图书列表页</h1>
    <table class="table table-bordered table-striped">
        <tr>
            <th>编号</th>
            <th>书名</th>
            <th>作者</th>
        </tr>
        {% for val in result.data %}
        <tr>
            <td>{{ val.id }}</td>
            <td>{{ val.name }}</td>
            <td>{{ val.author }}</td>
        </tr>
        {% endfor %}
    </table>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

list/index.css

.list h1{
    color:rebeccapurple;
}
:root{
    --mainColor: rebeccapurple;
}
.component-list{
    & h1{
        color:var(--mainColor);
    }
}
1
2
3
4
5
6
7
8
9
10
11

list/index.js

import('./index.css');
const list = {
    init() {
        console.log('list js 入口');
        $('#js-h1').click(function() {
            alert('欢迎选择图书');
        })
    }
}
export default list;
1
2
3
4
5
6
7
8
9
10

在这里我们使用了postcss来写样式,所以还需要一些loader

yarn add postcss-loader css-loader style-loader postcss-preset-env
1

创建一个 postcss.config.js

module.exports = {
    plugins: {
        'postcss-preset-env': {
            browsers: 'last 2 versions',
            stage:0,
            features:{
                'nesting-rules': true
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11

再在webpack中配置一下 webpack.config.js

const argv = require('yargs-parser')(process.argv.slice(2));
const _mode = argv.mode || 'development';
const _mergeConfig = require(`./config/webpack.${_mode}.js`);
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const glob = require('glob');
const { resolve, join } = require("path");
const HtmlAfterPlugin = require('./config/htmlAfterPlugin');
const _modeFlag = _mode == 'development' ? true : false;

// 入口文件
let _entry = {};
let _plugins = [];
// 寻找全部的entry
const files = glob.sync('./src/web/views/**/*.entry.js');
for(let item of files) {
    if (/.+\/([a-zA-Z]+-[a-zA-Z]+)(\.entry\.js$)/g.test(item) == true) {
        const entryKey = RegExp.$1;
        const [dist, template] = entryKey.split('-');
        _entry[entryKey] = item;
        _plugins.push(new HtmlWebpackPlugin({
            filename: `../views/${dist}/pages/${template}.html`,
            template: `src/web/views/${dist}/pages/${template}.html`,
            inject:false,
            chunks: ["runtime", entryKey]
        }));
    }
}

const webpackConfig = {
    entry: _entry,
    watch: _modeFlag,
    output: {
        path: join(__dirname, "./dist/assets"),
    },
    // ------------------------------
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    {loader: 'css-loader',
                options: {
                    importLoaders: 1
                }},
                'postcss-loader'
                ]
            }
        ]
    },
    // ------------------------------
    resolve: {
        alias: {
            "@components": resolve(__dirname, 'src/web/components'),
        },
    },
    optimization: {
        runtimeChunk: {
            name: 'runtime'
        }
    },
    plugins: [
        ..._plugins,
        new HtmlAfterPlugin()
    ],
}
// 将合并后的配置导出
module.exports = merge(webpackConfig, _mergeConfig);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

可以看到页面上出来样式了

但是现在还有个问题,dist中没有将css单提出来 再做点小手脚

yarn add mini-css-extract-plugin
1

配置一下webpack

const argv = require('yargs-parser')(process.argv.slice(2));
const _mode = argv.mode || 'development';
const _mergeConfig = require(`./config/webpack.${_mode}.js`);
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const glob = require('glob');
const { resolve, join } = require("path");
const HtmlAfterPlugin = require('./config/htmlAfterPlugin');
const _modeFlag = _mode == 'development' ? true : false;
// --------------------------------
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// --------------------------------
// 入口文件
let _entry = {};
let _plugins = [];
// 寻找全部的entry
const files = glob.sync('./src/web/views/**/*.entry.js');
for (let item of files) {
    if (/.+\/([a-zA-Z]+-[a-zA-Z]+)(\.entry\.js$)/g.test(item) == true) {
        const entryKey = RegExp.$1;
        const [dist, template] = entryKey.split('-');
        _entry[entryKey] = item;
        _plugins.push(new HtmlWebpackPlugin({
            filename: `../views/${dist}/pages/${template}.html`,
            template: `src/web/views/${dist}/pages/${template}.html`,
            inject: false,
            chunks: ["runtime", entryKey]
        }));
    }
}

const webpackConfig = {
    entry: _entry,
    watch: _modeFlag,
    output: {
        path: join(__dirname, "./dist/assets"),
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    // --------------------------------
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {},
                    },
                    // --------------------------------
                    // 'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 1
                        }
                    },
                    'postcss-loader'
                ]
            }
        ]
    },
    resolve: {
        alias: {
            "@components": resolve(__dirname, 'src/web/components'),
        },
    },
    optimization: {
        runtimeChunk: {
            name: 'runtime'
        }
    },
    plugins: [
        // --------------------------------
        new MiniCssExtractPlugin({
            // Options similar to the same options in webpackOptions.output
            // all options are optional
            filename: _modeFlag ? 'style/[name].css' : 'style/[name].[contenthash:5].css',
            chunkFilename: _modeFlag ? 'style/[id].css' : 'style/[id].[contenthash:5].css',
            ignoreOrder: false, // Enable to remove warnings about conflicting order
        }),
        // --------------------------------
        ..._plugins,
        new HtmlAfterPlugin()
    ],
}
// 将合并后的配置导出
module.exports = merge(webpackConfig, _mergeConfig);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

修改 config/htmlAfterPlugin.js 将css替换进去

const pluginName = 'htmlAfterPlugin';
const assetsHelp = (data) => {
    let js = [];
    let css = [];
    const dir = {
        css: item => `<link rel="stylesheet" href="${item}">`,
        js: item => `<script src="${item}"></script>`,
        lazyjs: item => `<script class="lazyload-js" src="${item}"></script>`,
    }
    // const whiteList = new Map();
    // whiteList.set('whiteList', true);
    for(let jsitem of data.js) {
        if (/runtime/g.test(jsitem)) {
            js.push(dir.js(jsitem));
        } else {
            js.push(dir.lazyjs(jsitem));
        }
    }
    for(let cssitem of data.css) {
        css.push(dir.css(cssitem));
    }
    return {
        js,
        css
    }
}
class HtmlAfterPlugin {
    apply(compiler) {
        compiler.hooks.compilation.tap(pluginName, compilation => {
            // HtmlWebpackPlugin留下的钩子,在插入html之前等一下,必须要泡在HtmlWebpackPlugin插件之后
            compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(pluginName, htmlPluginData => {
                // htmlPluginData能拿到相应的html页面,替换掉写好的占位符
                // 获取对应的html
                let _html = htmlPluginData.html;
                _html = _html.replace(/@layouts/g, '../../layouts');
                _html = _html.replace(/@components/g, '../../../components');
                // 拿到要插入的js
                const result = assetsHelp(htmlPluginData.assets);
                // 将js文件替换占位符
                _html = _html.replace('<!-- injectjs -->', result.js.join(''));
                _html = _html.replace('<!-- injectcss -->', result.css.join(''));
                htmlPluginData.html = _html;
            })
        })
    }
}
module.exports = HtmlAfterPlugin;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

修改 src/web/views/books/pages/list.html

{% extends '@layouts/layout.html' %}
{% block styles %}
<!-- inkectcss -->
{% endblock %}
{% block title %}图书展示页面{% endblock %}

{% block content %}

{% include "@components/list/index.html" %}
{% endblock %}
{% block scripts %}
<!-- injectjs -->
{% endblock %}
1
2
3
4
5
6
7
8
9
10
11
12
13

完活