导图社区 mini-vue vue3的runtime-core核心逻辑
剥离核心代码 当我们打开 vue3 的源码之后你会发现,代码量是如此之多。这个源码到底该从何读起。虽然 vue3 代码的可读性是很高的,但是架不住代码量大呀 mini...
编辑于2023-01-30 14:48:40 江西mini-vue vue3的runtime-core核心逻辑
runtime-core逻辑
创建app
进行初始化
基于rootComponent,初始化VNode
返回一个vnode对象。最主要的功能就是判断shapeFlag的类型。
ShapeFlags.ELEMENT (1)
ShapeFlags.FUNCTIONAL_COMPONENT(1 << 1)
ShapeFlags.STATEFUL_COMPONENT(1 << 2)
ShapeFlags.ARRAY_CHILDREN (1 << 3)
ShapeFlags.TEXT_CHILDREN (1 << 4)
ShapeFlags.SLOTS_CHILDREN (1 << 5)
ShapeFlags.TELEPORT(1 << 6)
ShapeFlags.SUSPENSE(1 << 7)
ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE(1 << 8)
ShapeFlags.COMPONENT_KEPT_ALIVE(1 << 9)
ShapeFlags.COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
对VNode进行render
调用patch
处理不同的节点
处理 Text节点
使用processText(n1, n2, container)处理
* 旧vnode为空
1、n1 是 null 说明是 init 的阶段。 2、基于 createText 创建出 text 节点,然后使用 insert 添加到 el 内。
* 调用hostInsert()插入真实的dom节点 * 旧vnode不为空
1、update阶段 2、先对比一下 updated 之后的内容是否和之前的不一样 3、在不一样的时候才需要 update text,这里抽离出来的接口是 setText
* 新旧的文本不一致 * 调用hostSetText()更改dom的文本
处理Comment节点
使用proceCommentNode(n1, n2, container)处理
* n1为null * 调用hostInsert()插入真实的dom节点 * n1不为null * n2,el = n1.el
不支持动态注释
处理Static 节点
n1为空
* 使用 mountStaticNode(n2,container)处理 * 调用hostInsertStaticContent()插入真实的dom节点
静态节点不需要花里胡哨的操作,直接渲染就行
开发环境
* 使用patchStaticNode(n1, n2, container)处理 * n2.children与n1.children一致
直接把n1.el赋值给n2.el
* n2.children与n1.children不一致 * 调用hostInsertStaticContent()插入真实的dom节点
删除n1.el, 插入n2.el。简单粗暴。
处理Fragment节点
使用processFragment(n1, n2, container)处理
* 旧vnode不为空(!n1)
只需要渲染 children ,然后给添加到 container 内
* 使用mountChildren函数
处理其他节点
基于vnode的类型判断处理不同的组件逻辑
* 处理shapeFlag & ShapeFlags.STATEFUL_COMPONENT类型 * 处理n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE类型
keepalive组件第一次是会进行正常初始化
* KeepAliveContext.activate()处理 * 1、move() 挂载节点 * 2、patch() 更新子组件的props * 3、queuePostRenderEffect() 执行子节点定义的actived()生命周期 * component初始化 * 1、初始化 component instance对象 * 2、setup component * initProps() 初始化props * initSlots() 初始化slot * 调用setupStatefulComponent() * option有setup * setCurrentInstance(instance)
设置当前 currentInstance 的值。 @必须要在调用 setup 之前
* 调用setup() * 调用handleSetupResult()
处理setup的执行结果
* typeof setupResult === "function"
返回的是 function 的话,那么绑定到instance的render 上
* typeof setupResult === "object"
返回的是object 的话,那么绑定到instance的setupState上
* 调用finishComponentSetup()
判断instance.render是否存在,不存在就将Component.render赋值给instance.render(处理的是setupResult为Object的情况)
* option没有setup * 调用finishComponentSetup() * 3、setupRenderEffect * 1、调用renderComponentRoot()获取vnode(子组件) * 2、触发beforeMount生命周期 * 3、调用patch初始化子组件(递归) * 4、触发mounted生命周期 * component更新 * 1、对比组件是否需要更新
更新组件的数据(props,slots 等)
* 2、触发beforeUpdated生命周期 * 3、生成当前更新的nextTree(也就是旧vnode) * 4、调用patch处理新旧vnode * 5、触发updated生命周期 * 处理shapeFlag & ShapeFlags.ELEMENT类型 * element初始化 * 1、调用hostCreateElement()创建真实dom * 2、处理children * 处理shapeFlag & ShapeFlags.TEXT_CHILDREN类型
这里 children 就是文本 ,只需要渲染一下就完事了
* 调用hostSetElementText()处理文本 * 处理shapeFlag & ShapeFlags.ARRAY_CHILDREN类型
这里 children 就是个数组了,就需要依次调用 patch 递归来处理。
* 使用mountChildren函数 * 3、调用hostPatchProp函数设置元素的prop * 4、触发 beforeMount() 钩子 * vnodeHook(虚拟节点) * onVnodeBeforeMount * DirectiveHook(指令) * beforeMount * transition(动画) * beforeEnter * 5、调用hostInsert()插入真实的dom节点 * 6、触发 mounted() 钩子 * vnodeHook(虚拟节点) * onVnodeMounted * DirectiveHook(指令) * mounted * transition(动画) * enter * element更新 * 对比props * prop的key不变,value改变 * 调用hostPatchProp()处理
key在oldProps 有,newProps 也有,但是 val 值变更了。 举个例子,之前: oldProps.id = 1 ,更新后:newProps.id = 2
@以 newProps 作为基准
* prop的key改变 * 调用hostPatchProp()处理
key在oldProps 有,而 newProps 没有了。 举个例子之前:{id:1,tId:2} 更新后: {id:1}
@以 oldProps 作为基准
* 对比children * 判断新vnode为是shapeFlag & ShapeFlags.TEXT_CHILDREN类型
1、如果 n2 的 children 是 text 类型的话 2、就看看和之前的 n1 的 children 是不是一样的 3、如果不一样的话直接重新设置一下 text 即可
* 新旧vnode的children不一样 * 调用hostSetElementText()处理文本 * 不是文本类型(不是上面的类型) * 新旧vnode的children都是shapeFlag & ShapeFlags.ARRAY_CHILDREN类型 * 调用patchKeyedChildren()处理(diff算法) * 处理shapeFlag & ShapeFlags.TELEPORT类型 * TeleportImpl.process()处理 * 处理shapeFlag & ShapeFlags.SUSPENSE类型 * SuspenseImpl.process()处理 * suspense初始化 * 1、初始化suspense实例 * 2、挂载subTree * 3、判断是否有异步的依赖 * (有)挂载fallback tree * (无)suspense.resolve() * suspense更新
keep-live组件
setup()
onMountd生命周期
调用cacheSubtree()缓存当前子数的数据
onUpdated生命周期
调用cacheSubtree()缓存当前子数的数据
onBeforeUnmount生命周期
遍历cache里的子组件缓存
1、当前实例将作为 keep-alive 卸载的一部分被卸载
2、queuePostRenderEffect() 调用子组件的deactivated生命周期
watch监听 include 和 exclude
计算cache缓存的子组件(先计算include,后计算exclude)
pruneCache()
return
type Funciton
5个判断条件,返回的类型
!slots.default 表示没有子组件
也就是没有
这种情况
返回null(current = null)
判断有多个子组件
例子:
返回 children ( current = null)
判断rowNode不是 VNode类型 或者 不是 rawVNode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT 和 rawVNode.shapeFlag & ShapeFlags.SUSPENSE
返回 rowNode (current = null)
当子组件名称不匹配 include 的配置值,或者子组件名称匹配了 exclude 的值,都不该被缓存,而是直接返回
返回rowNode (current = node)
是否有缓存(使用LUR算法)
有
* 使用缓存的数据
渲染函数执行时,若命中缓存时,则从 keys 中删除当前命中的 key,并往 keys 末尾追加 key 值,保存新鲜
* 返回rowNode (current = node)
无
* 添加进缓存中
未命中缓存时,则 keys 追加缓存数据 key 值,若此时缓存数据长度大于 max 最大值,则删除最旧的数据,这里的值是 keys 中第一个值
* 返回rowNode (current = node)