webpack中,我们需要在项目中安装css-loader才能让webpack识别css文件。
vue-cli基于webpack,内置了这个loader
Vite天生就是支持对CSS文件的直接处理的。
我们看一个例子:
我们在项目根目录里创建好index.html、main.js、index.css然后安装vite
index.html中我们引入main.js
<html lang="en"> <head> <title></title> </head> <body> <script src="./main.js" type="module"></script> <div>Vite天生就是支持对CSS文件的直接处理的。</div> </body> </html>
main.js中我们引入index.css
import "./index.css"
然后,index.css中我们加个全局背景
html,body { background: red; height: 100%; width: 100%; }
使用vite启动项目,可以发现不用安装任何插件就可以识别.css文件。
vite的样式添加原理
当vite在读取到main.js中引用的index.css时,会使用node的fs模块获取其内容。
首先,将其原内容替换为js脚本(方便热更新或者css模块化),同时设置Content-Type为js ,从而让浏览器以JS脚本的形式来执行该css后缀的文件。
然后,直接创建一个style标签, 将index.css中文件内的原内容直接复制进style标签里。最后,将style标签插入到index.html的head中。
CssModule
上述的样式文件引入方式是有瑕疵的,比如,两个.css文件定义了同一个类名,样式会被覆盖。
// index.css .wrap{ background: yellow; } // test.css .wrap{ background: yellow; }
// main.js import "./moduleA.js"; import "./moduleB.js"; // moduleA.js import "./index.css"; const div = document.createElement("div") document.body.appendChild(div) div.className = "footer" div.innerText ="别看我只是一只羊" // moduleB.js import "./test.css"; const div = document.createElement("div") document.body.appendChild(div) div.className = "footer" div.innerText ="羊儿的聪明难以想象!"
index.html中
<html lang="en"> <body> <script src="./main.js" type="module"></script> </body> </html>
显然,test.css内的样式覆盖了index.css内的样式。
一个项目如果是多人开发,类名重复的情况是很常见的,但是上述问题,显然会给我们的项目开发带来困扰。
因此,vite借助了CssModule解决了这一问题。
什么是CssModule:https://www.ruanyifeng.com/blog/2016/06/css_modules.html
CSS的规则都是全局的,任何一个组件的样式规则,都对整个页面有效。
产生局部作用域的唯一方法,就是使用一个独一无二的class的名字,不会与其他选择器重名。这就是 CSS Modules 的做法。
使用
想借助CssModule解决这一问题,我们只需要xxx.module.css的方式命名文件即可。
我们将index.css改为index.module.css,test.css改为test.module.css
然后,moduleA.js及moduleB.js稍作调整
// moduleA.js import index from "./index.module.css"; const div = document.createElement("div") document.body.appendChild(div) div.className = index.wrap div.innerText ="别看我只是一只羊" // moduleB.js import test from "./test.module.css"; const div = document.createElement("div"); document.body.appendChild(div); div.className = test.wrap; div.innerText = "羊儿的聪明难以想象";
注:导入的样式文件,我们可以打印一下 console.log(index):
会发现,css里面被替换成js后,导出的对象里包含一个原类名和修改后的类名对象。
所以,通过div.className = test.wrap的方式我们可以获取到修改后的类名。
修改完毕后,可以看到样式问题已经解决。
vite配置CSS
modules选项
通过vite.config.js的css.modules配置项,我们可以更改CssModule的一些默认内容,比如开启或关闭其功能,更改其哈希名称等。
import { defineConfig } from "vite"; export default defineConfig( { // 对css的行为进行配置 css:{ // 是对css模块化的默认行为进行覆盖 modules:{ } } });
modules 的配置其实都会被传递给 postcss,让它来帮忙处理,所以具体配置,可以参考: postcss-modules。
modules选项的完整配置
modules: { // 是否开启模块化 scopeBehaviour: 'global' | 'local' // 代表你不想参与到css模块化的路径 globalModulePaths: RegExp[] // 更改生成的哈希名称,一个字符串模板或者通过函数返回 generateScopedName: | string | ((name: string, filename: string, css: string) => string) // 生成hash名称的前缀 hashPrefix: string // 修改生成的配置对象的key的展示形式(驼峰还是中划线形式) localsConvention: | 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly' | null }
scopeBehaviour
配置建议:脑子不好了可以改
是否开启模块化功能。
- 可选参数:'global' | 'local'
- 默认值: 'local'
我们将默认值更改为 global
import { defineConfig } from "vite"; export default defineConfig({ css: { // 是对css模块化的默认行为进行覆盖 modules: { scopeBehaviour: "global" } } });
可以发现,模块化的功能被关闭了。
localsConvention
返回键值对的格式
默认值:camelCaseOnly
可选参数
- camelCase
- camelCaseOnly
- dashes
- dashesOnly
- null
我们配置成camelCase试试
import { defineConfig } from "vite"; export default defineConfig({ css: { // 是对css模块化的默认行为进行覆盖 modules: { localsConvention: "camelCase", scopeBehaviour: "local" } } });
// index.module.css .table-wrap{ background: yellow; } // moduleA.js import index from "./index.module.css"; console.log('index: ', index);
可以发现,样式引用时,使用 ele.className = index[驼峰命名],ele.className = index[中划线命名]都可以。
camelCaseOnly
null
dashes
dashesOnly