1、css modules是什么?
ACSS Moduleis a CSS file in which all class names and animation names are scoped locally by default. All URLs (url(...)
) and@imports
are in module request format (./xxx
and../xxx
means relative,xxx
andxxx/yyy
means in modules folder, i. e. innode_modules
).
CSS
模块是一个CSS
文件,默认情况下,所有类名和动画名都在本地作用域内。所有URL(url(...))
和@imports
均以模块请求格式表示(./ xxx
和../ xxx
表示相对,xxx
和xxx / yyy
表示模块文件夹,即在“ node_modules”
中)。
也就是说,css modules
是主要为了解决样式冲突问题,使得css
样式具有作用域。
2、vue项目中css modules和scoped区别?
vue项目中有两种解决css冲突的方案,一种是比较常见的使用scoped。另一种就是css modules。先对这两种方案原理做一个简单介绍,然后比较他们之间的区别:
scoped方案
这是在vue项目中非常常见的解决样式冲突方式,当在style标签中加上scoped,编译后会在该vue组件元素上加上hash标识属性,在vue组件里的每个元素都有同一个hash标识属性。无法完全避开css权重和类名重复的问题。
实例
<style scoped>
.example {
color: red;
}
</style>
<template>
<div class="example">hi</div>
</template>
编译后
<style>
.example[data-v-f3f3eg9]
{
color: red;
}
</style>
<template>
<div class="example" data-v-f3f3eg9>hi</div>
</template>
css modules方案
产生局部作用域的唯一方法,就是使用一个独一无二的class
的名字,为所有类名重新生成类名,有效避开了css权重和类名重复的问题,这就是 CSS Modules 的做法。css module直接替换了类名,排除了用户设置类名影响组件样式的可能性
实例
<style module>
.red {
color: red;
}
</style>
<template>
// 构建工具会将类名`style.red`编译成一个哈希字符串,这样一来,这个类名就变成独一无二了,只对对应组件有效。
<p :class="$style.red"> This should be red </p>
</template>
编译后
<style module>
._1yZGjg0pYkMbaHPr4wT6P__1 {
color: red;
}
</style>
<template>
<p class="_1yZGjg0pYkMbaHPr4wT6P__1"> This should be red </p>
</template>
两种方案的区别
- scoped方案在其他地方使用相同类名可能还是会影响组件样式,如果你子组件的某元素上有一个类已经在这个父组件中定义过了,那么这个父组件的样式就会泄露到子组件中。但是css modules方案通过算法计算出唯一类名替换原始类名避免了这种样式冲突。
//Father.vue
<template>
<div>
<son></son>
</div>
</template>
<script>
import Son from './Son'
export default {
name: 'Father',
components: {
Son
},
data () {
return {}
}
}
</script>
<style scoped>
.wrapper {
width: 300px;
height: 300px;
line-height: 300px;
vertical-align: middle;
background-color: #000;
color: #fff;
}
</style>
// Son.vue
<template>
<div class="wrapper">111111</div>
</template>
<script>
export default {
name: 'Son',
data () {
return {}
}
}
</script>
Father组件的wrapper样式会渗透到Son组件中并起作用
!
- 还有一些情况是我们需要对我们的子组件的深层结构设置样式——虽然这种做法并不受推荐且应该避免。为了简便起见,我们假设我们的父组件现在要对子组件设置样式,在 scoped 样式中,这种情况可以使用
>>>
连接符(或者/deep/
)实现。可是别忘记,我们却因此失去了组件的封装效果。这个组件内的所有的被父组件深度选择器选择的类的样式都会被浸染——即便是孙节点。而css modules方案下所有的 CSS 类可以通过$style
对象获取到,所以我们可以通过 props 将这些类传递到任何我们希望的深度中,这样,在子组件中的任意位置使用这些类就会变得极其容易
// Father.vue
<template>
<div>
<son :contentClass="$style.content"></son>
</div>
</template>
<script>
import Son from './Son'
export default {
name: 'Father',
components: {
Son
},
data () {
return {}
}
}
</script>
<style lang="scss" module>
.content {
color: blue;
}
</style>
// Son.vue
<template>
<div :class="$style.wrapper">
<span :class="contentClass">111111</span>
</div>
</template>
<script>
export default {
name: 'Son',
props: ['contentClass'],
data () {
return {}
}
}
</script>
<style lang="scss" module>
.wrapper {
width: 300px;
height: 300px;
line-height: 300px;
vertical-align: middle;
background-color: #000;
color: #fff;
}
</style>
效果:
- scoped会使标签选择器渲染变慢很多倍,而使用class或id则不会。
- 模块式 CSS 与 JS 有着很好的互操作性 (interoperability),这一点不只局限于 CSS 类。
3、css modules如何使用?
{
test: /\.(sc|sa|c)ss$/,
include: [path.join(__dirname, '.././', 'src')],
// 匹配规则时,只使用第一匹配的数组
oneOf: [
// 这里匹配 `<style module>`
{
// 与资源查询匹配的条件
resourceQuery: /module/,
use: [
// module需要使用vue-style-loader
'vue-style-loader',
{
loader: 'css-loader',
options: {
// 开启 CSS Modules
modules: true,
// 自定义生成的类名
localIdentName: '[name]_[local]_[hash:base64:5]'
}
},
'sass-loader'
]
},
// 这里匹配普通的 `<style>` 或 `<style scoped>`
{
use: [
// scoped使用style-loader
'style-loader',
'css-loader',
'sass-loader'
]
}
]
}
4、总结
其实两种方案都非常简单、易用,在某种程度上解决的是同样的问题。 那么你该选择哪种呢?
scoped 样式的使用不需要额外的知识,给人舒适的感觉。它所存在的局限,也正是它的使用简单的原因。它可以用于支持小型到中型的应用。
在更大的应用或更复杂的场景中,这个时候,对于 CSS 的运用,我们就会希望它更加显式,拥有更多的控制权。虽然在模板中大量使用 $style
看起来并不那么“性感”,但却更加安全和灵活,为此我们只需付出微小的代价。还有一个好处就是我们可以用 JS 获取到我们定义的一些变量(如色彩值、样式断点),这样我们就无需手动保持其在多个文件中同步。
参考:
https://juejin.im/post/5b9556...
https://www.jianshu.com/p/255...
http://www.ruanyifeng.com/blo...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。