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。

相关推荐
aiprtem34 分钟前
基于Flutter的web登录设计
前端·flutter
浪裡遊37 分钟前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术44 分钟前
Stack Overflow,轰然倒下!
前端·人工智能·后端
幽络源小助理1 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
GISer_Jing1 小时前
0704-0706上海,又聚上了
前端·新浪微博
止观止1 小时前
深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
前端·pnpm·前端工程化·包管理器
whale fall1 小时前
npm install安装的node_modules是什么
前端·npm·node.js
烛阴1 小时前
简单入门Python装饰器
前端·python
袁煦丞2 小时前
数据库设计神器DrawDB:cpolar内网穿透实验室第595个成功挑战
前端·程序员·远程工作
天天扭码2 小时前
从图片到语音:我是如何用两大模型API打造沉浸式英语学习工具的
前端·人工智能·github