Webpack: 如何借助预处理器、PostCSS 等构建现代 CSS 工程环境

news2024/11/23 15:50:00

概述

  • 在开发 Web 应用时,我们通常需要编写大量 JavaScript 代码 —— 用于控制页面逻辑;编写大量 CSS 代码 —— 用于调整页面呈现形式。问题在于,CSS 语言在过去若干年中一直在追求样式表现力方面的提升,工程化能力薄弱,例如缺乏成熟的模块化机制、依赖处理能力、逻辑判断能力等。为此,在开发现代大型 Web 应用时,通常会使用 Webpack 配合其它预处理器编写样式代码

  • 我们看下Webpack 中如何使用 CSS 代码处理工具,包括:

    • 如何使用 css-loaderstyle-loadermini-css-extract-plugin 处理原生 CSS 文件?
    • 如何使用 Less/Sass/Stylus 预处理器?
    • 如何使用 PostCSS ?

Webpack 如何处理 CSS 资源?

  • 原生 Webpack 并不能识别 CSS 语法,假如不做额外配置直接导入 .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> 标签方式插入到页面中。

  • PS:当 Webpack 版本低于 5.0 时,请使用 extract-text-webpack-plugin 代替 mini-css-extract-plugin

  • 三种组件各司其职:css-loader 让 Webpack 能够正确理解 CSS 代码、分析资源依赖;style-loadermini-css-extract-plugin 则通过适当方式将 CSS 插入到页面,对页面样式产生影响:

  • 下面我们先从 css-loader 聊起,css-loader 提供了很多处理 CSS 代码的基础能力,包括 CSS 到 JS 转译、依赖解析、Sourcemap、css-in-module 等,基于这些能力,Webpack 才能像处理 JS 模块一样处理 CSS 模块代码。接入时首先需要安装依赖:$ yarn add -D css-loader

  • 之后修改 Webpack 配置,定义 .css 规则:

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

此后,执行 npx webpack 或其它构建命令即可。经过 css-loader 处理后,样式代码最终会被转译成一段 JS 字符串:

源码转译后
.main-hd { font-size: 10px; } //... var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default())); // Module ___CSS_LOADER_EXPORT___.push([ module.id, ".main-hd {\n font-size: 10px;\n}", "" ]); // Exports /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); //...
  • 但这段字符串只是被当作普通 JS 模块处理,并不会实际影响到页面样式,后续还需要:

    • 开发环境:使用 style-loader 将样式代码注入到页面 <style> 标签;
    • 生产环境:使用 mini-css-extract-plugin 将样式代码抽离到单独产物文件,并以 <link> 标签方式引入到页面中。
  • 经过 css-loader 处理后,CSS 代码会被转译为等价 JS 字符串,但这些字符串还不会对页面样式产生影响,需要继续接入 style-loader 加载器。

  • 与其它 Loader 不同,style-loader 并不会对代码内容做任何修改,而是简单注入一系列运行时代码,用于将 css-loader 转译出的 JS 字符串插入到页面的 style 标签。接入时同样需要安装依赖:$ yarn add -D style-loader css-loader

  • 之后修改 Webpack 配置,定义 .css 规则:

    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/i,
            use: ["style-loader", "css-loader"],
          },
        ],
      },
    };
    
  • PS:注意保持 style-loader 在前,css-loader 在后

  • 上述配置语义上相当于 style-loader(css-loader(css)) 链式调用,执行后样式代码会被转译为类似下面这样的代码:

    // 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__
    )
    

至此,运行页面触发 injectStylesIntoStyleTag 函数将 CSS 代码注入到 <style> 标签,样式才真正开始生效。例如:

index.cssindex.js
body { background: yellow; font-weight: bold; } import './index.css'; const node = document.createElement('span'); node.textContent = 'Hello world'; document.body.appendChild(node);

页面运行效果:

