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

概述

  • 在开发 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.mzph.cn/web/34159.shtml

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

相关文章

Studio One 6.6.2中文破解版安装图文激活教程

Studio One 6.6.2中文破解版做为新生代音乐工作站&#xff0c;凭借更低的价格和完备的功能&#xff0c;获得了音乐人和直播行业工作者的青睐&#xff0c;尤其是对硬件声卡的适配支持更好&#xff0c;特别适合用来配合线上教学和电商带货。 最近网上出现不少关于StudioOne不能用…

html侧导航栏客服栏

ico 替换 ICO <html xmlns"http://www.w3.org/1999/xhtml"><head><meta http-equiv"Content-Type" content"text/html; charsetutf-8"><title>返回顶部</title><script src"js/jquery-2.0.3.min.js"…

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

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

VPS搭建论坛和社区网站有哪些优势?

想象一下&#xff1a;您已经建立了一个论坛。这是您的骄傲和喜悦——一个熙熙攘攘的在线多人广场&#xff0c;人们聚集在这里不仅可以聊天&#xff0c;还可以交流、分享和辩论。不过突然出现下面这些情况&#xff0c; 页面加载需要很长时间&#xff1b;光标不停旋转。会员流失…

【ai】tx2 nx: trition client安装nvidia-pyindex 一直失败

系统版本的pip和python虚拟环境的pipyolov4-triton-tensorrt的master分支 官方client jetson:pip3 install --user nvidia-pyindex 不成功啊 这个是让nvidia-pyindex 拉取nvidia@tx2-nx:~$ pip3 install --user nvidia-pyindex Collecting nvidia-pyindexDownloading https://…

STL——常用算法(二)

一、常用拷贝和替换算法 1.copy #include <iostream> #include <vector> #include <algorithm> using namespace std; void printVector(int val) {cout << val << " "; } void test01() {vector<int>v1;for (int i 0; i <…

【仿真】UR机器人相机标定、立体标定、手眼标定、视觉追踪(双目)

实现在CoppeliaSim环境中进行手眼标定和目标追踪的一个例子。它主要涉及到机器人、机器视觉和控制算法的编程&#xff0c;使用了Python语言。接下来对该代码的主要类和方法进行解析&#xff1a; 1. 导入相关库 用于与CoppeliaSim模拟器通过ZeroMQ接口通信。包含Rotation类&…

SF-YOLOv5——基于改进的特征融合模式的轻量级小目标检测算法

概述 提出了一种改进的YOLOv5模型&#xff0c;名为Small-Fast-YOLOv5 (SF-YOLOv5)&#xff0c;专门用于小目标检测。通过合理裁剪大目标检测层的特征图输出&#xff0c;显著减少了模型所需的计算资源&#xff0c;使模型变得更加轻量级。 还提出了一种改进的特征融合方法&#…

CV每日论文--2024.6.24

1、Whiteboard-of-Thought: Thinking Step-by-Step Across Modalities 中文标题&#xff1a;思维白板&#xff1a;跨模式逐步思考 简介&#xff1a;这段话描述了一种利用思维白板提示来增强大型语言模型在视觉推理任务中的性能的方法。 人类在解决需要视觉思考的问题时,通常会…

英语口语练习评测的开发实现

英语口语评测是一个复杂的系统&#xff0c;需要综合考虑语音识别、语音评测、自然语言处理等多种技术。以下是一些常见的英语口语评测开发框架。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 基于语音识别的口语评测框架这种框架主要…

第28课 绘制原理图——绘制导线

概述 放置完元器件之后&#xff0c;接着就要用导线将元器件的管脚一个一个连起来了。 绘制导线的方法 点击快速工具条上的“线”命令&#xff0c;进入绘制导线的过程。 点击选择某个管脚或电源端口&#xff0c;作为导线的起始端。 再点击选择另一个管脚或电源端口&#xff0c…

React@16.x(40)路由v5.x(5)常见应用场景(2)- 实现类似 vue 的路由模式

目录 1&#xff0c;vue-router2&#xff0c;React 模拟实现 1&#xff0c;vue-router vue 的路由配置文件&#xff0c; // src/router/index.ts const routes [{path: "/news",children: [{ path: "", component: NewsView },{ path: "detail"…

瞄准Windows的新兴僵尸网络:Kraken

2021 年 10 月&#xff0c;ZeroFox Intelligence 披露了名为 Kraken 的僵尸网络。Kraken 通过 SmokeLoader 进行传播&#xff0c;每次更新攻击基础设施时都会扩大规模。尽管与 2008 年发现的 Kraken 僵尸网络同名&#xff0c;但二者并没有其他共同点。 功能 自从 2021 年 10 …

养殖自动化通风系统:未来畜牧业的智能守护者

在现代畜牧业中&#xff0c;养殖环境的控制对于动物的健康成长和养殖业者的成功至关重要。养殖自动化通风系统凭借其先进技术和精密设备&#xff0c;实现了对养殖场内环境条件的精准调控&#xff0c;显著提升了养殖效率和动物福利。本文将深入探讨养殖自动化通风系统的必要性、…

Talking Web

1. curl 1.1 http curl http://127.0.0.1:80 向目标主机端口发送http请求 1.2 httphead curl -H “Host: 18ed3df584cd48328b5839443aa7b42b” http://127.0.0.1:80 1.3 httppath curl http://127.0.0.1:80/853c64cd218f80d0a59665666fb2ab80 1.4 URL编码路径 &#xff0…

天天大爆推文玩法营销号软件下载地址

天天大爆推文玩法营销号软件下载地址 天天大爆推文玩法营销号是一种营销策略&#xff0c;通过在社交媒体上发布频繁的推文来吸引关注和增加品牌曝光度。以下是一些可以用于天天大爆推文玩法营销号的策略&#xff1a; 营销号工具使用教程&#xff1a;https://iimenvrieak.feis…

数据结构之前缀树

写在前面 源码 。 前缀树&#xff0c;又叫做trie树&#xff0c;字典树&#xff0c;是一种多叉的树&#xff0c;一般用于单词前缀匹配的相关场景中&#xff0c;比如&#xff1a; 本文看下使用Java如何来实现这种数据结构。 1&#xff1a;基本介绍 思想&#xff1a;空间换时…

C++基础编程100题-014 OpenJudge-1.3-12 计算球的体积

更多资源请关注纽扣编程微信公众号 http://noi.openjudge.cn/ch0103/12/ 描述 对于半径为r的球&#xff0c;其体积的计算公式为$ V4/3*πr^3 $&#xff0c;这里取π 3.14。 现给定r&#xff0c;求V。 输入 输入为一个不超过100的非负实数&#xff0c;即球半径&#xff0…

FL Studio 21 集成了音频剪辑和增益控制,你可以更快、更精确地控制音频电平,包括自动交叉淡入淡出及更多功能

以通过控制色调、饱和度、亮度、文本、仪表和步进序列器的颜色来改变你的DAW外观&#xff0c; DAW“情绪主题”控制&#xff0c;水果编曲将变得与众不同。 更快的音频编辑 FL Studio 21 集成了音频剪辑包络和增益控制&#xff0c;你可以更快、更精确地控制音频电平&#xff0c…

aws的alb,多个域名绑定多个网站实践

例如首次创建的alb负载均衡只有www.xxx.com 需要添加 负载 test2.xxx.com aws的Route 53产品解析到负载均衡 www.xxx.com 添加CNAME&#xff0c;到负载均衡的dns字段axx test2.xxx.com 添加CNAME&#xff0c;到负载均衡的dns字段axx 主要介绍目标组和规则 创建alb就不介…