构建现代CSS工程环境「Webpack」

1. 前言

本文主要介绍在Webpack中如何使用CSS代码处理工具,以及这些工具的作用

2. Webpack

首先先来简单配置一下Webpack

js 复制代码
const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
        ]
    },
    plugins:[
        new CleanWebpackPlugin(),
    ]
}

CleanWebpack用来清理构建文件夹,这样就不用我们每次手动清理了

2.1 Webpack处理CSS资源

在Webpack中处理CSS文件,通常需要用到下面三个:

  • css-loader:该 Loader 会将 CSS 等价翻译为形如 module.exports = "${css}" 的JavaScript 代码,使得 Webpack 能够如同处理 JS 代码一样解析 CSS 内容与资源依赖
  • style-loader:该 Loader 将在产物中注入一系列 runtime 代码,这些代码会将 CSS 内容注入到页面的 <style> 标签,使得样式生效
  • mini-css-extract-plugin:该插件会将 CSS 代码抽离到单独的 .css 文件,并将文件通过 <link> 标签方式插入到页面中

2.1.1 css- loader

我们先从css-loader开始,css-loader提供了很多处理CSS代码的基础能力,包括 CSS 到 JS 转译、依赖解析、Sourcemap、css-in-module 等,基于这些能力,Webpack 才能像处理 JS 模块一样处理 CSS 模块代码

先创建一个src/index.css文件

css 复制代码
body{
    background-color: red;
}

首先需要安装依赖

bash 复制代码
pnpm install -D css-loader

之后在webpack.config.js配置文件中修改Webpack的配置,定义css文件的处理规则

js 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["css-loader"],
      },
    ],
  },
};

有了css-loader,我们在使用Webpack构建之后,经过css-loader处理后的样式代码最终会被转译成一段JS字符被放入最后的输出文件中

如下,我创建了一个src/index.js文件,并且引入了index.css

js 复制代码
import './index.css';
function foo() {
    console.log('foo');
}
foo()

运行webpack之后,构建文件夹如下:

src/index.css代码和src/index.js代码都在bundle.js文件当中

字段字符串会被当作普通JS模块处理,但是还不会影响到页面样式,如果想要它影响到页面样式,我们还需要继续接入style-loader

2.1.2 style-loader

与其他Loader不同,style-loader加载时并不会对代码内容做任何修改,而是简单注入一系列运行时代码 runtime,用于将css-loader转译出的JS字符串插入到运行页面的style标签中

首先也需要先安装依赖

bash 复制代码
pnpm install -D style-loader css-loader

之后修改Webpack中对样式文件的配置

js 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

注意style-loader在前,css-loader在后,因为Webpack执行时相当于style-loader(css-loader(css)) 链式调用,执行后样式代码会被转译为类似下面这样的代码:

js 复制代码
// Part1: css-loader 处理结果,对标到原始 CSS 代码
const __WEBPACK_DEFAULT_EXPORT__ = (
"body {\n    background: yellow;\n    font-weight: bold;\n}"
);
// Part2: style-loader 处理结果,将 CSS 代码注入到 `style` 标签
injectStylesIntoStyleTag(
 __WEBPACK_DEFAULT_EXPORT__
)

现在我们来测试一下style-loader + css-loader是否生效

我们需要使用html-webpack-plugin插件,这个插件默认将会在 output.path 的目录下创建一个 index.html 文件, 并把 webpack 打包后的静态文件自动插入到这个 html 文件当中

首先先来安装一下依赖

js 复制代码
pnpm install -D html-webpack-plugin

Webpack配置如下,我们添加了html-webpack-plugin 插件

js 复制代码
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    plugins:[
        new HtmlWebpackPlugin()
    ]
}

运行Webpack后发现构建文件夹如下所示,有了一个index.html文件

index.html文件中也自动引入了bundle.js文件

运行页面可以看到样式在页面中生效

body背景变成了我们编写的css样式的红色,控制台中也输出了foo字符串

经过css-loader + style-loader处理后的样式代码最终会被写入Bundle.js文件,并在运行时通过style标签注入到页面,最终将JS、CSS代码合并进同一个产物文件

但是这个方式会有两个问题

  • JS、CSS资源无法并行加载,从而降低页面性能
  • 资源缓存粒度变大,JS、CSS任意一种变更都会导致缓存失效

由此,在生产环境中我们通常会使用mini-css-extract-plugin插件替代style-loader,将样式代码抽离为单独的CSS文件

2.1.3 mini-css-extract-plugin

首先还是需要安装依赖

bash 复制代码
pnpm add -D mini-css-extract-plugin

之后添加Webpack的配置信息

