科学处理多个 Vue 项目中共用的组件的 Events 和 Vuex

我们在做项目的时候,应该会有这种情况:

"我写了一个组件,然后做成了 npm 包,然后给好几个项目一起用。"

Vue 组件也是可以这么干的,所以在公司内部可能会将组件封装成 npm 模块后分发给各个项目。

不过在 Vue 的项目中,有两个小地方可能需要精心处理下 (●’◡’●)

当公共组件使用 EventBus 时

EventBus 并不是什么独立的东西,而是 Vue 的事件系统的一个最佳实践,算是一种使用方式:

/**
 * EventBus.
 * src/event-bus.js
 */
export default new Vue({})


/**
 * 我的公用组件 my-component.
 */
import EventBus from 'src/event-bus'

export default {
  ...
  methods: {
    // 触发叫做 "SomeModule:SomeEvent" 的事件并传了值 "Yeah~" 过去~
    triggerSomeEvent () {
      EventBus.$emit('SomeModule:SomeEvent', 'Yeah~')
    },

    // 为我的组件注册两个事件~
    registerEvents () {
      EventBus.$on('MyComponent:Event-01', value => {
        console.log('Event-01 in MyComponent: ', value)
      }),

      EventBus.$on('MyComponent:Event-02', value => {
        console.log('Event-02 in MyComponent: ', value)
      })
    }
  },

  created () {
    this.registerEvents()
  }
}

当我们的公用模块在使用 EventBus 的时候,会有一个微小的问题,看这句话:

import EventBus from 'src/event-bus'

我怎么保证在使用我当前模块的不同的项目中的 EventBus 的路径都是 src/event-bus 呢?

所以,我们需要抽象一层,让模块并不关心这个 EventBus 是从哪里引入的:

// 我们将 EventBus 做成插件,这样就可以在项目的任何组件内使用了.
// 起名叫 $events.
// 当检测到 $events 存在的时候就使用,不存在的时候使用其他方法.

/**
 * 我们将 event-bus 封装为一个插件.
 * plugin/event-bus.js
 */
export default {
  install (Vue) {
    const EventBus = new Vue({})
    Vue.prototype.$events = EventBus
    Vue.EventBus = EventBus
  }
}

/**
 * 所以我的公用组件 my-component 要变为:
 */
export default {
  ...
  methods: {
    // 触发叫做 "SomeModule:SomeEvent" 的事件并传了值 "Yeah~" 过去~
    triggerSomeEvent () {
      if (this.$events) {
        this.$events.$emit('SomeModule:SomeEvent', 'Yeah~')        
      } else {
        // 其他方式...
      }
    },

    // 为我的组件注册两个事件~
    registerEvents () {
      if (this.$events) {
        this.$events.$on('MyComponent:Event-01', value => {
          console.log('Event-01 in MyComponent: ', value)
        }),

        this.$events.$on('MyComponent:Event-02', value => {
          console.log('Event-02 in MyComponent: ', value)
        })
      } else {
        // 其他方式...
      }
    }
  },

  created () {
    this.registerEvents()
  }
}

/**
 * 项目入口.
 * src/index.js
 */
import Vue from 'vue'
import EventBus from 'plugin/event-bus'
import MyComponent from 'my-component'

Vue.use(EventBus)

const Root = new Vue({
  components: {
    MyComponent
  },

  methods: {
    doSomething () {
      this.$events.$emit('MyComponent:Event-01', 'FA♂')
    }
  }
})

OK,这样我们的组件就可以在不同项目中适应 EventBus 了!

这里有一个组件 cklmercer/vue-events 就是解决这种问题而存在的.

当公共组件使用 Vuex 时

这个问题仅仅存在于 Vue 1.0 的项目中,Vue 2.0 + Vuex 2.0 已经解决这个问题:

/**
 * 我的公用组件 my-component.
 */
import store from 'src/vuex/store'
import actions from 'src/vuex/actions'
import getters from 'src/vuex/getters'

export default {
  ...
  store,

  vuex: {
    actions, getters
  },

  computed: {
    userName () {
      // "getUsername" 是 Vuex 中定义好的 getter.
      return this.getUsername
    }
  },

  methods: {
    changeDataInVuexByUsingAction () {
      // "setUserExperience" 是 Vuex 中定义好的 action.
      this.setUserExperience(450)
    }
  }
}

那么还是同样的问题,

我怎么保证在使用我当前模块的不同的项目中的 Vuex 的路径都是 src/vuex 呢?

所以方法一样啦,抽象出来引用路径,让模块并不关心是如何引入 Vuex 的:

// 我们将 Vuex 做成插件,这样就可以在项目的任何组件内使用了.
// 起名叫 $vuexer.
// 当检测到 $vuexer 存在的时候就使用 Vuex,不存在的时候就将数据写入组件自己内部的 state 中.

/**
 * 我们将 event-bus 封装为一个插件.
 * plugin/event-bus.js
 */
export default {
  install (Vue, { store, actions, getters }) {
    const vuexer = new Vue({
      store, actions, getters
    })
    Vue.prototype.$vuexer = vuexer
    Vue.vuexer = vuexer
  }
}

/**
 * 项目入口.
 * src/index.js
 */
import Vue from 'vue'
import Vuexer from 'plugin/vuexer'

import store from 'src/vuex/store'
import actions from 'src/vuex/actions'
import getters from 'src/vuex/getters'

import MyComponent from 'my-component'

Vue.use(Vuexer, {
  store, actions, getters
})

const Root = new Vue({
  components: {
    MyComponent
  },

  computed: {
    userExperience () {
      // "getExperience" 是在 Vuex 中定义好的 getter.
      return this.$vuexer.getExperience
    }
  },

  methods: {
    changeUsernameInVuex () {
      // "setUsername" 是在 Vuex 中定义好的 setter.
      this.$vuexer.setUsername('John Smith')
    }
  }
})

/**
 * 我的公用组件 my-component.
 */
export default {
  data () {
    return {
      _userName: '神秘用户',
      _userExperience: 65535
    }
  },

  computed: {
    userName () {
      // 如果有 Vuexer, 如果木有 Vuexer...
      return this.$vuexer
        ? this.$vuexer.getUsername
        : this._userName
    }
  },

  methods: {
    // 如果有 vuexer, 如果木有 Vuexer...    
    changeDataInVuexByUsingAction () {
      const userExperience = 450
      if (this.$vuexer) {
        this.$vuexer.setUserExperience(userExperience)
      } else {
        this._userExperience = userExperience
      }
    }
  }
}

妥!⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄

至于为什么 Vue 2.0 + Vuex 2.0 木有这个问题:

// 在 Vue 2.0 中使用 Vuex 要这么写:
// 创建一个组件.
const Components = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.getters.doneTodosCount  // 这是一个 getter.
    }
  }
}

注意 computed 中的 return this.$store.getters.doneTodosCount,看看其中的 this.$store

是不是和 this.$vuexer 有点像? (°∀°)ノ

这里还有一个组件 lancercomet/vuexer 就是为 Vue 1.0 解决这个问题的!

完结撒花~

By LancerComet at 01:22, 2017.01.21.

相关推荐