xinre / accumulate

All the things I've accumulated in my career
1 stars 0 forks source link

简易的single-spa #7

Open xinre opened 1 year ago

xinre commented 1 year ago
// single-spa.js
class SingleSpa {
  constructor() {
    this.apps = []
    this.currentApp = null
  }

  // 注册应用
  registerApplication(app) {
    this.apps.push(app)
  }

  // 启动
  start() {
    this.reroute()
    window.addEventListener('hashchange', () => {
      this.reroute()
    })
  }

  // 路由
  async reroute() {
    const { appsToLoad, appsToMount, appsToUnmount } = this.getAppChanges()

    // 卸载不需要的应用
    await Promise.all(appsToUnmount.map(app => this.toUnmountPromise(app)))

    // 加载需要的应用
    appsToLoad.map(async app => {
      app = await this.toLoadPromise(app)
      app = await this.toBootstrapPromise(app)
      return this.toMountPromise(app)
    })

    // 挂载需要的应用
    appsToMount.map(async app => {
      app = await this.toBootstrapPromise(app)
      return this.toMountPromise(app)
    })
  }

  // 获取当前应用状态
  getAppChanges() {
    const appsToLoad = []
    const appsToMount = []
    const appsToUnmount = []

    this.apps.forEach(app => {
      const isActive = app.activeWhen(window.location)
      switch (app.status) {
        case 'NOT_LOADED':
        case 'LOADING_SOURCE_CODE':
          if (isActive) {
            appsToLoad.push(app)
          }
          break
        case 'NOT_BOOTSTRAPPED':
        case 'BOOTSTRAPPING':
        case 'NOT_MOUNTED':
          if (isActive) {
            appsToMount.push(app)
          }
          break
        case 'MOUNTED':
          if (!isActive) {
            appsToUnmount.push(app)
          }
          break
      }
    })

    return { appsToLoad, appsToMount, appsToUnmount }
  }

  // 加载应用
  async toLoadPromise(app) {
    if (app.status !== 'NOT_LOADED') {
      return app
    }
    app.status = 'LOADING_SOURCE_CODE'
    let { bootstrap, mount, unmount } = await app.app()
    app.status = 'NOT_BOOTSTRAPPED'
    app.bootstrap = bootstrap
    app.mount = mount
    app.unmount = unmount
    return app
  }

  // 启动应用
  async toBootstrapPromise(app) {
    if (app.status !== 'NOT_BOOTSTRAPPED') {
      return app
    }
    app.status = 'BOOTSTRAPPING'
    await app.bootstrap()
    app.status = 'NOT_MOUNTED'
    return app
  }

  // 挂载应用
  async toMountPromise(app) {
    if (app.status !== 'NOT_MOUNTED') {
      return app
    }
    app.status = 'MOUNTING'
    await app.mount()
    app.status = 'MOUNTED'
    return app
  }

  // 卸载应用
  async toUnmountPromise(app) {
    if (app.status !== 'MOUNTED') {
      return app
    }
    app.status = 'UNMOUNTING'
    await app.unmount()
    app.status = 'NOT_MOUNTED'
    return app
  }
}

// 导出实例,保证全局只有一个实例(单例模式)
export default new SingleSpa()