Vue实现组件间通信的七种方式
1. props / $emit
父组件通过props
的方式向子组件传递数据,而通过$emit
子组件可以向父组件通信:
- 父传子:父组件通过import引入子组件,并注册,在子组件标签上添加要传递的属性,子组 件通过props接收,接收有两种形式一是通过数组形式[‘要接收的属性’ ],二是通过对象形式{ }
- 子传父:父组件向子组件传递事件方法,子组件通过
$emit
触发事件,回调给父组件
props的特点:
- props只能是父组件向子组件进行传值,props使得父子组件之间形成一个单向的下行绑定。子组件的数据会随着父组件的更新而响应式更新;但是子组件无法引起父组件的数据更新。
- props可以显示定义一个或一个以上的数据,对于接收的数据,可以是各种数据类型,同样也可以是传递一个对象或函数。
- props属性名规则:若在props中使用驼峰形式,模板中标签需要使用短横线的形式来书写。
代码示例:
父传子(prop的用法)
父组件:
<template><div id="father"><son :msg="msg" :fn="myFunc"></son></div>
</template><script>
import son from "./son.vue";
export default {name: "father",components: {son},data() {msg: "我是父组件";},methods: {myFunc() {console.log("我是父组件的方法");}}
};
</script>
子组件:
<template><div id="son"><p>{{msg}}</p><button @click="fn">按钮</button></div>
</template>
<script>
export default {name: "son",props: ["msg", "fn"]
};
</script>
子传父($emit的用法)
$emit 绑定一个自定义事件,当这个事件被执行的时候就会将参数传递给父组件,而父组件通过v-on监听并接收参数
父组件:
<template><div id="father"><son :arrList="arrList" @changeIndex="changeIndex"></son><p>{{currentIndex}}</p></div>
</template><script>
import son from './son.vue'
export default {name: 'father',components: { son},data() {return {currentIndex: -1,arrList: ['龙族', '绘梨衣', '前端','后端']}},methods: {changeIndex(index) {this.currentIndex = index}}
}
</script>
子组件:
<template><div><div v-for="(item, index) in arrList" :key="index" @click="emitIndex(index)">{{item}}</div></div>
</template><script>
export default {props: ['arrList'],methods: {emitIndex(index) {this.$emit('changeIndex', index) // 触发父组件的方法,并传递参数index}}
}
</script>
2.ref / $refs
ref:这个属性用在子组件上,它的引用就指向了该子组件的实例,可以通过实例来访问组件的数据和方法;如果在普通的 DOM 元素上使用,引用指向的就是 DOM元素。
父组件:
<template><child ref="child"></component-a>
</template>
<script>import child from './child.vue'export default {components: { child },mounted () {console.log(this.$refs.child.name); // mySonthis.$refs.child.sayHello(); // Hello father!}}
</script>
子组件:
<template><div id="app"></div>
</template>
<script>
export default {name:'child',data () {return {name: 'mySon'}},methods: {sayHello () {console.log('Hello father!')}}
}
</script>
3.eventBus(事件总线)
其原理就是:事件订阅发布,eventBus
又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
使用步骤如下:
(1)创建事件中心管理组件之间的通信
// event-bus.jsimport Vue from 'vue'
export const EventBus = new Vue()
(2)发送事件 假设有两个兄弟组件firstCom和secondCom:
firstCom和secondCom的父组件:
<template><div><first-com></first-com><second-com></second-com></div>
</template><script>
import firstCom from './firstCom.vue'
import secondCom from './secondCom.vue'
export default {components: { firstCom, secondCom }
}
</script>
在firstCom组件中发送事件:
<template><div><button @click="add">点击增加</button> </div>
</template><script>
import {EventBus} from './event-bus.js' // 引入事件中心export default {data(){return{num:0}},methods:{add(){EventBus.$emit('addition', {num:this.num++})}}
}
</script>
(3)接收事件
在secondCom组件中接收事件:
<template><div>求和: {{count}}</div>
</template><script>
import { EventBus } from './event-bus.js'
export default {data() {return {count: 0}},mounted() {EventBus.$on('addition', param => {this.count = this.count + param.num;})}
}
</script>
在上述代码中,这就相当于将num值存贮在了事件总线中,在其他组件中可以直接访问。事件总线就相当于一个桥梁,不用组件通过它来通信。虽然看起来比较简单,但是这种方法也有不变之处,如果项目过大,使用这种方式进行通信,后期维护起来会很困难。
4.Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.
Vuex 解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上
Vuex各个模块:
- state:用于数据的存储,是store中的唯一数据源
- getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
- mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件
- actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作
- modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护
Vuex使用步骤:
(1)这里我们先新建 store文件夹, 对Vuex进行一些封装处理
在 store 文件夹下添加 index.js 文件
// index.js// 自动挂载指定目录下的store
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)let modules = {}// @/store/module 目录下的文件自动挂载为 store 模块
const subModuleList = require.context('@/store/modules', false, /.js$/)
subModuleList.keys().forEach(subRouter => {const moduleName = subRouter.substring(2, subRouter.length - 3)modules[moduleName] = subModuleList(subRouter).default
})
//也可自己手动挂载(自行选择)export default new Vuex.Store({state: {},mutations: {},actions: {},modules
})
(2)在 store 文件夹下添加 module
文件夹,在module文件夹再新建 user.js
文件
// user.jsimport user from '@/utils/user.js'
import userApi from '@/apis/user'
import { OPEN_ACCOUNT_STAGE, STAGE_STATUS } from '@/constant'let getUserPromise = nullexport default {namespaced: true,state() {return {userInfo: null, // 用户信息isLogined: !!user.getToken(), // 是否已经登录}},mutations: {// 更新用户信息updateUser(state, payload) {state.isLogined = !!payloadstate.userInfo = payload},},actions: {// 获取当前用户信息getUserInfo(context, payload) {//相关代码},// 登出logout(context, payload = {}) {// 是否手动退出const { manual } = payloadif (manual) {await userApi.postLogout()}user.clearToken()context.commit('updateUser', null)},}
}
(3)然后在项目的 main.js
文件中引入
import Vue from 'vue'
import App from '@/app.vue'
import { router } from '@/router'
import store from '@/store/index'const vue = new Vue({el: '#app',name: 'root',router,store,render: h => h(App),
})
(4)封装很愉快结束了了,然后就正常操作即可
this.$store.state.user.isLogined
this.$store.state.user.userInfo
this.$store.commit('user/updateUser', {})await this.$store.dispatch('user/logout', { manual: true })
5.$attrs与 $listeners
现在我们来讨论另一种情况:如果我们给出的组件关系图中A组件与D组件是隔代关系, 那它们之前进行通信有哪些方式呢?
- 使用props绑定来进行一级一级的信息传递, 如果D组件中状态改变需要传递数据给A, 使用事件系统一级级往上传递
- 使用eventBus,这种情况下还是比较适合使用, 但是碰到多人合作开发时, 代码维护性较低, 可读性也低
- 使用Vuex来进行数据管理, 但是如果仅仅是传递数据, 而不做中间处理,使用Vuex处理感觉有点大材小用了.
所以就有了 $attrs / $listeners ,通常配合 inheritAttrs 一起使用。
inheritAttrs
默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。
通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过实例 property $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。
注意:这个选项不影响 class 和 style 绑定,Vue对class和style做了特殊处理
简单来说就是
- inheritAttrs:true 时继承除props之外的所有属性
- inheritAttrs:false 只继承class 和 style属性
- $attrs:包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。当一个组件没有声明任何 props 时,它包含所有父作用域的绑定 (class 和 style 除外)。
- $listeners:包含了父作用域中的 (不含 .native 修饰符) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。它是一个对象,里面包含了作用在这个组件上的所有事件监听器,相当于子组件继承了父组件的事件。
代码示例:
父组件:
<template><child :name="name" :age="age" :infoObj="infoObj" @updateInfo="updateInfo" @delInfo="delInfo" />
</template>
<script>import Child from '../components/child.vue'export default {name: 'father',components: { Child },data () {return {name: '绘梨衣',age: 22,infoObj: {from: '河北',job: 'superman',hobby: ['reading', 'writing', 'eating']}}},methods: {updateInfo() {console.log('update info');},delInfo() {console.log('delete info');}}}
</script>
儿子组件:
<template><!-- 通过 $listeners 将父作用域中的事件,传入 grandSon 组件,使其可以获取到 father 中的事件 --><grand-son :height="height" :weight="weight" @addInfo="addInfo" v-bind="$attrs" v-on="$listeners" />
</template>
<script>import GrandSon from '../components/grandSon.vue'export default {name: 'child',components: { GrandSon },props: ['name'],data() {return {height: '170cm',weight: '55kg'};},created() {console.log(this.$attrs); // 结果:age, infoObj, 因为父组件共传来name, age, infoObj三个值,由//于name被 props接收了,所以只有age, infoObj属性console.log(this.$listeners); // updateInfo: f, delInfo: f},methods: {addInfo () {console.log('add info')}}}
</script>
孙子组件:
<template><div>{{ $attrs }} --- {{ $listeners }}<div>
</template>
<script>export default {props: ['weight'],created() {console.log(this.$attrs); // age, infoObj, height console.log(this.$listeners) // updateInfo: f, delInfo: f, addInfo: fthis.$emit('updateInfo') // 可以触发 father 组件中的updateInfo函数}}
</script>
6.$parent / $children
- 使用$parent可以让组件访问父组件的实例(访问的是上一级父组件的属性和方法)。
- 使用 $children 可以让组件访问子组件的实例,但是, $children 并不能保证顺序,并且访问的数据也不是响应式的。
注意:
- 通过 $parent 访问到的是上一级父组件的实例,可以使用 $root 来访问根组件的实例
- 在组件中使用$children拿到的是所有的子组件的实例,它是一个数组,并且是无序的
- 在根组件 #app 上拿 $parent 得到的是 new Vue()的实例,在这实例上再拿 $parent 得到的是undefined,而在最底层的子组件拿 $children 是个空数组
- $children 的值是数组,而 $parent是个对象
用法:
<template><div><span>{{message}}</span><p>父组件的值为: {{parentVal}}</p></div>
</template><script>
export default {data() {return {message: 'Vue'}},computed:{parentVal(){return this.$parent.msg;}}
}
</script>
<template><div class="app"><div>{{msg}}</div><child></child><button @click="change">点击改变子组件值</button></div>
</template><script>
import child from './child.vue'
export default {components: { child },data() {return {msg: 'Hello'}},methods: {change() {// 获取到子组件this.$children[0].message = 'JavaScript'}}
}
</script>
7.依赖注入(provide / inject)
这种方式就是vue中依赖注入,该方法用于 父子组件之间 的通信。当然这里所说的父子不一定是真正的父子,也可以是祖孙组件,在层数很深的情况下,可以使用这种方式来进行传值。就不用一层一层的传递数据了。
provide和inject是vue提供的两个钩子,和data、methods是同级的。并且provide的书写形式和data一样。
- provide 钩子用来发送数据或方法
- inject钩子用来接收数据或方法
注意: 依赖注入所提供的属性是非响应式的。
用法:
父组件:
provide() { return { num: this.num };
}
子组件:
inject: ['num']
还有另一种写法,这种写法可以访问父组件中的所有属性:
provide() {return {app: this};
}
data() {return {num: 111};
}inject: ['app']
console.log(this.app.num)
总结
1.父子组件间通信
- 子组件通过 props 属性来接受父组件的数据,然后父组件在子组件上注册监听事件,子组件通过 emit 触发事件来向父组件发送数据。
- 通过 ref 属性给子组件设置一个名字。父组件通过 $refs 组件名来获得子组件,子组件通过 $parent 获得父组件,这样也可以实现通信。
- 使用 provide/inject,在父组件中通过 provide提供变量,在子组件中通过 inject 来将变量注入到组件中。不论子组件有多深,只要调用了 inject 那么就可以注入 provide中的数据
2.跨代组件间通信
跨代组件间通信其实就是多层的父子组件通信,同样可以使用上述父子组件间通信的方法,只不过需要多层通信会比较麻烦。
3.兄弟组件间通信
通过 $parent + $refs 以父组件为中间人来获取到兄弟组件,也可以进行通信。
4.任意组件间通信
使用 eventBus ,其实就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。它的本质是通过创建一个空的 Vue 实例来作为消息传递的对象,通信的组件引入这个实例,通信的组件通过在这个实例上监听和触发事件,来实现消息的传递。
Vue实现组件间通信的七种方式相关推荐
- vue中组件间通信的6种方式
前言 组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互进行直接的引用,所以组件间的相互通信是非常重要的. 除了使用vuex外还有下面6种组件间 ...
- Vue组件间通信的几种方式
引入 用vue可以是要组件复用的,而组件实例的作用域是相互独立,这意味着不同组件之间的数据无法互相引用 一般来说,组件之间可以有几种关系: 如上图所示,A 和 B.B 和 C.B 和 D 都是父子关系 ...
- vue组件间通信的13种方式
前言 vue是数据驱动视图更新的框架, 我们平时开发,都会把页面不同模块拆分成一个一个vue组件, 所以对于vue来说组件间的数据通信非常重要,那么组件之间如何进行数据通信的呢? 首先我们需要知道在v ...
- 容器间通信_Vue组件间通信的6种方式,前端工程师人人都会,网友:太简单了...
Vue 组件间通信只要指以下 3 类通信:父子组件通信.隔代组件通信.兄弟组件通信,下面分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信. (1)props / $emit 适用 父子组件通 ...
- Vue组件间通信的8种方式
1.常用的父子组件通讯方式:props,emit 父组件传入属性,子组件通过props来接收,在子组件中就可以用this.xxx方式使用. 子组件通过$emit(事件名,参数)向外弹出一个自定义事件, ...
- 【Vue】组件间传值的三种方式:父传子,子传父,非父子传值
引用官网的一句话:父子组件的关系可以总结为 prop 向下传递,事件向上传递.父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息,如下图所示: 1,父组件传值给子组件 代码: &l ...
- Vue中组件间通信的方式
Vue中组件间通信的方式 Vue中组件间通信包括父子组件.兄弟组件.跨级组件.非嵌套组件之间通信. props $emit props $emit适用于父子组件的通信,这种组件通信的方式是我们运用的非 ...
- 【Vue父子组件间通信】
Vue父子组件间通信 父子组件通信 父传子 (props) 子传父 (自定义事件) 完整代码 效果 父子组件通信 父传子 (props) (1)在父组件中使用子组件时,给子组件一个自定义属性,这个属性 ...
- 深入浅出,带你看懂Vue组件间通信的8种方案
前言 Vue种组件通信的情况有多种,总结有以下4种情况: 父子组件间通信 兄弟组件间通信 祖孙后代间通信 无关系组件间通信 8种解决方案 通过 props 传递 通过 $emit 触发自定义事件 使用 ...
最新文章
- framer x使用教程_如何使用Framer Motion将交互式动画和页面过渡添加到Next.js Web应用程序
- boost::stl_interfaces::reverse_iterator相关的测试程序
- 【个人感悟】效率,天道酬勤
- WCF Chat获奖人的奖品展示
- SQL Server 2017 RC1 现已实现 Linux 功能扩展
- tomcat如何查找请求资源的?
- 今天的天气好热哦!!!
- linux 添加硬盘
- 我在安装TFS 2008的时候遇到的问题以及解决方法一账户问题
- MATLAB函数取整
- Fortran并行计算的一些例子
- 腾讯支持html5吗,WebQQ全面升级支持IE9 充分运用HTML5优势
- html 里面的 role 属性是什么意思和用途
- vim 保存出错 E45: readonly option is set (add ! to override)
- TypeScript 使用 infer 实现类型推断
- 一篇文章带你深入理解 Java 中的Class.getClassLoader
- 5G 第五代移动通信系统你知多少?
- Material Design(三)--暗色主题设计
- 李飞飞划重点的「具身智能」,走到哪一步了?
- 如何设置EXCEL里标题在每页都打印?