Vue3 源码解析系列 - Setup(二)

320 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情

前言

上一节我们讲了 Setup 的使用以及介绍,并了解了 setup 的处理源码,知道了在什么时候进行调用,今天我们讲讲 setup方法的结果 setupResult 如何进行处理。

handleSetupResult

// packages/runtime-core/src/component.ts
function setupStatefulComponent(
  instance: ComponentInternalInstance,
  isSSR: boolean
) {
  /** 省略代码 */
  if (isPromise(setupResult)) {
    setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
    if (isSSR) {
      // return the promise so server-renderer can wait on it
      return setupResult
        .then((resolvedResult: unknown) => {
          handleSetupResult(instance, resolvedResult, isSSR)
        })
        .catch(e => {
          handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
        })
    } else if (__DEV__) {
      warn(
        `setup() returned a Promise, but the version of Vue you are using ` +
          `does not support it yet.`
      )
    }
  } else {
    handleSetupResult(instance, setupResult, isSSR)
  }
}

拿到 setup 函数的结果 setupResult 后,会进行分析,并分别做处理。

  • 首先判断 setupResult 是否为 promise,如果是先会执行 setupResult 并执行 unsetCurrentInstance 这个方法中把 currentInstance 设置为空。然后判断是否是服务端渲染,如果是则执行 setupResult 并把结果传给 handleSetupResult 否则会报错 setup() returned a Promise, but the version of Vue you are using does not support it yet. 意思就是 setup 返回 promise,当前还不被满足。所以我们在写代码时候,要避免返回 promise。
  • 如果不是 promise 则直接调用 handleSetupResult
// packages/runtime-core/src/component.ts
export function handleSetupResult(
  instance: ComponentInternalInstance,
  setupResult: unknown,
  isSSR: boolean
) {
  if (isFunction(setupResult)) {
    // setup returned an inline render function
    if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
      // when the function's name is `ssrRender` (compiled by SFC inline mode),
      // set it as ssrRender instead.
      instance.ssrRender = setupResult
    } else {
      instance.render = setupResult as InternalRenderFunction
    }
  } else if (isObject(setupResult)) {
    instance.setupState = proxyRefs(setupResult)
  } else if (__DEV__ && setupResult !== undefined) {
    warn(
      `setup() should return an object. Received: ${
        setupResult === null ? 'null' : typeof setupResult
      }`
    )
  }
  finishComponentSetup(instance, isSSR)
}
  1. handleSetupResult 中判断 setupResult 是否为函数,如果是,会把这个函数当作 渲染render 方法,赋值给 instance.render。
  2. 如果setupResult为对象,则调用 proxyRefs 并传入 setupResult。我们来看看 proxyRefs 的实现。
// packages/reactivity/src/ref.ts
export function proxyRefs<T extends object>(
  objectWithRefs: T
): ShallowUnwrapRef<T> {
  return isReactive(objectWithRefs)
    ? objectWithRefs
    : new Proxy(objectWithRefs, shallowUnwrapHandlers)
}

可以看到,如果 setupResult 已经是响应式对象,则直接方法,否则调用 new Proxy 进行代理,转成响应式对象。

  1. 如果不为函数也不为对象,则会报出警告,setup 应该返回一个对象或方法

总结

我们通过两篇分析了 setup 的使用和源码,通过了解源码,我们知道了在写 setup 代码的时候如何提高性能,比如 context没使用时,不要写在形参上 等等。