getCurrentInstance何时会返回null

548 阅读2分钟

先看一个简单的问题现状:

<script setup lang="ts">
i0mport { getCurrentInstance } from 'vue';

setTimeout(() => {
  const ins = getCurrentInstance()
  console.log(ins) // null
});
</script>

setTimeoutgetCurrentInstance的返回值是null

getCurrentInstance的源码中看getCurrentInstance返回两个全局变量currentIntancecurretRenderingInstancecurrentIntance的默认值就是null,说明currentIntance在组件创建时才会被赋值为当前创建时的组件实例,由于每个组件实例创建的时候都需要依赖这里定义的currentIntance,所以每个组件在创建完成后需要再将getCurrentInstance重置为null,以便下个组件创建时使用。curretRenderingInstance是组件已经创建完成后,在生成vNode并挂载时产生的实例,当前问题可先忽略这个。

vuejs/core/packages/runtime-core/src/component.ts

当前源码文件中还定义了setCurrentInstanceunsetCurrentInstance两个函数,setCurrentInstance返回一个重置的函数,执行该重置函数时可以重置currentIntance,如果在执行setCurrentInstance之前currentIntance为初始状态的null,则重置的时候currentIntance就会被重置为null

上述问题可以直接从setup函数执行这部分源码来找原因,setup执行的源码还是在当前这个文件

setup的执行结果就是setupStatefulComponent函数的执行结果,所以再看setupStatefulComponent的源码(还是在当前文件中)

setup在执行之前currentIntance被赋值为当前组件实例,setup执行结束又执行了setCurrentInstance返回的重置函数,currentIntance又被重置为nullsetTimeout的回调会被加入到定时器任务队列(宏任务队列),在同步代码执行完成后才执行,所以在setTimeout的回调中getCurrentInstance返回的是null

再引发一个问题:

在生命周期钩子中为什么在异步代码执行之后getCurrentInstance也返回null?

<script setup lang="ts">
import { getCurrentInstance, onMounted } from 'vue';

function getData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('Hello, World!')
    }, 1000)
  })
}

onMounted(async () => {
  await getData()
  const instance = getCurrentInstance()
  console.log(instance) // null
})
</script>

原因跟setTimeout中取到null的原因是一样的,先看onMounted源码的实现

vuejs/core/packages/runtime-core/src/apiLifecycle.ts

onMounted钩子函数是通过createHook函数创建生成,onMounted实际有两个参数,第一个参数就是待执行的回调函数,第二个参数target就是组件实例,参数默认值就是currentIntanceonMounted执行时会执行injectHook,并把target传递给injectHook

injectHook的作用就是对onMounted传入的hook进行一次包装,在传入的hook执行前调用setCurrentInstance,此时onMounted是在setup函数中执行,setup函数执行的时候currentIntance被赋值为了当前组件实例,所以在onMounted传入的hook中通过同步代码调用getCurrentInstance时可以拿到当前组件实例,由于hook执行完成后currentIntance又会被重置,所以getCurrentInstance在异步的回调中是拿不到组件实例的,进而返回重置后的默认值null