props
props通常用来进行 父 -> 子 之间的组件通信
通信内容也可以是一个组件当中的方法
常规使用方式如下
组合式API
vue
<script setup>
import ChildA from '@/components/ChildA.vue'
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div class="parents">
<h1>父组件</h1>
<ChildA :count="count"/>
</div>
</template>
vue
<script setup>
defineProps({
count: {
type: Number,
default: 0
}
})
</script>
<template>
<div class="child_a">
<h2>ChildA-{{ count }}</h2>
</div>
</template>
选项式API
vue
<template>
<div class="parents">
<h1>父组件</h1>
<ChildA :count="count"/>
</div>
</template>
<script>
import ChildA from '@/components/ChildA.vue'
export default {
name: "Parents",
data(){
return {
count: 1
}
},
components: {
ChildA
}
}
</script>
vue
<template>
<div class="child-a">
<h2>child-a-{{ count }}</h2>
</div>
</template>
<script>
export default {
name: "ChildA",
props: {
count: {
type: Number,
default: 0
}
}
}
</script>
自定义事件:event
自定义事件,通常用来进行 父 -> 子 之间的事件传递
发生在子组件用来告知父组件状态变更,触发一定的代码逻辑或者是传递部分参数
常规使用方式如下
组合式API
vue
<script setup>
import { ref } from 'vue'
import ChildA from '@/components/ChildA.vue'
function parentEvent(){
alert(`父组件传递过来的方法被点击${count.value}次`)
count.value++
}
const count = ref(0)
</script>
<template>
<div class="example">
<h1>Parents</h1>
<ChildA :count="count" @parentEvent="parentEvent"/>
</div>
</template>
vue
<script setup>
defineProps({
count: {
type: Number,
default: 0
}
})
const emit = defineEmits(['parentEvent'])
function onRowClick(){
emit('parentEvent')
}
</script>
<template>
<div class="child-a">
<h2 @click="onRowClick">Child-A-{{ count }}</h2>
</div>
</template>
选项式API
vue
<template>
<div class="parents">
<h1>父组件</h1>
<ChildA :count="count" @parentEvent="parentEvent"/>
</div>
</template>
<script>
import ChildA from '@/components/ChildA.vue'
export default {
name: "Parents",
data(){
return {
count: 1
}
},
components: {
ChildA
},
methods: {
parentEvent(){
alert(`父组件传递过来的方法被点击${this.count}次`)
this.count++
}
}
}
</script>
vue
<template>
<div class="child-a">
<h2 @click="onRowClick">child-a-{{ count }}</h2>
</div>
</template>
<script>
export default {
name: "ChildA",
props: {
count: {
type: Number,
default: 0
}
},
methods: {
onRowClick(){
this.$emit('parentEvent')
}
}
}
</script>
provide / inject
provide 和 inject 通常用来处理跨组件之间的通信
通过props也能实现,但是会造成参数跨越多层级组件穿透传递
组合式API
vue
<script setup>
import { ref, provide } from 'vue'
import ChildA from '@/components/ParentsV3/ChildA.vue'
const count = ref(0)
// 提供响应值
provide('count',count)
// 提供静态值
provide('msg','provide传递过来的数据')
</script>
<template>
<div class="example">
<h1>Parents</h1>
<ChildA />
</div>
</template>
vue
<script setup>
import GrandChild from '@/components/ParentsV3/GrandChild.vue'
</script>
<template>
<div class="child-a">
<h2>Child-A</h2>
<GrandChild />
</div>
</template>
vue
<script setup>
import { inject } from 'vue'
const msg = inject('msg')
const count = inject('count')
</script>
<template>
<div class="example">
V3孙子组件 --- msg-{{ msg }} ---count-{{ count }}
</div>
</template>
选项式API
注意:在选项式API中,provide 默认提供的值是非响应式的,如果需要传递响应式的数据,需要通过 computed 方法返回响应式数据
vue
<template>
<div class="parents">
<h1>父组件</h1>
<ChildA :count="count" @parentEvent="parentEvent"/>
</div>
</template>
<script>
import ChildA from '@/components/ParentsV2/ChildA.vue'
import { computed } from 'vue'
export default {
name: "Parents",
data(){
return {
count: 1
}
},
provide(){
return {
msg: 'v2-provide传递过来的数据',
// count: this.count 这样写是非响应式
count: computed(() => this.count)
}
},
components: {
ChildA
},
}
</script>
vue
<template>
<div class="child-a">
<h2>child-a</h2>
<GrandChild />
</div>
</template>
<script>
import GrandChild from '@/components/ParentsV2/GrandChild.vue'
export default {
name: "ChildA",
components: {
GrandChild
}
}
</script>
vue
<template>
<div class="example">
V2孙子组件--msg--{{ msg }}---count---{{ count }}
</div>
</template>
<script>
export default {
name: "GrandChild",
inject: ['msg','count'],
}
</script>
模拟Vuex(了解)
基与provide / inject 可以无视组件层级进行通信的背景下,我们可以模拟出 Vuex 的功能
试想一下,在一个 Vue 应用中,所有组件的根组件都是 APP.vue
所以当我们在 APP.vue 中通过 provide 暴露出去 state的时候,就是全局共用的状态了
如果我们暴露出去的是 APP.vue 的 this呢?
然后再通过 minxin 插入到APP.vue中,就实现了一个简单版的 Vuex
注意:Vue3 已经不再建议使用 minxin,以及拿不到this了,以下内容了解即可,使用Vue2 语法示例
vue
<script>
import Parents from './components/Parents.vue'
import appMixin from './mixins/index'
export default {
mixins: [appMixin],
components: {
Parents
},
provide(){
return {
app: this
}
}
}
</script>
<template>
<div class="wrapper">
<Parents/>
</div>
</template>
vue
export default {
data(){
return {
msg: 'Hello Vue3',
}
}
}
vue
<script setup>
import Child from './Child.vue'
</script>
<template>
<div class="greetings">
<Child />
</div>
</template>
vue
<template>
<div class="example">
<div>Child----{{ app.msg }}</div>
</div>
</template>
<script>
export default {
inject: ['app'],
}
</script>
找到任意组件实例
Vue3在选项式API中保留了 组件实例调用,理论上基于 <math xmlns="http://www.w3.org/1998/Math/MathML"> p a r e n t 和 parent 和 </math>parent和children,我们可以找到 APP.vue上面所有的组件实例
注意:下面案例中的 父组件范指上级组件,子组件范指下级组件
最近的指定父组件
js
// 由一个组件,向上找到最近的指定父组件
function findComponentUpward(context, componentName) {
let parent = context.$parent;
let name = parent.$options.name;
while (parent && (!name || [componentName].indexOf(name) < 0)) {
parent = parent.$parent;
if (parent) name = parent.$options.name;
}
return parent;
}
export { findComponentUpward }
vue
<template>
<div class="example">
<h1>Parents组件</h1>
<Child />
</div>
</template>
<script>
import Child from './Child.vue'
export default {
name: 'Parents',
components: {
Child
},
data() {
return {
parentMsg: '父组件状态'
};
},
}
</script>
vue
<template>
<div class="example">
<h2>Child</h2>
<div>{{ upwardComp ? upwardComp.parentMsg : '' }}</div>
</div>
</template>
<script>
import { findComponentUpward } from '@/utils/index';
export default {
name: 'Child',
data(){
return {
upwardComp: null
}
},
mounted(){
this.upwardComp = findComponentUpward(this,'Parents')
}
}
</script>
所有的指定父组件
js
//由当前组件开始,向上找到所有的指定父组件
function findComponentsUpward (context, componentName) {
let parents = [];
const parent = context.$parent;
if (parent) {
if (parent.$options.name === componentName) parents.push(parent);
return parents.concat(findComponentsUpward(parent, componentName));
} else {
return [];
}
}
export { findComponentsUpward };
最近的指定子组件
js
// 由一个组件,向下找到最近的指定组件
function findComponentDownward (context, componentName) {
const childrens = context.$children;
let children = null;
if (childrens.length) {
for (const child of childrens) {
const name = child.$options.name;
if (name === componentName) {
children = child;
break;
} else {
children = findComponentDownward(child, componentName);
if (children) break;
}
}
}
return children;
}
export { findComponentDownward };
所有的指定子组件
js
// 由一个组件,向下找到所有指定的组件
function findComponentsDownward (context, componentName) {
return context.$children.reduce((components, child) => {
if (child.$options.name === componentName) components.push(child);
const foundChilds = findComponentsDownward(child, componentName);
return components.concat(foundChilds);
}, []);
}
export { findComponentsDownward };
所有指定组件的兄弟组件
js
// 由一个组件,找到指定组件的兄弟组件
// 第三个参数用来控制是否包含自己
function findBrothersComponents (context, componentName, exceptMe = true) {
let res = context.$parent.$children.filter(item => {
return item.$options.name === componentName;
});
let index = res.findIndex(item => item._uid === context._uid);
if (exceptMe) res.splice(index, 1);
return res;
}
export { findBrothersComponents };
EventBus
EventBus 常用来在两个没有关联之间的页面进行通信,或用在跨多层级之间的组件通信
全局初始化
js
import Vue from 'vue'
Vue.prototype.$EventBus = new Vue()
引入初始化
js
import Vue form 'vue'
export const EventBus = new Vue();
接收事件
vue
<template>
<div>
{{ msg }}
</div>
</template>
<script>
export default {
data(){
return {
msg: ''
}
},
mounted(){
this.$EventBus.$on('testEvnet',(msg) => {
this.msg = msg;
})
}
}
</script>
触发事件
vue
<template>
<div>
<button @click="onBtnClick">EventBus Demo</button>
</div>
</template>
<script>
export default {
methods: {
onBtnClick(){
this.$EventBus.$emit('testEvent','测试EventBus消息接收')
}
}
}
</script>
Pinia
可参考笔者高赞文章