scoped在vue单文件组件中用来标记style标签,让该标签内的css选择器只能选中组件内的元素。就是说让组件内的样式与其它组件隔离,不会影响父组件、子组件的样式。
vue模板编译器在编译有scoped的stye标签时,会生成对应的postCSS插件,该插件会给每个scoped标记的style标签模块,生成唯一一个对应的data-v-xxxhash
字符串,同时:
该字符串会作为属性,添加到该组件内,每一个元素上。
如果存在子组件,则只会在子组件顶级根元素添加该属性(透传),无法给子组件内部元素添加。
scoped标记的style标签内,每个选择器都会联合该属性选择器(所有选择器都会默认被重写)。
如果是嵌套选择器,会在最内层的选择器联合上属性选择器。
.ipt {}
.ipt .el-input__wrapper {}
.ipt .el-input__wrapper span {}
/*结果,每个scoped对应的唯一字符串,会联合到最内层的选择器*/
.ipt[data-v-88888888] {}
.ipt .el-input__wrapper[data-v-88888888] {}
.ipt .el-input__wrapper span[data-v-88888888] {}
样式隔离效果:
有scoped的style标签内的选择器,只能选中本组件元素,无法选中子组件的元素。即,在父组件使用子组件的选择器修改样式,是无法选中子组件内的元素的。
样式穿透意思是:让父组件在有scoped的标签里,使用子元素的选择器来修改子元素的样式,即父组件定义的样式穿透到子组件(默认无法穿透)。
vue3中只能使用:deep()
来实现样式穿透,而>>> /deep/
已经被废弃。
原理:
生成联合选择器时,不再默认给最内层的选择器联合,而是手动指定某个选择器的上一级选择器进行联合。
vue编译分为三部分:script、template、style,scoped处理就在style编译里处理。
/* 指定给.el-input__wrapper前面的ipt联合属性选择器 */
.union .ipt :deep(.el-input__wrapper) input {}
/* 结果: */
.union .ipt[data-v-0904fc8e] .el-input__wrapper input {}
css Modules的作用和vue中的scoped作用一样,都是用来实现样式的隔离。在webpack的项目中是css-loader提供的能力。在vite中已经适配了vue3单文件组件。
将导入的css模块里的自定义css类名,替换为一个无语义,但唯一的字符串类名,并提供一个对象充当map映射来访问转换后的类名。
我们先来介绍wepack中的实现,来理解它的执行过程。
在webpack中通过配置css-loader,即可实现CSS Modules。可以应用在react或原生等场景中。但它的应用场景更多是让两个css模块(即使有相同的类名)能够互相隔离。
原理:
效果:
当导入两个css文件,即使有相同的类名,也不会相互影响。但是需要根据拿到的映射对象,给元素加上被替换后的唯一类名。
为了避免所有css文件都会进行转换类名操作(有些全局css模块不需要转换),可以给不需要转换的css文件起一个特殊的文件名,并通过test正则,为它们匹配一套不会转换的css-loader配置。
实现:
配置css-loader,处理css文件(模块)时,进行类名替换
module.exports = {
module: {
rules: [
{
test:/\.css$/,
exclude: /node_modules/, // 排除依赖里的代码
use: [
'style-loader', // 替换类名后,把css插入html头部
{
loader: 'css-loader',
options: {
modules: true, // 开启css模块
}
}
]
}
]
}
}
导入css文件(模块)时,用一个变量接收导入结果,这个结果就包含原始类名和被替换后类名的映射。其中key是原始类名,value是被替换后的。
// a.css
.test .div {
color: green;
}
// b.css
.test .div {
color: yellow;
}
// index.js
import style1 from 'a.css'
import style2 from 'b.css'
// 把页面上第一个有test类的元素加上a.css里test类对应的样式
document.queryselector('.test').classList.add(style1.test)
vue3组件中应用CSS Modules是通过在style添加module属性,让该style中的类名都替换成唯一类名,并且类名映射对象只能在组件内部访问。最终的效果就是和scoped一样,实现组件样式与其它组件隔离。
步骤:
在单位组件的style标签添加module属性,取值为自定义的映射名(说白了就是映射对象的名字)
<style module="cssmodule">
.red {
color: red;
}
.box .text {
font-size: large;
}
style>
template中可直接拿到该映射对象。
<template>
<div :class="cssmodule.box">
<span :class="[cssmodule.text, cssmodule.red]">
hellow
span>
div>
template>
setup标签中通过useCssModule
hooks也可以拿到css映射对象
<script setup lang="ts">
import { useCssModule } from 'vue'
// 具名情况下, 返回