天地不仁以万物为刍狗,宇宙无义视众生如蝼蚁
——萧鼎和我
上一节列出了5个关键点,第一个路由已经解决了,接下来解决第二个问题:
组件的通信问题
一、组件的关系
组件之间的关系无非就是两种父子关系和没有父子关系。为什么我这样说呢?
按道理应该还有兄弟关系(也就是并列的组件,比如一个组件中引用了hreder和footer组件。),还有爷孙关系(比如我有七个Calabash Brothers组件,放在的HanHan组件下,而HanHan组件放在了Chairman Mao组件下)
那么不应该是父子、爷孙、兄弟关系吗?
然而并不是,因为我看了vue的文档。他的意思就是父子通信和非父子通信。
二、父子之间的通信——Prop和自定义事件
组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。
prop 是父组件用来传递数据的一个自定义属性。子组件需要显式地用 props 选项声明 “prop”。
将我们的App.vue当作父组件,将test当作子组件(什么当作,本来就是)。
在App.vue中修改我们的
在Test.vue中接收,并在页面中显示:
我是全英雄联盟最骚的骚猪
说: {{say}}
综上所述可以看出,其实所谓的prop就是在
上面的例子很漂亮的把父传子的通信方式展现出来了。但是子传父呢?
vue文档中使用的自定义事件。
使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件
我们还是用APP.vue作为父组件,Test.vue作为子组件
App.vue
...
/*增加一个位置来显示子组件传过来的值*/
我儿子对我说: {{noSay}}
/* 增加一个自定义的事件mychild,并给他指定触发的方法*/
...
data () {
return {
noSay: '' // 用来接收子组件穿过来的数据
}
},
methods: {
toFatherSay: function(massage) { // mychlid事件触发调用的方法
this.noSay = massage // massage就是子组件穿过来的内容
}
}
Test.vue
....
/*增加一个按钮,一点击就向父组件传值*/
....
data() {
return {
massage: '我才不说呢' // 定义一个向父组件传递的值
}
},
methods: {
toFather: function() { // 按钮点击触发的方法
this.$emit('myChild',this.massage)// 使用$emit来向父组件传播
}
},
....
三、非父子关系之间的通信——eventBus
在veu文档上,非父子之间的通信是通过使用一个空的Vue实例作为中央事件总线。
空的Vue实例? and 中央事件总线?
空的Vue实例也就是说
var bus = new Vue(); // 的确是一个空的
中央事件总线,难道组件通信要通过全局的事件来进行?
的确是这样,vue提供了$emit和$on方法来进行参数监听(其实就是个发布订阅模式)。
创建一个空的Vue实例 Bus.js:
import Vue from 'vue'
export default new Vue();
将我们的Apart.vue和Bpart.vue当作非父子关系组件:
Apart.vue
我是Apart
点我切换
Bpart.vue
然后在浏览器中测试一下:
有问题!!!无论怎么点击我们发现Bpart中定义的whiteSay并没有改变,并且第一次点击控制台没有打印。我在Bpart中写了这段代码:
data () {
return {
whiteSay: 'nihao'
}
},
created: function() { // 在组件被创建时候将会执行此函数 相当于进入页面的自执行
Bus.$on('whiteSay', function(data) { // 使用$on方法监听white属性并执行一个回调函数
this.whiteSay = data
console.log(this.whiteSay)
});
}
按道理在元素被创建的时候,会将监听到的值赋给whiteSay并且打印。但是我们注意到第一次点击,两个操作都没有执行,也就是说没有监听到whiteSay值的变化。而第二次之后都监听到了。这是为什么?为什么把值赋给data中定义的whiteSay之后没有网页没有更新?
带着这两个问题我去问了度娘和股哥。一下是答案:
第一个为什么: 项目中使用了vue-router,会先加載新的組件,等新的組件渲染好但是還沒掛載前,銷毀舊組件,在掛載新組件。将Apart.vue的代码修改为:
...
methods: {
goPage: function () {
this.$router.push('/bb')
}
},
/*Vue 实例销毁后调用 就是所谓的生命周期钩子*/
destroyed() {
Bus.$emit('whiteSay', '克里斯,关下门') // 使用$emit方法创建一个键值对
},
...
这样第一个问题就解决了。附上找到的答案连接:https://segmentfault.com/q/10...
第二个为什么:这个是我自己代码有问题,问了隔壁大神。说是我的作用域有问题,将Bpart.vue中的代码改为:
···
created: function() { // 在组件被创建时候将会执行此函数 相当于进入页面的自执行
var _self = this; // 将当前作用域保存在变量中,和$on()的作用域区分开来
Bus.$on('whiteSay', function(data) { // 使用$on方法监听white属性并执行一个回调函数
_self.whiteSay = data
console.log(_self.whiteSay)
});
}
···
这样所有的问题就都解决了。
四、Vuex
当我使用了上面几种方法来实现组件的通信存在着一些缺陷。比如父组件向子组件传一个值,子组件将值处理完了返回给父组件,这将同时用到prop和自定义事件。还不如直接写一个所有组件都可以访问的变量呢来得方便呢。比如:
/*这是vuex文档中的例子*/
const sourceOfTruth = {}
const vmA = new Vue({
data: sourceOfTruth
})
const vmB = new Vue({
data: sourceOfTruth
})
再比如当项目过大,组件之间的通信将变得难以管理。veux的初衷就是为何更好的管理组件的状态。一下是vuex文档对vuex的定义:
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
写得好累,还好最近没事做,不会被boss骂。
接下来直接开始使用vuex。
先下载
在根目录下打开cmd:
npm install vuex -save
下载成功看到一下数据:
C:\Users\59227\Desktop\x-chat>npm install vuex --save
[email protected] C:\Users\59227\Desktop\x-chat
`-- [email protected]
npm WARN optional Skipping failed optional dependency /chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: [email protected]
然后在main.js中引用,并安装到Vue上面
import Vuex from 'vuex'
Vue.use(Vuex)
前面两步将Vuex引入到了项目当中,接下来如何使用Vuex。
Vuex的核心是一个store(仓库)这个仓库的作用就是用来管理应用中的state(状态)。这里状态该怎么理解?
我个人的理解是:所有组件共享的并可以进行更改的对象。
除了state的,store还有getter、Mutations、Actions以及Modules。在vuex文档中都有非常详细的说明:http://vuex.vuejs.org/zh-cn/s...
笼统的说:
组件获取 state 用 vuex 的 getter
组件触发动作用 vuex 的 action
修改 state 用 vuex 的 mutation
知乎上看到的,说得很贴切易懂。
直接上代码,建议撸完代码,再去看一遍vuex的文档。
main.js
....
const store = new Vuex.Store({ //创建一个仓库
state: {
showDagger: true, // 定义一个状态
},
mutations: {// 定义 mutation ,更改 Vuex 的 store 中的状态的唯一方法是提交mutation
daggerCtrl (state) { // 一定要传入state,并且是第一个参数
state.showDagger = !state.showDagger // 将showDagger值取反
}
}
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router, // 将router对象传给vue,这样就可以通过this.$router获取到router对象了
store, // 将store对象传给vue,这样就可以通过this.$store获取到store对象了
template: ' ',
components: { App }
})
然后更改App.vue:
我儿子对我说: {{noSay}}
1.添加按钮和组件
dagger.vue
Dagger
使用vuex实现组件通信就搞定了,更多的用法请参考vuex文档。