Skip to content

webpack的样式处理

分离样式文件

让我们从最简单的情况说起——处理工程中的纯 CSS。style-loader 与 css-loader,通过 JS 引用 CSS 的方式打包样式,可以更清晰地描述模块间的依赖关系。

然而,当时还有一个问题没有解决,我们是通过附加 style 标签的方式引入样式的,那么如何输出单独的 CSS 文件呢?一般来说,在生产环境下,我们希望样式存在于 CSS 文件中而不是 style 标签中,因为文件更有利于客户端进行缓存。Webpack 社区有专门的插件:extract-text-webpack-plugin(适用于 Webpack 4 之前版本)和 mini-css-extract-plugin(适用于 Webpack 4 及以上版本),它们就是专门用于提取样式到 CSS 文件的。

extract-text-webpack-plugin

我们先通过一个简单的例子来直观认识该插件是如何工作的。使用 npm 安装:

sh
npm install extract-text-webpack-plugin

在 webpack.config.js 中引入:

js
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
    entry: './app.js',
    output: {
        filename: 'bundle.js',
    },
    mode: 'development',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader'
                    use: 'css-loader',
                }),
            }
        ],
    },
    plugins: [
        new ExtractTextPlugin("bundle.css")
    ],
};

在 module.rules 中我们设置了处理 CSS 文件的规则,其中的 use 字段并没有直接传入 loader,而是使用了插件的 extract 方法包了一层。内部的 fallback 属性用于指定当插件无法提取样式时所采用的 loader,use(extract 方法里面的)用于指定在提取样式之前采用哪些 loader 来预先进行处理。除此之外,还要在 Webpack 的 plugins 配置中添加该插件,并传入提取后的资源文件名。

plugins 用于接收一个插件数组,我们可以使用 Webpack 内部提供的一些插件,也可以加载外部插件。Webpack 为插件提供了各种 API,使其可以在打包的各个环节中添加一些额外任务,就像 extract-text-webpack-plugin 所实现的样式提取一样。

多样式文件的处理

样式的提取是以资源入口开始的整个 chunk 为单位的。假设我们的应用从 index.js 开始一层层引入了几百个模块,也许其中很多模块都引入了各自的样式,但是最终只会生成一个 CSS 文件,因为它们都来自同一个入口模块。

上面我们将 bundle.css 作为文件名传给了 extract-text-webpack-plugin,但当工程有多个入口时就会发生重名问题。就像在前面的章节中我们配置动态的 output.filename 一样,这里我们也要对插件提取的 CSS 文件使用类似模板的命名方式。

js
// webpack.config.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    ···
    plugins: [
        new ExtractTextPlugin('[name].css')
    ],
};

mini-css-extract-plugin

mini-css-extract-plugin 可以理解成 extract-text-webpack-plugin 的升级版,它拥有更丰富的特性和更好的性能,从 Webpack 4 开始官方推荐使用该插件进行样式提取(Webpack 4 以前的版本是用不了的)。

说到 mini-css-extract-plugin 的特性,最重要的就是它支持按需加载 CSS,以前在使用 extract-text-webpack-plugin 的时候我们是做不到这一点的。举个例子,a.js 通过 import()函数异步加载了 b.js,b.js 里面加载了 style.css,那么 style.css 最终只能被同步加载(通过 HTML 的 link 标签)。但是现在 mini-css-extract-plugin 会单独打包出一个 0.css(假设使用默认配置),这个 CSS 文件将由 a.js 通过动态插入 link 标签的方式加载。

请看下面的例子:

js
// app.js
import './style.css';
import('./next-page');
document.write('app.js<br/>');

// next-page.js
import './next-page.css';
document.write('Next page.<br/>');

/* style.css */
body { background-color: #eee; }

/* next-page.css */
body { background-color: #999; }

// webpack.config.js
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'
      ],
    }],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
    })
  ]
};”

在配置上 mini-css-extract-plugin 与 extract-text-webpack-plugin 有以下几点不同:

  • loader 规则设置的形式不同,并且 mini-css-extract-plugin 支持配置 publicPath,用来指定异步 CSS 的加载路径。
  • 不需要设置 fallback。
  • 在 plugins 设置中,除了指定同步加载的 CSS 资源名(filename),还要指定异步加载的 CSS 资源名(chunkFilename

样式预处理

样式预处理指的是在开发中我们经常会使用一些样式预编译语言,如 SCSS、Less 等,在项目打包过程中再将这些预编译语言转换为 CSS。借助这些语言强大和便捷的特性,可以降低项目的开发和维护成本。

PostCSS

严格说来,PostCSS 并不能算是一个 CSS 的预编译器,它只是一个编译插件的容器。它的工作模式是接收样式源代码并交由编译插件处理,最后输出 CSS。开发者可以自己指定使用哪些插件来实现特定的功能。

  • postcss-loader 可以将 PostCss 和 webpack 连接起来
  • 可以于 Autoprefixer 连接起来实现自动前缀,添加支持的特性和需要兼容的浏览器。
  • stylelint 是一个 CSS 质量检测工具
  • CSSNext 从而使用最新的 CSS 特性

CSS Modules

CSS Modules 是近年来比较流行的一种开发模式,其理念就是把 CSS 模块化,让 CSS 也拥有模块的特点,具体如下:

  • 每个 CSS 文件中的样式都拥有单独的作用域,不会和外界发生命名冲突。
  • 对 CSS 进行依赖管理,可以通过相对路径引入 CSS 文件。
  • 可以通过 composes 轻松复用其他 CSS 模块。 使用 CSS Modules 不需要额外安装模块,只要开启 css-loader 中的 modules 配置项即可。

备案号:闽ICP备2024028309号-1