题引:
在我们vue3中,由于过多的全局变量导致我们挂载到vue实例上。那么在setup语法糖的时候,想要那this就得借用 getCurrentInstance
函数来实现。但是在开发的时候,我们会发现有时候拿到的返回值是 null 。这得来说道说道。
结论:
在这里我们直接给出测试代码,我们可以得到一个明显的结论。
<script setup>
import {
getCurrentInstance,
} from "vue";
import HelloWorld from "./components/HelloWorld.vue";
// 同步获取有值
let proxy = getCurrentInstance()
console.log(getCurrentInstance());
console.log(proxy);
let promise = new Promise((resolve, reject) => {
resolve(getCurrentInstance()) // new Promise里的回调函数是同步任务,即也有值
})
// 定时器为异步,则获取为null
setTimeout(() => {
console.log(getCurrentInstance());
console.log(proxy); // 这里有值是因为它已经保存了一份引用地址
});
// 点击事件也是为null,因为在触发点击的时候 getCurrentInstance函数返回 currentInstance = null
const click = () => {
console.log(getCurrentInstance()); // null
promise.then(res => {
console.log(getCurrentInstance()); // null
console.log(res); // 有值,promise的resolve值
})
};
</script>
<template>
<button @click="click">test</button>
</template>
结论: getCurrentInstan
函数只能在 同步任务 执行才有值,放在 异步任务或者函数体 里面为 null 。
剖析:
在vue3.2的版本中是这个路径:@vue>runtime-core>dist>runtime-core.esm-bundler.js
直接给出 getCurrentInstance
的调试栈路径,也只讲框框中的函数。如下:
先贴上 getCurrentInstance
函数的源码。
now,我们从 mountComponent 函数开始讲解。
该函数在组件挂载时触发,并且会调用 createComponentInstance 函数创建vue组件实例,里面就是我们使用ref或者getCurrentInstance时获取的对象属性。
接着处理一些边缘判断,再触发 setupComponent 函数。
我们可以理解 setupComponent 函数为一个阀门值,如果组件实例里面是有状态的的话就会触发setup的初始化函数入口 setupStatefulComponent 。
这里是setup的内部处理函数。里面主要分了以下几个处理
- 环境判断,对production环境进行特殊处理(不涉及getCurrentInstance所以不深入)
- 对实例的属性进行添加,如对实例的上下文进行proxy数据劫持进而赋值给proxy属性(这相当于我们vue2的
this
) - 从组件中解构setup函数
- 调用createSetupContext函数给setup的上下文添加属性,如attrs、slots、emit
- 紧接着调用setCurrentInstance函数把vue实例赋值给currentInstance变量
- 接下来就是自调用callWithErrorHandling函数,在该函数内部进行fn(...args)。(fn即setup函数,args即setup的上下文)
- 调用完将currentInstance变量赋值为null
- 对setup函数的执行结果进行处理
这就是setup内部的重要处理流程,这也就是为什么我们在同步任务中通过 getCurrentInstance
函数可以获取vue3实例,而在异步任务中取不到的原因。就是因为人家内部在setup函数同步调用完之后就置空了,异步是不可能获取到的。所以这也是为什么要用一个变量来保留。
结尾
最后,文章如有解释不对之处,欢迎评论区交流,如果感觉有所收获,还请点赞支持!