Webpack4学习笔记(四)——CSS处理 - 简书

这是一个关于Webpack4的文章系列,其中包含以下文章:

本系列文章源代码:

git clone https://github.com/davenkin/webpack-learning.git
    • *

上一篇文章中,我们讲到了多入口文件的代码分割,本文我们将讲到CSS的处理。

欢迎访问本文github地址

最基本的CSS处理

Webpack最基本的css处理:css-loader + style-loader。其中css-loader用于处理css文件中的@importurl(...),而style-loader用于将css-loader的输出生成js中的函数调用将css动态添加到html文件中。

安装css-loader和style-loader:

cnpm install --save-dev css-loader style-loader

首先创建index.html:

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Webpack4-CSS处理-index</title>
</head>
<body>
</body>
</html>

然后创建main.css:

body {
    background-color: red;
}

这里我们将body设置成红色。

然后在index.js文件中直接importmain.css

import './main.css'

然后配置webpack.config.js,使webpack可以将css文件当做module对待(即可以进行import操作)以及使用css-loader和style-loader对css文件进行处理。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  }
plugins:[
   new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html',
        })
 ]
}

运行cnpm start,可以看到红色页面出现。

抽离独立的css文件

style-loader将css动态添加到html文件中,有时(特别是在生产环境下)我们希望将所有的css抽离为独立的css文件,此时可以借助mini-css-extract-plugin,安装mini-css-extract-plugin

cnpm install --save-dev mini-css-extract-plugin

将style-loader改为mini-css-extract-plugin的loader:

...
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

...
{
        test: /\.css$/,
        use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
      }
...

另外,还需要引入MiniCssExtractPlugin插件本身:

...
plugins: [
        new CleanWebpackPlugin(['distribution']),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html',
        }),
        new MiniCssExtractPlugin({
            filename: "[name].[contenthash].css",
            chunkFilename: "[name].[contenthash].css"
        })
    ]
...

运行cnpm run build,可以看到生成了独立的index._.css文件,另外所生成的index._.html文件中自动的引用了该css文件。

CSS Module

根据CSS Module的官网,CSS Module只对类名和动画的名字起作用。

css-loader支持CSS Module,CSS Module为每个独立的css文件处理为一个module。此时需要修改webpack.config.js文件中css-loader的配置:

...
{
                        loader: 'css-loader',
                        options: {
                            sourceMap: true,
                            modules: true,
                            localIdentName: '[name]---[local]---[hash:base64:5]'
                        }
                    }, 
...

其中,modules: true,表示启用CSS Module,localIdentName设置css类名的命名方式。

在main.css中加入更多的css:

body {
    background-color: red;
}

div {
    box-sizing: border-box;
}

.red {
    background-color:red;
}

:global(.gold){
    background-color:gold;
}


:local(.aqua){
    background-color: aqua;
}

请注意这里的:global(.gold):local(.aqua),前者表示脱离模块化约束而使用全局的声明(即效果跟普通css相同),后者表示使用模块化化处理,这里的:local有点多余,因为当启用了CSS Module之后,css文件中的类名默认便是:local的。

运行cnpm run build,输出对应的css文件为:

body {
    background-color: red;
}

div {
    box-sizing: border-box;
}

.main---red---1GVbX {
    background-color:red;
}

.gold{
    background-color:gold;
}


.main---aqua---34_W6{
    background-color: aqua;
}

可以看到,.main---red---1GVbX对应原文件中的.red,命名规则有localIdentName: '[name]---[local]---[hash:base64:5]'配置完成。.gold由于标记有:global,所以生成的全局的类名。

使用SASS

SASS的基本使用请参考笔者这篇文章

安装sass-loader:

npm install sass-loader node-sass webpack --save-dev

请注意,要使用sass-loader,同时需要安装node-sass。

修改webpack.config.js文件,首先让原来处理的css的loader也能处理scss文件:

...
    test: /\.(s*)css$/,
...

然后加入sass-loader:

...
{
         loader: 'sass-loader',
         options: {
             sourceMap: true
         }
 }
...

注意,sass-loader需要是处理scss/css文件的第一个loader,也即需要配置在最后面。

为了演示sass的使用,创建partial的_global.scss:

$base-color:red;

然后将公共的css内容移动到base.scss:

body {
    background-color: red;
}

div {
    box-sizing: border-box;
}

修改main.css文件为main.scss,内容如下:

@import './global';

:global(.gold){
    background-color:gold;
}


:local(.aqua){
    background-color: aqua;
}

.localRed {
    background-color: $base-color;
}

可以看到,在main.scss文件中我们通过@import引入了_global.scss,然后对.localRed应用了_global.scss中的变量$base_color

