Vue2组件通信
props
父传子
:boom:利用props属性,由父组件传递给子组件,子组件收到的数据不能修改,不然会报错
<template>
<div>
我是父组件
<div>
<child :data="paraentData"></child>
</div>
</div>
</template>
<script>
import child from '../views/Home.vue'
export default {
name:'parent',
data() {
return {
paraentData:'我来自父组件数据'
}
},
components:{
child,
},
}
</script>
++++++++++++++++++++++++++++++
<template>
<div>
<div>子组件</div>
{{data}}
</div>
</template>
<script>
export default {
name: 'child',
props:["data"],
mounted() {
console.log(this.data)
},
}
</script>
$emit
子传父
:boom:在父组件总个子组件身上绑定一个事件,子组件通过this.$emit('父组件给绑定的事件名',传给父组件的数据)调用父组件给绑定的事件将数据作为参数传递给父组件
<template>
<div>
我是父组件
<div>
<child @GetData="GetChildData"></child>
</div>
</div>
</template>
<script>
import child from "../views/Home.vue";
export default {
name: "parent",
components: {
child,
},
methods: {
GetChildData(data) {
alert(data);
},
},
};
</script>
+++++++++++++++++++
<template>
<div>
<div>我是子组件</div>
<button @click="SendData">向父组件传递数据</button>
</div>
</template>
<script>
export default {
name: 'child',
methods:{
SendData(){
this.$emit('GetData','我是来自子组件用$emit传递的数据')
}
}
}
</script>
children
父子互通
:boom:通过children就可以访问组件的实例,拿到实例代表什么?代表可以访问此组件的所有方法和data。(官方更推荐 props和events的传递方法)
:boom:要注意边界情况,如在#app上拿parent得到的是undefined,而在最底层的子组件拿parent和children 的值是数组,而$parent是个对象
<template>
<div>
我是父组件: {{ data }}
<button @click="ChangeChildData">改变子组件中的数据</button>
<div>
<child></child>
</div>
</div>
</template>
<script>
import child from "../views/Home.vue";
export default {
name: "parent",
data() {
return {
data: "000",
};
},
components: {
child,
},
methods: {
ChangeChildData() {
console.log(this.$children[0].message);
this.$children[0].message = "被改变了";
},
},
};
</script>
++++++++++++++++++++++++++
<template>
<div>
<div>我是子组件:{{message}}</div>
<button @click="ChangeParentData">改变父组件中数据</button>
</div>
</template>
<script>
export default {
name: 'child',
data() {
return {
message:'123'
}
},
methods:{
ChangeParentData(){
console.log(this.$parent)
this.$parent.data = '被子组件改变了'
}
}
}
</script>
provide/inject
父传子孙
:boom:祖先元素通过provide选项提供变量,子孙元素可以通过inject选项获取祖先元素提供的变量数据(这里不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据)
//祖先
<template>
<div>
我是父组件: 我将通过provide向子孙组件传递数据
<div>
<child></child>
</div>
</div>
</template>
<script>
import child from "../views/Home.vue";
export default {
name: "helloWorld",
provide:{
data:'传给子组件和孙组件的数据'
},
components: {
child,
},
};
</script>
+++++++++++++++++++++++++
//子组件
<template>
<div>
<div><h4>我是子组件home:{{data}}</h4></div>
<about></about>
</div>
</template>
<script>
import about from './About.vue'
export default {
name: 'home',
inject:['data'],
components:{
about,
},
}
</script>
+++++++++++++++++++++++++++
//孙组件
<template>
<div>
<h4>我是home组件的子组件about:{{data}}</h4>
</div>
</template>
<script>
export default {
name:'about',
inject:['data']
}
</script>
ref/refs
父传子
:boom:ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据
<template>
<div>
我是父组件: 我将通过ref获取子组件实例,数据:{{data}}
<div>
<child ref="home"></child>
</div>
</div>
</template>
<script>
import child from "../views/Home.vue";
export default {
name: "helloWorld",
data() {
return {
data:''
}
},
components: {
child,
},
mounted() {
this.data = this.$refs.home.desc
this.$refs.home.SendData()
},
};
</script>
+++++++++++++++++++++++++
<template>
<div>
<div><h4>我是子组件home</h4></div>
</div>
</template>
<script>
export default {
name: 'home',
data() {
return {
desc:'我是home组件的数据'
}
},
methods: {
SendData(){
console.log(this.desc)
}
},
}
</script>
eventBus
任意组件通信
:boom:思路:先创建出一个事件总局(eventBus),相当于第三方,所有组件都可以来这里存放事件,所有组件都可以来这里调用存放在这里的任何组件(不管是不是自己存放的事件)
:boom:缺点:当多个组件间需要共享数据时,事件的存放和调用就会显得很多,特别是过了一段时间再来维护项目时,很多事件你都不知道是在哪里提出又是在哪里调用的,不好维护
:o:创建事件总局
- 做成模块,要使用的时候导入(较好)
import Vue from 'vue'
const eventBus = new Vue();
export default eventBus;
- 直接将唯一的Vue实例绑定在Vue的原型上,通过this.$eventBus调用
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
beforeCreate(){
Vue.prototype.$eventBus = this
},
render: h => h(App)
}).$mount('#app')
:boom:以下用第一种创建总局方式实现兄弟间的通信示例
<template>
<div>
我是父组件: 让两个儿子实现相互通信
<div>
<home></home>
<about></about>
</div>
</div>
</template>
++++++++++++++++++
<template>
<div>
<div>
我是子组件home
<button @click="SendData">向事件总局存放事件</button>
</div>
</div>
</template>
<script>
import eventBus from "../utils/eventBus";
export default {
name: "home",
data() {
return {
desc: "我是home组件存放在事件总局的事件提供的数据",
};
},
methods: {
SendData() {
//事件名自定义
eventBus.$emit("ShareData", this.desc);
},
},
//销毁的时候移出存放的事件
beforeDestroy() {
eventBus.$off("ShareData", {});
},
};
</script>
+++++++++++++++++++
<template>
<div>
<h4>我是home组件,后面是我从事件总局home兄弟组件存放的事件总获取的数据: {{data}}</h4>
</div>
</template>
<script>
import eventBus from '../utils/eventBus'
export default {
name:'about',
data() {
return {
data:''
}
},
mounted() {
eventBus.$on('ShareData',value => {
this.data = value
})
},
}
</script>
vuex
任意组件通信
:o:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。详情看Vuex篇
//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
homeData:''
},
mutations: {
SetHomeData(state,data){
state.homeData = data
}
},
})
下面为兄弟组件通信
<template>
<div>
<div>我是home组件
<button @click="SendData">点我存放数据</button>
</div>
</div>
</template>
<script>
export default {
name: 'about',
data () {
return {
data: '我来自home'
}
},
methods: {
SendData(){
this.$store.commit('SetHomeData',this.data)
console.log(this.$store)
}
},
}
</script>
++++++++++++++++++++++++++++++
<template>
<div>
<div>
我是兄弟组件about {{data}}
<button @click="GetData">点我获取vuex中home存放的数据</button>
</div>
</div>
</template>
<script>
export default {
name: 'home',
data () {
return {
data: ''
}
},
methods: {
GetData () {
this.data = this.$store.state.homeData
}
},
}
</script>
localStorage/sessionStorage
任意组件
:boom:利用window.localStorage.setItem('key',value)和window.localStorage.getItem('key')可以达到存储数据的目的,和eventBus差不多都是一个第三方中介,缺点容易混乱,可以配合vuex使用可以做数据持久化,需要注意的是存的时候需要将对象转化成JSON格式,取出再转化为对象。
let obj = { name: 'zhangsan', age: 23 }
window.localStorage.setItem('zhangsan',JSON.stringify(obj))
console.log(JSON.parse(window.localStorage.getItem('zhangsan')))
inheritAttrs/$attrs
父传子孙
:boom:attrs 可访问,插槽也是,但父组件传过来HTML结构而子组件没有用 solt 坑位接收时,会被 solts可捡漏
:boom:默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例 property $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。
示例:
<template>
<div>
我是父组件: 让两个儿子实现相互通信
<div>
<home
:name='name'
:age='age'
:gender='gender'></home>
</div>
</div>
</template>
<script>
import home from '../views/Home.vue'
export default {
name: 'helloWorld',
components: {
home,
},
data () {
return {
name: '张三',
age:34,
gender:'男',
}
}
}
</script>
++++++++++++++++++++++++++++++++++++
<template>
<div id="root">
<div>
我是home组件,我用props只接收了name属性,其他传递过来的没接收,name为:{{name}}
<!-- 可以用v-bind直接传递 -->
<about v-bind='$attrs'></about>
</div>
</div>
</template>
<script>
import about from './About.vue'
export default {
name: 'home',
//没接收的age和gender属性被放在了id=root的根标签上(如果不配置inheritAttrs:false的话
props:{
name:''
},
data () {
return {
data: ''
}
},
inheritAttrs:false,//配置之后没用props接收的属性可以通过this.$attts拿到
components:{
about
},
mounted() {
console.log(this.$attrs) //{age: 34, gender: '男'}
},
}
</script>
++++++++++++++++++++
<template>
<div>
<div v-bind="$attrs">我是about组件,我只接收了age属性:{{age}}
<button @click="SendData">我是$attrs:{{$attrs}}</button>
</div>
</div>
</template>
<script>
export default {
name: 'about',
props:{
age:0
},
inheritAttrs:false,
data () {
return {
data: '我来自home'
}
},
methods: {
SendData(){
this.$store.commit('SetHomeData',this.data)
console.log(this.$store)
}
},
}
</script>
Vue3组件通信
:boom:详情请看官方文档的组件基础和组件深入
父传子
props
<!-- parent.vue -->
<template>
<child1 :message1 = "forChild1"/>
</template>
<script setup lang="ts">
import { ref } from "vue";
import child1 from "./Child1.vue";
let forChild1 = ref('message for child1')
</script>
<!-- child1.vue -->
<template>
<div>
Child1——来自父元素内容:{{message1}}
</div>
</template>
<script setup lang="ts">
defineProps(['message1'])
</script>
provide/inject
:boom:祖先元素可以向所有后代元素传递信息
<!-- parent -->
<template>
<child1/>
</template>
<script setup lang="ts">
import { provide } from "vue";
import child1 from "./Child1.vue";
provide('fromParent',{origin:'parent',message:'我来自祖先元素'})
</script>
<!-- child1 -->
<template>
<div>
Child1——来自父元素内容:{{
fromParent ? fromParent.message : '暂时未接收到来自父元素的信息'
}}
</div>
</template>
<script setup lang="ts">
import { inject } from 'vue'
interface FromParent{
origin: string,
message: string
}
const fromParent = inject('fromParent') as FromParent
</script>
attrs
:boom: 父元素绑定在子组件身上的所有属性和事件,除了class和style和背子组件用props接收的外,其他的都可以用 attrs 接收到。如果子组件模板中只有一个根元素,又没有用props和emit接收,那就会穿透绑定到该根元素上,如果该子组件不止一个根元素,则会发出警告
<!-- parent -->
<template>
<child1 :mes1="'myChild1'" :mes2="'myChild2'" @event1="handel1" @event2="handel1" />
</template>
<script setup lang="ts">
import child1 from './Child1.vue'
function handel1(){console.log('我是通过@click传给子元素的方法2')}
function handel2(){console.log('我是通过@click传给子元素的方法2')}
</script>
<!-- child1 -->
<template>
<div>
Child1——来自父元素内容:{{ mes1 ? mes1 : '暂时未接收到来自父元素的信息' }}
</div>
</template>
<script setup lang="ts">
import { useAttrs } from 'vue'
defineProps(['mes1'])
const attrs = useAttrs()
console.log(attrs)
//输出 Proxy {mes2: 'myChild2', __vInternal: 1, onEvent1: ƒ, onEvent2: ƒ}
</script>
<!-- 如果不是 <script setup>模式 -->
<script>
export default {
setup(props, ctx) {
// 透传 attribute 被暴露为 ctx.attrs
console.log(ctx.attrs)
}
}
</script>
子传父
emit
<!-- parent -->
<template>
<child1 @Event1="event1" />
<div>
parent——来自child1的信息:{{
fromChild1 ? fromChild1 : '目前没有收到来自child1的信息'
}}
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import child1 from './Child1.vue'
let fromChild1 = ref('')
function event1(message: string) {
fromChild1.value = message
}
</script>
<!-- child1 -->
<template>
<div>
<div>
<button @click="$emit('event1', 'hello, 我来自child1')">
点击向父元素传递信息
</button>
</div>
</div>
</template>
<script setup lang="ts">
defineEmits(['event1'])
</script>
expose/ref
<!-- parent 需要注意的是:由于生命周期调用的顺序关系,最开始myChild1.value值为null-->
<template>
<child1 ref="myChild1" />
<div><button @click="handle">输出来自child1的expose</button></div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import child1 from './Child1.vue'
const myChild1 = ref(null)
function handle() {
console.log(myChild1.value)
}
</script>
<!-- child1 -->
<script setup lang="ts">
defineExpose({message:'我来自child1'})
</script>
任意组件通信
mitt
:boom:因为vue3没有了eventBus,所以有叫mitt的包代替 先安装 npm i mitt -S
import mitt from 'mitt'
const mymitt = mitt()
export default mymitt
<!-- parent -->
<template>
<child1 />
<div>{{childMessage}}</div>
</template>
<script setup lang="ts">
import child1 from './Child1.vue'
import { ref,onUnmounted } from 'vue'
import mitt from '../utils/mitt'
let childMessage = ref('')
mitt.on('handleChange',handle)
function handle(message:any){
childMessage.value = message
}
onUnmounted(()=>{
mitt.off('handleChange',handle)
})
</script>
<!-- child1 -->
<template>
<button @click="handle">调用绑定在mitt身上的事件</button>
</template>
<script setup lang="ts">
import mitt from '../utils/mitt'
function handle() {
mitt.emit('handleChange', '我来自child1')
}
</script>
pinia
:boom:相当于Vuex5.0,去除了mutation,只有 state、getters、actions
:boom: npm i pinia
// main.ts
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)
// parentStore.js
import { defineStore } from "pinia";
export const useParentStore = defineStore('parent',{
state: () => {
return{
message1: 'child1',
message2: 'child2'
}
},
getters:{
getMes1(state){
return '我来自parentStore ' + state.message1
},
getMes2(state){
return '我来自parentStore ' + state.message2
}
},
actions:{
updateMessage1(tartget:any){
console.log(tartget)
this.message1 = tartget
},
updateMessage2(tartget:any){
this.message2 = tartget
}
}
})
<!-- child1.vue -->
<template>
<div>{{getMes1}}</div>
<button @click="handle">调用parentStore上的actions事件</button>
</template>
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useParentStore } from "../store/parent";
// 想要解构出的变量有响应,必须借助storeToRefs辅助函数
const parentStore = useParentStore()
const { getMes1 } = storeToRefs(parentStore)
function handle() {
parentStore.updateMessage1('我被child1修改了')
}
</script>
<template>
<div>{{getMes2}}</div>
<button @click="handle">调用parentStore上的actions事件</button>
</template>
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useParentStore } from "../store/parent";
// 想要解构出的变量有响应,必须借助storeToRefs辅助函数
const parentStore = useParentStore()
const { getMes2 } = storeToRefs(parentStore)
function handle() {
parentStore.updateMessage2('我被child2修改了')
}
</script>