前言
首先我们需要知道为何要使用 vuex 父子组件通信用 prop 和自定义事件可以搞定, 简单的非父子组件通信用 bus(一个空的 Vue 实例) 那么使用 vuex 就是为了解决复杂的非父子组件通信
仅仅会使用 vuex 没什么, 看过文档敲敲代码大家都会难道你就不想知道 vuex 是如何实现的?!
抛开 vuex 的源码, 我们先来想想如何实现一个简单的 "vuex" 有多简单呢, 我不要 gettermutationaction 等, 我只要 state 就行了
非父子组件通信
在实现之前, 我们得来温故一下 bus 的实现, 借用官网的例子:
- var bus = new Vue()
- // 触发组件 A 中的事件
- bus.$emit('id-selected', 1)
- // 在组件 B 创建的钩子中监听事件
- bus.$on('id-selected', function (id) {
- // ...
- })
遥想当年, 实例化后的 bus 不知放哪好, 最后无奈将其放到了 window 下, 一直 window.bus 的使用虽然这样也没问题, 但还是影响到了全局作用域
突然的某一天, 我发现可以挂载在 vue 的根实例下 (从此告别 window.bus), 于是便有了:
- var app = new Vue({
- el: '#app',
- bus: bus
- })
- // 使用 bus
- app.$options.bus
- // or
- this.$root.$options.bus
然后又发现了, bus 其实不只是 on 事件才可以通信其实 bus 是一个 Vue 实例, 其中 data 是响应的比如在 app 这个根实例下有两个非父子组件, 都使用到了 bus 的 data, 那么它们是响应同步的
- var bus = new Vue({
- data: {
- count: 0
- }
- })
以上, 子组件 a 修改了 count, 如果子组件 b 有用到 count, 那么它就能响应到最新 count 的值
说了这么多, 你还没发现吗? 这个不就是实现了非组件之间通信, vuex 的 state 吗?!
封装 bus
是的, 把刚刚的 bus 封装一下, 这个就是一个最简单的 "vuex" (仅仅只有 state 的功能) 首先, 我们将有一个根实例 app, 实例下有两个非父子组件 childA 和 childB
html 代码的实现如下:
- <div id="app">
- <child-a></child-a>
- <child-b></child-b>
- </div>
非父子组件的实现
然后是两个非父子组件和 app 的实现, 子组件都使用到了 bus 的 count, 这里用 store.state 表示, 跟 vuex 一致:
- // 待实现
- const store = new Store(Vue, {
- state: {
- count: 0
- }
- })
- // 子组件 a
- const childA = {
- template: '<button @click="handleClick">click me</button>',
- methods: {
- handleClick() {
- this.$store.state.count += 1
- }
- }
- }
- // 子组件 b
- const childB = {
- template: '<div>count: {{ count }}</div>',
- computed: {
- count() {
- return this.$store.state.count
- }
- }
- }
- new Vue({
- el: '#app',
- components: {
- 'child-a': childA,
- 'child-b': childB
- },
- store: store
- })
看到代码里还有一个 Store 待实现所需要的参数, 因为这里懒得用 Vue.use(), 所以直接将 Vue 作为参数传入以供使用, 然后第二个参数跟我们使用 vuex 传入的参数一致
Store 的实现
接下来就是 Store 的实现, 两步实现:
创建一个 bus 实例;
让子组件都能访问到 this.$store
第 1 步骤上面已经有了, 第 2 步骤主要用到了 Vue.mixin 来全局混入, 但仅仅只是找到有 store 的根实例并赋值 Vue 原型上的
store, 也能够让根实例 app 不用专门写 mixins 混入
- class Store {
- constructor(Vue, options) {
- var bus = new Vue({
- data: {
- state: options.state
- }
- }) this.install(Vue, bus)
- }
- install(Vue, bus) {
- Vue.mixin({
- beforeCreate() {
- if (this.$options.store) {
- Vue.prototype.$store = bus
- }
- }
- })
- }
- }
实现的 Store 就是一个简单的 "vuex", 它拥有了 vuex 的 state, 足够让非父子组件之间进行简单通信
在 Store 的构造函数里创建一个 bus 实例, 并将其注入 Vue 的原型, 实现了组件都能访问到 this.$store 即 bus 实例 this.$store 就是一个 Vue 实例, 所以访问了
this.$store.state.count
实际上就是访问到了 data, 从而实现了非父子组件之间的响应同步全部源码参考 这里
来源: https://juejin.im/post/5a7a935851882524713dcd05