
🚀 欢迎来到我的CSDN博客:Optimistic _ chen
✨ 一名热爱技术与分享的全栈开发者,在这里记录成长,专注分享编程技术与实战经验,助力你的技术成长之路,与你共同进步!
🚀我的专栏推荐:
| 专栏 | 内容特色 | 适合人群 |
|---|---|---|
| 🔥C语言从入门到精通 | 系统讲解基础语法、指针、内存管理、项目实战 | 零基础新手、考研党、复习 |
| 🔥Java基础语法 | 系统解释了基础语法、类与对象、继承 | Java初学者 |
| 🔥Java核心技术 | 面向对象、集合框架、多线程、网络编程、新特性解析 | 有一定语法基础的开发者 |
| 🔥Java EE 进阶实战 | Servlet、JSP、SpringBoot、MyBatis、项目案例拆解 | 想快速入门Java Web开发的同学 |
| 🔥Java数据结构与算法 | 图解数据结构、LeetCode刷题解析、大厂面试算法题 | 面试备战、算法爱好者、计算机专业学生 |
| 🔥Redis系列 | 从数据类型到核心特性解析 | 项目必备 |
🚀我的承诺:
✅ 文章配套代码:每篇技术文章都提供完整的可运行代码示例
✅ 持续更新:专栏内容定期更新,紧跟技术趋势
✅ 答疑交流:欢迎在文章评论区留言讨论,我会及时回复(支持互粉)
🚀 关注我,解锁更多技术干货!
⏳ 每天进步一点点,未来惊艳所有人!✍️ 持续更新中,记得⭐收藏关注⭐不迷路 ✨
📌 标签:#技术博客 #编程学习 #Java #C语言 #算法 #程序员
文章目录
scoped
前面忘记说style标签中的scoped属性,我们在vue文件中使用vbase快捷键(安装 Vue VSCode Snippets 插件即可)生成的Vue 组件的初始模板代码中style默认会携带scoped属性。
scoped通常指的是一种样式作用域,使用 scoped 样式可以确保 CSS 只对当前组件生效,从而防止全局样式污染,让样式只作用于当前组件的标签。
父组件:
javascript
<template>
<div class="box">
<p>父组件的段落</p>
<!-- 引入子组件 -->
<Child />
</div>
</template>
<script>
import Child from './Child.vue'
export default {
components: {
Child
}
}
</script>
<style scoped>
/* 这里也加了 scoped,样式仅作用于当前组件 */
.box {
border: 2px solid red; /* 红色边框 */
padding: 10px;
}
p {
color: red; /* 红色文字 */
}
</style>
子组件:
javascript
<template>
<div class="child-box">
<p>子组件的段落</p>
</div>
</template>
<style scoped>
/* 这里加了 scoped,样式仅作用于当前组件 */
.child-box {
border: 2px solid blue; /* 蓝色边框 */
padding: 10px;
margin: 10px;
}
p {
color: blue; /* 蓝色文字 */
font-weight: bold;
}
</style>

scoped原理
上面的例子佐证了scoped的作用,观察F12开发者工具,Vue给父组件里的所有元素(包括最外层的 div)添加了一个类似data-v-7a7a37b1的特殊属性。同时,它把父组件的<style scoped>里的所有 CSS 选择器,都改成了类似 .box[data-v-7a7a37b1] 的形式。

父组件的样式规则就只能匹配到带有 data-v-f3f3eg9 这个属性的元素,而无法影响到子组件内部的元素,因为子组件内部的元素拥有的是另一个不同的属性。这就实现了样式的隔离。
组件通信
在上篇博客中我们探讨了组件和组件化的概念。当项目被拆分为多个独立组件后,数据是否也需要拆分呢?显然不行。为了实现组件间的数据共享,当一个组件需要访问另一个组件的数据时,就需要用到组件通信机制。
进行组件通信,首要明确两个组件之间的关系,进而选择不同的通信方案。
比如:父子关系 :谁被使用,谁就是子组件,当前组件就是父组件
非父子关系 :跨组件通信
父子间通信
父子间通信分为两部分: 父传子 与 子传父
Props声明:一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是props。
使用组合式API的<script setup>单文件组件中,props可以使用defineProps()来声明;
javascript
<script setup>
//数组方法
const props = defineProps(['foo'])
console.log(props.foo)
</script>
在选项式API的组件中,props使用props选项来声明
javascript
export default {
props: ['foo'],
setup(props) {
// setup() 接收 props 作为第一个参数
console.log(props.foo)
}
}
上述使用数组方法声明,我们更多的时候使用对象来声明:
javascript
//完整语法
const props = defineProps({
属性名: {
type: 类型, // Number String Boolean ...
required: true, // 是否必填
default: 默认值, // 默认值
// value: ⽗传⼦的值
validator(value) {
//⾃定义校验逻辑
return 布尔值
}
}
})
//对象方法
defineProps({
title: String,
likes: Number
})
以对象形式声明的每个属性,key 是 prop 的名称,而值则是该 prop 预期类型的构造函数。
一句话总结:props 就是子组件声明它可以接收哪些数据,然后父组件通过自定义属性的形式把这些数据传进去
父传子(子接父传)
传递步骤:
- 子组件通过
defineProps接收数据(子接) - 父组件通过
自定义属性传递数据(父传)

