程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Vue2

    • Vue简介
    • Vue 基础使用
    • Vue的基础指令
    • 过滤器(Filters)
    • 侦听器(Watch)
    • 计算属性(computed)
    • vue-cli
    • vue.config.js配置
    • Vue组件
      • 1. 什么是组件化开发
      • 2. Vue中的组件化开发
      • 3. Vue组件的三个组成部分
        • 3-1 template
        • 3-2 script
        • 3-3 style
      • 4. 组件之间的父子关系
      • 5. 使用组件的三个步骤
        • 5-1 定义组件
        • 5-2 注册组件 (私有注册)
        • 5-3 使用组件
      • 6. 通过 components 注册的是私有子组件
      • 7. 注册全局组件
      • 8. 组件的 props
        • 8-1 props 的定义
        • 8-2 props 的数据传递
        • 8-3 props 使用示例
      • 9. props 是只读的
      • 10. props 的 default 默认值
      • 11. props 的 type 值类型
      • 12. props 的 required 必填项
      • 13. 组件之间的样式冲突问题
      • 14. 解决组件样式冲突的问题
      • 15. style节点的 scoped属性
        • 15-1 浏览器中查看效果
        • 15-2 data-v-xxxxxxx 属性的行为
      • 16. /deep/样式穿透
        • 16-1 Vue 2 和 Vue 3 的差异
        • 16-2 样式穿透机制原理
        • 16-3 最佳实践
    • 生命周期和数据共享
    • Vue 组件实例与数据代理 (this)
    • $refs 引用
    • 动态组件
    • 插槽 (Slots)
    • 混入 (Mixin)
    • 自定义指令 (directives)
    • 插件 (Plugins)
    • 初识Vue-router
    • Vue-router的常见用法
  • Vue3

  • vue3 + TS 项目集成

  • Vue全家桶
  • Vue2
scholar
2024-07-30
目录

Vue组件

# Vue组件

# 1. 什么是组件化开发

组件化开发指的是:根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护。

组件化开发的核心思想是将页面划分为多个独立的、可复用的部分,每个部分都是一个组件。这些组件可以单独开发、测试和维护,最后通过组合这些组件来构建整个应用。

# 2. Vue中的组件化开发

Vue 是一个支持组件化开发的前端框架。Vue 中规定:组件的文件后缀名是 .vue。在之前的例子中,我们已经接触到的 App.vue 文件本质上就是一个 Vue 的组件。

# 3. Vue组件的三个组成部分

每个 .vue 组件文件通常由三个部分组成,分别是:

  1. template:组件的模板结构
  2. script:组件的 JavaScript 行为
  3. style:组件的样式

其中,组件中必须包含 template 模板结构,而 script 和 style 是可选的部分。

# 3-1 template

Vue 规定:每个组件的模板结构需要定义在 <template> 标签内。

<template>
  <div class="my-component">
    <h1>{{ title }}</h1>
    <p>{{ description }}</p>
  </div>
</template>
1
2
3
4
5
6

注意:

  1. <template> 是 Vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素。
  2. <template> 中只能包含唯一的根节点,这意味着 <template> 标签内部的所有内容必须嵌套在一个唯一的根元素中,通常我们会使用一个 <div> 标签来包裹所有的内容。

# 3-2 script

Vue 规定:开发者可以在 <script> 标签内封装组件的 JavaScript 业务逻辑。

<template>
  <div class="my-component">
    <h1>{{ title }}</h1>
    <p>{{ description }}</p>
  </div>
</template>