image.png

  • 经过 style-loader + css-loader 处理后,样式代码最终会被写入 Bundle 文件,并在运行时通过 style 标签注入到页面。这种将 JS、CSS 代码合并进同一个产物文件的方式有几个问题:
    • JS、CSS 资源无法并行加载,从而降低页面性能;
    • 资源缓存粒度变大,JS、CSS 任意一种变更都会致使缓存失效。
  • 因此,生产环境中通常会用 mini-css-extract-plugin 插件替代 style-loader,将样式代码抽离成单独的 CSS 文件。使用时,首先需要安装依赖:$ yarn add -D mini-css-extract-plugin
  • 之后,添加配置信息:
    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()
        ]
    }
    

这里需要注意几个点:

  • mini-css-extract-plugin 库同时提供 Loader、Plugin 组件,需要同时使用

  • mini-css-extract-plugin 不能与 style-loader 混用,否则报错,所以上述示例中第 9 行需要判断 process.env.NODE_ENV 环境变量决定使用那个 Loader

  • mini-css-extract-plugin 需要与 html-webpack-plugin 同时使用,才能将产物路径以 link 标签方式插入到 html 中

  • 至此,运行 Webpack 后将同时生成 JS、CSS、HTML 三种产物文件,如:

index.cssindex.js
body { background: yellow; font-weight: bold; } import './index.css'; const node = document.createElement('span'); node.textContent = 'Hello world'; document.body.appendChild(node);
产物 main.css外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传产物 main.js外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
产物 index.html<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Webpack App</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script defer src="main.js"></script> <link href="main.css" rel="stylesheet"> </head> <body> </body> </html>
  • 可以看到,样式代码会被抽离到单独的 CSS 文件,并且在最终生成的 html 中包含了指向 JS、CSS 两种资源的标签:

    <script defer src="main.js"></script>
    <link href="main.css" rel="stylesheet">
    
  • 综上,在 Webpack 中处理 CSS,通常需要使用 css-loader + style-loadercss-loader + mini-css-extract-plugin 组合,两种方式最终都能实现加载样式代码的效果。但鉴于原生 CSS 语言的种种缺陷,我们还可以在此基础上增加更多 Webpack 组件,更优雅、高效地编写页面样式,下面一一展开介绍。

使用预处理器

  • CSS —— Cascading Style Sheet-级联样式表,最初设计用于描述 Web 界面样式的描述性语言,经过这么多年的发展其样式表现力已经突飞猛进,但核心功能、基本语法没有发生太大变化,至今依然没有提供诸如循环、分支判断、扩展复用、函数、嵌套之类的特性,以至于原生 CSS 已经难以应对当代复杂 Web 应用的开发需求。

  • 为此,社区在 CSS 原生语法基础上扩展出一些更易用,功能更强大的 CSS 预处理器(Preprocessor),比较知名的有 Less、Sass、Stylus 。这些工具各有侧重,但都在 CSS 之上补充了扩展了一些逻辑判断、数学运算、嵌套封装等特性,基于这些特性,我们能写出复用性、可读性、可维护性更强,条理与结构更清晰的样式代码,以 Less 为例:

    // 变量
    @size: 12px;
    @color: #006633;
    
    // 混合
    .mx-bordered() {
        border: 1px solid #000;
    }
    
    // 嵌套
    body {
        // 函数计算
        background: spin(lighten(@color, 25%), 8);
        font-weight: bold;
        padding: @size;
    
        .main {
            // 数学运算
            font-size: @size * 2;
            .mx-bordered;
            color: darken(@color, 10%);
            padding: @size * 0.6;
        }
    }
    
  • 在 Webpack 中只需使用适当 Loader 即可接入预处理器,以 Less 为例,首先安装依赖:$ yarn add -D less less-loader

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

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

可以看到这里需要同时使用三种 Loader,其中 less-loader 用于将 Less 代码转换为 CSS 代码,上述示例转换结果:

.less 源码less-loader 处理结果:
// 变量 @size: 12px; @color: #006633; // 混合 .mx-bordered() { border: 1px solid #000; } // 嵌套 body { // 函数计算 background: spin(lighten(@color, 25%), 8); font-weight: bold; padding: @size; .main { // 运算 font-size: @size * 2; .mx-bordered; color: darken(@color, 10%); padding: @size * 0.6; } } body { background: #00e691; font-weight: bold; padding: 12px; } body .main { font-size: 24px; border: 1px solid #000; color: #00331a; padding: 7.2px; }

