# 第5章 样式处理

# 分离样式文件

webpack 4之前版本是:extract-text-webpack-plugin
webpack 4及以上版本:mini-css-extract-plugin

# extrach-text-webpack-plugin

const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
    module: {
        rule: [
            {
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: 'css-loader'
                })
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin('bundle.css');
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

fallback属性用于指定当前插件无法提取样式时所采用的loader.

# 多样式文件的处理

样式的提取是以资源入口开始的整个chunk为单位的。chunk是有一组有依赖关系的模块的封装。

const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
    entry: {
        foo: './src/scripts/foo.js',
        bar: './src/scripts/bar.js'
    },
    module: {
        rule: [
            {
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: 'css-loader'
                })
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin('[name].css');
    ]
}

// foo.js
// foo.css
// bar.js
// bar.css
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

# mini-css-extract-plugin

mini-css-extract-plugin是extract-text-webpack-plugin的升级版。webpack4以后使用它进行样式的提取,4之前的版本是用不了的。

它最大的特性是支持按需加载css。a.js通过import()函数异步加载了b.js,b.js里面加载了style.js,那么使用extract-text-webpack-plugin的话,style.css最终只能被同步加载。使用mini-css-extract-plugin会单独打包出一个0.css,这个css文件将由a.js通过动态插入link标签的方式加载。

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    entry: './app.js',
    output: {
        filename: '[name].js',
    },
    mode: 'development',
    module: {
        rules: [{
            test: /\.css$/,
            use:[
                {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                        publicPath: '../',
                    }
                },
                'css-loader'
            ]
        }]
    },
    plugin: [
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[id].css'
        })
    ]
}
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

支持配置publicPath,用来指定异步css的加载路径。
不需要设置fallback。
除了指定同步加载的css资源名filename,还需要指定异步加载的css资源名chunkFIlename。

# 样式预处理

# sass-loader

sass-loader就是将SCSS语法编译为CSS。

npm i sass-loader node-sass
1
rules: [
    {
        test: /\.scss$/,
        use: ['style-loader','css-loader','sass-loader']
    }
]
1
2
3
4
5
6

假如想要在浏览器的调试工具中查看代码,需要分别为sass-loader和css-loader单独添加source map的配置项。

test: /\.scss$/,
use: [
    'style-loader',
    {
        loader: 'css-loader',
        options: {
            sourceMap: true
        }
    },
    {
        loader: 'sass-loader',
        options: {
            sourceMap: true
        }
    },
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# less

npm i less-loader less
1

# PostCSS

PostCSS并不是一个CSS预编译器,它只是一个编译插件的平台。它的工作模式是接收样式源代码并交由编译插件处理。PostCSS 提供了一个解析器,它能够将 CSS 解析成抽象语法树(AST),我们可以通过“插件”来传递AST,然后再把AST转换成一个串,最后再输出到目标文件中去。

rules: [
    {
        test: /\.scss$/,
        use: [
            'style-loader',
            'css-loader',
            'postcss-loader'
        ]
    }
]
1
2
3
4
5
6
7
8
9
10

PostCSS要求必须有个单独的配置文件。webpack2中PostCSS不在支持从loader传入。需要在项目根目录下创建一个postcss.config.js。

// postcss.config.js
module.exports = {};
1
2

# 自动前缀

PostCSS最广泛的场景就是与Autoprefixer结合,为css自动添加厂商前缀。

npm i autoprefixer
1
// postcss.config.js
const autoprefixer = require('autoprefixer');
module.exports = {
    plugins: [
        autoprefixer({
            grid: true,
            browsers:[
                '> 1%',
                'last 3 versions',
                'android 4.2',
                'ie 8'
            ]
        })
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.container {
    display: grid;
}

/* 编译后 */
.container {
    display: -ms-grid;
    display: grid;
}
1
2
3
4
5
6
7
8
9

# stylelint

stylelint是一个css的质量检测工具,用来统一代码风格,确保代码质量。

npm i stylelint
1
// postcss.config.js
const stylelint = require('stylelint');
module.exports = {
    plugins: [
        stylelint({
            config: {
                rules: {
                    'declaration-no-important': true
                }
            }
        })
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13

如果代码中出现了“!important”时就会给出警告。

# CSSNext

PostCSS可以与CSSNext结合使用,让我们在应用中使用最新的CSS语法特性。

npm i postcss-cssnext
1
//postcss.config.css
const postcssCssnext = require('postcss-cssnext');
module.exports = {
    plugins: [
        postcssCssnext({
            // 指定所支持的浏览器
            browsers: [
                '> 1%',
                'last 2 versions'
            ]
        })
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/* style.css */
:root {
    --highlightColor: hwb(190, 35%, 20%);
}
body {
    color: var(--highlightColor);
}

/* 打包后 */
body {
    color: rgb(89, 185, 204);
}
1
2
3
4
5
6
7
8
9
10
11
12

# CSS Modules

CSS Modules就是把CSS模块化,让CSS也拥有模块的特点。使用CSS Modules不需要安装额外的模块,只需要开启css-loader中modules配置项。

module: {
    rules: [
        {
            test: /\.css/,
            use: [
                'style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        modules: true,
                        localIdentName: '[name]__[local]__[hash:base64:5]',
                    }
                }
            ]
        }
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

localIdentName用于指明CSS代码中的类名会如何来编译。

/* style.css */
.title {
    color: red;
}

/* 编译后 */
.style__title__1CFy6
1
2
3
4
5
6
7

使用CSS Modules时CSS文件会导出一个对象:

// style.css
.title {
    color: red;
}

// app.js
import styles from './style.css';
document.write(`<h1 class="${styles.title}"></h1>`);
1
2
3
4
5
6
7
8

最终这个HTML中的class才能和编译后的CSS类名匹配上。