运行带有副作用的render函数
const setupRenderEffect = (
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
) => {
instance.update = effect(function componentEffect() {
if (!instance.isMounted) {
let vnodeHook
const { el, props } = initialVNode
const { bm, m, parent } = instance
// beforeMount hook 执行beforeMount(挂载之前)钩子函数,是个数组。
if (bm) {
invokeArrayFns(bm)
}
// 生成子树结构,创建组件的vnode
// 执行组件内部的render函数 (手写或template编译生成) 生成渲染vnode,渲染vnode就是组件实际要渲染出来的
// 内容对应的vnode,并将渲染vnode存储到组件实例上
// 注意:执行render函数会访问组件实例上的响应式数据,从而触发依赖收集,当前定义的renderEffect会被收集到依赖仓库
// 当后续发生数据变化时,renderEffect则会被派发,触发re-render
const subTree = (instance.subTree = renderComponentRoot(instance))
if (el && hydrateNode) {
} else {
// 将子树渲染到container中
patch(
null,
subTree,
container,
anchor,
instance,
parentSuspense,
isSVG
)
initialVNode.el = subTree.el
}
// mounted hook
// 执行mount(挂载之后)钩子函数
if (m) {
queuePostRenderEffect(m, parentSuspense)
}
instance.isMounted = true
initialVNode = container = anchor = null
} else {
// updateComponent 更新组件,非首次
let { next, bu, u, parent, vnode } = instance
let originNext = next
let vnodeHook
if (next) {
next.el = vnode.el
updateComponentPreRender(instance, next, optimized)
} else {
next = vnode
}
const nextTree = renderComponentRoot(instance)
const prevTree = instance.subTree
instance.subTree = nextTree
// 把对应的subTree渲染到container中,子树可能是element、text、component等
patch(
prevTree,
nextTree,
// parent may have changed if it's in a teleport
hostParentNode(prevTree.el),
// anchor may have changed if it's in a fragment
getNextHostNode(prevTree),
instance,
parentSuspense,
isSVG
)
next.el = nextTree.el
}
}, createDevEffectOptions(instance))
}
总结
该方法只要是实现 instance.update
方法,这个方法其实就是一个 effect
。
- 在没有传入
options.lazy
时,会默认执行一次; - 触发
effect
的get,然后收集当前的componentEffect
,当数据有变化时,会执行trigger
- 当配置
scheduler
为queueJob
时,会放入全局队列queue
中等待nextTick
运行,因此多个状态会合并在一起并更新视图
主要做了3件事:
- 更新组件VNode节点
- 渲染子树VNode
- 根据新旧子VNode,执行
patch