<script>
export default {
  name: 'MyComponent',
  data() {
    return {
      title: '这是一个标题',
      description: '这是描述内容'
    };
  }
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

.vue 组件中的 data 必须是函数

Vue 规定:.vue 组件中的 data 必须是一个函数,不能直接指向一个数据对象。这样做是为了确保每个组件实例都有独立的数据副本,而不会共享同一个数据对象。

错误示例:

data: {
  return {
    title: '这是一个标题',
    description: '这是描述内容'
  };
}
1
2
3
4
5
6

这种写法会导致多个组件实例共用同一份数据,产生数据污染问题。

正确示例:

data() {
  return {
    title: '这是一个标题',
    description: '这是描述内容'
  };
}
1
2
3
4
5
6

# 3-3 style

Vue 规定:组件内的 <style> 标签是可选的,开发者可以在 <style> 标签内编写样式来美化当前组件的 UI 结构。

<template>
  <div class="my-component">
    <h1>{{ title }}</h1>
    <p>{{ description }}</p>
  </div>
</template>

<script>
export default {
  name: 'MyComponent',
  data() {
    return {
      title: '这是一个标题',
      description: '这是描述内容'
    };
  }
};
</script>

<style scoped>
.my-component {
  padding: 20px;
  background-color: #f0f0f0;
}
.my-component h1 {
  color: #333;
}
.my-component p {
  color: #666;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

<style scoped> 表示样式只作用于当前组件,防止样式污染全局。

# 4. 组件之间的父子关系

image-20240730213424357

在 Vue 中,组件可以嵌套使用,从而形成父子关系。父组件可以通过 props 向子组件传递数据,子组件可以通过 $emit 向父组件发送消息。

示例代码:

<!-- 父组件 ParentComponent.vue -->
<template>
  <div>
    <h1>父组件</h1>
    <child-component :message="parentMessage" @child-event="handleChildEvent"></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  name: 'ParentComponent',
  components: {
    ChildComponent
  },
  data() {
    return {
      parentMessage: '来自父组件的消息'
    };
  },
  methods: {
    handleChildEvent(data) {
      console.log('收到子组件的消息:', data);
    }
  }
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- 子组件 ChildComponent.vue -->
<template>
  <div>
    <h2>子组件</h2>
    <p>{{ message }}</p>
    <button @click="sendMessageToParent">发送消息给父组件</button>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  props: ['message'],
  methods: {
    sendMessageToParent() {
      this.$emit('child-event', '这是子组件的消息');
    }
  }
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

在这个示例中:

  • 父组件 ParentComponent 向子组件 ChildComponent 传递了一个 message 属性。
  • 子组件 ChildComponent 可以使用 props 接收到 message,并通过 $emit 发送事件 child-event 给父组件。
  • 父组件通过 @child-event 监听子组件的事件,并调用 handleChildEvent 方法处理事件。

# 5. 使用组件的三个步骤

使用 Vue 组件的步骤主要包括定义组件、注册组件和使用组件。具体步骤如下:

  1. 定义组件:创建一个 .vue 文件,定义组件的模板、逻辑和样式。
  2. 注册组件:在父组件或 Vue 实例中注册子组件,可以是私有注册或全局注册。
  3. 使用组件:在模板中使用自定义标签来引入子组件。

# 5-1 定义组件

创建一个 .vue 文件,定义组件的模板、逻辑和样式。










 









<!-- ChildComponent.vue -->
<template>
  <div class="child-component">
    <h2>子组件</h2>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent', // 组件名
};
</script>

<style scoped>
.child-component {
  color: blue; /* 设置组件的样式 */
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

在这个示例中,我们定义了一个子组件 ChildComponent。

# 5-2 注册组件 (私有注册)

在父组件的 components 选项中注册子组件,这样子组件只能在当前父组件中使用。















 
 



<!-- ParentComponent.vue -->
<template>
  <div>
    <h1>父组件</h1>
    <child-component></child-component> <!-- 使用子组件 -->
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue'; // 导入子组件

export default {
  name: 'ParentComponent', // 组件名
  components: {
    ChildComponent, // 私有注册子组件
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 5-3 使用组件

在模板中使用自定义标签来引入子组件。





 














<!-- ParentComponent.vue -->
<template>
  <div>
    <h1>父组件</h1>
    <child-component></child-component> <!-- 使用子组件 -->
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue'; // 导入子组件

export default {
  name: 'ParentComponent', // 组件名
  components: {
    ChildComponent, // 私有注册子组件
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 6. 通过 components 注册的是私有子组件

在 Vue 组件的 components 选项中注册的子组件是私有的,仅在当前组件中可用。例如:



 
 
 


export default {
  name: 'ParentComponent', // 组件名
  components: {
    ChildComponent // 私有子组件,只能在 ParentComponent 中使用
  }
};
1
2
3
4
5
6

在这个例子中,ChildComponent 只能在 ParentComponent 中使用,不能在其他组件中使用。

# 7. 注册全局组件

在 Vue 项目的 main.js 入口文件中,通过 Vue.component() 方法可以注册全局组件。全局组件可以在任何组件中使用。

示例代码:





 
 
 





// main.js
import Vue from 'vue'; // 导入 Vue
import App from './App.vue'; // 导入主组件 App

import GlobalComponent from './components/GlobalComponent.vue'; // 导入全局组件
// 注册全局组件
Vue.component('global-component', GlobalComponent);

new Vue({
  render: h => h(App), // 渲染主组件 App
}).$mount('#app'); // 挂载 Vue 实例到 id 为 app 的元素上
1
2
3
4
5
6
7
8
9
10
11

在 main.js 中注册的全局组件 GlobalComponent 可以在任何地方使用,无需导入。




 









<!-- 使用全局组件 -->
<template>
  <div>
    <global-component></global-component> <!-- 使用全局组件 -->
  </div>
</template>

<script>
export default {
  name: 'App', // 组件名
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12

在这个示例中,GlobalComponent 被注册为全局组件,可以在任何组件中使用。这样做可以提高组件的复用性,方便在项目的不同部分使用相同的组件。

# 8. 组件的 props

props 是在子组件中定义的自定义属性,用于接收父组件传递的数据。父组件在使用子组件时,通过绑定属性的方式向子组件传递数据。通过 props,父组件可以向子组件传递数据。props 的定义有两种语法格式:使用数组和使用对象。

# 8-1 props 的定义

使用数组定义 props

数组形式适合简单情况,只需要声明 props 名称。



 


export default {
  // 使用数组定义 props
  props: ['propName']  // 这里的 propName 是自定义属性名
}
1
2
3
4

使用对象定义 props

对象形式更为灵活,适合当你需要指定 props 的类型、默认值、是否必需等。




 
 
 
 
 



export default {
  // 使用对象定义 props
  props: {
    propName: {
      type: String,
      required: true,
      default: '默认值'
    }
  }
}
1
2
3
4
5
6
7
8
9
10

提示

在 Vue 中,props 可以定义的类型包括:String(字符串)、Number(数字)、Boolean(布尔值)、Array(数组)、Object(对象)、Function(函数)和任意类型 Any(可以是多种类型的组合)。

在 Vue.js 中,给子组件传递数据时,通常使用属性绑定 (:) 的方式来传递数据。这是因为属性绑定允许动态地将父组件的数据传递给子组件,从而在父组件的数据变化时自动更新子组件的显示内容。

# 8-2 props 的数据传递

使用属性绑定传递数据

属性绑定通过 : 语法实现,它会动态地将父组件的数据作为属性值传递给子组件:

<!-- ParentComponent.vue -->
<template>
  <div>
    <!-- 使用子组件并通过 :message="parentMessage" 传递数据 -->
    <ChildComponent :message="parentMessage"></ChildComponent>
  </div>
</template>
1
2
3
4
5
6
7

在上面的例子中,parentMessage 是父组件中的一个响应式数据,它通过 :message="parentMessage" 的方式传递给子组件的 props。

使用静态字符串传递数据

如果传递的是一个静态字符串常量,而不是响应式数据,可以不使用属性绑定,直接传递字符串值:

<!-- ParentComponent.vue -->
<template>
  <div>
    <!-- 直接传递一个静态字符串 -->
    <ChildComponent message="Static message"></ChildComponent>
  </div>
</template>
1
2
3
4
5
6
7

在这种情况下,子组件接收到的是一个固定不变的字符串值 Static message。这种方法适用于需要传递固定值的场景。

总结

  • 动态数据:使用 : 绑定动态数据,使得子组件能够响应父组件的数据变化。
  • 静态数据:直接传递字符串常量,无需使用 :。

# 8-3 props 使用示例

在父组件中定义一个子组件并传递数据 (这里使用数组定义 props):





 












 





<!-- ParentComponent.vue -->
<template>
  <div>
    <!-- 使用子组件并通过 :message="parentMessage" 传递数据 -->
    <ChildComponent :message="parentMessage"></ChildComponent>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue'; // 导入子组件

export default {
  components: {
    ChildComponent // 注册子组件
  },
  data() {
    return {
      parentMessage: 'Hello from Parent' // 定义父组件中的数据
    };
  }
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

在子组件中接收 props:





 






 



<!-- ChildComponent.vue -->
<template>
  <div>
    <!-- 使用 props 中的数据 -->
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  // 定义 props,用于接收父组件传递的数据
  props: ['message']
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意无论你使用哪种方式定义 props,在父组件中传递 props 的方法都是一样的。

# 9. props 是只读的

Vue 规定:组件中封装的自定义属性是只读的,程序员不能直接修改 props 的值,否则会报错。

示例

export default {
  props: ['message'],
  mounted() {
    this.message = 'New Message'; // 试图修改 props 的值,会报错
  }
};
1
2
3
4
5
6

image-20240730221520960

要想修改 props 的值,可以将 props 的值转存到 data 中,因为 data 中的数据是可读可写的。

示例









 


 



 




<template>
  <div>
    <p>{{ localMessage }}</p> <!-- 使用 data 中的值 -->
  </div>
</template>

<script>
export default {
  props: ['message'],
  data() {
    return {
      localMessage: this.message // 将 props 的值转存到 data 中
    };
  },
  mounted() {
    this.localMessage = 'New Message'; // 可以修改 data 中的值
  }
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

在这个例子中,我们将 props 的值 message 转存到 data 中的 localMessage,然后可以自由地修改 localMessage。

# 10. props 的 default 默认值

在声明自定义属性时,可以通过 default 来定义属性的默认值。

示例





 




export default {
  props: {
    message: {
      type: String,
      default: 'Default Message' // 定义默认值
    }
  }
};
1
2
3
4
5
6
7
8

在这个例子中,如果父组件没有传递 message 属性,子组件会使用默认值 'Default Message'。

# 11. props 的 type 值类型

在声明自定义属性时,可以通过 type 来定义属性的值类型。

示例








 





export default {
  props: {
    message: {
      type: String, // 定义属性的类型
      default: 'Default Message'
    },
    count: {
      type: Number, // 定义属性的类型
      default: 0
    }
  }
};
1
2
3
4
5
6
7
8
9
10
11
12

在这个例子中,message 必须是一个字符串,count 必须是一个数字。

# 12. props 的 required 必填项

在声明自定义属性时,可以通过 required 选项,将属性设置为必填项,强制用户必须传递属性的值。

示例





 




export default {
  props: {
    message: {
      type: String,
      required: true // 定义必填项
    }
  }
};
1
2
3
4
5
6
7
8

在这个例子中,如果父组件没有传递 message 属性,Vue 会发出警告,因为 message 被标记为必填项。

# 13. 组件之间的样式冲突问题

默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。导致组件之间样式冲突的根本原因是:

  • 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的。
  • 每个组件中的样式,都会影响整个 index.html 页面中的 DOM 元素。

# 14. 解决组件样式冲突的问题

为了解决样式冲突问题,可以为每个组件分配唯一的自定义属性,并在编写组件样式时,通过属性选择器来控制样式的作用域。示例代码如下:

<template>
  <div class="component" my-unique-attribute>
    <!-- 组件的内容 -->
  </div>
</template>

<style>
/* 使用属性选择器限定样式作用域 */
[my-unique-attribute] .component {
  color: red; /* 这个样式只会作用于带有 my-unique-attribute 属性的元素 */
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12

# 15. style节点的 scoped属性

Vue.js 提供了 scoped 属性用于 <style> 标签中,以确保样式仅在当前组件的范围内生效。这是一个用于解决组件之间样式冲突的功能,特别是在组件化开发中非常有用。

  • 作用:scoped 属性用于将 CSS 样式限定在当前组件内,防止组件之间的样式相互影响。
  • 工作原理:当一个 <style> 标签使用 scoped 属性时,Vue 会为该组件生成一个唯一的属性(如 data-v-xxxxxxx),这个属性会附加到组件的根元素及其子元素上,用于标识该组件的样式作用域。
  • 效果:使得 CSS 规则只作用于特定的组件,避免全局样式的污染。

下面是一个使用 scoped 属性的基本示例:

<template>
  <div class="component">
    <!-- 组件的内容 -->
    <p>This is a component-specific paragraph.</p>
  </div>
</template>

<style scoped>
.component {
  color: red; /* 这个样式只会作用于当前组件的 .component 类 */
}

p {
  font-size: 16px; /* 这个样式只会作用于当前组件内的 p 元素 */
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 15-1 浏览器中查看效果

当你在浏览器的开发者工具中查看组件元素时,会看到每个元素上都有一个自动生成的 data-v-xxxxxxx 属性:

<div class="component" data-v-3c83f0b7>
  <p data-v-3c83f0b7>This is a component-specific paragraph.</p>
</div>
1
2
3

scoped 的局限性

  1. 不支持跨组件样式:scoped 样式不能直接影响子组件的样式。如果需要对子组件进行样式覆盖,可以使用 /deep/ 或 ::v-deep(Vue 3)选择器。
  2. 动态内容的样式应用:对于动态插入的内容,确保它们也带有正确的 data-v-xxxxxxx 属性,否则样式可能不生效。

# 15-2 data-v-xxxxxxx 属性的行为

组件中的 data-v-xxxxxxx 属性

  1. 唯一性:每个组件实例在编译时都会生成一个唯一的 data-v-xxxxxxx 标识符,这个标识符会附加到组件的根元素及其子元素上,用于标识该组件的样式作用域。

  2. 一致性:同一个组件内部的所有元素会使用相同的 data-v-xxxxxxx 属性,以确保组件内样式的作用域一致。

  3. 样式隔离:通过这种机制,Vue.js 实现了组件样式的隔离,确保一个组件的样式不会影响到其他组件,除非有明确的样式穿透(如 /deep/)操作。

嵌套组件中的 data-v-xxxxxxx 属性

  1. 父组件样式作用:当一个父组件中嵌套了一个子组件,父组件的 data-v-xxxxxxx 属性也会附加到子组件的根元素上。这使得父组件定义的样式可以影响子组件的外观。

  2. 子组件独立样式:子组件会生成自己的 data-v-xxxxxxx 属性,用于确保子组件的样式仅在其内部生效。

假设有以下组件结构:

  • ParentComponent 有一个 data-v-aaaaaaa 标识符。
  • ChildComponent 有一个 data-v-bbbbbbb 标识符。

渲染的 DOM 会如下所示:

<div class="parent" data-v-aaaaaaa>
  <div class="child" data-v-aaaaaaa data-v-bbbbbbb>
    <p data-v-bbbbbbb>Child component content</p>
  </div>
</div>
1
2
3
4
5
  • 父组件样式:可以通过 [data-v-aaaaaaa] .child { /* styles */ } 作用于 ChildComponent。
  • 子组件样式:通过 [data-v-bbbbbbb] .child { /* styles */ } 作用于其内部元素。

这种机制允许在保证样式隔离的前提下,父组件的样式仍然可以适度地影响子组件。

# 16. /deep/样式穿透

在 Vue 中,当你使用 scoped 属性时,组件的样式会被限定在组件的模板范围内。这种隔离机制是通过为每个组件生成一个独特的 data-v-* 属性来实现的。这意味着组件样式不会影响到其子组件或其他组件。然而,在某些情况下,你可能希望修改子组件或第三方组件库的默认样式,这时可以使用 /deep/ 选择器来实现样式穿透。

定义:/deep/ 是一个 CSS 选择器,用于在 scoped 样式中穿透组件的样式隔离,使得样式规则能够作用到子组件或深层嵌套的元素上。

用途:主要用于修改子组件样式或覆盖第三方组件库的默认样式。


使用场景

  1. 修改第三方组件库样式
    • 当引入的第三方组件库的默认样式不符合项目需求时,可以使用 /deep/ 选择器来覆盖这些样式。
  2. 对子组件应用样式
    • 在父组件中,需要对子组件中的某些元素应用特定样式时,使用 /deep/ 选择器是一个解决方案。

# 16-1 Vue 2 和 Vue 3 的差异

Vue 2:使用 /deep/

在 Vue 2 中,样式穿透使用 /deep/ 选择器。Vue 2 默认支持 /deep/ 选择器来穿透子组件的样式。

<!-- ParentComponent.vue -->
<template>
  <div class="parent">
    <ChildComponent />
  </div>
</template>

<style scoped>
.parent {
  color: blue; /* 父组件内元素默认颜色为蓝色 */
}

/* 使用 /deep/ 选择器让样式作用于子组件中的 button 元素 */
.parent /deep/ button {
  background-color: red; /* 作用于子组件中的 button */
  color: white;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 在 Vue 2 中,你可以直接使用 /deep/ 来让父组件样式影响子组件中的 button 元素。

Vue 3:使用 :deep()

在 Vue 3 中,/deep/ 选择器已被弃用,官方推荐使用 :deep() 作为深度选择器。这是一个函数形式的 CSS 选择器,用于穿透 scoped 样式隔离。

<!-- ParentComponent.vue -->
<template>
  <div class="parent">
    <ChildComponent />
  </div>
</template>

<style scoped>
.parent {
  color: blue; /* 父组件内元素默认颜色为蓝色 */
}

/* 使用 :deep() 选择器让样式作用于子组件中的 button 元素 */
.parent :deep(button) {
  background-color: red; /* 作用于子组件中的 button */
  color: white;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 在 Vue 3 中,使用 :deep() 代替 /deep/,并把目标选择器作为参数传递给 :deep()。

# 16-2 样式穿透机制原理

  • 原理:使用 scoped 属性时,Vue 会为每个组件生成唯一的 data-v-* 属性,确保组件的样式仅在该组件的范围内生效,避免样式污染。
  • 样式穿透:通过使用 /deep/ 或 :deep() 选择器,父组件的样式可以突破 scoped 的限制,作用到子组件或第三方库的元素上。使用穿透选择器时,可以访问并修改子组件或深层嵌套元素的样式。
  • 效果:在浏览器中查看元素样式时,样式会应用到指定的深层元素,并通过选择器穿透 scoped 样式的作用范围。例如:
    • 父组件的样式:.parent[data-v-3c83f0b7]
    • 使用穿透选择器后:.parent[data-v-3c83f0b7] /deep/ div

注意事项

  1. 性能影响:
    • 过度使用 /deep/ 或 :deep() 选择器可能导致样式管理混乱,降低性能,因此应该谨慎使用。
  2. 兼容性问题:
    • 在 Vue 3 中,推荐使用 :deep() 替代 /deep/,因为 /deep/ 是非标准的,且未来的 CSS 规范可能不再支持它。
  3. 可维护性:
    • 样式穿透会使得组件的样式管理变得复杂,过度依赖穿透可能会影响可维护性。应该尽量考虑使用更清晰的解决方案。
  4. 兼容 Vue 2 和 Vue 3:
    • 如果你需要支持 Vue 2 和 Vue 3,可以通过条件判断或使用不同的 CSS 方案来保证兼容性。例如,在 Vue 2 中使用 /deep/,在 Vue 3 中使用 :deep()。

# 16-3 最佳实践

特性 Vue 2 Vue 3
样式穿透选择器 /deep/ :deep()
使用方式 /deep/ <inner-selector> :deep(<inner-selector>)
推荐理由 Vue 2 支持 /deep/,不需要额外修改 Vue 3 推荐使用 :deep(),符合标准
兼容性问题 Vue 2 项目中使用 /deep/ Vue 3 项目中使用 :deep()
  • Vue 2:继续使用 /deep/ 选择器来穿透子组件或第三方库样式。
  • Vue 3:使用 :deep() 替代 /deep/,这是 Vue 3 中官方推荐的做法,符合 CSS 标准,保证兼容性和可维护性。

通过合理使用这些选择器,能够在不破坏组件封装的情况下,灵活地修改子组件或第三方组件的样式,避免样式冲突和管理问题。

编辑此页 (opens new window)
上次更新: 2025/01/30, 23:55:43
vue.config.js配置
生命周期和数据共享

← vue.config.js配置 生命周期和数据共享→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式