持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情
本篇文章是给之后实现provide/inject功能做铺垫的,我们会实现vue3中的一个API -- getCurrentInstance,它能够允许我们在setup中获取到当前组件实例对象,有了它,之后实现provide/inject会很方便
1. 思考实现方式
getCurrentInstance用于获取内部组件实例,它只能在setup或生命周期钩子当中调用
这是vue3官方文档中对getCurrentInstance的描述,根据这个描述,我们可以思考一下如何实现
既然只能在setup中调用,那么肯定要找到调用setup的地方,设置一个全局变量currentInstance,然后在调用setup之前将该变量指向当前的组件实例,那么在执行setup的时候,用户在setup中调用getCurrentInstance时,就可以获取到这个标记变量指向的组件实例了
思路还是比较直接的,接下来就实现它吧!
2. 实现
该功能明显是和组件有关的,因此在component.ts中实现,创建一个全局变量currentInstance用于记录当前组件实例,以及函数getCurrentInstance用于获取当前组件实例
let currentInstance = null;
export function getCurrentInstance() {
return currentInstance;
}
函数的实现很简单,直接返回全局变量的值即可
然后在index.ts中导出,从而能够让rollup进行打包
export { createApp } from './createApp';
export { h } from './h';
export { renderSlots } from './helpers/renderSlots';
export { createTextVNode } from './vnode';
+ export { getCurrentInstance } from './component';
还需要在setupStatefulComponent函数中执行setup之前将currentInstance指向当前组件实例,并在执行完setup之后将其置空
function setupStatefulComponent(instance: any) {
const Component = instance.type;
const { setup } = Component;
// ctx -- context
instance.proxy = new Proxy({ _: instance }, PublicInstanceProxyHandlers);
if (setup) {
+ currentInstance = instance;
const setupResult = setup(shallowReadonly(instance.props), {
emit: instance.emit.bind(null, instance),
});
+ currentInstance = null;
// setupResult 可能是 function 也可能是 object
// - function 则将其作为组件的 render 函数
// - object 则注入到组件的上下文中
handleSetupResult(instance, setupResult);
}
}
现在整个getCurrentInstance就算实现完了,接下来通过一个简单的demo测试一下
3. 通过案例测试
首先是App.js
{ getCurrentInstance, h } from '../../lib/plasticine-mini-vue.esm.js';
import { Foo } from './Foo.js';
export const App = {
name: 'App',
setup() {
const currentInstance = getCurrentInstance();
console.log('App: ', currentInstance);
},
render() {
return h('div', {}, [h('div', {}, 'App'), h(Foo)]);
},
};
在setup中调用getCurrentInstance获取到组件实例并打印出来
然后是Foo.js
import { getCurrentInstance, h } from '../../lib/plasticine-mini-vue.esm.js';
export const Foo = {
name: 'Foo',
setup() {
const currentInstance = getCurrentInstance();
console.log('Foo: ', currentInstance);
},
render() {
return h('div', {}, 'Foo');
},
};
同样也是在setup中调用getCurrentInstace并打印
可以看到成功获取到了
4. 重构
虽然实现的方式比较简单,代码也比较少,但仍然有一个地方可以重构,那就是currentInstance = instance这一句代码,就这样一句代码有什么好重构的呢?
思考一下,实际使用的时候是会有很多组件的,那么currentInstance = instance这句代码就会被执行很多次,而万一有那么一次,这个赋值出错了,是不利于我们追踪出错的地方的
但是如果用一个setCurrentInstance函数去封装,情况就不一样了,我们可以直接在这个函数内打上断点,就可以知道哪个地方出错了
function setupStatefulComponent(instance: any) {
const Component = instance.type;
const { setup } = Component;
// ctx -- context
instance.proxy = new Proxy({ _: instance }, PublicInstanceProxyHandlers);
if (setup) {
- currentInstance = instance;
+ setCurrentInstance(instance);
const setupResult = setup(shallowReadonly(instance.props), {
emit: instance.emit.bind(null, instance),
});
- currentInstance = null;
+ setCurrentInstance(null);
// setupResult 可能是 function 也可能是 object
// - function 则将其作为组件的 render 函数
// - object 则注入到组件的上下文中
handleSetupResult(instance, setupResult);
}
}
+ function setCurrentInstance(instance) {
+ currentInstance = instance;
+ }