setup中的内容到底是什么时候执行的

前言

这是vue3系列源码的第四章,使用的vue3版本是3.2.45

推荐

createApp都发生了什么

mount都发生了什么

页面到底是从什么时候开始渲染的

背景

在上一篇文章里,我们探索了vue3的源码中,加载页面部分的源码。纯纯的页面,没有涉及到js代码,那么我们今天想看看,js代码到底是什么时候执行的。

我们今天说的js代码不包括各个周期钩子和副作用函数,只是纯纯的js代码。

前置

我们来看一下我们这次的App.vue组件长什么样子。

js 复制代码
<template>
  <div>{{ aa }}</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'

const aa = ref(11)
console.log('setup', aa.value)
onMounted(() => {
  console.log('onMounted',aa.value)
})
</script>

这里我们只看js部分,其实div是不需要的,不影响。

下面我们就来看看到底会在源码执行的哪一步,打印我们的内容。

在上一篇mount都发生了什么文章中的mountComponent函数中,我们提到过setupComponent这个函数,当时我们没有详细的去说,那么今天,我们讲深入的去了解这个函数。

setupComponent

js 复制代码
function setupComponent(
  instance: ComponentInternalInstance,
  isSSR = false
) {
  isInSSRComponentSetup = isSSR

  const { props, children } = instance.vnode
  const isStateful = isStatefulComponent(instance)
  initProps(instance, props, isStateful, isSSR)
  initSlots(instance, children)

  const setupResult = isStateful
    ? setupStatefulComponent(instance, isSSR)
    : undefined
  isInSSRComponentSetup = false
  return setupResult
}

这里的参数很简单,就是组件的实例。

整个函数看似内容不多。我们可以一个一个看下去。

首先是isStateful,这里其实就是一个与的运算,最终得到的是4,在下面的判断中代表true。

接着是initPropsinitSlots,但是我们这里并没有。而且也不是我们这一章的重点。我们将在后面的章节专门深入。

最后到了setupStatefulComponent,前面都不是重点,那么他就是绝对的重点。

setupStatefulComponent

js 复制代码
function setupStatefulComponent(
  instance: ComponentInternalInstance,
  isSSR: boolean
) {
  const Component = instance.type as ComponentOptions
  // 0. create render proxy property access cache
  instance.accessCache = Object.create(null)
  // 1. create public instance / render proxy
  // also mark it raw so it's never observed
  instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))
  
  // 2. call setup()
  const { setup } = Component
  if (setup) {
    const setupContext = (instance.setupContext =
      setup.length > 1 ? createSetupContext(instance) : null)

    setCurrentInstance(instance)
    pauseTracking()
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      ErrorCodes.SETUP_FUNCTION,
      [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    )
    resetTracking()
    unsetCurrentInstance()

    if (isPromise(setupResult)) {
        ...
    } else {
        handleSetupResult(instance, setupResult, isSSR)
    }
  } else {
    ...
  }
}

参数还是组件的实例。

这段代码的重点在:

  • callWithErrorHandling(setup, instance...),就是在这里,我们的setup的代码执行了。
  • handleSetupResult

setup

我们先看一下setup的执行。

回顾我们的代码段:

js 复制代码
import { ref, onMounted } from 'vue'

const aa = ref(11)
console.log('setup', aa.value)
onMounted(() => {
  console.log('onMounted',aa.value)
})

在setup的执行里,进行了以下步骤:

  • 调用了ref函数创建了ref对象aa
  • 执行到console.log('setup', aa.value),触发了aa对象的get
  • 执行了打印操作,就是在这里,打印了setup 11
  • 调用了createHook函数,向该组件的onMounted钩子注入定义的函数

由此可见,我们setup中的内容其实就是在这里执行了。定义变量和console以及一般的js代码都会在这里得到执行。

我们还发现了,定义在钩子里面的代码,也就是传给钩子的回调函数也是在这里进行了注册,然后等待相应的时机再执行。

这里加了console.log('onMounted',aa.value)纯粹是为了和console.log('setup', aa.value)执行上的不同,关于钩子函数的执行,不在本章讨论,我们将会在后面,详细看看钩子回调函数的执行。

这里其实还涉及到了ref,我们也将会在后面的章节里面进行分析。

handleSetupResult

js 复制代码
function handleSetupResult(
  instance: ComponentInternalInstance,
  setupResult: unknown,
  isSSR: boolean
) {
  if (isFunction(setupResult)) {
    ...
  } else if (isObject(setupResult)) {
    instance.setupState = proxyRefs(setupResult)
  } else if (__DEV__ && setupResult !== undefined) {
   ...
  }
  finishComponentSetup(instance, isSSR)
}

我们先看一下参数:

  • instance,组件实例
  • setupResult, 就是上一步setup执行的结果,是一个对象,具体的内容见下图

这个函数其实和标题setup内容的执行关系不太大了,因为我们想要的已经知道了,这里只是顺带提一下proxyRefs

通过proxyRefs方法对setupResult对象进行了代理,这样我们在组件template中访问aa的时候就不用写aa.value了,而是直接写aa就可以了。

总结

我们虽然只探索了setup的执行,但是其实也顺带着了解了一下ref和生命周期钩子。

从这章开始,我们将逐渐的去了解vue3的那些API。

相关推荐
赵小川15 分钟前
Taro 包升级实录 — 从 3.3 到 3.6.3 完整指南
前端·架构
_志哥_24 分钟前
解除有些网站不能复制的终极办法
前端·chrome
愚昧之山绝望之谷开悟之坡39 分钟前
什么是uv和传统的区别
前端·chrome·uv
SRC_BLUE_171 小时前
NSSCTF - Web | 【第五空间 2021】pklovecloud
android·前端
golang学习记1 小时前
从0死磕全栈之Next.js 数据安全实战指南:从零信任到安全架构
前端
云中雾丽1 小时前
flutter中 getx 的使用
前端
Jay丶1 小时前
聊聊入职新公司两个月,试用期没过这件事
前端·面试
ZTeam前端全栈进阶圈1 小时前
Vue新技巧:<style>标签里的 CSS 也能响应式!
前端
ღ_23331 小时前
vue3二次封装element-plus表格,slot透传,动态slot。
前端·javascript·vue.js
摸着石头过河的石头1 小时前
JavaScript继承的多种实现方式详解
前端·javascript