js 复制代码
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HTMLWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    module: {
        rules: [{
            test: /\.css$/,
            use: [
                // 根据运行环境判断使用那个 loader
                (process.env.NODE_ENV === 'development' ?
                    'style-loader' :
                    MiniCssExtractPlugin.loader),
                'css-loader'
            ]
        }]
    },
    plugins: [
        new MiniCssExtractPlugin(),
        new HTMLWebpackPlugin()
    ]
}

这个时候运行Webpack就能发现css文件被单独抽离出来了,并且在html文件中被引用

运行一下页面,也是符合预期的效果

但是, 以下是几点需要注意的:

  • mini-css-extract-plugin库同时提供Loader、Plugin组件,需要同时使用
  • mini-css-extract-plugin不能与style-loader混用,否则报错
  • mini-css-extract-plugin需要与html-webpack-plugin同时使用,才能将产物路径以link标签方式插入到html中

生产环境使用mini-cssextract-plugin而不使用style-loader后,运行Webpack后将同时生成JS、CSS、HTML三种产物文件

总结,在Webpack中处理CSS,通常需要使用css-loader + style-loader或 css-loader + mini-css-extract-plugin 组合,两种方式最终都能实现加载样式代码的效果。

但鉴于原生 CSS 语言的种种缺陷,我们还可以在此基础上增加更多 Webpack 组件,更优雅、高效地编写页面样式,下面一一展开介绍。

2.2 CSS预处理器

原生 CSS 已经难以应对当代复杂 Web 应用的开发需求。为此,社区在 CSS 原生语法基础上扩展出一些更易用,功能更强大的 CSS 预处理器,下面我们以 Less 为例:

首先先安装依赖

bash 复制代码
pnpm install -D less less-loader

其次,修改Webpack配置,添加.less处理规则

js 复制代码
module.exports = {
    module: {
        rules: [{
            test: /\.less$/,
            use: [
                'style-loader',
                'css-loader',
                'less-loader'
            ]
        }]
    }
}

这里我们需要安装三种依赖,其中less-loader用于将less代码转换为css代码,转换为的css代码随后会被css-loader于style-loader处理,最终在页面生效

2.3 PostCSS

与上面的预处理器类似,PostCSS也能够在原生CSS基础之上增加更多的表达力、可维护性、可读性更强的语言特性。

两者主要区别在于预处理器通常定义了一套 CSS 之上的超集语言;PostCSS 并没有定义一门新的语言,而是与 @babel/core 类似,只是实现了一套将 CSS 源码解析为 AST 结构,并传入 PostCSS 插件做处理的流程框架,具体功能都由插件实现

先来说说PostCss的一些常用插件:

那么让我们来上手使用,依旧是先安装依赖

bash 复制代码
pnpm install -D postcss postcss-loader

之后添加Webpack配置

js 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          "style-loader", 
          "css-loader", 
          "postcss-loader"
        ],
      },
    ],
  }
};

不过,这个时候的 PostCSS 还只是个空壳,下一步还需要使用适当的 PostCSS 插件进行具体的功能处理,例如我们可以使用 autoprefixer 插件自动添加浏览器前缀,首先安装依赖

js 复制代码
pnpm install -D autoprefixer

之后修改Webpack配置

js 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          "style-loader", 
          {
            loader: "css-loader",            
            options: {
              importLoaders: 1
            }
          }, 
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                // 添加 autoprefixer 插件
                plugins: [require("autoprefixer")],
              },
            },
          }
        ],
      },
    ],
  }
};

之后,再次运行 Webpack 即可为 CSS 代码自动添加浏览器前缀,例如:

并且PostCSS与预处理器并非互斥关系,我们完全可以在同一个项目中同时使用两者

3.结尾

以上是笔者对一本掘金小册中内容的整理总结与实践,在输入和输出的过程中都学到了很多,感谢掘金🙏

相关推荐
顾尘眠2 小时前
http常用状态码(204,304, 404, 504,502)含义
前端
王先生技术栈3 小时前
思维导图,Android版本实现
java·前端
悠悠:)4 小时前
前端 动图方案
前端
星陈~4 小时前
检测electron打包文件 app.asar
前端·vue.js·electron
Aatroox4 小时前
基于 Nuxt3 + Obsidian 搭建个人博客
前端·node.js
每天都要进步哦4 小时前
Node.js中的fs模块:文件与目录操作(写入、读取、复制、移动、删除、重命名等)
前端·javascript·node.js
brzhang5 小时前
开源了一个 Super Copy Coder ,0 成本实现视觉搞转提示词,效率炸裂
前端·人工智能
diaobusi-886 小时前
HTML5-标签
前端·html·html5
我命由我123456 小时前
CesiumJS 案例 P34:场景视图(3D 视图、2D 视图)
前端·javascript·3d·前端框架·html·html5·js
就是蠢啊6 小时前
封装/前线修饰符/Idea项目结构/package/impore
java·服务器·前端