在我们Vue开发中,精髓就是组件,一个优秀的项目中,各种组件总是必不可少,我们暂且将组件分成三类:
A与B 是父子关系 或者隔代关系。 B和C、B和D都是父子关系,C和D是兄弟关系。
因为组件与组件直接的复杂关系,我们每次组件直接的通信传值就变得尤其重要;在适合的项目中,使用适合的传值方式,可以让代码规范性可读性甚至性能上有了很大提高,那么下面我们来系统的讨论一下组件通讯传值的方式
我们首先大致罗列出组件直接通信的方式,然后再一一的详细分析:
1. 路由之间传值 将值拼接在路由后面,然后再新页面中 通过 $route.query.xxx方式获取
2. sessionStorage 和 localStorage
3. Vuex
4. 父传子 props
5. $attrs 自定义属性的形式 另外额外使用 v-bind 可以向更深层传递
6. provide/inject 可以 父传子 或者隔代相传
7. $emit + $on 事件传值
8. eventBus 事件传值
9. $ref获取
10. $parent $children
我们一个个的整理:
首先在一个页面点击跳转到对应路由,并将参数拼接在路由后面
this.$router.push(`/router-test?name=${this.name}&id=${this.id}`)
然后再进入新页面后,通过$route中的query 获取
created () {
console.log(this.$route.query)
}
简单分析一下两者之间的区别:
localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。
sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。
具体用法也很简单,就不具体用代码演示了,一个页面存数据,一个页面取数据~~
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例;比如在父组件中的子组件实例上,定义一个ref
测试$emit 和 $on 自我封装 $dispatch 与 $broadcast 函数
HelloWord.vue
...
...
这个方法的使用我就不再重复的去写了,大家可以看我的另一篇博客 Vue属性 inheritAttrs、$attrs和$listeners初窥
EventBus 又称之为事件总线。实现的主要原理 我通俗的理解为:
借用Vue中的$emit 和 $on 两个实例方法(独立于整个Vue项目的Vue实例)。
基于这个简单的理解,我们可以将EventBus分为两种:局部 和 全局。
我们建立一个单独的js 名字就命名为 event-bus.js,内容如下:
import Vue from 'vue'
export const EventBus = new Vue()
非常简单的两行,导出一个新的Vue实例命名为 EventBus,然后我们在哪里用就在那里引入:
// HelloWord.vue
测试$emit 和 $on 自我封装 $dispatch 与 $broadcast 函数
我是child
这样我们就实现了组件之间数据通信。
EventBus.$off(‘eventName’, {}) 移除xxx事件
EventBus.$off(‘eventName’) 移除xxx事件
EventBus.$off() 移除所有事件
全局EventBus 比局部的方法更加优雅,但是因为是全局,定义在mian.js中,后期当代码量上来之后可能会导致混乱并不易维护。
// main.js
var EventBus = new Vue();
Object.defineProperties(Vue.prototype, {
$bus: {
get: function () {
return EventBus
}
}
})
使用方式也非常简单,不需要再每个使用的组件中单独引用。我们把上面的例子稍稍改动一下就好:
// HelloWord.vue
methods: {
testEventBus () {
this.$bus.$emit('addCount', { count: 1 })
}
}
// Child.vue
this.$bus.$on('addCount', obj => {
console.log(obj)
})
provide和inject 这个两个方法总是同时出现,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
官方认为 provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中
使用方法如下面示例(HelloWord.vue和child.vue是父子组件):
// HelloWord.vue
export default {
name: 'HelloWorld',
provide () {
return {
child: 'provide 传入子组件'
}
}
// child.vue
export default {
name: 'child',
inject: ['child'],
mounted() {
console.log(this.child) // 打印 'provide 传入子组件'
}
}
另外provide和inject 还可以作为全局状态管理(一般大型项目还是推荐使用Vuex);我们来看一个demo:
// App.vue
export default {
name: 'App',
data () {
return {
userinfo: {}
}
},
provide () {
return {
app: this
}
},
created () {
this.getUserinfo('wangyang')
},
methods: {
getUserinfo (name) {
this.userinfo = {
name: name,
age: 28,
sex: 'man'
}
}
}
}
一般我们的项目中,都会有一个全局使用的用户状态,一般以前我们都是使用 SessionStorage、Vuex去做管理,此时我们使用provide/reject一样可以。我们在最底层的组件App.vue中,设定provide中 app 为 App.vue中的this。当我们去其他组件中如果去调用和修改呢? 请看下面代码:
// HelloWorld.vue
inject: ['app'],
mounted () {
console.log(this.app.userinfo) // wangyang
this.name = this.app.userinfo.name
},
methods: {
editorUser () {
this.app.getUserinfo('editor')
this.name = this.app.userinfo.name // editor
}
}
部分代码已省略~~
我们在HelloWorld中mounted生命周期函数值打印this.app.getUserinfo(‘editor’) 值为 wangyang。
然后我们点击页面中的按钮(事件editorUser)将新的name 传入App.vue中的userinfo修改方法getUserinfo。
然后再次打印,name值改变为 editor。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
在此引用一下官方的定义,我想大家写Vue项目,多少都用到了Vuex,在此我就不过多的介绍它的使用方法了,这也不是一句两句可以解释清楚的。贴出官方文档的连接跳转 Vuex文档。另外网上也有很多优秀的Vuex使用说明博客,有兴趣的童鞋自己去谷歌。
在此列举的Vue组件通信方式并不能包含网上所有的方法。但是学以致用,在适合的项目中使用最好的方法才是我们需要追求的,另外搞懂Vue的整体运作,还需要更多的了解Vue的源码。