vue 组件之间传值几种方式
今天说一下 vue3 的组件间传值,学习过 vue2 的宝子们肯定知道,组件传值是 vue 项目开发过程中必不可少的功能场景,在 vue2 里面有很多传值的方式,vue3 的传值方式呢,在这里稍微整理总结一下,但是不是很全,大家交流补充哦。
Vue2
1. 父组件
<template>
<div>
<children :title="title" @getChildren="getChildren"></children>
<-- 父组件通过 @监听事件名称="触发的方法"来进行监听 -->
<div>子组件说: {
{ childrenAsk }}</div>
</div>
</template>
<script>
import children from "./children.vue"
export default {
data() {
return {
title: "我是父组件传过来的值",
childrenAsk: ""
}
},
methods: {
getChildren(val) {
this.childrenAsk = val
}
}
}
</script>
2. 子组件
<template>
<div>
<div>父组件传过来的值: {
{ title }}</div>
<button @click="askToFather">点击发送给父组件</button>
</div>
</template>
<script>
export default {
props: {
title: {
type: String
}
},
data() {
return {
askMsg: "这是我给父组件说的话"
}
},
methods: {
askToFather() {
this.$emit("getChildren", this.askMsg)
}
}
}
</script>
3. 兄弟组件
就是创建一个事件中心,相当于中转站,可以用它来传递和接收事件
1.创建全局空 Vue 实例: eventBus
import Vue from 'vue';
const eventBus = new Vue();
export default eventBus;
2.具体页面使用 $emit 发布事件 — 传递值
import eventBus from '@/eventBus'
eventBus.$emit('send','hello')
3.具体页面使用 $on 订阅事件 — 接收组件值
import eventBus from '@/eventBus'
eventBus.$on('send',mes=>{
console.log(msg) //输出 hello
})
注意:
$on 先进行监听,一旦 $emit 发布事件后所有组件都可以 $on 监听到事件。所以传递参数的时候一定已经先进行了监听才能得到参数。比如在父组件中 $emit 事件放在mounted钩子函数中,等待子组件创建并 $on 开始监听事件后再去触发 $emit 发布事件。
4.$off() 移除事件监听
import eventsBus from '@/eventBus
eventBus.$off('send')
vue3
对比vue2来看,vue发生了不小的变化,通过ref,或者reactive实现数据的响应式
在vue2中,子组件向父组件传值通过this.$emit的形式实现,
setup 代替了 beforeCreate 和 Created setup 会在beforeCreate 之前调用
vue3中setup 接收了props 和 context 两个参数,props 接收当前组件props选择的值,即获取父组件传递过来的参数,接收参数后,return出去就可以在模板中使用
contex 接收一个上下文对象,该对象中包含了一些vue2 中需要通过 this 才能访问到的属性
子组件向父组件传值核心的部分是emit事件,子组件绑定一个emit触发事件,父组件监听事件就可以达到子组件向父组件传值的目的。
1. 父组件
<template>
<div style="color: aqua">父组件</div>
<div>子组件对我说:{
{ children_msg }}</div>
<children :title="msg" @listen="listenToChildren"></children>
{
{ value }}
</template>
<script lang="ts">
import children from "@/views/component_emit/children.vue"
import {
defineComponent, ref } from "vue"
export default defineComponent({
components: {
children,
},
name: "father",
setup() {
let msg = "我是父组件"
let children_msg = ref("") // ref的作用是实现响应式, 如果没有ref则不能实现响应式(引用数据类型用reactive)
let listenToChildren = (val) => {
children_msg.value = val // 使用ref包裹的数据,需要通过.value的形式访问他的值
}
return {
msg,
children_msg,
listenToChildren,
}
},
})
</script>
<style></style>
2. 子组件
<template>
<div style="color: brown">子组件</div>
<!-- 父传子使用方法和vue2相同 -->
<div>父组件传过来的值为:{
{ title }}</div>
<button @click="sayToFather">向父组件说话</button>
</template>
<script lang="ts">
import {
defineComponent } from "vue"
export default defineComponent({
name: "children",
props: {
title: {
type: String,
},
},
setup(props, context) {
// context作用是获取上下文对象,
// 如果setup写法为setup(props, { emit })的方式的话,下面的context可以省略
const sayToFather = () => {
const ask = "我是子组件,我对父组件说话"
context.emit("listen", ask)
}
return {
sayToFather,
}
},
})
</script>
<style></style>
3. 兄弟传值
在 Vue3 中,Vue 不再是构造函数,而是 Vue.createApp({}) 返回一个没有 o n 、 on、 on、emit 和 $once 方法的对象。
根据官方文档 Vue 3 迁移指南所建议的,我们可以使用 mitt 或 tiny-emitter 库在组件之间调度事件。
1.安装依赖 yarn add mitt 或 npm install mitt
2.用法
与 Vue2 一样,封装为 myBus.js:
import mitt from 'mitt'
export default mitt()
或者,也可以定义为全局变量:
import {
createApp } from 'vue'
import App from './App.vue'
import mitt from 'mitt'
const app = createApp(App)
app.config.globalProperties.emitter = mitt()
然后封装一个 hooks
// src/hooks/useEmitter.js
import {
getCurrentInstance } from 'vue'
export default function useEmitter() {
const internalInstance = getCurrentInstance()
const emitter = internalInstance.appContext.config.globalProperties.emitter
return emitter
}
当然,为了方便管理,你也可以在需要用到的地方单独引入 mitt。
示例:假设我们有一个 sidebar 和 header,其中包含一个关闭/打开侧栏的按钮,我们需要该按钮来切换侧边栏展开或关闭。
//header 点击传递值
<template>
<button @click="todoSidebar">todoSidebar</button>
</template>
<script setup>
import {
ref } from 'vue'
import useEmitter from '@/hooks/useEmitter'
const sidebarOpen = ref(true)
const emitter = useEmitter()
const toggleSidebar = () => {
sidebarOpen.value = !sidebarOpen.value
emitter.emit('todo-sidebar', sidebarOpen.value)
}
</script>
// sider 接收值
<template>
<aside class="sidebar" :class="{'sidebar--toggled': !isOpen}">
{
{
isOpen }}
</aside>
</template>
<script setup>
import {
ref, onMounted } from 'vue'
import useEmitter from '@/hooks/useEmitter'
const isOpen = ref(true)
const emitter = useEmitter()
onMounted(() => {
emitter.on('todo-sidebar', (isOpen) => {
isOpen.value = isOpen
})
})
</script>