一般定义组件的方式是使用一个对象,对于异步组件,使用一个工厂函数定义组件(区分是否是异步组件的依据)。

异步组件的基本思路是异步加载组件,当加载完成后通知对应的vue实例更新状态,vue实例重新解析要渲染的组件。加载期间可以渲染一个加载组件,默认是什么都不渲染。

下面的代码取自 resolve-async-component.js 文件

// factory 是工厂函数
// baseCtor 是Vue,用来将组件对象加工成子类
// context 是使用这个组件的vue实例

export function resolveAsyncComponent (
  factory: Function,
  baseCtor: Class<Component>,
  context: Component
): Class<Component> | void {
  // error是加载失败的标志位
  // errorComp 是失败时使用的组件对应的vue子类
  if (isTrue(factory.error) && isDef(factory.errorComp)) {
    return factory.errorComp
  }

  // 如果加载成功,返回异步组件对应的子类
  if (isDef(factory.resolved)) {
    return factory.resolved
  }

  // 加载时展示的组件
  if (isTrue(factory.loading) && isDef(factory.loadingComp)) {
    return factory.loadingComp
  }

  // contexts 变量保存的是使用这个工厂函数的vue实例
  if (isDef(factory.contexts)) {
    // already pending
    factory.contexts.push(context)
  } else {
    const contexts = factory.contexts = [context]
    let sync = true

    // 加载成功或者失败都调用这个方法更新
    const forceRender = () => {
      for (let i = 0, l = contexts.length; i < l; i++) {
        // vue实例刷新
        contexts[i].$forceUpdate()
      }
    }

    // 加载成功
    const resolve = once((res: Object | Class<Component>) => {
      // 缓存异步组件对应的vue子类
      factory.resolved = ensureCtor(res, baseCtor)
      // 如果是同步的会在最后返回这个假装是异步的同步组件,不需要更新
      if (!sync) {
        forceRender()
      }
    })

    // 加载失败
    const reject = once(reason => {
      process.env.NODE_ENV !== 'production' && warn(
        `Failed to resolve async component: ${String(factory)}` +
        (reason ? `\nReason: ${reason}` : '')
      )
      if (isDef(factory.errorComp)) {
        // 标记为加载失败
        factory.error = true
        forceRender()
      }
    })

    const res = factory(resolve, reject)

    // 对不同语法的支持,默认是require语法,不做特殊处理

    if (isObject(res)) {

      if (typeof res.then === 'function') {
        // ()=>import 语法
        if (isUndef(factory.resolved)) {
          res.then(resolve, reject)
        }
      } else if (isDef(res.component) && typeof res.component.then === 'function') {
        // 处理加载状态语法

        res.component.then(resolve, reject)

        if (isDef(res.error)) {
          factory.errorComp = ensureCtor(res.error, baseCtor)
        }

        if (isDef(res.loading)) {
          factory.loadingComp = ensureCtor(res.loading, baseCtor)
          // 其实我没怎么理解这个delay是为什么场景设计的
          if (res.delay === 0) {
            factory.loading = true
          } else {
            setTimeout(() => {
              if (isUndef(factory.resolved) && isUndef(factory.error)) {
                factory.loading = true
                forceRender()
              }
            }, res.delay || 200)
          }
        }

        // 加载超时机制
        if (isDef(res.timeout)) {
          setTimeout(() => {
            if (isUndef(factory.resolved)) {
              reject(
                process.env.NODE_ENV !== 'production'
                  ? `timeout (${res.timeout}ms)`
                  : null
              )
            }
          }, res.timeout)
        }
      }
    }

    sync = false
    // return in case resolved synchronously
    return factory.loading
      ? factory.loadingComp
      : factory.resolved
  }
}

results matching ""

    No results matching ""