父组件:
javascript
<template>
<div class="parent">
<h3>父组件</h3>
<!-- 传递 props -->
<!-- 自定义属性:message age user -->
<Child
message="Hello from parent"
:age="25"
:user="{ name: '张三', age: 18 }"
/>
</div>
</template>
<script setup>
import Child from './Child.vue'
</script>
子组件:
javascript
<template>
<div class="child">
<h3>子组件</h3>
<p>收到的消息:{{ message }}</p>
<p>年龄:{{ age }}</p>
<p>用户信息:{{ user.name }} - {{ user.age }}</p>
</div>
</template>
<script setup>
// 定义 props(不需要引入,直接使用)
const props = defineProps({
message: String,
age: Number,
user: {
type: Object,
default: () => ({})
}
})
</script>
总结:所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。也就是说,数据还是属于父组件的,子组件只能"看"而不能写。
子传父
想要修改数据子组件通知(emit)父组件,让父组件修改后重新传递数据,子组件重新渲染即可
- ⽗组件内,⼦组件标签上,绑定(监听)⾃定义事件,
@⾃定义事件="⽗修改数据的函数"(⽗绑定) - ⼦组件恰当时机,触发⽗组件的⾃定义事件,
emit('⾃定义事件', 携带的参数...), 从⽽导致⽗组件修改函数的时候(⼦触发)

父组件:
javascript
<template>
<div class="parent">
<h3>父组件</h3>
<p>从子组件接收到的消息:{{ receivedMessage }}</p>
<p>从子组件接收到的计数:{{ childCount }}</p>
<!-- 监听子组件的事件 -->
<Child
@send-message="handleMessage"
@update-count="handleCount"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const receivedMessage = ref('')
const childCount = ref(0)
const handleMessage = (msg) => {
receivedMessage.value = msg
}
const handleCount = (count) => {
childCount.value = count
}
</script>
子组件:
javascript
<template>
<div class="child">
<h3>子组件</h3>
<p>当前计数:{{ count }}</p>
<button @click="sendToParent">发送数据给父组件</button>
<button @click="updateCount">更新内部计数</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
// 定义要触发的事件
const emit = defineEmits(['send-message', 'update-count'])
// 子组件自己的数据
const count = ref(0)
// 触发事件,传给父组件
const sendToParent = () => {
emit('send-message', '这是子组件传来的数据')
}
const updateCount = () => {
count.value++
// 把最新的 count 传给父组件
emit('update-count', count.value)
}
</script>

props数据和响应式数据的区别
| 角度 | 响应式数据 | props |
|---|---|---|
| 谁的数据 | 组件自己的 | 父组件的 |
| 能否修改 | 能✅ | 不能❌ |
| 定义方式 | 组件内部 | 父组件传递 |
| 更新方式 | 随数据变化 | 父组件更新才会更新 |
| API | ref() reavtive() | defineProps() |
非父子间通信
当两个组件不是直接的父子关系时(比如兄弟组件、隔代组件、无关联组件),就需要用其他方式通信。
方案一:
安装第三方库mitt
bash
npm i mitt
创建事件(中间人)
javascript
// utils/eventBus.js
import mitt from 'mitt'
const emitter = mitt()
export default emitter
组件A发送数据
javascript
<template>
<div>
<h3>组件A</h3>
<button @click="sendMessage">发送消息</button>
</div>
</template>
<script setup>
import eventBus from '../utils/eventBus'
const sendMessage = () => {
// 发送事件,任何地方都能监听
eventBus.emit('message', '你好,我是组件A')
}
</script>
组件B接收数据
javascript
<template>
<div>
<h3>组件B</h3>
<p>收到的消息:{{ message }}</p>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import eventBus from '../utils/eventBus'
const message = ref('')
// 监听事件
onMounted(() => {
eventBus.on('message', (msg) => {
message.value = msg
})
})
// 组件销毁时取消监听(重要!)
onUnmounted(() => {
eventBus.off('message')
})
</script>
方法二:状态管理(Pinia )
当应用复杂,很多组件需要共享状态时,用专门的状态管理库,所有数据都写到管理库,其他组件直接去调用接口
安装
bash
npm i pinia
创建store
javascript
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
user: { name: '张三' }
}),
actions: {
increment() {
this.count++
},
setUser(name) {
this.user.name = name
}
}
})
使用
javascript
<template>
<div>
<p>计数:{{ counter.count }}</p>
<p>用户:{{ counter.user.name }}</p>
<button @click="counter.increment()">增加</button>
</div>
</template>
<script setup>
import { useCounterStore } from '../stores/counter'
const counter = useCounterStore()
// 直接使用,任何组件都能访问同一个状态
</script>
总结:mitt 是广播 而 Pinia 集中管理
完结撒花!🎉

如果这篇博客对你有帮助,不妨点个赞支持一下吧!👍
你的鼓励是我创作的最大动力~
✨ 想获取更多干货? 欢迎关注我的专栏 → optimistic_chen
📌 收藏本文,下次需要时不迷路!
我们下期再见!💫 持续更新中......
悄悄说:点击主页有更多精彩内容哦~ 😊
