diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index f48c2bb5..8a141fe5 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -1,6 +1,6 @@ import { HMRRuntime } from '../src/hmr' import '../src/hmr' -import { ComponentOptions, RenderFunction } from '../src/component' +import { ComponentOptions, InternalRenderFunction } from '../src/component' import { render, nodeOps, @@ -18,7 +18,9 @@ const { createRecord, rerender, reload } = __VUE_HMR_RUNTIME__ function compileToFunction(template: string) { const { code } = baseCompile(template) - const render = new Function('Vue', code)(runtimeTest) as RenderFunction + const render = new Function('Vue', code)( + runtimeTest + ) as InternalRenderFunction render._rc = true // isRuntimeCompiled return render } diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index cdde1ffb..fb945bcb 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -3,9 +3,10 @@ import { MethodOptions, ComponentOptionsWithoutProps, ComponentOptionsWithArrayProps, - ComponentOptionsWithObjectProps + ComponentOptionsWithObjectProps, + RenderFunction } from './componentOptions' -import { SetupContext, RenderFunction, FunctionalComponent } from './component' +import { SetupContext, FunctionalComponent } from './component' import { ComponentPublicInstance } from './componentProxy' import { ExtractPropTypes, ComponentPropsOptions } from './componentProps' import { EmitsOptions } from './componentEmits' diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 2136ae79..dd1bf62c 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -47,11 +47,28 @@ import { startMeasure, endMeasure } from './profiling' export type Data = { [key: string]: unknown } +// Note: can't mark this whole interface internal because some public interfaces +// extend it. export interface SFCInternalOptions { + /** + * @internal + */ __scopeId?: string + /** + * @internal + */ __cssModules?: Data + /** + * @internal + */ __hmrId?: string + /** + * @internal + */ __hmrUpdated?: boolean + /** + * This one should be exposed so that devtools can make use of it + */ __file?: string } @@ -59,6 +76,7 @@ export interface FunctionalComponent< P = {}, E extends EmitsOptions = Record > extends SFCInternalOptions { + // use of any here is intentional so it can be a valid JSX Element constructor (props: P, ctx: SetupContext): any props?: ComponentPropsOptions

emits?: E | (keyof E)[] @@ -105,7 +123,10 @@ export interface SetupContext { emit: EmitFn } -export type RenderFunction = { +/** + * @internal + */ +export type InternalRenderFunction = { ( ctx: ComponentPublicInstance, cache: ComponentInternalInstance['renderCache'] @@ -113,39 +134,89 @@ export type RenderFunction = { _rc?: boolean // isRuntimeCompiled } +/** + * We expose a subset of properties on the internal instance as they are + * useful for advanced external libraries and tools. + */ export interface ComponentInternalInstance { uid: number type: Component parent: ComponentInternalInstance | null - appContext: AppContext root: ComponentInternalInstance + appContext: AppContext + /** + * Vnode representing this component in its parent's vdom tree + */ vnode: VNode + /** + * The pending new vnode from parent updates + * @internal + */ next: VNode | null + /** + * Root vnode of this component's own vdom tree + */ subTree: VNode + /** + * The reactive effect for rendering and patching the component. Callable. + */ update: ReactiveEffect - render: RenderFunction | null - effects: ReactiveEffect[] | null + /** + * The render function that returns vdom tree. + * @internal + */ + render: InternalRenderFunction | null + /** + * Object containing values this component provides for its descendents + * @internal + */ provides: Data - // cache for proxy access type to avoid hasOwnProperty calls + /** + * Tracking reactive effects (e.g. watchers) associated with this component + * so that they can be automatically stopped on component unmount + * @internal + */ + effects: ReactiveEffect[] | null + /** + * cache for proxy access type to avoid hasOwnProperty calls + * @internal + */ accessCache: Data | null - // cache for render function values that rely on _ctx but won't need updates - // after initialized (e.g. inline handlers) + /** + * cache for render function values that rely on _ctx but won't need updates + * after initialized (e.g. inline handlers) + * @internal + */ renderCache: (Function | VNode)[] - // assets for fast resolution + /** + * Asset hashes that prototypally inherits app-level asset hashes for fast + * resolution + * @internal + */ components: Record + /** + * @internal + */ directives: Record // the rest are only for stateful components --------------------------------- // main proxy that serves as the public instance (`this`) proxy: ComponentPublicInstance | null - // alternative proxy used only for runtime-compiled render functions using - // `with` block + + /** + * alternative proxy used only for runtime-compiled render functions using + * `with` block + * @internal + */ withProxy: ComponentPublicInstance | null - // This is the target for the public instance proxy. It also holds properties - // injected by user options (computed, methods etc.) and user-attached - // custom properties (via `this.x = ...`) + /** + * This is the target for the public instance proxy. It also holds properties + * injected by user options (computed, methods etc.) and user-attached + * custom properties (via `this.x = ...`) + * @internal + */ ctx: Data // internal state @@ -156,34 +227,91 @@ export interface ComponentInternalInstance { refs: Data emit: EmitFn - // setup + /** + * setup related + * @internal + */ setupState: Data + /** + * @internal + */ setupContext: SetupContext | null - // suspense related + /** + * suspense related + * @internal + */ suspense: SuspenseBoundary | null + /** + * @internal + */ asyncDep: Promise | null + /** + * @internal + */ asyncResolved: boolean // lifecycle isMounted: boolean isUnmounted: boolean isDeactivated: boolean + /** + * @internal + */ [LifecycleHooks.BEFORE_CREATE]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.CREATED]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.BEFORE_MOUNT]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.MOUNTED]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.BEFORE_UPDATE]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.UPDATED]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.UNMOUNTED]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.RENDER_TRACKED]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.RENDER_TRIGGERED]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.ACTIVATED]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.DEACTIVATED]: LifecycleHook + /** + * @internal + */ [LifecycleHooks.ERROR_CAPTURED]: LifecycleHook - // hmr marker (dev only) + /** + * hmr marker (dev only) + * @internal + */ renderUpdated?: boolean } @@ -386,7 +514,7 @@ export function handleSetupResult( ) { if (isFunction(setupResult)) { // setup returned an inline render function - instance.render = setupResult as RenderFunction + instance.render = setupResult as InternalRenderFunction } else if (isObject(setupResult)) { if (__DEV__ && isVNode(setupResult)) { warn( @@ -413,7 +541,7 @@ export function handleSetupResult( type CompileFunction = ( template: string | object, options?: CompilerOptions -) => RenderFunction +) => InternalRenderFunction let compile: CompileFunction | undefined @@ -435,7 +563,7 @@ function finishComponentSetup( // template / render function normalization if (__NODE_JS__ && isSSR) { if (Component.render) { - instance.render = Component.render as RenderFunction + instance.render = Component.render as InternalRenderFunction } } else if (!instance.render) { if (compile && Component.template && !Component.render) { @@ -449,7 +577,7 @@ function finishComponentSetup( endMeasure(instance, `compile`) } // mark the function as runtime compiled - ;(Component.render as RenderFunction)._rc = true + ;(Component.render as InternalRenderFunction)._rc = true } if (__DEV__ && !Component.render) { @@ -471,7 +599,7 @@ function finishComponentSetup( } } - instance.render = (Component.render || NOOP) as RenderFunction + instance.render = (Component.render || NOOP) as InternalRenderFunction // for runtime-compiled render functions using `with` blocks, the render // proxy used needs a different `has` handler which is more performant and diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 78ef9c3d..5a773f08 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -2,7 +2,6 @@ import { ComponentInternalInstance, Data, SetupContext, - RenderFunction, SFCInternalOptions, PublicAPIComponent, Component @@ -51,6 +50,7 @@ import { EmitsOptions } from './componentEmits' import { Directive } from './directives' import { ComponentPublicInstance } from './componentProxy' import { warn } from './warning' +import { VNodeChild } from './vnode' /** * Interface for declaring custom options. @@ -70,6 +70,8 @@ import { warn } from './warning' */ export interface ComponentCustomOptions {} +export type RenderFunction = () => VNodeChild + export interface ComponentOptionsBase< Props, RawBindings, @@ -95,13 +97,6 @@ export interface ComponentOptionsBase< // Luckily `render()` doesn't need any arguments nor does it care about return // type. render?: Function - // SSR only. This is produced by compiler-ssr and attached in compiler-sfc - // not user facing, so the typing is lax and for test only. - ssrRender?: ( - ctx: any, - push: (item: any) => void, - parentInstance: ComponentInternalInstance - ) => void components?: Record directives?: Record inheritAttrs?: boolean @@ -109,11 +104,34 @@ export interface ComponentOptionsBase< // Internal ------------------------------------------------------------------ - // marker for AsyncComponentWrapper + /** + * SSR only. This is produced by compiler-ssr and attached in compiler-sfc + * not user facing, so the typing is lax and for test only. + * + * @internal + */ + ssrRender?: ( + ctx: any, + push: (item: any) => void, + parentInstance: ComponentInternalInstance + ) => void + + /** + * marker for AsyncComponentWrapper + * @internal + */ __asyncLoader?: () => Promise - // cache for merged $options + /** + * cache for merged $options + * @internal + */ __merged?: ComponentOptions + // Type differentiators ------------------------------------------------------ + + // Note these are internal but need to be exposed in d.ts for type inference + // to work! + // type-only differentiator to separate OptionWithoutProps from a constructor // type returned by defineComponent() or FunctionalComponent call?: never diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index eff67e63..4a37f4f8 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -1,7 +1,7 @@ import { ComponentInternalInstance, ComponentOptions, - RenderFunction + InternalRenderFunction } from './component' import { queueJob, queuePostFlushCb } from './scheduler' @@ -65,14 +65,14 @@ function createRecord(id: string, comp: ComponentOptions): boolean { return true } -function rerender(id: string, newRender?: RenderFunction) { +function rerender(id: string, newRender?: Function) { const record = map.get(id) if (!record) return // Array.from creates a snapshot which avoids the set being mutated during // updates Array.from(record.instances).forEach(instance => { if (newRender) { - instance.render = newRender + instance.render = newRender as InternalRenderFunction } instance.renderCache = [] // this flag forces child components with slot content to update diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index d8bf480b..a7049f17 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -1,4 +1,4 @@ -// Public API ------------------------------------------------------------------ +// Core API ------------------------------------------------------------------ export const version = __VERSION__ export { @@ -51,15 +51,11 @@ export { getCurrentInstance } from './component' // For raw render function users export { h } from './h' -export { - createVNode, - cloneVNode, - mergeProps, - openBlock, - createBlock -} from './vnode' -// Internal Components -export { Text, Comment, Fragment } from './vnode' +// Advanced render function utilities +export { createVNode, cloneVNode, mergeProps } from './vnode' +// VNode types +export { Fragment, Text, Comment, Static } from './vnode' +// Built-in components export { Teleport, TeleportProps } from './components/Teleport' export { Suspense, SuspenseProps } from './components/Suspense' export { KeepAlive, KeepAliveProps } from './components/KeepAlive' @@ -67,10 +63,8 @@ export { BaseTransition, BaseTransitionProps } from './components/BaseTransition' - // SFC CSS Modules export { useCSSModule } from './helpers/useCssModule' - // SSR context export { useSSRContext, ssrContextKey } from './helpers/useSsrContext' @@ -125,6 +119,7 @@ export { } from './apiCreateApp' export { VNode, + VNodeChild, VNodeTypes, VNodeProps, VNodeArrayChildren, @@ -134,7 +129,6 @@ export { Component, FunctionalComponent, ComponentInternalInstance, - RenderFunction, SetupContext } from './component' export { @@ -143,7 +137,8 @@ export { ComponentOptionsWithObjectProps, ComponentOptionsWithArrayProps, ComponentCustomOptions, - ComponentOptionsBase + ComponentOptionsBase, + RenderFunction } from './componentOptions' export { ComponentPublicInstance, @@ -202,6 +197,8 @@ export { renderSlot } from './helpers/renderSlot' export { createSlots } from './helpers/createSlots' export { pushScopeId, popScopeId, withScopeId } from './helpers/scopeId' export { + openBlock, + createBlock, setBlockTracking, createTextVNode, createCommentVNode, diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index eee9a1b8..d3e3ef4d 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -82,12 +82,17 @@ export interface VNodeProps { onVnodeUnmounted?: VNodeMountHook | VNodeMountHook[] } -type VNodeChildAtom = VNode | string | number | boolean | null | void +type VNodeChildAtom = + | VNode + | string + | number + | boolean + | null + | undefined + | void -export interface VNodeArrayChildren< - HostNode = RendererNode, - HostElement = RendererElement -> extends Array {} +export interface VNodeArrayChildren + extends Array {} export type VNodeChild = VNodeChildAtom | VNodeArrayChildren @@ -134,17 +139,22 @@ export interface VNode { const blockStack: (VNode[] | null)[] = [] let currentBlock: VNode[] | null = null -// Open a block. -// This must be called before `createBlock`. It cannot be part of `createBlock` -// because the children of the block are evaluated before `createBlock` itself -// is called. The generated code typically looks like this: -// -// function render() { -// return (openBlock(),createBlock('div', null, [...])) -// } -// -// disableTracking is true when creating a fragment block, since a fragment -// always diffs its children. +/** + * Open a block. + * This must be called before `createBlock`. It cannot be part of `createBlock` + * because the children of the block are evaluated before `createBlock` itself + * is called. The generated code typically looks like this: + * + * ```js + * function render() { + * return (openBlock(),createBlock('div', null, [...])) + * } + * ``` + * disableTracking is true when creating a fragment block, since a fragment + * always diffs its children. + * + * @internal + */ export function openBlock(disableTracking = false) { blockStack.push((currentBlock = disableTracking ? null : [])) } @@ -168,15 +178,20 @@ let shouldTrack = 1 * _cache[1] * ) * ``` + * * @internal */ export function setBlockTracking(value: number) { shouldTrack += value } -// Create a block root vnode. Takes the same exact arguments as `createVNode`. -// A block root keeps track of dynamic nodes within the block in the -// `dynamicChildren` array. +/** + * Create a block root vnode. Takes the same exact arguments as `createVNode`. + * A block root keeps track of dynamic nodes within the block in the + * `dynamicChildren` array. + * + * @internal + */ export function createBlock( type: VNodeTypes | ClassComponent, props?: { [key: string]: any } | null,