diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index a954efadc..db0f72374 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -1,24 +1,17 @@ -import { VNode, normalizeVNode, VNodeChild, createVNode, Empty } from './vnode' +import { VNode, VNodeChild } from './vnode' import { ReactiveEffect, reactive, readonly } from '@vue/reactivity' import { RenderProxyHandlers, ComponentRenderProxy } from './componentProxy' import { ComponentPropsOptions } from './componentProps' import { Slots } from './componentSlots' -import { PatchFlags } from './patchFlags' -import { ShapeFlags } from './shapeFlags' import { warn } from './warning' import { ErrorTypes, - handleError, callWithErrorHandling, callWithAsyncErrorHandling } from './errorHandling' import { AppContext, createAppContext } from './apiApp' import { Directive } from './directives' -import { - applyOptions, - resolveAsset, - ComponentOptions -} from './componentOptions' +import { applyOptions, ComponentOptions } from './componentOptions' import { EMPTY_OBJ, isFunction, @@ -309,109 +302,3 @@ function createSetupContext(instance: ComponentInstance): SetupContext { } as any return __DEV__ ? Object.freeze(context) : context } - -// mark the current rendering instance for asset resolution (e.g. -// resolveComponent, resolveDirective) during render -export let currentRenderingInstance: ComponentInstance | null = null - -export function renderComponentRoot(instance: ComponentInstance): VNode { - const { - type: Component, - vnode, - renderProxy, - props, - slots, - attrs, - emit - } = instance - - let result - currentRenderingInstance = instance - try { - if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) { - result = normalizeVNode( - (instance.render as RenderFunction).call(renderProxy) - ) - } else { - // functional - const render = Component as FunctionalComponent - result = normalizeVNode( - render.length > 1 - ? render(props, { - attrs, - slots, - emit - }) - : render(props, null as any) - ) - } - } catch (err) { - handleError(err, instance, ErrorTypes.RENDER_FUNCTION) - result = createVNode(Empty) - } - currentRenderingInstance = null - return result -} - -export function shouldUpdateComponent( - prevVNode: VNode, - nextVNode: VNode, - optimized?: boolean -): boolean { - const { props: prevProps, children: prevChildren } = prevVNode - const { props: nextProps, children: nextChildren, patchFlag } = nextVNode - if (patchFlag) { - if (patchFlag & PatchFlags.DYNAMIC_SLOTS) { - // slot content that references values that might have changed, - // e.g. in a v-for - return true - } - if (patchFlag & PatchFlags.FULL_PROPS) { - // presence of this flag indicates props are always non-null - return hasPropsChanged(prevProps as Data, nextProps as Data) - } else if (patchFlag & PatchFlags.PROPS) { - const dynamicProps = nextVNode.dynamicProps as string[] - for (let i = 0; i < dynamicProps.length; i++) { - const key = dynamicProps[i] - if ((nextProps as any)[key] !== (prevProps as any)[key]) { - return true - } - } - } - } else if (!optimized) { - // this path is only taken by manually written render functions - // so presence of any children leads to a forced update - if (prevChildren != null || nextChildren != null) { - return true - } - if (prevProps === nextProps) { - return false - } - if (prevProps === null) { - return nextProps !== null - } - if (nextProps === null) { - return prevProps !== null - } - return hasPropsChanged(prevProps, nextProps) - } - return false -} - -function hasPropsChanged(prevProps: Data, nextProps: Data): boolean { - const nextKeys = Object.keys(nextProps) - if (nextKeys.length !== Object.keys(prevProps).length) { - return true - } - for (let i = 0; i < nextKeys.length; i++) { - const key = nextKeys[i] - if (nextProps[key] !== prevProps[key]) { - return true - } - } - return false -} - -export function resolveComponent(name: string): Component | undefined { - return resolveAsset('components', name) as any -} diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 85cdb26cc..790f1fcfd 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -1,7 +1,6 @@ import { ComponentInstance, Data, - currentRenderingInstance, currentInstance, Component, SetupContext @@ -35,6 +34,7 @@ import { ComponentPropsOptions, ExtractPropTypes } from './componentProps' import { Directive } from './directives' import { VNodeChild } from './vnode' import { ComponentRenderProxy } from './componentProxy' +import { currentRenderingInstance } from './componentRenderUtils' interface ComponentOptionsBase< Props, @@ -385,7 +385,15 @@ function applyMixins(instance: ComponentInstance, mixins: ComponentOptions[]) { } } -export function resolveAsset(type: 'components' | 'directives', name: string) { +export function resolveComponent(name: string): Component | undefined { + return resolveAsset('components', name) as any +} + +export function resolveDirective(name: string): Directive | undefined { + return resolveAsset('directives', name) as any +} + +function resolveAsset(type: 'components' | 'directives', name: string) { const instance = currentRenderingInstance || currentInstance if (instance) { let camelized diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts new file mode 100644 index 000000000..b988f67df --- /dev/null +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -0,0 +1,105 @@ +import { ComponentInstance, FunctionalComponent, Data } from './component' +import { VNode, normalizeVNode, createVNode, Empty } from './vnode' +import { ShapeFlags } from './shapeFlags' +import { handleError, ErrorTypes } from './errorHandling' +import { PatchFlags } from './patchFlags' + +// mark the current rendering instance for asset resolution (e.g. +// resolveComponent, resolveDirective) during render +export let currentRenderingInstance: ComponentInstance | null = null + +export function renderComponentRoot(instance: ComponentInstance): VNode { + const { + type: Component, + vnode, + renderProxy, + props, + slots, + attrs, + emit + } = instance + + let result + currentRenderingInstance = instance + try { + if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) { + result = normalizeVNode((instance.render as Function).call(renderProxy)) + } else { + // functional + const render = Component as FunctionalComponent + result = normalizeVNode( + render.length > 1 + ? render(props, { + attrs, + slots, + emit + }) + : render(props, null as any) + ) + } + } catch (err) { + handleError(err, instance, ErrorTypes.RENDER_FUNCTION) + result = createVNode(Empty) + } + currentRenderingInstance = null + return result +} + +export function shouldUpdateComponent( + prevVNode: VNode, + nextVNode: VNode, + optimized?: boolean +): boolean { + const { props: prevProps, children: prevChildren } = prevVNode + const { props: nextProps, children: nextChildren, patchFlag } = nextVNode + if (patchFlag) { + if (patchFlag & PatchFlags.DYNAMIC_SLOTS) { + // slot content that references values that might have changed, + // e.g. in a v-for + return true + } + if (patchFlag & PatchFlags.FULL_PROPS) { + // presence of this flag indicates props are always non-null + return hasPropsChanged(prevProps as Data, nextProps as Data) + } else if (patchFlag & PatchFlags.PROPS) { + const dynamicProps = nextVNode.dynamicProps as string[] + for (let i = 0; i < dynamicProps.length; i++) { + const key = dynamicProps[i] + if ((nextProps as any)[key] !== (prevProps as any)[key]) { + return true + } + } + } + } else if (!optimized) { + // this path is only taken by manually written render functions + // so presence of any children leads to a forced update + if (prevChildren != null || nextChildren != null) { + return true + } + if (prevProps === nextProps) { + return false + } + if (prevProps === null) { + return nextProps !== null + } + if (nextProps === null) { + return prevProps !== null + } + return hasPropsChanged(prevProps, nextProps) + } + return false +} + +function hasPropsChanged(prevProps: Data, nextProps: Data): boolean { + const nextKeys = Object.keys(nextProps) + if (nextKeys.length !== Object.keys(prevProps).length) { + return true + } + for (let i = 0; i < nextKeys.length; i++) { + const key = nextKeys[i] + if (nextProps[key] !== prevProps[key]) { + return true + } + } + return false +} diff --git a/packages/runtime-core/src/createRenderer.ts b/packages/runtime-core/src/createRenderer.ts index fd0d6aa29..a70dce625 100644 --- a/packages/runtime-core/src/createRenderer.ts +++ b/packages/runtime-core/src/createRenderer.ts @@ -9,11 +9,13 @@ import { } from './vnode' import { ComponentInstance, - renderComponentRoot, - shouldUpdateComponent, createComponentInstance, setupStatefulComponent } from './component' +import { + renderComponentRoot, + shouldUpdateComponent +} from './componentRenderUtils' import { isString, EMPTY_OBJ, diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts index dd3f542a6..41a184efb 100644 --- a/packages/runtime-core/src/directives.ts +++ b/packages/runtime-core/src/directives.ts @@ -14,10 +14,10 @@ return applyDirectives(h(comp), [ import { VNode, cloneVNode } from './vnode' import { extend, isArray, isFunction } from '@vue/shared' import { warn } from './warning' -import { ComponentInstance, currentRenderingInstance } from './component' +import { ComponentInstance } from './component' +import { currentRenderingInstance } from './componentRenderUtils' import { callWithAsyncErrorHandling, ErrorTypes } from './errorHandling' import { HostNode } from './createRenderer' -import { resolveAsset } from './componentOptions' import { ComponentRenderProxy } from './componentProxy' export interface DirectiveBinding { @@ -133,7 +133,3 @@ export function invokeDirectiveHook( callWithAsyncErrorHandling(hook, instance, ErrorTypes.DIRECTIVE_HOOK, args) } } - -export function resolveDirective(name: string): Directive | undefined { - return resolveAsset('directives', name) as any -} diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 5b794cf39..c5f7d1585 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -36,9 +36,9 @@ export { callWithAsyncErrorHandling } from './errorHandling' -// For the compiler -export { resolveComponent } from './component' -export { applyDirectives, resolveDirective } from './directives' +// For compiler generated code +export { applyDirectives } from './directives' +export { resolveComponent, resolveDirective } from './componentOptions' // Types -----------------------------------------------------------------------