vue组件之间的数据共享
前期写项目时,无论项目大小,都是用的vuex来实现的数据共享,随着学习的深入,发现使用vuex有点大材小用了。一般情况下,vuex适合大型项目、组件结构复杂的项目实现数据共享,vuex无视组件的父子、兄弟关系,只要在store中进行了注册,所有的组件都可以共享,但是也会存在一些问题,比如刷新页面会导致一些信息丢失的问题,虽然能解决,但也挺麻烦。通过学习,了解了更简单的组件间传递数据的方法,记录一下。
父向子传值
原则:父组件向子组件共享数据需要使用自定义属性
子组件Left.vue:
<template>
<div class="left-container">
<h3>Left组件</h3>
<p>msg的值是: {{ msg }}</p>
<p>user的值是: {{user}}</p>
</div>
</template>
<script>
export default {
props: ['msg', 'user']
}
</script>
<style lang="less">
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
</style>
父组件App.vue:
<template>
<div id="app">
<h1>App根组件</h1>
<hr />
<div class="box">
<Left :msg="message" :user="userinfo"></Left>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
export default {
data() {
return {
message: '我是父组件',
userinfo: {name: 'wsc', age: 18}
}
},
components: {
Left,
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
前端页面上显示的内容:
梳理一下逻辑:
- 先写子组件,在子组件中定义自定义属性(props: [‘msg’, ‘user’])
- 父组件导入、注册、使用子组件
- 父组件在使用子组件时,在标签中使用v-bind向子组件传递子组件中自定义属性并绑定对应的数据
<Left :msg="message" :user="userinfo"></Left>
- 子组件使用父组件传递过来的数据
<p>msg的值是: {{ msg }}</p>
以上就是父组件向子组件传值的全过程,核心要点是——使用自定义属性
的确比vuex简单得多,但是需要注意的是:不要直接修改props中的值
示例:
在子组件的页面中添加一个按钮:
<button @click="msg='abc'">修改msg</button>
这种写法虽然不报错,但是控制台会警告,vue不建议这么操作,正确做法是转存数据,然后修改转存后的数据
子向父传值
原则:子组件向父组件共享数据,使用自定义事件
接着上面的父向子传值的代码写
子组件Right.vue
<template>
<div class="right-container">
<h3>Right组件--{{count}}</h3>
<button @click="add">+1</button>
</div>
</template>
<script>
export default {
data() {
return {
// 子组件的值,要传给父组件App.vue
count: 0
}
},
methods: {
add() {
this.count++
// 修改数据时,同时通过$emit()发射自定义事件,第一个参数是父组件中的自定义事件名,第二个参数是向父组件传递的值
this.$emit('numChange', this.count)
}
}
}
</script>
<style lang="less">
.right-container {
padding: 0 20px 20px;
background-color: lightskyblue;
min-height: 250px;
flex: 1
}
</style>
几个要点:
- 首先在data中定义一个需要向父组件传递的值count
- 通过按钮点击事件使count值发生变化,也就是自增
- 在点击按钮改变count值时,同时$emit()发射自定义事件,第一个参数是父组件中的自定义事件名,第二个参数是向父组件传递的值
子组件中要做的事情已经做完,现在看看父组件是如何在接收数据的
父组件App.vue
<template>
<div id="app" class="app-container">
<h1>App根组件--{{countFromRight}}</h1>
<hr />
<div class="box">
<Left :msg="message" :user="userinfo"></Left>
<Right @numChange="getNewCount"></Right>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
data() {
return {
message: '我是父组件',
userinfo: { name: 'wsc', age: 18 },
// 接收子组件传递过来的数据
countFromRight: 0
}
},
components: {
Left,
Right
},
methods: {
getNewCount(val){
this.countFromRight = val
}
}
}
</script>
<style>
#app {
padding: 1px 20px 20px;
background-color: #efefef
}
.box {
display: flex;
}
</style>
同样几个要点:
- 父组件中定义一个值countFromRight,用来接收子组件传递过来的数据
- 父组件使用子组件(right标签)时,添加自定义事件,实际上就是子组件中发射的numChange事件(怎么理解这个自定义事件呢,就把它理解为点击事件,和点击事件的逻辑一样的),并为这个自定义事件绑定处理函数getNewCount
- 在methods中写自定义事件的处理函数getNewCount,这个函数接收一个参数,也就是子组件传递过来的数据,把这个数据的值赋给countFromRight,这样,在父组件中使用countFromRight就完成了子向父传值的过程
看下页面上显示的东西:
兄弟组件数据共享
原则:使用EventBus
最重要的一步,新建一个eventBus.js文件,作为兄弟组件之间的桥梁,实现数据共享,这个文件的内容很简单,代码如下:
eventBus.js
import Vue from "vue";
// 向外共享Vue实例对象
export default new Vue()
就两行代码,导入vue,创建vue实例并向外暴露
下面是兄弟组件的代码,发送数据的组件是Left.vue,接收数据的组件是Right.vue
先看发送数据的组件Left.vue
<template>
<div class="left-container">
<h3>Left组件</h3>
<p>msg的值是: {{ msg }}</p>
<p>user的值是: {{user}}</p>
<hr />
<button @click="send">向右侧兄弟组件传递数据</button>
</div>
</template>
<script>
import bus from './eventBus'
export default {
props: ['msg', 'user'],
data() {
return {
str: '右边的兄弟你好'
}
},
methods: {
send() {
bus.$emit('share', this.str)
}
}
}
</script>
<style lang="less">
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
</style>
相关的代码如下:
<button @click="send">向右侧兄弟组件传递数据</button>
data() {
return {
str: '右边的兄弟你好'
}
},
methods: {
send() {
bus.$emit('share', this.str)
}
}
要点:
- 定义需要发送的数据str
- 通过按钮点击事件发送数据
- 为按钮点击事件绑定处理函数send
- send中通过bus发射自定义时间share,并传递要共享的数据
再来看看接收数据的组件Right.vue
<template>
<div class="right-container">
<h3>Right组件--{{count}}</h3>
<button @click="add">+1</button>
<hr />
<p>来自左侧的兄弟组件的数据: {{msgFromLeft}}</p>
</div>
</template>
<script>
import bus from './eventBus'
export default {
data() {
return {
// 子组件的值,要传给父组件App.vue
count: 0,
// 接收来自兄弟组件Left.vue的数据
msgFromLeft: ''
}
},
methods: {
add() {
this.count++
// 修改数据时,同时通过$emit()发射自定义事件,第一个参数是父组件中的自定义事件名,第二个参数是向父组件传递的值
this.$emit('numChange', this.count)
}
},
created() {
bus.$on('share', val=> {
this.msgFromLeft = val
})
}
}
</script>
<style lang="less">
.right-container {
padding: 0 20px 20px;
background-color: lightskyblue;
min-height: 250px;
flex: 1
}
</style>
相关的代码
<p>来自左侧的兄弟组件的数据: {{msgFromLeft}}</p>
data() {
return {
// 子组件的值,要传给父组件App.vue
count: 0,
// 接收来自兄弟组件Left.vue的数据
msgFromLeft: ''
}
},
created() {
bus.$on('share', val=> {
this.msgFromLeft = val
})
}
要点:
- data中定义接收数据的变量msgFromLeft
- 页面上通过模板语法使用msgFromLeft值
- 通过钩子函数created方法,在页面加载之前,就接收数据,接收数据的方式是使用bus.$on()函数来接收,同样也是两个参数,一个是接收的自定义事件名,另一个参数是一个处理函数,函数接收的参数是兄弟组件共享的数据,可以在函数中将这个共享的数据赋值给msgFromLeft
- 因为created在加载页面前执行,所以页面上可以直接获取到msgFromLeft的值
看看页面上的内容
这个兄弟组件的数据共享可以理解成:
- bus是父组件
- left是子组件,left子组件通过自定义时间将数据发送给bus父组件
- bus父组件将接收到的值传递给另一个子组件right
至此,简单的组件之间的数据共享已经全部实现,不过个人还是倾向于使用vuex传值,可能是先入为主了,作为一种备选方案保留吧。