这段 CSS 随后会被 css-loaderstyle-loader 处理,最终在页面生效。

目前,社区比较流行的预处理器框架有:Less、Sass、Stylus,它们接入 Webpack 的方式非常相似:

LessSassStylus
安装依赖yarn add -D less less-loader yarn add -D sass sass-loader yarn add -D stylus stylus-loader
添加配置module.exports = { module: { rules: [ { test: /\.less$/, use: [ "style-loader", "css-loader", "less-loader" ], }, ], } }; module.exports = { module: { rules: [ { test: /\.s(ac)ss$/, use: [ "style-loader", "css-loader", "sass-loader" ], }, ], } }; module.exports = { module: { rules: [ { test: /\.styl$/, use: [ "style-loader", "css-loader", "stylus-loader" ], }, ], } };
  • 大家可根据项目背景选择接入适当的预处理器框架

使用 post-css

  • 与上面介绍的 Less/Sass/Stylus 这一类预处理器类似,PostCSS 也能在原生 CSS 基础上增加更多表达力、可维护性、可读性更强的语言特性。两者主要区别在于预处理器通常定义了一套 CSS 之上的超集语言;PostCSS 并没有定义一门新的语言,而是与 @babel/core 类似,只是实现了一套将 CSS 源码解析为 AST 结构,并传入 PostCSS 插件做处理的流程框架,具体功能都由插件实现。

  • 预处理器之于 CSS,就像 TypeScript 与 JavaScript 的关系;而 PostCSS 之于 CSS,则更像 Babel 与 JavaScript。

  • PostCSS 的接入步骤也很简单,首先安装依赖:$ yarn add -D postcss postcss-loader

  • 之后添加 Webpack 配置:

    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [
              "style-loader", 
              "css-loader", 
              "postcss-loader"
            ],
          },
        ],
      }
    };
    
  • 不过,这个时候的 PostCSS 还只是个空壳,下一步还需要使用适当的 PostCSS 插件进行具体的功能处理,例如我们可以使用 autoprefixer 插件自动添加浏览器前缀,首先安装依赖:$ yarn add -D autoprefixer

  • 之后,修改 Webpack 配置:

    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 代码自动添加浏览器前缀,例如:

CSS 源码PostCSS 处理结果
::placeholder { color: gray; } ::-moz-placeholder { color: gray; } :-ms-input-placeholder { color: gray; } ::placeholder { color: gray; }

此外,还可以选择将 PostCSS 相关配置抽离保存到 postcss.config.js 文件:

postcss.config.jswebpack.config.js
module.exports = { plugins: [ require("autoprefixer") ], }; module.exports = { module: { rules: [ { test: /\.css$/, use: [ "style-loader", { loader: "css-loader", options: { importLoaders: 1 } }, "postcss-loader" ], }, ], } };
  • 值得一提的是,PostCSS 与预处理器并非互斥关系,我们完全可以在同一个项目中同时使用两者,例如:

    module.exports = {
      module: {
        rules: [
          {
            test: /\.less$/,
            use: [
              "style-loader", 
              {
                loader: "css-loader",            
                options: {
                  importLoaders: 1
                }
              }, 
              "postcss-loader",
              "less-loader"
            ],
          },
        ],
      }
    };
    
  • 基于这一特性,我们既能复用预处理语法特性,又能应用 PostCSS 丰富的插件能力处理诸如雪碧图、浏览器前缀等问题。

  • PostCSS 最大的优势在于其简单、易用、丰富的插件生态,基本上已经能够覆盖样式开发的方方面面。实践中,经常使用的插件有:

    • autoprefixer:基于 Can I Use 网站上的数据,自动添加浏览器前缀
    • postcss-preset-env:一款将最新 CSS 语言特性转译为兼容性更佳的低版本代码的插件
    • postcss-less:兼容 Less 语法的 PostCSS 插件,类似的还有:postcss-sass、poststylus
    • stylelint:一个现代 CSS 代码风格检查器,能够帮助识别样式代码中的异常或风格问题

