阿里云主机折上折
  • 微信号
Current Site:Index > Component style scoping (:deep/:slotted/:global)

Component style scoping (:deep/:slotted/:global)

Author:Chuan Chen 阅读数:38260人阅读 分类: Vue.js

Component Style Scoping (:deep/:slotted/:global)

In Vue's Single File Components (SFCs), <style scoped> is the core mechanism for achieving style isolation. When the scoped attribute is added to a <style> tag, Vue adds a unique data-v-xxxxxx attribute to all DOM elements within the component and achieves style isolation through attribute selectors. However, in real-world development, we often need to bypass this scoping to modify child component styles, which is where special selectors like :deep(), :slotted(), and :global() come into play.

Basic Principles of Scoped Styles

<template>
  <div class="demo">
    <p>This text will be affected by scoped styles</p>
  </div>
</template>

<style scoped>
.demo {
  color: red;
}
/* After compilation, it becomes */
.demo[data-v-f3f3eg9] {
  color: red;
}
</style>

Scoped styles are implemented through PostCSS transformation, which adds attribute constraints to selectors. This mechanism ensures styles only affect the current component but also introduces three common issues:

  1. Inability to directly modify child component internal styles
  2. Inability to modify slotted content styles
  3. Lack of flexibility when adding global styles

:deep() Deep Selector

:deep() is used to bypass scoping and modify child component internal styles. In Vue 2, the equivalent syntax was >>> or /deep/, while Vue 3 standardizes on :deep().

<template>
  <ChildComponent class="child-wrapper"/>
</template>

<style scoped>
/* Invalid syntax */
.child-wrapper .inner-element {
  color: blue;
}

/* Correct syntax */
:deep(.child-wrapper .inner-element) {
  color: blue;
}
/* After compilation, it becomes */
[data-v-f3f3eg9] .child-wrapper .inner-element {
  color: blue;
}

Practical use case examples:

<!-- Modifying Element Plus's el-input internal styles -->
<style scoped>
:deep(.el-input__inner) {
  background-color: #f5f7fa;
}
</style>

<!-- Modifying third-party component library styles -->
<style scoped>
:deep(.third-party-component .title) {
  font-size: 18px;
}
</style>

:slotted() Slot Selector

:slotted() is used to modify styles for content passed through slots. By default, scoped styles do not affect slotted content.

<template>
  <div class="container">
    <slot name="header"></slot>
  </div>
</template>

<style scoped>
/* Invalid syntax */
.container .header {
  color: red;
}

/* Correct syntax */
:slotted(.header) {
  color: red;
}
/* After compilation, it becomes */
[data-v-f3f3eg9] .header {
  color: red;
}

Complex example:

<template>
  <div class="card">
    <slot name="title"></slot>
    <slot name="content"></slot>
  </div>
</template>

<style scoped>
:slotted([slot="title"]) {
  font-size: 24px;
  border-bottom: 1px solid #eee;
  padding-bottom: 10px;
}

:slotted([slot="content"]) {
  font-size: 14px;
  line-height: 1.6;
}
</style>

:global() Global Selector

:global() is used to define global styles within scoped styles, typically for overriding third-party styles or defining animations.

<style scoped>
/* Defining global animations */
:global(.fade-enter-active),
:global(.fade-leave-active) {
  transition: opacity 0.5s;
}
:global(.fade-enter-from),
:global(.fade-leave-to) {
  opacity: 0;
}

/* Modifying body background color */
:global(body) {
  background-color: #f8f8f8;
}
</style>

Mixed usage example:

<style scoped>
/* Component-specific styles */
.container {
  padding: 20px;
}

/* Global styles */
:global(.ant-btn) {
  margin-right: 10px;
}

/* Deep selector */
:deep(.el-dialog__body) {
  padding: 20px;
}

/* Slot styles */
:slotted(.item) {
  margin-bottom: 10px;
}
</style>

Combination Techniques

In real projects, these selectors are often used in combination:

<template>
  <Modal>
    <template #header>
      <h2 class="modal-title">Title</h2>
    </template>
    <ChildComponent class="content"/>
  </Modal>
</template>

<style scoped>
/* Modifying modal header slotted content */
:slotted(.modal-title) {
  color: var(--primary-color);
}

/* Modifying child component internal elements */
:deep(.content .item) {
  padding: 15px;
}

/* Global style resets */
:global(.reset-ul) {
  margin: 0;
  padding: 0;
  list-style: none;
}
</style>

Performance Considerations and Best Practices

  1. Moderate Usage: Overusing :deep() can lead to loss of style scoping control; use only when necessary.
  2. Specific Selectors: Use specific selector paths to avoid global impact.
    /* Not recommended */
    :deep(*) {
      color: red;
    }
    
    /* Recommended */
    :deep(.child .item) {
      color: red;
    }
    
  3. Combining with CSS Modules: In large projects, consider combining with CSS Modules.
    <style module scoped>
    .container {
      /* Local styles */
    }
    :global(.ant-btn) {
      /* Global styles */
    }
    </style>
    

Common Issues and Solutions

Issue 1: Styles not applying after using :deep()

  • Check if the child component correctly renders the DOM structure
  • Verify the selector path is correct
  • Check for higher-priority style overrides

Issue 2: Slot styles not applying

  • Ensure slotted content is actually passed in
  • Check if the :slotted() selector matches the slotted content's class name
  • Note the limitations of scoped styles on slotted content

Issue 3: Styles polluting globally

  • Avoid using overly generic selectors in :global()
  • Consider using naming conventions like BEM to reduce conflicts
  • Add specific prefixes to necessary global styles
<!-- Bad example -->
<style scoped>
:global(.btn) {
  /* This affects all .btn elements */
}
</style>

<!-- Good example -->
<style scoped>
:global(.my-component-btn) {
  /* Limited scope */
}
</style>

Comparison with Other Technical Solutions

  1. CSS Modules:

    • Achieves isolation through unique class names
    • Requires manually passing class names to child components
    • Cannot directly modify child component internal styles
  2. CSS-in-JS:

    • Complete style isolation
    • Higher runtime overhead
    • Less integrated with Vue ecosystem compared to scoped styles
  3. Naming Conventions like BEM:

    • Relies on developer discipline
    • Cannot achieve true isolation
    • Requires manual namespace management

Configuration in Build Tools

In vite.config.js, you can configure scoped style behavior:

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/variables.scss" as *;`
      }
    },
    modules: {
      scopeBehaviour: 'local' // or 'global'
    }
  }
})

In webpack, configure via vue-loader:

module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          css: {
            modules: {
              localIdentName: '[name]_[local]_[hash:base64:5]'
            }
          }
        }
      }
    ]
  }
}

Future Development of Style Scoping

Vue 3.3+ introduces combined usage of scoped and module attributes on <style> tags:

<style scoped module>
/* Combines scoping with CSS Modules syntax */
</style>

Composition API's useCssModule:

import { useCssModule } from 'vue'

export default {
  setup() {
    const style = useCssModule()
    return { style }
  }
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.