Vue CLI 中的 CSS 处理完全指南:预处理器、PostCSS 与 CSS Modules 配置实战
【免费下载链接】vue-cli🛠️ webpack-based tooling for Vue.js Development项目地址: https://gitcode.com/gh_mirrors/vu/vue-cli
本指南以 Vue CLI 官方文档 docs/guide/css.md 为核心脉络,系统讲解 Vue CLI 项目中样式处理的完整方案:从 CSS 中引用静态资源的路径规则,到 Sass/Less/Stylus 预处理器的安装与配置、PostCSS 与 Autoprefixer 的默认行为、CSS Modules 的三种使用方式,以及通过css.loaderOptions向各样式 loader 精确传递参数。读完本文,你将能够在 Vue CLI 项目中熟练配置样式编译链路,并理解这些配置在 cli-service 的 css 配置源码 中的底层作用机制。
概览:开箱即用的 CSS 能力
Vue CLI 项目天生支持三种样式技术栈:
- PostCSS:Vue CLI 内部集成了 PostCSS 作为样式后处理管线;
- CSS Modules:通过
<style module>与.module.css文件约定,开箱即用; - 预处理器:Sass、Less、Stylus 三种主流预处理器均内置了 webpack 规则支持。
这一点在创建项目时的交互提示中也有体现。查看 cssPreprocessors 提示模块 可以发现,其提示文案明确写着"PostCSS, Autoprefixer and CSS Modules are supported by default",即 PostCSS、Autoprefixer 与 CSS Modules 默认支持,用户只需在"CSS Pre-processors"特性中选择是否启用 Sass/SCSS、Less 或 Stylus。
在 CSS 中引用静态资源
所有编译后的 CSS 都会经过 css-loader 处理,其中的url()引用会被解析为模块请求。这意味着你可以基于本地文件结构,使用相对路径引用静态资源:
/* 相对路径,基于当前文件位置 */ .logo { background: url('../assets/logo.png'); }特别注意~前缀的用法:如果希望引用 npm 依赖包内部的文件,或者通过 webpack alias 引用的路径,必须在路径前加上~前缀以避免歧义:
/* 引用 npm 包内的资源 */ .icon { background: url('~some-package/icons/foo.png'); } /* 引用 webpack alias(如 @ 指向 src/)下的资源 */ .bg { background: url('~@/assets/bg.png'); }关于 URL 的完整转换规则,请参见 HTML 与静态资源处理 文档中的 URL Transform Rules 小节,其核心规则可概括为:
- 以绝对路径(如
/images/foo.png)开头的 URL 原样保留; - 以
.开头的 URL 被当作相对模块请求,按文件系统目录结构解析; - 以
~开头的 URL 其后内容被当作模块请求解析; - 以
@开头的 URL 同样被当作模块请求(Vue CLI 默认将@别名为<projectRoot>/src,仅限模板中使用)。
在该小节中还可看到,Vue CLI 内部使用 webpack 的 Asset Modules 决定最终文件的存放位置(带内容哈希与正确的 public base path),并将小于 8KiB 的资源自动内联为 Data URL,从而减少 HTTP 请求数;如需调整该阈值,可通过chainWebpack修改images规则的dataUrlCondition.maxSize。
预处理器:Sass / Less / Stylus
创建项目时选择
你可以在使用vue create创建项目时选择预处理器。查看 cssPreprocessors 提示模块 源码可见其提供的选项为:Sass/SCSS (with dart-sass)、Less、Stylus。选择结果会写入项目选项cssPreprocessor,随后由 cli 生成器 自动向devDependencies注入对应依赖,例如选择 Sass 会得到sass与sass-loader,选择 Less 会得到less与less-loader,选择 Stylus 会得到stylus与stylus-loader。
手动安装 loader
即使创建项目时没有选择预处理器,cli-service 内置的 webpack 配置依然预配置好了对 Sass/Less/Stylus 的全部处理规则,你只需手动安装对应的 webpack loader 即可:
# Sass npm install -D sass-loader sass # Less npm install -D less-loader less # Stylus npm install -D stylus-loader stylus从 css 配置源码 可以看到,createCSSRule为每种语言都注册了独立规则:scss/sass对应sass-loader,less对应less-loader,stylus对应stylus-loader,且.sass规则会强制注入sassOptions: { indentedSyntax: true }以启用缩进语法。
安装完成后,即可直接导入对应文件类型,或在*.vue单文件组件中通过lang属性使用:
<style lang="scss"> $color: red; </style>webpack 4 兼容性说明
当前仓库为 Vue CLI 5(默认基于 webpack 5),但如果你仍在使用 webpack 4(Vue CLI 4 的默认版本),必须确保 loader 与其兼容,否则会遇到 peer dependencies 冲突报错。此时可安装仍兼容 webpack 4 的旧版 loader:
# Sass(webpack 4 环境) npm install -D sass-loader@^10 sassDart Sass 性能提示
使用 Dart Sass 时,同步编译默认比异步编译快两倍,原因是异步回调存在额外开销。为避免该开销,可安装fibers包,让异步 importer 走同步代码路径:
npm install -D fibers需要留意的是,fibers是原生模块,在不同操作系统与构建环境下可能存在兼容性问题。遇到问题时,执行npm uninstall -D fibers即可恢复。
自动化导入全局样式(变量、颜色、mixin)
如果你希望在每个单文件组件和每个预处理器样式文件中自动导入公共样式(如颜色、变量、mixin),推荐使用style-resources-loader。以下示例针对 Stylus,在每个 SFC 和每个 Stylus 文件中自动导入./src/styles/imports.styl:
// vue.config.js const path = require('path') module.exports = { chainWebpack: config => { const types = ['vue-modules', 'vue', 'normal-modules', 'normal'] types.forEach(type => addStyleResource(config.module.rule('stylus').oneOf(type))) }, } function addStyleResource (rule) { rule.use('style-resource') .loader('style-resources-loader') .options({ patterns: [ path.resolve(__dirname, './src/styles/imports.styl'), ], }) }这里的types数组对应 cli-service 为每种样式语言生成的四条规则分支(详见下文 CSS 规则结构),因此需要逐一挂载。除此之外,也可以直接使用社区插件vue-cli-plugin-style-resources-loader免去手写配置。
PostCSS
Vue CLI 内部使用了 PostCSS,这意味着你无需任何额外配置即可获得 PostCSS 处理能力。
配置方式
可以通过以下两种方式配置 PostCSS:
- 配置文件:
.postcssrc或任何被 postcss-load-config 支持的配置源(如postcss.config.js、.postcssrc.js、.postcssrc.json等); vue.config.js:通过css.loaderOptions.postcss配置 postcss-loader。
从 css 配置源码 可知,cli-service 会检测项目是否存在合法的 PostCSS 配置(loaderOptions.postcss、pkg.postcss或上述配置文件)。若不存在,则自动注入默认配置:使用autoprefixer插件。若存在自定义配置,则用户的配置优先,与默认的 Autoprefixer 互不干扰——这一点也被 css.spec.js 中的override postcss config测试用例验证。
Autoprefixer 与浏览器目标
Autoprefixer 插件默认开启。要配置目标浏览器,请使用package.json中的browserslist字段(或独立的.browserslistrc文件)。该字段同时驱动@babel/preset-env的 JS 转译与 Autoprefixer 的 CSS 前缀生成,详见 浏览器兼容性文档 的 browserslist 小节。
{ "browserslist": [ "> 1%", "last 2 versions", "not dead" ] }关于厂商前缀规则的注意事项:在生产构建中,Vue CLI 会优化 CSS,并基于你的浏览器目标丢弃不必要的厂商前缀规则。由于 Autoprefixer 默认开启,你应始终只编写无前缀的 CSS 规则,其余交给工具链处理。
CSS Modules
CSS Modules 提供作用域隔离的样式能力,Vue CLI 中支持三种使用方式。
方式一:在*.vue文件中使用<style module>
<template> <p :class="$style.red">This should be red</p> </template> <style module> .red { color: red; } </style><style module>开箱即用,编译后类名会被替换为哈希化的唯一名称,模板中通过$style.red访问。
方式二:在 JavaScript 中以模块方式导入样式文件
在 JS 中导入 CSS 或预处理器文件作为 CSS Modules 时,文件名必须以.module.(css|less|sass|scss|styl)结尾:
import styles from './foo.module.css' // 所有支持的预处理器同样适用 import sassStyles from './foo.module.scss'这种方式由 cli-service 内置的normal-modules规则(匹配\.module\.\w+$)支持,相关测试用例见 css.spec.js 中的Auto recognition of CSS Modules by file names。
方式三:将全部样式文件视为 CSS Modules
如果想去掉文件名中的.module约定,让所有样式文件都按 CSS Modules 处理,可在vue.config.js中配置css-loader的modules.auto:
// vue.config.js module.exports = { css: { loaderOptions: { css: { modules: { auto: () => true } } } } }值得说明的是:在 Vue CLI 4 及更早版本中,该需求是通过css.requireModuleExtension: false实现的(中文文档 docs/zh/guide/css.md 保留了这一写法);而当前仓库的 css 配置源码 中,<style module>分支(vue-modules规则)会强制将modules.auto设为() => true,.module.*文件(normal-modules规则)始终按模块处理,因此自定义modules.auto是面向当前版本(css-loader v5/v6)的推荐方式。测试用例CSS Moduels Options验证了设置modules: false时.module.css不被转换、设置auto: () => true时所有样式文件均被转换的行为。
自定义生成的类名
如需自定义 CSS Modules 生成的类名,同样通过css.loaderOptions.css配置,所有css-loader选项均受支持:
// vue.config.js module.exports = { css: { loaderOptions: { css: { // 注意:以下配置格式在不同 Vue CLI 版本之间存在差异 // Vue CLI v3 使用 css-loader v1 // Vue CLI v4 使用 css-loader v3 // Vue CLI v5 使用 css-loader v5 // 具体格式请查阅对应版本的 css-loader 文档 modules: { localIdentName: '[name]-[hash]', exportLocalsConvention: 'camelCaseOnly' } } } } }版本兼容性提示:css-loader的选项命名随版本演进有所不同——Vue CLI v3 对应 css-loader v1,Vue CLI v4 对应 css-loader v3(类名配置在modules.localIdentName下、camelCase 通过localsConvention: 'camelCaseOnly'),Vue CLI v5 对应 css-loader v5(即本仓库当前使用的配置格式)。升级 Vue CLI 时请对照相应版本的 css-loader 文档核对选项名。
默认类名格式
从源码可见,只要cssLoaderOptions.modules存在,cli-service 会合并默认的localIdentName: '[name]_[local]_[hash:base64:5]',即默认类名格式为"文件名_原始类名_哈希"。若你不做任何modules配置,<style module>与.module.*文件即按此格式生成类名。
向预处理器 Loader 传递选项(css.loaderOptions)
有时你需要向预处理器的 webpack loader 传递选项,最推荐的方式是使用vue.config.js中的css.loaderOptions。例如,向所有 Sass/Less 样式传入共享的全局变量:
// vue.config.js module.exports = { css: { loaderOptions: { // 给 sass-loader 传递选项 // @/ 是 src/ 的别名 // 所以这里假设你有 src/variables.sass 这个文件 // 注意:在 sass-loader v8 中,这个选项名是 "prependData" sass: { additionalData: `@import "~@/variables.sass"` }, // 默认情况下 sass 选项会同时作用于 sass 和 scss 两种语法 // 因为 scss 语法在内部也是由 sass-loader 处理的 // 但在配置 prependData 时: // scss 语法要求语句结尾必须有分号,sass 语法则要求不能有分号 // 因此可用 scss 选项对 scss 语法单独配置 scss: { additionalData: `@import "~@/variables.scss";` }, // 给 less-loader 传递 Less.js 选项 less: { // less.js 全局变量(globalVars 为字段名) globalVars: { primary: '#fff' } } } } }loaderOptions.sass与loaderOptions.scss的区别在源码与测试中均有体现:从 css 配置源码 可见,.scss规则使用loaderOptions.scss || loaderOptions.sass合并配置,而.sass规则使用loaderOptions.sass并追加indentedSyntax: true;css.spec.js 的scss loaderOptions测试进一步确认:分别配置sass.prependData与scss.prependData时,两者互不污染(scss的选项不会被合并进sass规则)。
可通过 loaderOptions 配置的 loader
支持通过loaderOptions配置的 loader 包括:
css-loaderpostcss-loadersass-loaderless-loaderstylus-loader
这一点与 options.js 中的配置校验 一致:css.loaderOptions仅接受css、sass、scss、less、stylus、postcss六个键名,配置错误会触发校验告警。
为什么推荐 loaderOptions 而非 chainWebpack
官方明确建议使用loaderOptions而不是用chainWebpack手动挂载 loader:因为这些选项需要在多个使用对应 loader 的位置同时生效,手动挂载极易遗漏。
从 css 配置源码 的createCSSRule可以清晰地看到这一点:每种样式语言(css、postcss、scss、sass、less、stylus)都会生成四条规则分支:
vue-modules:匹配resourceQuery(/module/),即<style module>;vue:匹配resourceQuery(/\?vue/),即普通<style>;normal-modules:匹配\.module\.\w+$,即.module.*文件导入;normal:普通样式文件导入。
每条分支都要依次挂载样式注入 loader、css-loader、postcss-loader 以及预处理器 loader,并通过Object.assign({ sourceMap }, loaderOptions.xxx)合并你的选项。因此,通过loaderOptions一处配置即可覆盖全部四条分支,这正是官方推荐的原因。
深入源码:生产构建的样式链路
最后,从 css 配置源码 梳理一条完整的生产构建样式链路,帮助你理解 Vue CLI 在样式层面的整体设计:
- CSS 提取:生产环境默认通过
mini-css-extract-plugin将 CSS 提取为独立文件(默认输出路径css/[name].[contenthash:8].css,可通过css.extract关闭或自定义);开发环境则使用vue-style-loader将样式注入页面,支持热更新。测试用例production defaults与css.extract验证了这两种模式下的 loader 序列差异。 - Source Map:默认关闭(
css.sourceMap: false),开启后同时作用于 css-loader、postcss-loader 与预处理器 loader。 - 内联压缩:当生产环境不提取 CSS(
extract: false)时,内联样式不会经过压缩插件,因此 cli-service 会额外注入一个带cssnano的 postcss-loader 实例做内联压缩,同时把importLoaders从 2 提升到 3。 - 提取后的压缩:生产环境提取 CSS 后,通过
css-minimizer-webpack-plugin配合 cssnano 压缩,并可选开启多核并行(parallel),且会基于浏览器目标剔除多余的厂商前缀。
如果你希望进一步微调这些行为,vue.config.js中css对象的完整可用键(extract、sourceMap、loaderOptions)与默认值定义在 options.js 中,可作为配置参考;相关自动化测试见 css.spec.js,涵盖了默认 loader 序列、生产环境差异、PostCSS 覆盖、CSS Modules 识别、loaderOptions 传递等全部核心行为。
【免费下载链接】vue-cli🛠️ webpack-based tooling for Vue.js Development项目地址: https://gitcode.com/gh_mirrors/vu/vue-cli
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考