一些问题

1.当连续触发响应式的trigger会导致组件的effectComponent连续执行更新吗?

并不会。第一次触发trigger的时候会把事件队列状态改成Pending,把flushJobs丢进microtask来执行,第二次触发trigger会检测到当前状态如果是Pending或者是Flushing状态的时候将不进行下去。
可在packages/runtime-core/tests/rendererAttrsFallthrough.spec.ts ‘should allow attrs to fallthrough’测试用例中调试。

一些问题 - 图1

2.patch到底有多少种类型?分别是干什么的?

在patch中查看有processText、processCommentNode、mountStaticNode、patchStaticNode、processFragment、processElement(流程图已有)、processComponent(流程图已有)、Teleport、SUSPENSE。

3.instance.props、instance.attrs和vnode.props分别是什么?关系是什么?

先说一下设置的阶段,和如何设置的:
1.instance.props和instance.attrs是在第二步骤中setupComponent中的initProps实现的,是根据vnode.props来设置的,instance和组件类型相关。
2.vnode.props创建vnode的时候传入的参数,比如h(Child, { foo: 1, class: 'parent' })

使用阶段,拿这个做例子:

  1. const Hello = {
  2. setup() {
  3. const count = ref(0)
  4. function inc() {
  5. count.value++
  6. }
  7. return () =>
  8. h(Child, {
  9. foo: count.value + 1,
  10. id: 'test',
  11. class: 'c' + count.value,
  12. style: { color: count.value ? 'red' : 'green' },
  13. onClick: inc,
  14. 'data-id': count.value + 1
  15. })
  16. }
  17. }
  18. const Child = {
  19. setup(props: any) {
  20. return () =>
  21. h(
  22. 'div',
  23. {
  24. class: 'c2',
  25. style: { fontWeight: 'bold' }
  26. },
  27. props.foo
  28. )
  29. }
  30. }
  31. const root = document.createElement('div')
  32. document.body.appendChild(root)
  33. render(h(Hello), root)

步骤一:第一次渲染,流程图②setupComponent的initProps,使用Hello组件的vnode.props(当前为空,所以输出的attrs都是为空的),标准化生成vnode.attrs和vnode.props

  1. if (!instance.type.props) {
  2. // functional w/ optional props, props === attrs
  3. instance.props = attrs
  4. } else {
  5. // functional w/ declared props
  6. instance.props = props
  7. }

步骤二:②setupConponent中,renderComponentRoot(instance)会把instance.attrs与调用instance.render(Hello.setup返回的方法)所返回的vnode的props进行合并。

步骤三:子组件进行patch,同样经过initProps处理,
在②中调用setup方法,并传入instance.props, instance.setupConetxt,这个时候子组件就会利用到父组件传入的props了。

那更新的时候,是怎么样的?

触发父组件instance,renderComponentRoot(instance)中调用instance.render(这个过程就是vnode.props的更新了)
再次进行当前track追踪(effect运行的特性,是会删除当前追踪),patch(旧vnode1, 新vnode2),processComponent updateComponent,
新vnode2引用vnode1.component(instance2,子组件instance2),删除queue任务中的instance2.update,instance2.next = n2,执行instance2.update,
检测到有next字段,执行updateComponentPreRender,这里instance2是为了标识是子instance

  1. nextVNode.component = instance2 // 对当前没用,因为已经引用了
  2. const prevProps = instance2.vnode.props
  3. instance2.vnode = nextVNode // 更新vnode
  4. instance2.next = null // 删除next
  5. updateProps(instance2, nextVNode.props, prevProps, optimized)
  6. updateSlots(instance2, nextVNode.children)

updateProps会利用setFullProps来更新attrs和props,这里的更新过的props和attrs可以提供給Child内部使用,如当前使用了props.foo。