Skip to content

变更scoped样式

概览

为单文件组件提供一致的自定义css拓展

基础用例

vue

<style scoped>
/* deep selectors */
::v-deep(.foo) {
}

/* shorthand */
:deep(.foo) {
}

/* targeting slot content */
::v-slotted(.foo) {
}

/* shorthand */
:slotted(.foo) {
}

/* one-off global rule */
::v-global(.foo) {
}

/* shorthand */
:global(.foo) {
}
</style>

动机

vue SFC 的scoped标记的style只会作用于当前组件。可以改进许多用户遇到的情况。

深度选择器

有时我们想要准确的作用子组件指定元素的样式。

最开始的时候,我们支持>>>关系选择器来支持深度selector。但是,一些css预处理器类似SASS存在解析问题,因为它并不是官方的关系选择器。

后来我们专项了/deep/,之前作为提议添加到CSS中(甚至已经添加到了chrome中),但最终被废弃。这对一些用户造成了困惑,因为他们担心在Vue SFC中使用/deep/会使已经废弃这个功能的浏览器不支持他们的代码。但是,像>>>/deep/仅会在Vue SFC compiler编译时重写css时使用,并且会在最终的css中移除。

为了避免/deep/关系选择器废弃带来的困惑,我们介绍了另外一种自定义的关系选择器:::v-deep ,可以更加准确的知道这是vue独有的拓展,而且使用伪类语法以便预处理器可以解析它。

先前版本的深度关系选择器也会在当前的vue2 SFC compiler中继续支持,仍会继续对用户产生困惑。在vue3中,我们会废弃>>>/deep/ 的支持。

在我们为vue3开发新的SFC compiler时,我们注意到从语义上来说css伪类元素并不是关系选择器。而是更多与接收参数的伪类选择器保持一致,因此我们使::v-deep() 按照这个方式工作。如果你不关心准确的v-前缀,你也可以使用更短的:deep(),它们工作原理完全相同。

目前使用::v-deep作为关系选择器仍然会继续支持,但是我们考虑将它废弃并且抛出警告。

影响或忽略指定slot内容样式

目前,从父组件中传递过来的插槽内容会同时受父组件的scoped样式和子组件的scoped样式同时影响。并没有方式准确的影响插槽内容,或者不影响它。

在vue3中,我们试图让子组件的scoped样式默认不影响插槽内容。并且提供了::v-slotted()(可以简写为::slotted())准确的影响插槽内容样式。

一次性的全局样式

目前添加全局样式时,我们必须要再声明一个非scoped的style标签。为此,我们介绍一个新的::v-global()(简写为::global() )伪类元素来添加一次性的全局样式。

我们意思到有些用户想要在单页面组件的style中使用props或者state,我们计划支持这项功能,但是不在当前RFC中。

详细设计

  • 废弃>>> /deep/
  • ::v-deep作为关系选择器使用方式被废弃
css
/* DEPRECATED */
::v-deep .bar {
}

取而代之,提供一个接收其他选择器作为参数的伪类元素来实现:

css
::v-deep(.bar) {
}

被编译成:

css
[v-data-xxxxxxx] .bar {
}
  • 从父组件中传递到子组件的插槽内容不再默认受子组件的scoped样式影响。而是,子组件需要使用新的伪类::v-slotted()作用于指定的元素:
css
::v-slotted(.foo) {
}

被编译成:

css
.foo[v-data-xxxxxxx-s] {
}

注意-s后缀,意味着它只会被作用于插槽内容

  • 新的伪类元素::v-global()可以被用来在scoped样式中添加全局样式:
css
::v-global(.foo) {
}
css
.foo {
}
  • @vue/compiler-sfc的测试用例提供了很多编译转换细节

采取的策略

所有之前的深度选择器都会被支持,而且会抛出废弃警告。在大多数用户完成迁移之后,我们将会在未来的某个版本移除它们。

插槽内容行为的更改在技术上应该会让父组件和子组件之间的样式更加解耦,但是该行为确实会破坏依赖于应用于插槽内容的子样式的现有样式。我们可能需要提供一个选项来保留旧的行为。

未解决的问题

我们不确定插槽内容行为的更改会影响多少现有的组件 - 对于任何反馈,我们都表示感谢。