Vue.js 3.0设计了一个很强大的API-Compositon API, 它主要用来优化代码逻辑的组织和复用。它提供了一个Setup的启动函数。接下我们来看看它到底是怎么运行的。这里主要调用的一个函数是:setupComponent:
function setupComponent(instance, isSSR = false) {
isInSSRComponentSetup = isSSR;
const { props, children } = instance.vnode;
// 判断是否是一个有状态的组件
const isStateful = isStatefulComponent(instance);
// 初始化props
initProps(instance, props, isStateful, isSSR);
// 初始化插槽
initSlots(instance, children);
// 设置有状态的组件实例
const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
: undefined;
isInSSRComponentSetup = false;
return setupResult;
}
我们从组件vnode中获取了props, children属性,然后分别对props和插槽进行了初始化。这里面有一个比较重要的函数:setupStatefulComponent:
function setupStatefulComponent(instance, isSSR) {
...
const { setup } = Component;
if (setup) {
...
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null);
currentInstance = instance;
pauseTracking();
const setupResult = callWithErrorHandling(setup, instance, 0 /* SETUP_FUNCTION */, [shallowReadonly(instance.props) , setupContext]);
resetTracking();
...
}
}
从源码上看,应该就是在这个函数,这几句代码开始处理Setup的方法。创建了一个setupContext。这里主要有三个步骤:创建setup函数上下文、执行setup函数并获取结果、处理setup函数的执行结果。
const setupContext = (instance.setupContext = setup.length > 1 ? createSetupContext(instance) : null);
这是判断setup函数的参数长度,如果大于1,则创建setupContext上下文。 举个例子: HelloWorld组件,代码如下:
<template>
<p>{{msg}}</p>
<button @click="onClick">Toggle</button>
</template>
<script>
export default{
props:{
msg: string
},
setup(props, {emit}){
function onClick(){
emit('toggle')
}
return {
onClick
}
}
}
</script>
父组件:
<template>
<HelloWorld @toggle="toggle" :msg="msg"></HelloWorld>
</template>
<script>
import { ref } from 'vue'
import HelloWorld from './HelloWorld.js'
export default{
components: {
HelloWorld,
},
setup(){
const msg = ref('Hello World')
function toggle(){
msg.value = msg.value === 'Hello World' ? 'Hello Vue' : 'Hello World'
}
return {
msg,
toggle
}
}
}
</script>
其中HelloWorld组件中的第二个参数:emit,实际上就是setupContext。 我们来看一下创建这个对象的函数:createSetupContext
function createSetupContext(instance) {
const expose = exposed => {
if (instance.exposed) {
warn(`expose() should be called only once per setup().`);
}
instance.exposed = proxyRefs(exposed);
};
{
// We use getters in dev in case libs like test-utils overwrite instance
// properties (overwrites should not be done in prod)
return Object.freeze({
get attrs() {
return new Proxy(instance.attrs, attrHandlers);
},
get slots() {
return shallowReadonly(instance.slots);
},
get emit() {
return (event, ...args) => instance.emit(event, ...args);
},
expose
});
}
}
这里返回了一个对象,包括attrs、slots、emit三个属性。