总结

  • 本文介绍 css-loaderstyle-loadermini-css-extract-pluginless-loaderpostcss-loader 等组件的功能特点与接入方法,内容有点多,重点在于:

  • Webpack 不能理解 CSS 代码,所以需要使用 css-loaderstyle-loadermini-css-extract-plugin 三种组件处理样式资源;

  • Less/Sass/Stylus/PostCSS 等工具可弥补原生 CSS 语言层面的诸多功能缺失,例如数值运算、嵌套、代码复用等

  • 这些工具几乎已经成为现代 Web 应用开发的标配,能够帮助我们写出更清晰简洁、可复用的样式代码,帮助我们解决诸多与样式有关的工程化问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1862541.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录算法训练营第三十四天|56. 合并区间、738.单调递增的数字、968.监控二叉树

56. 合并区间 题目链接&#xff1a;56. 合并区间 文档讲解&#xff1a;代码随想录 状态&#xff1a;无语&#xff0c;这题从右边界排序做不了&#xff01; 思路&#xff1a; 排序&#xff1a;按照区间的起始位置进行排序&#xff0c;这样后面处理时可以顺序合并重叠区间。合并…

【zabbix】zabbix 自动发现与自动注册、proxy代理

1、配置zabbix自动发现&#xff0c;要求发现的主机不低于2台 zabbix 自动发现&#xff08;对于 agent2 是被动模式&#xff09; zabbix server 主动的去发现所有的客户端&#xff0c;然后将客户端的信息登记在服务端上。 缺点是如果定义的网段中的主机数量多&#xff0c;zabbi…

YOLOv8改进 | 主干网络| 可变形卷积网络C2f_DCN【CVPR2017】

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录&#xff1a;《YOLOv8改进有效涨点》专栏介绍 & 专栏目录 | 目前已有40篇内容&#xff0c;内含各种Head检测头、损失函数Loss、B…

基于 ESP8266 和 MQ 气体传感器的微信告警系统设计与实现

接线: ESP8266MQ3vVCCGND GND A0 A0微信通知截图: 摘要:本文主要探讨了一种利用 ESP8266 微控制器与 MQ 气体传感器构建的气体检测微信告警系统。详细阐述了系统的硬件组成、软件设计以及与微信平台的交互机制。通过该系统,能够实时监测环境中的气…

vxe-表尾单元格进行合并后更改其表尾背景颜色

1.场景 在vxe-table的官网API中可以使用footer-cell-class-name给单元格添加背景颜色或者其他样式&#xff0c;但是本人场景进行了表尾合并的操作&#xff1b;参考API进行更改背景颜色失败&#xff1b; 2.解决 利用表尾css类名的区别&#xff0c;用子类选择器进行对应的选择设…

mulesoft --环境安装与搭建

1.mavenjdkpostman 2.anypoint statdio 下载安装 下载 Anypoint Studio & Mule |骡子软件 (mulesoft.com) 填好基本信息后&#xff0c;会发邮件&#xff0c;在邮件中下载&#xff0c;跳到官网下载 3注册账号 Download Anypoint Studio & Mule | MuleSoft 4.Connect…

StarRocks 存算分离成本优化最佳实践

序言 StarRocks 存算分离借助对象存储来实现计算和存储能力分离&#xff0c;而存算分离版本 StarRocks 一般来说有以下三方面成本&#xff1a; 计算成本&#xff0c;也即机器使用成本&#xff0c;尤其是运行在公有云上时存储成本&#xff0c;该部分与对象存储上存储的数据量相…

埃特巴什码加解密小程序

埃特巴什码加解密小程序 这几天在看CTF相关的课程&#xff0c;涉及到古典密码学和近代密码学还有现代密码学。 简单替换密码 Atbash Cipher 埃特巴什码(Atbash Cipher)其实可以视为下面要介绍的简单替换密码的特例&#xff0c;它使用字母表中的最后 一个字母代表第一个字母…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(十七)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 27节&#xff09; P27《26.Stage模型-UIAbility的启动模式》 本节讲解 UIAbility的启动模式&#xff1a;Stage模型的应用&#x…

语言的数据结构:树与二叉树(二叉树篇)

