父子组件通信
vue采用的是单向数据流的方式进行通信。数据修改权始终在父组件身上。
- :bind props
父组件通过:bind 绑定data里的数据状态。 或者setup函数里的数据状态。
js
<ChildOne :msg1="msg1" :msg2="msg2"/>
而子组件通过props或者defineProps来限定数据类型与数据是否必须。不过vue3.2之后这种编译宏就无需显示的导入了。
js
export default {
props:["msg1","msg2"],
setup(props){
console.log(props)
}
}
js
defineProps({
// msg2:String
msg2:{
type:String,
required: true
}
})
2.emit
子组件首先通过定义defineEmit定义子组件能触发哪些事件,接着使用emit给固定的事件推送消息。
js
<template>
<div>
<button @click="handleClick">按钮</button>
<button @click="emit('myClick','hello')">按钮</button>
</div>
</template>
<script setup>
import { defineEmits } from 'vue'
const emit = defineEmits(['myClick'])
const handleClick = () => {
emit('myClick','hello')
}
</script>
而父组件通过自定义事件来接受参数。
js
<template>
<EmitChild @myClick="onMyClick"></EmitChild>
</template>
<script setup>
import EmitChild from './EmitChild.vue'
const onMyClick = (msg) => {
console.log('子组件的消息:' + msg)
}
</script>
3.expose ref 在vue2中父组件能通过获取子组件对象获取所有的属性和方法,这种方法不安全。vue3引入了编译宏defineExpose,父组件只能访问子组件Expose的方法和属性。提高了组件的封装性。 子组件通过defineExpose来向外暴露本身的属性。父组件通过组件的ref来绑定组件的对象。获取属性
js
<script setup>
import { ref } from 'vue'
const internalCounter = ref(0)
const publicApi = {
getCount: () => internalCounter.value,
increment: () => internalCounter.value++
}
defineExpose(publicApi)
</script>
js
<script setup>
const childRef = ref(null)
// 只能通过暴露的API操作
childRef.value?.increment()
console.log(childRef.value?.getCount())
</script>
4.v-model :其实是自定义事件和:bind的缩写
js
<CustomInput v-model="searchText" />
<!-- 等价于 -->
<CustomInput
:modelValue="searchText"
@update:modelValue="newValue => searchText = newValue"
/>
5.provide inject
可以给后代组件使用,无论嵌套多深的后代组件都能使用。但是他的数据流向不清晰,可能还会出现祖父明明冲突,父组件会覆盖。
js
// Parent.vue
<script setup>
import { provide } from "vue"
provide("name", "沐华")
</script>
// Child.vue
<script setup>
import { inject } from "vue"
const name = inject("name")
console.log(name) // 沐华
</script>
6.attrs 父组件通过给子组件传递属性。 接着子组件通过useAttrs hooks来接收。他能访问到没有被props显示声明的属性。 并且可以做到组件属性穿透传递
js
<template>
<MiddleLayer
data-important="true"
@deep-event="handleDeepEvent"
/>
</template>
<script setup>
const handleDeepEvent = (val) => {
console.log('Received from deep child:', val)
}
</script>
js
<template>
<!-- 完全透明的中间层 -->
<DeepChild v-bind="$attrs" />
</template>
js
<template>
<button
:data-important="$attrs.dataImportant"
@click="triggerEvent"
>
Click me
</button>
</template>
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
const triggerEvent = () => {
if(attrs.onDeepEvent) {
attrs.onDeepEvent('Data from deep')
}
}
</script>
7.mitt 他是一个基于发布订阅模式的一个监听系统 我们将mitt放到全局属性配置中。在使用的地方通过上下文获取。确保他是单例模式。
- 入口配置事件总线
js
import { createApp } from 'vue'
import App from './App.vue'
import mitt from 'mitt'
const app = createApp(App)
app.config.globalProperties.$eventBus = mitt() // 此行是关键配置
app.mount('#app')
2.发送方
js
<script setup>
import { getCurrentInstance } from 'vue'
const sendNotification = () => {
const bus = getCurrentInstance().appContext.config.globalProperties.$eventBus
bus.emit('message', {
from: 'Sender',
content: '重要通知'
})
}
</script>
<template>
<button @click="sendNotification">发送全局通知</button>
</template>
3.接收方
js
<script setup>
import { onMounted, onUnmounted } from 'vue'
import { eventBus } from '../main.js'
const handler = (msg) => {
console.log('收到消息:', msg)
}
// 组件挂载时监听
onMounted(() => {
eventBus.on('message', handler)
})
// 组件销毁时移除
onUnmounted(() => {
eventBus.off('message', handler)
})
</script>
8.pinia
js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app') // 对应你当前文件的 <div id="app">
js
// store.js
import { defineStore } from 'pinia';
export const useMainStore = defineStore('main', {
state: () => ({
count: JSON.parse(localStorage.getItem('count')) || 0,
}),
actions: {
increment() {
this.count++;
localStorage.setItem('count', JSON.stringify(this.count)); // 保存到 localStorage
},
},
});
js
<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// ✅ 正确解构方式
const { count, doubleCount } = storeToRefs(store)
// ✅ 方法可直接解构(方法引用是稳定的)
const { increment } = store
</script>
pinia还可以对数据进行持久化。可以通过插件进行配置,也可以自己手动配置,将数据持久化到localstorage或者sessionStorage;
js
// main.js
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(createPersistedState({
storage: localStorage, // 默认存储位置
key: id => `__persisted__${id}`, // 自定义存储键名
serializer: { // 序列化配置
serialize: JSON.stringify,
deserialize: JSON.parse
}
}))
app.use(pinia)
js
import { defineStore } from 'pinia';
import { ref, computed, watch } from 'vue';
export const useMainStore = defineStore('main', () => {
// 状态
const count = ref(Number(localStorage.getItem('count')) || 0);
// 计算属性
const doubleCount = computed(() => count.value * 2);
// 监听状态变化
watch(count, (newValue) => {
localStorage.setItem('count', newValue);
});
return { count, doubleCount };
});