「这是我参与2022首次更文挑战的第35天,活动详情查看:2022首次更文挑战」
本次的主题是细究setup。
问题和结论.
1 setup的执行时机,
create之前.这个可以简单验证
2 setup的两个参数是什么
props, ctx:{attrs, slots, emit}
3 setup 的返回值, 如果data中有同名属性结果如何?
一般会return 响应式对象和函数,其效果就像是挂到了data 和 methods中,在optionApi中可直接调用 , 如果有命名冲突,会以setup优先。
setup的调用时机
开始探究, 想知道setup的调用时机,直接在setup这里打个断点,然后我们来看调用栈。
flowchart LR
app.mount --> mount --> render --> patvh --> processComponenet --> callWidthErrorHandling--> setup
这样,我们就知道了, 是在第一次mount的时候,执行到setupComponent这个函数里,调用了setupStatefulComponent,这个函数里就是调用setup的地方。 callWidthErrorHandling,是一个通用的有错误捕获的调用函数的函数, 就是说,它的作用形同Function.prototype.call ,只不过它还有错误捕获逻辑。
setupComponent
我们就再次从setupComponent开始看。在初始化 属性和插槽之后,会判断是否状态组件。我们简单猜测一下,状态组件就是写了setup的组件。 这样才会继续走setupStatefulComponent。
在这个建立状态组件的函数里,前面有一堆在开发环境会调用的,我们折起来,暂且不看。
直接看有注释( call setup)的那一行 。 先是结构,然后判断其参数个数,没有参数自然是用不着上下文。有就会去创建一个。我们直接看 createSetupContext的返回值, 开发环境全部都是只读,生产环境只有props只读,总而言之,只读就对了。 略过两个和当前主题无关的函数,我们终于看到setup的调用了,而到目前为止,都没有处理options, 所以执行上就是setup最先执行。
setup里面为什么没有create的钩子
我们已经知道setup早于create,写个OnCreated是完全可以的。 但是,首先没有必要,既然我比BeforeCreate 还早为啥不直接写在setup里面,同样可以拿到属性值, 要修改什么数据(data选项)的话,也可以直接写在return 里面就行了。
并且, 此时已经有了instance, 组件实例vnode的初始化已经完成(ensureRenderer().createApp()),所以才能把组件实例当参数传进去。 mount的时候是先初始化vnode,然后调用render,调用render这才走setup后面的函数。可以说逻辑上的创建已完成,只不过还没调用钩子函数罢了。
setup的两个参数是什么
callWithErrorHandling 的第四个参数就是即将调用函数的入参。 第一个参数是要调用的函数。 instance.props 就是第一个参数,就是直接取自组件,生产环境下会保证为只读,正常使用它也应该是个只读。 第二个参数, 是前面调用createSetupContext方法的返回值,这个方法基本上就是保证 {attrs, slots, emits}三个属性不会被重写。
setup的返回值去哪了
好了,接下来,就直接看返回值。 我们setup的返回值当然不是Promise,所以直接走handleSetupResult, 这里函数里似乎没有直接处理, 只是把结果挂到实例上,instance.setupState = proxyRefs(setupResult), 又进入到finishComponentSetup里面。
但是实际上就是在这里处理的, proxy决定了我们访问instance的时候到底得到什么。我们点进这个handler里面去看,然后发现,这里有注释。 大意就是访问到data props ctx 里面的属性的时候, 就是直接写this. ,如果我们的属性名不是开头了。
这个accessCache就是开头从实例上解构下来的,初始的时候,这个缓存就是一个空对象 instance.accessCache = Object.create(null), 所以,我们先看 n === undefined的逻辑, 它首先就是判断在setup返回值里有就先返回它,并缓存(在我看来是标记这个值从setup里取)。 后面依次就是data props ctx 。 至此,优先级是怎么回事就清楚了,它并不是直接合并,而是控制了get。 data和 setup就能共存了,而且保留了原始数据。