[关闭]
@zhouyy 2018-05-28T06:11:25.000000Z 字数 3944 阅读 546

Events 和 Vuex

vue


来源:https://segmentfault.com/a/1190000008184629

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

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

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

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

当公共组件使用 EventBus 时

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

  1. /**
  2. * EventBus.
  3. * src/event-bus.js
  4. */
  5. export default new Vue({})
  6. /**
  7. * 我的公用组件 my-component.
  8. */
  9. import EventBus from 'src/event-bus'
  10. export default {
  11. ...
  12. methods: {
  13. // 触发叫做 "SomeModule:SomeEvent" 的事件并传了值 "Yeah~" 过去~
  14. triggerSomeEvent () {
  15. EventBus.$emit('SomeModule:SomeEvent', 'Yeah~')
  16. },
  17. // 为我的组件注册两个事件~
  18. registerEvents () {
  19. EventBus.$on('MyComponent:Event-01', value => {
  20. console.log('Event-01 in MyComponent: ', value)
  21. }),
  22. EventBus.$on('MyComponent:Event-02', value => {
  23. console.log('Event-02 in MyComponent: ', value)
  24. })
  25. }
  26. },
  27. created () {
  28. this.registerEvents()
  29. }
  30. }

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

  1. import EventBus from 'src/event-bus'

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

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

  1. // 我们将 EventBus 做成插件,这样就可以在项目的任何组件内使用了.
  2. // 起名叫 $events.
  3. // 当检测到 $events 存在的时候就使用,不存在的时候使用其他方法.
  4. /**
  5. * 我们将 event-bus 封装为一个插件.
  6. * plugin/event-bus.js
  7. */
  8. export default {
  9. install (Vue) {
  10. const EventBus = new Vue({})
  11. Vue.prototype.$events = EventBus
  12. Vue.EventBus = EventBus
  13. }
  14. }
  15. /**
  16. * 所以我的公用组件 my-component 要变为:
  17. */
  18. export default {
  19. ...
  20. methods: {
  21. // 触发叫做 "SomeModule:SomeEvent" 的事件并传了值 "Yeah~" 过去~
  22. triggerSomeEvent () {
  23. if (this.$events) {
  24. this.$events.$emit('SomeModule:SomeEvent', 'Yeah~')
  25. } else {
  26. // 其他方式...
  27. }
  28. },
  29. // 为我的组件注册两个事件~
  30. registerEvents () {
  31. if (this.$events) {
  32. this.$events.$on('MyComponent:Event-01', value => {
  33. console.log('Event-01 in MyComponent: ', value)
  34. }),
  35. this.$events.$on('MyComponent:Event-02', value => {
  36. console.log('Event-02 in MyComponent: ', value)
  37. })
  38. } else {
  39. // 其他方式...
  40. }
  41. }
  42. },
  43. created () {
  44. this.registerEvents()
  45. }
  46. }
  47. /**
  48. * 项目入口.
  49. * src/index.js
  50. */
  51. import Vue from 'vue'
  52. import EventBus from 'plugin/event-bus'
  53. import MyComponent from 'my-component'
  54. Vue.use(EventBus)
  55. const Root = new Vue({
  56. components: {
  57. MyComponent
  58. },
  59. methods: {
  60. doSomething () {
  61. this.$events.$emit('MyComponent:Event-01', 'FA♂')
  62. }
  63. }
  64. })

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

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

当公共组件使用 Vuex 时
这个问题仅仅存在于 Vue 1.0 的项目中,Vue 2.0 + Vuex 2.0 已经解决这个问题:

  1. /**
  2. * 我的公用组件 my-component.
  3. */
  4. import store from 'src/vuex/store'
  5. import actions from 'src/vuex/actions'
  6. import getters from 'src/vuex/getters'
  7. export default {
  8. ...
  9. store,
  10. vuex: {
  11. actions, getters
  12. },
  13. computed: {
  14. userName () {
  15. // "getUsername" 是 Vuex 中定义好的 getter.
  16. return this.getUsername
  17. }
  18. },
  19. methods: {
  20. changeDataInVuexByUsingAction () {
  21. // "setUserExperience" 是 Vuex 中定义好的 action.
  22. this.setUserExperience(450)
  23. }
  24. }
  25. }

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

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

  1. // 我们将 Vuex 做成插件,这样就可以在项目的任何组件内使用了.
  2. // 起名叫 $vuexer.
  3. // 当检测到 $vuexer 存在的时候就使用 Vuex,不存在的时候就将数据写入组件自己内部的 state 中.
  4. /**
  5. * 我们将 event-bus 封装为一个插件.
  6. * plugin/event-bus.js
  7. */
  8. export default {
  9. install (Vue, { store, actions, getters }) {
  10. const vuexer = new Vue({
  11. store, actions, getters
  12. })
  13. Vue.prototype.$vuexer = vuexer
  14. Vue.vuexer = vuexer
  15. }
  16. }
  17. /**
  18. * 项目入口.
  19. * src/index.js
  20. */
  21. import Vue from 'vue'
  22. import Vuexer from 'plugin/vuexer'
  23. import store from 'src/vuex/store'
  24. import actions from 'src/vuex/actions'
  25. import getters from 'src/vuex/getters'
  26. import MyComponent from 'my-component'
  27. Vue.use(Vuexer, {
  28. store, actions, getters
  29. })
  30. const Root = new Vue({
  31. components: {
  32. MyComponent
  33. },
  34. computed: {
  35. userExperience () {
  36. // "getExperience" 是在 Vuex 中定义好的 getter.
  37. return this.$vuexer.getExperience
  38. }
  39. },
  40. methods: {
  41. changeUsernameInVuex () {
  42. // "setUsername" 是在 Vuex 中定义好的 setter.
  43. this.$vuexer.setUsername('John Smith')
  44. }
  45. }
  46. })
  47. /**
  48. * 我的公用组件 my-component.
  49. */
  50. export default {
  51. data () {
  52. return {
  53. _userName: '神秘用户',
  54. _userExperience: 65535
  55. }
  56. },
  57. computed: {
  58. userName () {
  59. // 如果有 Vuexer, 如果木有 Vuexer...
  60. return this.$vuexer
  61. ? this.$vuexer.getUsername
  62. : this._userName
  63. }
  64. },
  65. methods: {
  66. // 如果有 vuexer, 如果木有 Vuexer...
  67. changeDataInVuexByUsingAction () {
  68. const userExperience = 450
  69. if (this.$vuexer) {
  70. this.$vuexer.setUserExperience(userExperience)
  71. } else {
  72. this._userExperience = userExperience
  73. }
  74. }
  75. }
  76. }

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

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

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

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

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

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注