|Vue.js源码全方位深入解析,快人一步进名企-完结( 三 )


// 初始化 插槽
initSlots(instance children)
// 设置有状态的组件实例
const setupResult = isStateful
? setupStatefulComponent(instance isSSR)
: undefined
return setupResult

可以看到 , 我们从组件 vnode 中获取了 props、children、shapeFlag 等属性 , 然后分别对 props 和插槽进行初始化 , 这两部分逻辑在后续的章节再详细分析 。 根据 shapeFlag 的值 , 我们可以判断这是不是一个有状态组件 , 如果是则要进一步去设置有状态组件的实例 。 接下来我们要关注到 setupStatefulComponent 函数 , 它主要做了三件事:创建渲染上下文代理、判断处理 setup 函数和完成组件实例设置 。 它代码如下所示:
function setupStatefulComponent (instance isSSR) <{p>
const Component = instance.type
// 创建渲染代理的属性访问缓存
instance.accessCache = {
// 创建渲染上下文代理
instance.proxy = new Proxy(instance.ctx PublicInstanceProxyHandlers)
// 判断处理 setup 函数
const { setup= Component
if (setup) <{p>
// 如果 setup 函数带参数 , 则创建一个 setupContext
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
// 执行 setup 函数 , 获取结果
const setupResult = callWithErrorHandling(setup instance 0 /* SETUP_FUNCTION */ [instance.props setupContext
)
// 处理 setup 执行结果
handleSetupResult(instance setupResult)

else <{p>
// 完成组件实例设置
finishComponentSetup(instance)

复制代码
创建渲染上下文代理
首先是创建渲染上下文代理的流程 , 它主要对 instance.ctx 做了代理 。 在分析实现前 , 我们需要思考一个问题 , 这里为什么需要代理呢?
其实在 Vue.js 2.x 中 , 也有类似的数据代理逻辑 , 比如 props 求值后的数据 , 实际上存储在 this._props 上 , 而 data 中定义的数据存储在 this._data 上 。 举个例子:
<template>
<p>< msg ></p>
</template>
<script>
export default <{p>
data() <{p>
msg: 1


</script>复制代码
在初始化组件的时候 , data 中定义的 msg 在组件内部是存储在 this._data 上的 , 而模板渲染的时候访问 this.msg , 实际上访问的是 this._data.msg , 这是因为 Vue.js 2.x 在初始化 data 的时候 , 做了一层 proxy 代理 。
到了 Vue.js 3.0 , 为了方便维护 , 我们把组件中不同状态的数据存储到不同的属性中 , 比如存储到 setupState、ctx、data、props 中 。 我们在执行组件渲染函数的时候 , 为了方便用户使用 , 会直接访问渲染上下文 instance.ctx 中的属性 , 所以我们也要做一层 proxy , 对渲染上下文 instance.ctx 属性的访问和修改 , 代理到对 setupState、ctx、data、props 中的数据的访问和修改 。
明确了代理的需求后 , 我们接下来就要分析 proxy 的几个方法:get、set 和 has 。 当我们访问 instance.ctx 渲染上下文中的属性时 , 就会进入 get 函数 。 我们来看一下它的实现:
const PublicInstanceProxyHandlers = <{p>
get ({ _: instancekey) <{p>
const { ctx setupState data props accessCache type appContext= instance
if (key[0
!== '$') <{p>
// setupState / data / props / ctx
// 渲染代理的属性访问缓存中
const n = accessCache[key

if (n !== undefined) <{p>
// 从缓存中取
switch (n) <{p>
case 0: /* SETUP */
return setupState[key

case 1 :/* DATA */
return data[key

case 3 :/* CONTEXT */
return ctx[key

case 2: /* PROPS */