Vue2的16种传参通信方式总结和示例讲解
作者:Monstar_0°-蒙
前言
先直入主题列出有哪些传参方式,下面再通过事例一一讲解。
props
(父传子)$emit
与v-on
(子传父)EventBus
(兄弟传参).sync
与update:
(父子双向)v-model
(父子双向)ref
$children
与$parent
$attrs
与$listeners
(爷孙双向)provide
与inject
(多层传参)Vuex
(全局)Vue.prototype
(全局)
路由
浏览器缓存 (全局)window
(全局)$root
(顶层)slot
(父传子)
一、props
(父传子)
思路简述:父组件直接用冒号
:
绑定变量,然后子组件通过props
接收父组件传过来的内容。
父组件代码:核心代码在第3
行,直接用:message="message"
传参。
<template> <div> <child :message="message" /> </div> </template> <script> import child from './child .vue'; export default { components: { child }, data() { return { message: '这是父组件传过去的' }; } }; </script>
子组件代码: 用props
接收消息后可以直接使用,如下第3
行和第16
行中直接使用
注意: props
有两种接收方法,如下第9
行注释的就是简写用法,此用法不能设置默认值。
<template> <div> <p>接收到的消息: {{ message }}</p> </div> </template> <script> export default { //props:['message'], props: { message: { type: String, default: '', // 这里能设置默认值,如果父组件没有传参,默认值会生效 }, }, mounted() { console.log(this.message); }, }; </script>
注意: 此传参方式是单向的,即子组件接收到父组件的数据后,是不能直接修改props
接收的数据,否则会直接报错。
二、$emit
与v-on
(子传父)
思路简述: 子组件通过$emit
触发父组件的指定方法并且在此方法中携带任意参数,父组件通过在被触发的方法中拿到携带的参数完成子传父。
语法:$emit(方法名,参数)
子组件代码:核心代码在第11
行,触发方法并且携带参数。
<template> <div> <button @click="sendParent">点击发送数据给父组件</button> </div> </template> <script> export default { methods: { sendParent() { this.$emit('my-event', '这是传递的参数'); }, }, }; </script>
父组件代码:核心代码在第3
行触发事件,获取到子组件的传参。
<template> <div> <child v-on:my-event="childEvent" /> // 或者用@简写代替v-on <child @my-event="childEvent" /> </div> </template> <script> import child from './child.vue'; export default { components: { child, }, methods: { childEvent(item) { console.log('接收到子组件的传参:', item); }, }, }; </script>
三、EventBus
(兄弟传参)
思路简述: 先创建一个全局的事件总线Bus
(可以随意命名),并挂载在Vue.prototype
上。
然后兄弟组件A
通过$emit
发送参数,兄弟组件B
通过$on
接收参数。
- 有两种使用方法,下面分别讲解。
方法一:直接挂载全局事件总线,全局直接使用不需要额外引入。
先在项目的入口文件中(main.js
或main.ts
)创建全局事件Bus
并且挂载在Vue.prototype
*
import Vue from 'vue'; const Bus = new Vue(); Vue.prototype.$Bus = Bus;
兄弟组件A
代码:通过this.$Bus.$emit(方法名,参数)
发送参数。
<template> <div> <button @click="sendSibling">点击发送内容给兄弟组件</button> </div> </template> <script> export default { methods: { sendSibling() { this.$Bus.$emit('my-event', '参数'); }, }, }; </script>
兄弟组件B
代码:通过this.$Bus.$on(对应$emit的方法,本地方法)
触发本地的方法,从而接收参数。
<template> <div> 我是兄弟组件B </div> </template> <script> export default { created() { this.$Bus.$on('my-event', this.handleMessage); }, beforeDestroy() { this.$Bus.$off('my-event', this.handleMessage); }, methods: { handleMessage(message) { console.log('来自兄弟的参数:', message); }, }, }; </script>
注意: 如上第10-12
行所示,在组件销毁前要在 beforeDestroy
生命周期中使用$off
移除移除$on
的事件监听器,防止避免内存泄漏影响性能。如下所示
方法二: 不创建全局事件总线,单独开一个文件,哪里需要就哪里引用。
创建一个单独文件命名为Bus.js
(可以自由命名)
import Vue from "vue" export default new Vue()
兄弟组件A
代码: 先引入Bus.js
文件,然后通过Bus.$emit(方法名,参数)
发送参数。
<template> <div> <button @click="sendSibling">点击发送内容给兄弟组件</button> </div> </template> <script> import Bus from './Bus.js'; export default { methods: { sendSibling() { Bus.$emit('my-event', '参数'); }, }, }; </script>
兄弟组件B
代码:先引入Bus.js
文件,然后通过Bus.$on(对应$emit的方法,本地方法)
触发本地的方法,从而接收参数。同样也需要使用$off
销毁事件监听。
<template> <div> 我是兄弟组件B </div> </template> <script> import Bus from './Bus.js'; export default { created() { Bus.$on('my-event', this.handleMessage); }, beforeDestroy() { Bus.$off('my-event', this.handleMessage); }, methods: { handleMessage(message) { console.log('来自兄弟的参数:', message); }, }, }; </script>
四、.sync
与update:
(父子双向)
思路简述:
.sync
其实是一个语法糖, 配合子组件用this.$emit('update:绑定的属性名', 方法)
修改父组件属性, 能解决props
只能单向传递的问题。
父组件代码:核心代码在第3行,比普通的父传子多使用了.sync
修饰符。
<template> <div> <chile :myprop.sync="myData" /> </div> </template> <script> import chile from './chile.vue'; export default { components: { chile }, data() { return { myData: '父组件数据' }; } }; </script>
子组件代码:核心代码是第14
行,通过this.$emit
同步修改父组件内容。
<template> <div> <button @click="updateData">点击子修改父传过来的数据</button> </div> </template> <script> export default { props: { myprop: String, }, methods: { updateData() { this.$emit('update:myprop', 新内容); }, }, }; </script>
注意:使用.sync
修饰符时,this.$emit
里面总是以update:
开头,后面接要修改的属性名称。
五、v-model
(父子双向)
思路简述:v-model
最常用于表单,它其实是一个语法糖,并且和上面.sync
有点类似。v-model
本质上是v-bind:value
和@input
组件效果。通过v-bind:value
绑定数据父传子,通过@input
触发对应事件子传父从而实现双向绑定。
父组件代码:直接用v-model
绑定要传给子组件的参数,当子组件触发input
事件时父组件myData
会同步更新。
<template> <div> <child v-model="myData" /> </div> </template> <script> import child from './child.vue'; export default { components: { child }, data() { return { myData: '天天鸭' }; } }; </script>
子组件代码:当input
输入框的内容发生变化时,就会触发@input
事件,然后this.$emit
同步修改父组件的值
<template> <div> <input :value="childData" @input="handleChange" /> </div> </template> <script> export default { model: { prop: 'myProp', event: 'input' }, props: { myProp: String }, data() { return { childData: this.myProp }; }, methods: { handleChange(event) { this.childData = event.target.value; this.$emit('input', this.childData); } } }; </script>
注意:在子组件当中,必须要定义model
来指定props
和事件名称(名称默认为input
)。
六、ref
思路讲解: ref
主要用来访问子组件的方法和属性,是直接操纵DOM
的方式。主要用法是在子组件上绑定一个ref
,然后父组件用this.$refs
直接访问子组件的方法
父组件代码:子组件上用ref="refChild"
绑定一个ref
,然后用this.$refs.refChild
获取到子组件实例,能获取到子组件属性和操纵子组件方法。
<template> <div> <child ref="refChild" /> </div> </template> <script> import child from './child.vue'; export default { components: { child, }, mounted() { let childObj = this.$refs.refChild; console.log(childObj.name); // 直接获取到子组件的属性内容 打印出来:天天鸭 childObj.childMethod('参数'); // 触发子组件的方法 }, }; </script>
子组件代码:
<template> <div></div> </template> <script> export default { data() { return { name: '天天鸭', }; }, methods: { childMethod(val) { console.log(val); }, }, }; </script>
七、$children
与$parent
简述: $children
和 $parent
是Vue
用于访问子组件实例和父组件实例的特殊属性。其中$children
能获取所有子组件实例但不能获取孙子的,而$parent
获取当前组件的父组件实例。
父组件代码: 直接使用this.$children
即可获取。
注意: 获取到的实例可能为空,因此需要判空。并且如果有多个子组件时返回的是一个数组,所以需要通过下标确认对应的子组件数据。
<template> <div> <child /> <button @click="getChildMethod">调用子组件方法</button> </div> </template> <script> import child from './child.vue'; export default { components: { child, }, methods: { getChildMethod() { // 判空,然后用下标获取 if (this.$children.length > 0) { this.$children[0].childMethod(); // 使用子组件方法 this.$children[0].name; // 使用子组件属性 } }, }, }; </script>
子组件代码: 类似地,父组件也是同样用法,但区别是返回的不是数组而且一个对象,能直接使用。
<template> <div></div> </template> <script> export default { mounted(){ this.$parent.parMethod() this.$parent.name } }; </script>
八、$attrs
与$listeners
(爷孙双向)
简述: $attrs
和 $listeners
相当于是使用在父亲组件上的一个中转站。 $attrs
用于将props
外的数据从爷组件传递给孙组件的,而$listeners
用于从孙组件中触发爷组件中事件达到传参效果。
下面把$attrs
与$listeners
分开讲解更易于理解。
$attrs
使用流程代码:
(1)爷组件代码: 类似父传子,正常用冒号:
绑定属性传参。
<GrandParent :name=name></GrandParent>
(2)父组件代码:$attrs
作用在父组件,意思是把props
之外属性全部传递给到孙子。
注意:如果这里父组件用
props
接收了name
属性,那么用$attrs
无法传递到孙子组件,因为只能传递props
之外属性。
<Parent v-bind="$attrs"></Parent>
(3)孙组件代码:类似父传子,正常用popos
接收爷组件传过来的参数。
<template> <div> 爷组件传来的:{{ name }} </div> </template> <script> export default { props: { name: { default: String, }, }, }; </script>
$listeners
使用流程代码:
(1)孙组件代码 直接this.$emit
类似子传父
<template> <div> <el-button @click="update">点击孙传爷</el-button> </div> </template> <script> export default { methods: { update() { this.$emit('my-event', '孙传给爷的数据'); }, }, }; </script>
(2)父组件代码:$listeners
作用在父组件。
<Parent v-bind="$listeners"></Parent>
(3)爷组件代码: 类似子传父中的父组件,触发对应孙子组件this.$emit
中的my-event
事件接收到参数。
<template> <div> <Parent @my-event="getMyEvent" /> </div> </template> <script> import Parent from './Parent.vue'; export default { components: { Parent, }, methods: { getMyEvent(val) { console.log('爷组件接收到的数据:', val); }, }, }; </script>
这里感觉算两种(爷传孙与孙传爷)传参方式了,但由于都是类似中转站效果,所以放一起说比较好理解。
九、provide
与inject
(多层传参)
简述: provide
与inject
无论多少层组件都能传参。顶层组件通过provide
传参,下面所有组件都能用inject
接收,而且子组件也能通过方法给顶层组件传参。
顶层组件代码: 核心代码在第8
行的provide()
中,可以传递常量、变量和方法。
<template> <div> </div> </template> <script> export default { provide() { return { name: '天天鸭', age: this.age, myMethod: this.myMethod, }; }, data() { return { age: '18', }; }, methods: { myMethod(data) { console.log('收到来自某个孙子的数据:', data); }, }, }; </script>
子孙组件代码:核心代码在第10
行接收参数, 除了能接收顶层参数外,还能通过参考第13
行的用法,通过顶层给到的方法传参给顶层组件。
<template> <div> <el-button @click="myMethod('我是孙子组件')">我是孙子组件</el-button> 这是顶层传下来的参数: {{ name }} </div> </template> <script> export default { inject: ['name', 'age', 'myMethod'], methods: { myMethod(data) { this.myMethod(data); // 传参给顶层祖先组件 }, }, }; </script>
十、Vuex
(全局)
有针对性写过对应的文章,可以直接跳转细看:对比学习vuex和pinia用法
十一、Vue.prototype
(全局)
简述:能用Vue.prototype
把任何属性和方法挂载在Vue
实例了,让所有Vue
实例共用。
(1)挂载属性 直接往Vue.prototype
挂载即可
Vue.prototype.$testName = '天天鸭';
(2)挂载方法直接往Vue.prototype
挂载即可
Vue.prototype.$testMethod = function(val) { console.log(val); };
调用:直接在任何页面用this
调用
this.$appName; this.$testMethod('参数');
十二、浏览器缓存
简述: localStorage
和sessionStorage
:主要是浏览器用来持久化存储的,这算是用的不多,但也是必用的一种通信方式。两者区别如下
sessionStorage
(临时存储):最大空间5M
,为每一个数据源维持一个存储区域,但只在浏览器打开期间存在,关闭后数据会不会消失,包括页面重新加载。localStorage
(长期存储):最大空间5M
,与 sessionStorage 一样,但是哪怕浏览器关闭后,数据依然会一直存在,除非手动删除。
具体用法如下所示:
// 存储 sessionStorage.setItem('key', 'value'); localStorage.setItem('key', 'value'); // 获取 let valueFromSessionStorage = sessionStorage.getItem('key'); let valueFromLocalStorage = localStorage.getItem('key'); // 删除 sessionStorage.removeItem('key'); localStorage.removeItem('key'); // 清空所有 sessionStorage.clear(); localStorage.clear();
注意:存储的数据只能是字符串形式,因此如果要存储对象或者数组,则需要使用JSON.stringify
来转换后再存储,读取后用JSON.parse
还原。
十三、window
(全局)
简述: 直接用语法 window.age = '18'
定义然后全局
通用即可。(此方式是存放在内存刷新会清空)
注意:在 Vue
应用中,虽然可以直接将属性挂载到 window
对象上实现全局通用,但并推荐,因为这可能会出现命名冲突、导致代码难以维护
。
添加属性和方法:直接定义,可以是属性也可以是对象
window.Obj = { test: '挂载对象' } window.name = '天天鸭'
使用:
console.log( window.Obj); console.log( window.name);
十四、路由
简述: Vue
在路由跳转时携带参数其实也很常用的方式,下面汇总一下三种路由传参。
(1)通过 params
传参 跳转页面用法:
this.$router.push({name:"index", params:{id}})
目标页面接收参数:
this.$route.params.id
(2)通过 query
传参
跳转页面用法:有几种方式
this.$router.push({ name:"index", query:{id}}) this.$router.push({ path:"/index", query:{id}}) this.$router.push('/index?name='+obj.name+'&age='+obj.age)
目标页面接收参数:
this.$route.query.id
(3)通过动态路由传参
注意: 如果用动态路由传参需要对路由进行配置;并且参数会在 url
中显示。 如下所示,在path
后面要配置:name/:age
.
{ path: '/about/:name/:age' , name: 'About', component() => import('@/views/About.vue') }
跳转页面用法:
this.$router.push('/about/'+obj.name+'/'+obj.age)
目标页面接收参数:
this.$route.params
十五、$root
(顶层)
简述: 可以通过 $root
访问到整个 Vue
树的根实例,也就是可以使用 $root
来访问全局的属性或者修改全局的属性。
示例:在main.js
文件中定义一个globalName
属性,可以全局使用。
import App from './App.vue'; import Vue from 'vue'; new Vue({ el: '#app', render: h => h(App), data: { globalName: '天天鸭' } });
在下层任意组件使用或者修改内容
<template> <div> {{ globalName }} </div> </template> <script> export default { mounted() { this.$root.globalName = '修改数据'; }, }; </script>
十六、slot
(父传子)
简述: 通过插槽也是可以传递参数,这也是很多人忽略的一种方式。父组件可以通过插槽向子组件传递参数,然后子组件拿到参数进行渲染。
下面主要讲解具名插槽和默认插槽两种使用方式。
(1)具名插槽用法
子组件代码: 用slot
定义一个名叫header
的插槽。
<template> <div> <slot name="header"></slot> </div> </template> <script> export default { }; </script>
父组件代码:用v-slot:header
向子组件的header
插槽传递内容。这边传递什么那边就会在对应区域显示什么。
<template> <div> <child> <template v-slot:header> <h1>这是插槽的内容</h1> </template> </child> </div> </template> <script> import child from './child.vue'; export default { components: { child } }; </script>
(2)无参数传递的默认插槽
子组件代码: 用slot
定义插槽时不需要指定name
名称。
<template> <div> <slot></slot> </div> </template> <script> export default { }; </script>
父组件代码:不需要指定插槽名称,只要在组件中间填写内容就会渲染在默认插槽中。
<template> <div> <child> <p>默认插槽中的内容。</p> </child> </div> </template> <script> import child from './child.vue'; export default { components: { child } }; </script>
总结
Vue2中路由传参数方式:props(父传子),$emit与v-on(子传父),EventBus(兄弟传参),.sync与update:(父子双向),v-model(父子双向),ref $children与$parent,$attrs与$listeners(爷孙双向),provide与inject(多层传参),Vuex,Vue.prototype,路由,浏览器缓存,window,$root,slot(父传子)。
到此这篇关于Vue2的16种传参通信方式总结和示例讲解的文章就介绍到这了,更多相关Vue2的16种传参通信方式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!