在架构升级过程中,我们不但要保证重构后新代码完全符合设计规范,还要保证老代码能正常运行,同时兼容原来的事件通知。但老代码的事件通知可能有 eventbus、vuex 等等实现方式,在新的架构下,我们将逐步淘汰这种通信方式。要实现新老代码顺利过渡,请大家参考如下示例迁移代码。

迁移 eventBus

原业务组件代码:

  1. export default {
  2. mounted() {
  3. this.$eventBus.$on('userLogin', this.login)
  4. },
  5. beforeDestory() {
  6. this.$eventBus.$off('userLogin', this.login)
  7. },
  8. methods: {
  9. logout() {
  10. this.$eventBus.$emit('userLogout')
  11. },
  12. login() {
  13. console.log('用户登录成功了')
  14. }
  15. }
  16. }

通过分析以上代码的两个事件,我们可以把两个事件均归类为用户消息通知。我们可以在 UserSerivce 中来桥接原来的事件。 如果还有其它事件,我们需要以业务类型,如支付消息,我们就需要在 PayService 里做桥接。总之,我们需要把所有的数据通讯归类到具体的业务类型。后续的开发的服务、组件或重构的组件均通过相关桥接服务访问原来的事件。从而达到逐步替换原来设计,并最终取代。 我们需要先用一个类作为依赖注入的 eventBus

  1. @Injectable()
  2. export class EventBusService extends Vue {}
  3. export const eventBus = new EventBusService()

然后我们把 EventBusService 在根组件或 App 实例提供(根据 vue 版本不同而不同),以 vue2 为例

  1. // # main.ts
  2. Vue.prototype.$eventBus = eventBus
  1. // # App.vue
  2. export default defineComponent({
  3. setup() {
  4. // 在根组件容器内提供 eventBus 桥接类,以供所有其它组件和服务访问
  5. useRootReflectiveInjector([{
  6. provide: EventBusService,
  7. useValue: eventBus
  8. },
  9. UserService
  10. ])
  11. }
  12. })

重构后的消息通知类

  1. @Injectable()
  2. export class UserService {
  3. onLogin = new Subject<void>()
  4. onLogout = new Subject<void>()
  5. // 当项目内再也没有组件通过 eventBus 依赖这两个事件时,下面的桥接代码就可以删除了
  6. // 桥接代码 start...
  7. constructor(private eventBus: EventBusService) {
  8. let isLoginFromSelf = false
  9. let isLogoutFromSelf = false
  10. eventBus.$on('userLogin', () => {
  11. isLoginFromSelf = true
  12. this.onLogin.next()
  13. isLoginFromSelf = false
  14. })
  15. eventBus.$on('userLogout', () => {
  16. isLogoutFromSelf = true
  17. this.onLogout.next()
  18. isLogoutFromSelf = false
  19. })
  20. this.onLogin.subscribe(() => {
  21. if (isLoginFromSelf) {
  22. return
  23. }
  24. eventBus.$emit('userLogin')
  25. })
  26. this.onLogout.subscribe(() => {
  27. if (isLogoutFromSelf) {
  28. return
  29. }
  30. eventBus.$emit('userLogout')
  31. })
  32. }
  33. // 桥接代码 end...
  34. }

在上面代码中,我们用 Subject 类作为新的事件分发器。同时我们用了两个变量 isLoginFromSlef、isLogoutFromSelf 来作为状态机,防止事件分发进入死循环。在做桥接时,尤其要注意死循环!
有了桥接服务后,我们来正式重构我们的组件代码。

  1. export default defineComponent({
  2. setup() {
  3. const injector = useReflectiveInjector()
  4. const user = injector.get(UserService)
  5. function logout() {
  6. user.onLogout.next()
  7. }
  8. const subs = []
  9. onMounted(() => {
  10. subs.push(
  11. user.onLogin.subscribe(() => {
  12. console.log('用户登录成功了')
  13. })
  14. )
  15. })
  16. onUnmounted(() => {
  17. subs.forEach(i => i.unsubscribe())
  18. })
  19. }
  20. })

我们可以按上面的方法迁移其它的事件通知,当所有原来的依赖迁移完成后,就可以把 EventBusService 和原来的 eventBus 删除了。