作为演示,另外再创建main2.scss,情况与main.scss相似:

@import './global';

.red {
    background-color: red;
}


.localRed {
    background-color: $base-color;
}

最后,在index.js文件中引用base.scss,main.scss和main2.scss:

import './base.scss'
import './main.scss'
import './main2.scss'

运行cnpm run build,输出css文件如下:

body {
  background-color: red; }

div {
  box-sizing: border-box; }

.gold {
  background-color: gold; }

.main---aqua---Faovj {
  background-color: aqua; }

.main---localRed---2p9T9 {
  background-color: red; }

.main2---red---2xwE1 {
  background-color: red; }

.main2---localRed---3yuRF {
  background-color: red; }

可以看到,生成的css文件中有公共的base.scss中的内容,main.scss和main2.scss中的内容。

请注意,如果让main.scss和main2.scss也引用base.scss,那么body和div将出现3次,也即index.js、main.scss和main2.scss对base.scss的引用会重复出现;base.css中如果有类名声明,那么这些类名声明会同时出现在这3个引用它的module中,因为SASS分别以这三个文件为编译单位进行处理,最后将他们的内容合并到一起。因此,对于SASS来说,有个原则是如果有文件作为公共css文件,那么建议它最多只引用1次,通常是在入口文件中进行引用,比如VueJS的App.vue文件。

另外,除了@import partial文件(下划线作为前缀,比如_global.scss,SASS不会单独进行编译)之外,一个scss文件中也可以@import其他常规scss文件,此时与partial一样,相当于将常规scss文件内容拷贝到了目的文件。

PostCSS

安装postcss-loader:

cnpm install --save-dev postcss-loader

添加postcss-loader:

{
                        loader: 'postcss-loader',
                        options: {
                            plugins: [require("autoprefixer")],
                            sourceMap: true
                        }
                    }

请注意,该loader的作用顺序应该在sass-loader和css-loader之间。

这里我们仅演示autoprefixer的使用,安装autoprefixer:

cnpm install --save-dev autoprefixer

然后在package.json中添加:

"browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]

此时的输出文件中,请注意base.scss中的div配置:


div {
  -webkit-box-sizing: border-box;
          box-sizing: border-box; }

其中的-webkit-box-sizing为autoprefixer自动加上的前缀。

多入口

当项目中有多个入口文件时,webpack会为每个入口文件生成对应的css bundle文件。

为了演示真实场景,创建模块createElement.js,让两个入口文件都依赖于该模块:

import style from "./createElement.scss";
export function createElement(className) {
    let element = document.createElement('div');
    element.style.height = "50px";
    element.classList.add(style[className]);
    element.style.marginBottom = "10px";
    element.innerHTML = "Hello World.";
    document.body.appendChild(element);
} 

以及对应的scss文件createElement.scss:

.blue {
    background-color: blue;
}

.yellow {
    background-color: yellow;
}

.green {
    background-color: green;
}

.gray {
    background-color:gray;
}


:global(.deepskyblue){
    background-color:deepskyblue;
}


:local(.fuchsia){
    background-color: fuchsia;
}

修改index.js文件,使其依赖于createElement.js:

import _ from 'lodash'
import './base.scss'
import './main.scss'
import './main2.scss'
import {createElement} from './createElement'

createElement('yellow');
createElement('blue');
createElement('green');

增加index2.html文件:

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Webpack4-CSS处理-index2</title>
</head>
<body>
</body>
</html>

相应地增加index2.js文件:

import _ from 'lodash'
import './main.scss'
import {createElement} from './createElement'

createElement('fuchsia');
createElement('gray');

再次运行cnpm run build,可以看到webpack为两个入口文件分别生成了css bundle文件。

合并css文件

有时我们需要将真个项目中所有生成的css合并为一个文件,此时可以使用splitChunks:

splitChunks: {
            cacheGroups: {
                default: false,
                vendors: false,
               
                styles: {
                    name: 'styles',
                    test: /\.(s*)css$/,
                    chunks: 'all',
                    enforce: true
                }
            }
        }

修改HtmlWebpackPlugin配置,使每个入口文件引用自己的bundle以及公共的styles chunk文件:

new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html',
            chunks: ['index', 'manifest', 'vendors', 'styles']
        }),
        new HtmlWebpackPlugin({
            template: './src/index2.html',
            filename: 'index2.html',
            chunks: ['index2', 'manifest', 'vendors', 'styles']
        }),

此时运行cnpm run build,可以看到只有一个styles.*.css文件生成。

在下一篇文章中,我们将讲到开发环境与chan


Original url: Access
Created at: 2019-05-05 16:58:54
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论