语言的数据结构&#xff1a;树与二叉树&#xff08;二叉树篇&#xff09; 前言概念特别的二叉树满二叉树完全二叉树 存储结构顺序存储链式存储 查找方式 前言 上文说到了树&#xff0c;有人认为二叉树是树的每一个分支都有两个子节点。其实这也对。但二叉树在此基础上还做了限…

支持离线翻译任意语言的桌面应用程序;单张图像高效生成高质量的 3D 模型;2500种色彩映射的集合,适用于matplotlib和seaborn

✨ 1: Lingo Lingo是一款支持离线翻译任意语言的桌面应用程序 Lingo 是一款支持离线翻译的桌面应用程序&#xff0c;用户可以在不连接互联网的情况下进行多语言翻译。这款软件利用了Meta公司提供的nllb-200-distilled-600M 多语言模型&#xff0c;以实现高效的翻译功能。 没…

jupyter notebook的markdown语法不起作用

在这个界面编辑&#xff0c;发现markdown你编辑的是什么就是什么&#xff0c;不起作用&#xff0c;然而点一下&#xff1a; 右上角“Notebook转发”&#xff0c;就会单独跳出一个jupyter notebook的界面&#xff0c;此时就会奏效&#xff1a;

6.25作业

1.整理思维导图 2.终端输入两个数&#xff0c;判断两数是否相等&#xff0c;如果不相等&#xff0c;判断大小关系 #!/bin/bash read num1 read num2 if [ $num1 -eq $num2 ] then echo num1num2 elif [ $num1 -gt $num2 ] then echo "num1>num2" else echo &quo…

大学生搜题神器网站?分享七个支持答案和解析的工具 #职场发展#学习方法

在现代科技的帮助下&#xff0c;大学生们有幸能够利用各种日常学习工具来提升自己的学习效果。 1.全球翻译官 是一款在线翻译语言的服务平台&#xff0c;在app中&#xff0c;用户能够在线通过语音,拍照来翻译语言&#xff0c;非常的便捷&#xff0c;也支持文字翻译哦 全球翻…

SDA626 3A,4.5V-16V输入,500kHz同步降压型转换器

一般说明 该SDA626是一个完全集成&#xff0c;高效率的3A同步整流降压转换器。该SDA626工作在一个宽的输出电流负载范围高效率该设备提供两种工作模式&#xff0c;PWM控制和PFM模式切换控制&#xff0c;这使得在更广泛的负载范围内具有较高的效率。 SDA626需要最…

浦语·灵笔2 模型部署图片理解实战

效果图镇楼 1、使用 huggingface_hub 下载模型中的部分文件&#xff08;演示练习与模型实战无关&#xff09; 使用 Hugging Face 官方提供的 huggingface-cli 命令行工具。安装依赖: pip install -U huggingface_hub 然后新建 python 文件&#xff0c;填入以下代码&#xf…

Ubuntu20.04使用Samba

目录 一、Samba介绍 Samba 的主要功能 二、启动samba 三、主机操作 四、Ubuntu与windows系统中文件互联 五、修改samba路径 一、Samba介绍 Samba 是一个开源软件套件&#xff0c;用于在 Linux 和 Unix 系统上实现 SMB&#xff08;Server Message Block&#xff09;协议…

iptables(4)规则匹配条件(源、目、协议、接口、端口)

简介 前面我们已经介绍了iptables的基本原理,表、链,数据包处理流程。如何查询各种表的信息。还有基本的增、删、改、保存的基础操作。 经过前文介绍,我们已经能够熟练的管理规则了,但是我们只使用过一种匹配条件,就是将”源地址”作为匹配条件。那么这篇文章中,我们就来…

为什么小程序每次提交会有很多unpackage下的文件

解决办法.gitignore 文件 .DS_Store /unpackage# local env files .env.local .env.*.local# Log files npm-debug.log* yarn-debug.log* yarn-error.log*# Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw?

获取 S 参数仿真中属性所对应的值

获取 S 参数仿真中属性所对应的值 引言正文对 S41 参数进行获取方法一方法二方法三引言 应该有很多小伙伴有过困惑,在进行 S 参数扫描后,想要可视化一下对应参数的图像,每次手动点击一个是比较麻烦,另一个是显示的效果并不是很美观。此时,如果我们可以使用 Lumerical 脚本…