diff --git a/packages/runtime-core/README.md b/packages/runtime-core/README.md index e36fb1cbf..f83b857c4 100644 --- a/packages/runtime-core/README.md +++ b/packages/runtime-core/README.md @@ -9,8 +9,7 @@ For full exposed APIs, see `src/index.ts`. You can also run `yarn build runtime- ``` ts import { createRenderer, createAppAPI } from '@vue/runtime-core' -// low-level render method -export const render = createRenderer({ +const { render, createApp } = createRenderer({ pathcProp, insert, remove, @@ -18,7 +17,10 @@ export const render = createRenderer({ // ... }) -export const createApp = createAppAPI(render) +// `render` is the low-level API +// `createApp` returns an app instance with configurable context shared +// by the entire app tree. +export { render, createApp } export * from '@vue/runtime-core' ``` diff --git a/packages/runtime-core/src/apiApp.ts b/packages/runtime-core/src/apiApp.ts index a42c69f2d..6a932822a 100644 --- a/packages/runtime-core/src/apiApp.ts +++ b/packages/runtime-core/src/apiApp.ts @@ -2,13 +2,13 @@ import { Component, Data, ComponentInternalInstance } from './component' import { ComponentOptions } from './componentOptions' import { ComponentPublicInstance } from './componentPublicInstanceProxy' import { Directive } from './directives' -import { HostNode, RootRenderFunction } from './createRenderer' +import { RootRenderFunction } from './createRenderer' import { InjectionKey } from './apiInject' import { isFunction } from '@vue/shared' import { warn } from './warning' import { createVNode } from './vnode' -export interface App { +export interface App { config: AppConfig use(plugin: Plugin, options?: any): this mixin(mixin: ComponentOptions): this @@ -18,7 +18,7 @@ export interface App { directive(name: string, directive: Directive): this mount( rootComponent: Component, - rootContainer: string | HostNode, + rootContainer: HostElement, rootProps?: Data ): ComponentPublicInstance provide(key: InjectionKey | string, value: T): void @@ -70,7 +70,9 @@ export function createAppContext(): AppContext { } } -export function createAppAPI(render: RootRenderFunction): () => App { +export function createAppAPI( + render: RootRenderFunction +): () => App { return function createApp(): App { const context = createAppContext() @@ -128,7 +130,11 @@ export function createAppAPI(render: RootRenderFunction): () => App { } }, - mount(rootComponent, rootContainer, rootProps?: Data): any { + mount( + rootComponent: Component, + rootContainer: string | HostElement, + rootProps?: Data + ): any { if (!isMounted) { const vnode = createVNode(rootComponent, rootProps) // store app context on the root VNode. diff --git a/packages/runtime-core/src/createRenderer.ts b/packages/runtime-core/src/createRenderer.ts index 987f0ac7e..614a5ced0 100644 --- a/packages/runtime-core/src/createRenderer.ts +++ b/packages/runtime-core/src/createRenderer.ts @@ -38,6 +38,8 @@ import { PatchFlags } from './patchFlags' import { ShapeFlags } from './shapeFlags' import { pushWarningContext, popWarningContext, warn } from './warning' import { invokeDirectiveHook } from './directives' +import { ComponentPublicInstance } from './componentPublicInstanceProxy' +import { App, createAppAPI } from './apiApp' const prodEffectOptions = { scheduler: queueJob @@ -67,40 +69,64 @@ function invokeHooks(hooks: Function[], arg?: any) { } } -export type HostNode = any - -export interface RendererOptions { +export interface RendererOptions { patchProp( - el: HostNode, + el: HostElement, key: string, value: any, oldValue: any, isSVG: boolean, - prevChildren?: VNode[], + prevChildren?: VNode[], parentComponent?: ComponentInternalInstance | null, unmountChildren?: ( - children: VNode[], + children: VNode[], parentComponent: ComponentInternalInstance | null ) => void ): void - insert(el: HostNode, parent: HostNode, anchor?: HostNode): void + insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void remove(el: HostNode): void - createElement(type: string, isSVG?: boolean): HostNode + createElement(type: string, isSVG?: boolean): HostElement createText(text: string): HostNode createComment(text: string): HostNode setText(node: HostNode, text: string): void - setElementText(node: HostNode, text: string): void + setElementText(node: HostElement, text: string): void parentNode(node: HostNode): HostNode | null nextSibling(node: HostNode): HostNode | null - querySelector(selector: string): HostNode | null + querySelector(selector: string): HostElement | null } -export type RootRenderFunction = ( - vnode: VNode | null, - dom: HostNode | string +export type RootRenderFunction = ( + vnode: VNode | null, + dom: HostElement | string ) => void -export function createRenderer(options: RendererOptions): RootRenderFunction { +/** + * The createRenderer function accepts two generic arguments: + * HostNode and HostElement, corresponding to Node and Element types in the + * host environment. For example, for runtime-dom, HostNode would be the DOM + * `Node` interface and HostElement would be the DOM `Element` interface. + * + * Custom renderers can pass in the platform specific types like this: + * + * ``` js + * const { render, createApp } = createRenderer({ + * patchProp, + * ...nodeOps + * }) + * ``` + */ +export function createRenderer< + HostNode extends object = any, + HostElement extends HostNode = any +>( + options: RendererOptions +): { + render: RootRenderFunction + createApp: () => App +} { + type HostVNode = VNode + type HostVNodeChildren = VNodeChildren + const { insert: hostInsert, remove: hostRemove, @@ -116,10 +142,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } = options function patch( - n1: VNode | null, // null means this is a mount - n2: VNode, - container: HostNode, - anchor: HostNode = null, + n1: HostVNode | null, // null means this is a mount + n2: HostVNode, + container: HostElement, + anchor: HostNode | null = null, parentComponent: ComponentInternalInstance | null = null, isSVG: boolean = false, optimized: boolean = false @@ -183,16 +209,16 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { optimized ) } else if (__DEV__) { - warn('Invalid VNode type:', n2.type, `(${typeof n2.type})`) + warn('Invalid HostVNode type:', n2.type, `(${typeof n2.type})`) } } } function processText( - n1: VNode | null, - n2: VNode, - container: HostNode, - anchor: HostNode + n1: HostVNode | null, + n2: HostVNode, + container: HostElement, + anchor: HostNode | null ) { if (n1 == null) { hostInsert( @@ -201,7 +227,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { anchor ) } else { - const el = (n2.el = n1.el) + const el = (n2.el = n1.el) as HostNode if (n2.children !== n1.children) { hostSetText(el, n2.children as string) } @@ -209,10 +235,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function processEmptyNode( - n1: VNode | null, - n2: VNode, - container: HostNode, - anchor: HostNode + n1: HostVNode | null, + n2: HostVNode, + container: HostElement, + anchor: HostNode | null ) { if (n1 == null) { hostInsert((n2.el = hostCreateComment('')), container, anchor) @@ -222,10 +248,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function processElement( - n1: VNode | null, - n2: VNode, - container: HostNode, - anchor: HostNode, + n1: HostVNode | null, + n2: HostVNode, + container: HostElement, + anchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean, optimized: boolean @@ -241,9 +267,9 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function mountElement( - vnode: VNode, - container: HostNode, - anchor: HostNode, + vnode: HostVNode, + container: HostElement, + anchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean ) { @@ -264,7 +290,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { hostSetElementText(el, vnode.children as string) } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { mountChildren( - vnode.children as VNodeChildren, + vnode.children as HostVNodeChildren, el, null, parentComponent, @@ -280,9 +306,9 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function mountChildren( - children: VNodeChildren, - container: HostNode, - anchor: HostNode, + children: HostVNodeChildren, + container: HostElement, + anchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean, start: number = 0 @@ -294,13 +320,13 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function patchElement( - n1: VNode, - n2: VNode, + n1: HostVNode, + n2: HostVNode, parentComponent: ComponentInternalInstance | null, isSVG: boolean, optimized: boolean ) { - const el = (n2.el = n1.el) + const el = (n2.el = n1.el) as HostElement const { patchFlag, dynamicChildren } = n2 const oldProps = (n1 && n1.props) || EMPTY_OBJ const newProps = n2.props || EMPTY_OBJ @@ -353,7 +379,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { next, prev, isSVG, - n1.children as VNode[], + n1.children as HostVNode[], parentComponent, unmountChildren ) @@ -378,7 +404,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { if (dynamicChildren != null) { // children fast path - const olddynamicChildren = n1.dynamicChildren as VNode[] + const olddynamicChildren = n1.dynamicChildren as HostVNode[] for (let i = 0; i < dynamicChildren.length; i++) { patch( olddynamicChildren[i], @@ -403,8 +429,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function patchProps( - el: HostNode, - vnode: VNode, + el: HostElement, + vnode: HostVNode, oldProps: any, newProps: any, parentComponent: ComponentInternalInstance | null, @@ -422,7 +448,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { next, prev, isSVG, - vnode.children as VNode[], + vnode.children as HostVNode[], parentComponent, unmountChildren ) @@ -438,7 +464,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { null, null, isSVG, - vnode.children as VNode[], + vnode.children as HostVNode[], parentComponent, unmountChildren ) @@ -449,24 +475,26 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function processFragment( - n1: VNode | null, - n2: VNode, - container: HostNode, - anchor: HostNode, + n1: HostVNode | null, + n2: HostVNode, + container: HostElement, + anchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean, optimized: boolean ) { - const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateComment('')) + const fragmentStartAnchor = (n2.el = n1 + ? n1.el + : hostCreateComment('')) as HostNode const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor - : hostCreateComment('')) + : hostCreateComment('')) as HostNode if (n1 == null) { hostInsert(fragmentStartAnchor, container, anchor) hostInsert(fragmentEndAnchor, container, anchor) // a fragment can only have array children mountChildren( - n2.children as VNodeChildren, + n2.children as HostVNodeChildren, container, fragmentEndAnchor, parentComponent, @@ -486,10 +514,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function processPortal( - n1: VNode | null, - n2: VNode, - container: HostNode, - anchor: HostNode, + n1: HostVNode | null, + n2: HostVNode, + container: HostElement, + anchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean, optimized: boolean @@ -505,7 +533,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { hostSetElementText(target, children as string) } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { mountChildren( - children as VNodeChildren, + children as HostVNodeChildren, target, null, parentComponent, @@ -517,7 +545,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } } else { // update content - const target = (n2.target = n1.target) + const target = (n2.target = n1.target) as HostElement if (patchFlag === PatchFlags.TEXT) { hostSetElementText(target, children as string) } else if (!optimized) { @@ -534,8 +562,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { hostSetElementText(target, '') hostSetElementText(nextTarget, children as string) } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { - for (let i = 0; i < (children as VNode[]).length; i++) { - move((children as VNode[])[i], nextTarget, null) + for (let i = 0; i < (children as HostVNode[]).length; i++) { + move((children as HostVNode[])[i], nextTarget, null) } } } else if (__DEV__) { @@ -548,10 +576,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function processComponent( - n1: VNode | null, - n2: VNode, - container: HostNode, - anchor: HostNode, + n1: HostVNode | null, + n2: HostVNode, + container: HostElement, + anchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean, optimized: boolean @@ -580,9 +608,9 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function mountComponent( - initialVNode: VNode, - container: HostNode, - anchor: HostNode, + initialVNode: HostVNode, + container: HostElement, + anchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean ) { @@ -624,7 +652,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } else { // updateComponent // This is triggered by mutation of component's own state (next: null) - // OR parent calling processComponent (next: VNode) + // OR parent calling processComponent (next: HostVNode) const { next } = instance if (__DEV__) { @@ -654,7 +682,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { prevTree, nextTree, // parent may have changed if it's in a portal - hostParentNode(prevTree.el), + hostParentNode(prevTree.el as HostNode) as HostElement, // anchor may have changed if it's in a fragment getNextHostNode(prevTree), instance, @@ -689,10 +717,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function patchChildren( - n1: VNode | null, - n2: VNode, - container: HostNode, - anchor: HostNode, + n1: HostVNode | null, + n2: HostVNode, + container: HostElement, + anchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean, optimized: boolean = false @@ -708,8 +736,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { // this could be either fully-keyed or mixed (some keyed some not) // presence of patchFlag means children are guaranteed to be arrays patchKeyedChildren( - c1 as VNode[], - c2 as VNodeChildren, + c1 as HostVNode[], + c2 as HostVNodeChildren, container, anchor, parentComponent, @@ -720,8 +748,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } else if (patchFlag & PatchFlags.UNKEYED) { // unkeyed patchUnkeyedChildren( - c1 as VNode[], - c2 as VNodeChildren, + c1 as HostVNode[], + c2 as HostVNodeChildren, container, anchor, parentComponent, @@ -735,7 +763,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { if (shapeFlag & ShapeFlags.TEXT_CHILDREN) { // text children fast path if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) { - unmountChildren(c1 as VNode[], parentComponent) + unmountChildren(c1 as HostVNode[], parentComponent) } if (c2 !== c1) { hostSetElementText(container, c2 as string) @@ -745,7 +773,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { hostSetElementText(container, '') if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { mountChildren( - c2 as VNodeChildren, + c2 as HostVNodeChildren, container, anchor, parentComponent, @@ -756,8 +784,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { // two arrays, cannot assume anything, do full diff patchKeyedChildren( - c1 as VNode[], - c2 as VNodeChildren, + c1 as HostVNode[], + c2 as HostVNodeChildren, container, anchor, parentComponent, @@ -766,17 +794,17 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { ) } else { // c2 is null in this case - unmountChildren(c1 as VNode[], parentComponent, true) + unmountChildren(c1 as HostVNode[], parentComponent, true) } } } } function patchUnkeyedChildren( - c1: VNode[], - c2: VNodeChildren, - container: HostNode, - anchor: HostNode, + c1: HostVNode[], + c2: HostVNodeChildren, + container: HostElement, + anchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean, optimized: boolean @@ -810,10 +838,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { // can be all-keyed or mixed function patchKeyedChildren( - c1: VNode[], - c2: VNodeChildren, - container: HostNode, - parentAnchor: HostNode, + c1: HostVNode[], + c2: HostVNodeChildren, + container: HostElement, + parentAnchor: HostNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean, optimized: boolean @@ -878,7 +906,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { if (i > e1) { if (i <= e2) { const nextPos = e2 + 1 - const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor + const anchor = + nextPos < l2 ? (c2[nextPos] as HostVNode).el : parentAnchor while (i <= e2) { patch( null, @@ -960,7 +989,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } else { // key-less node, try to locate a key-less node of the same type for (j = s2; j <= e2; j++) { - if (isSameType(prevChild, c2[j] as VNode)) { + if (isSameType(prevChild, c2[j] as HostVNode)) { newIndex = j break } @@ -977,7 +1006,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } patch( prevChild, - c2[newIndex] as VNode, + c2[newIndex] as HostVNode, container, null, parentComponent, @@ -997,9 +1026,11 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { // looping backwards so that we can use last patched node as anchor for (i = toBePatched - 1; i >= 0; i--) { const nextIndex = s2 + i - const nextChild = c2[nextIndex] as VNode + const nextChild = c2[nextIndex] as HostVNode const anchor = - nextIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor + nextIndex + 1 < l2 + ? (c2[nextIndex + 1] as HostVNode).el + : parentAnchor if (newIndexToOldIndexMap[i] === 0) { // mount new patch(null, nextChild, container, anchor, parentComponent, isSVG) @@ -1017,25 +1048,29 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } } - function move(vnode: VNode, container: HostNode, anchor: HostNode) { + function move( + vnode: HostVNode, + container: HostElement, + anchor: HostNode | null + ) { if (vnode.component !== null) { move(vnode.component.subTree, container, anchor) return } if (vnode.type === Fragment) { - hostInsert(vnode.el, container, anchor) - const children = vnode.children as VNode[] + hostInsert(vnode.el as HostNode, container, anchor) + const children = vnode.children as HostVNode[] for (let i = 0; i < children.length; i++) { - hostInsert(children[i].el, container, anchor) + hostInsert(children[i].el as HostNode, container, anchor) } - hostInsert(vnode.anchor, container, anchor) + hostInsert(vnode.anchor as HostNode, container, anchor) } else { - hostInsert(vnode.el, container, anchor) + hostInsert(vnode.el as HostNode, container, anchor) } } function unmount( - vnode: VNode, + vnode: HostVNode, parentComponent: ComponentInternalInstance | null, doRemove?: boolean ) { @@ -1069,14 +1104,14 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { unmountChildren(dynamicChildren, parentComponent, shouldRemoveChildren) } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { unmountChildren( - children as VNode[], + children as HostVNode[], parentComponent, shouldRemoveChildren ) } if (doRemove) { - hostRemove(vnode.el) + hostRemove(vnode.el as HostNode) if (anchor != null) hostRemove(anchor) } @@ -1110,7 +1145,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } function unmountChildren( - children: VNode[], + children: HostVNode[], parentComponent: ComponentInternalInstance | null, doRemove?: boolean, start: number = 0 @@ -1120,9 +1155,9 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } } - function getNextHostNode(vnode: VNode): HostNode { + function getNextHostNode(vnode: HostVNode): HostNode | null { return vnode.component === null - ? hostNextSibling(vnode.anchor || vnode.el) + ? hostNextSibling((vnode.anchor || vnode.el) as HostNode) : getNextHostNode(vnode.component.subTree) } @@ -1130,7 +1165,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { ref: string | Function | Ref, oldRef: string | Function | Ref | null, parent: ComponentInternalInstance, - value: HostNode | ComponentInternalInstance | null + value: HostNode | ComponentPublicInstance | null ) { const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs const renderContext = toRaw(parent.renderContext) @@ -1163,39 +1198,33 @@ export function createRenderer(options: RendererOptions): RootRenderFunction { } } - return function render(vnode: VNode | null, dom: HostNode | string) { - if (isString(dom)) { - if (isFunction(hostQuerySelector)) { - dom = hostQuerySelector(dom) - if (!dom) { - if (__DEV__) { - warn( - `Failed to locate root container: ` + - `querySelector returned null.` - ) - } - return - } - } else { + function render(vnode: HostVNode | null, rawContainer: HostElement | string) { + let container: any = rawContainer + if (isString(container)) { + container = hostQuerySelector(container) + if (!container) { if (__DEV__) { warn( - `Failed to locate root container: ` + - `target platform does not support querySelector.` + `Failed to locate root container: ` + `querySelector returned null.` ) } return } } if (vnode == null) { - debugger - if (dom._vnode) { - unmount(dom._vnode, null, true) + if (container._vnode) { + unmount(container._vnode, null, true) } } else { - patch(dom._vnode, vnode, dom) + patch(container._vnode || null, vnode, container) } flushPostFlushCbs() - dom._vnode = vnode + container._vnode = vnode + } + + return { + render, + createApp: createAppAPI(render) } } diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts index 6acf27c4a..2119bec9d 100644 --- a/packages/runtime-core/src/directives.ts +++ b/packages/runtime-core/src/directives.ts @@ -17,7 +17,6 @@ import { warn } from './warning' import { ComponentInternalInstance } from './component' import { currentRenderingInstance } from './componentRenderUtils' import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling' -import { HostNode } from './createRenderer' import { ComponentPublicInstance } from './componentPublicInstanceProxy' export interface DirectiveBinding { @@ -29,7 +28,7 @@ export interface DirectiveBinding { } export type DirectiveHook = ( - el: HostNode, + el: any, binding: DirectiveBinding, vnode: VNode, prevVNode: VNode | null diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 46d0406fd..365b23f1b 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -28,7 +28,6 @@ export { PublicShapeFlags as ShapeFlags } from './shapeFlags' export { getCurrentInstance } from './component' // For custom renderers -export { createAppAPI } from './apiApp' export { createRenderer } from './createRenderer' export { handleError, diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index f81533e75..bd398f05f 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -7,17 +7,16 @@ import { extend } from '@vue/shared' import { ComponentInternalInstance, Data, SetupProxySymbol } from './component' -import { HostNode } from './createRenderer' import { RawSlots } from './componentSlots' import { PatchFlags } from './patchFlags' import { ShapeFlags } from './shapeFlags' import { isReactive } from '@vue/reactivity' import { AppContext } from './apiApp' -export const Fragment = Symbol('Fragment') -export const Text = Symbol('Text') -export const Empty = Symbol('Empty') -export const Portal = Symbol('Portal') +export const Fragment = __DEV__ ? Symbol('Fragment') : Symbol() +export const Text = __DEV__ ? Symbol('Text') : Symbol() +export const Empty = __DEV__ ? Symbol('Empty') : Symbol() +export const Portal = __DEV__ ? Symbol('Portal') : Symbol() export type VNodeTypes = | string @@ -28,24 +27,42 @@ export type VNodeTypes = | typeof Text | typeof Empty -type VNodeChildAtom = VNode | string | number | boolean | null | void -export interface VNodeChildren extends Array {} -export type VNodeChild = VNodeChildAtom | VNodeChildren +type VNodeChildAtom = + | VNode + | string + | number + | boolean + | null + | void -export type NormalizedChildren = string | VNodeChildren | RawSlots | null +export interface VNodeChildren + extends Array< + | VNodeChildren + | VNodeChildAtom + > {} -export interface VNode { +export type VNodeChild = + | VNodeChildAtom + | VNodeChildren + +export type NormalizedChildren = + | string + | VNodeChildren + | RawSlots + | null + +export interface VNode { type: VNodeTypes props: Record | null key: string | number | null ref: string | Function | null - children: NormalizedChildren + children: NormalizedChildren component: ComponentInternalInstance | null // DOM el: HostNode | null anchor: HostNode | null // fragment anchor - target: HostNode | null // portal target + target: HostElement | null // portal target // optimization only shapeFlag: number @@ -209,7 +226,7 @@ export function cloneVNode(vnode: VNode): VNode { } } -export function normalizeVNode(child: VNodeChild): VNode { +export function normalizeVNode(child: VNodeChild): VNode { if (child == null) { // empty placeholder return createVNode(Empty) @@ -241,7 +258,7 @@ export function normalizeChildren(vnode: VNode, children: unknown) { children = isString(children) ? children : children + '' type = ShapeFlags.TEXT_CHILDREN } - vnode.children = children as NormalizedChildren + vnode.children = children as NormalizedChildren vnode.shapeFlag |= type } diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts index d3f4fc875..7cb88d260 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -1,14 +1,18 @@ -import { createRenderer, VNode, createAppAPI } from '@vue/runtime-core' +import { createRenderer } from '@vue/runtime-core' import { nodeOps } from './nodeOps' import { patchProp } from './patchProp' -export const render = createRenderer({ +const { render, createApp } = createRenderer({ patchProp, ...nodeOps -}) as (vnode: VNode | null, container: HTMLElement) => void +}) -export const createApp = createAppAPI(render) +export { render, createApp } // re-export everything from core // h, Component, reactivity API, nextTick, flags & types export * from '@vue/runtime-core' + +export interface ComponentPublicInstance { + $el: Element +} diff --git a/packages/runtime-dom/src/modules/events.ts b/packages/runtime-dom/src/modules/events.ts index f283b9922..d9d03c5c4 100644 --- a/packages/runtime-dom/src/modules/events.ts +++ b/packages/runtime-dom/src/modules/events.ts @@ -45,7 +45,7 @@ export function patchEvent( name: string, prevValue: EventValue | null, nextValue: EventValue | null, - instance: ComponentInternalInstance | null + instance: ComponentInternalInstance | null = null ) { const invoker = prevValue && prevValue.invoker if (nextValue) { diff --git a/packages/runtime-dom/src/nodeOps.ts b/packages/runtime-dom/src/nodeOps.ts index 9316571f9..e7b3e86b7 100644 --- a/packages/runtime-dom/src/nodeOps.ts +++ b/packages/runtime-dom/src/nodeOps.ts @@ -36,5 +36,6 @@ export const nodeOps = { nextSibling: (node: Node): Node | null => node.nextSibling, - querySelector: (selector: string): Node | null => doc.querySelector(selector) + querySelector: (selector: string): Element | null => + doc.querySelector(selector) } diff --git a/packages/runtime-dom/src/patchProp.ts b/packages/runtime-dom/src/patchProp.ts index 51f780e98..a634cd98f 100644 --- a/packages/runtime-dom/src/patchProp.ts +++ b/packages/runtime-dom/src/patchProp.ts @@ -4,7 +4,7 @@ import { patchAttr } from './modules/attrs' import { patchDOMProp } from './modules/props' import { patchEvent } from './modules/events' import { isOn } from '@vue/shared' -import { VNode } from '@vue/runtime-core' +import { VNode, ComponentInternalInstance } from '@vue/runtime-core' export function patchProp( el: Element, @@ -13,7 +13,7 @@ export function patchProp( prevValue: any, isSVG: boolean, prevChildren?: VNode[], - parentComponent?: any, + parentComponent?: ComponentInternalInstance, unmountChildren?: any ) { switch (key) { diff --git a/packages/runtime-test/src/index.ts b/packages/runtime-test/src/index.ts index fc1e440dd..34df836fe 100644 --- a/packages/runtime-test/src/index.ts +++ b/packages/runtime-test/src/index.ts @@ -1,14 +1,14 @@ -import { createRenderer, VNode, createAppAPI } from '@vue/runtime-core' -import { nodeOps, TestElement } from './nodeOps' +import { createRenderer, VNode } from '@vue/runtime-core' +import { nodeOps, TestNode, TestElement } from './nodeOps' import { patchProp } from './patchProp' import { serializeInner } from './serialize' -export const render = createRenderer({ +const { render, createApp } = createRenderer({ patchProp, ...nodeOps -}) as (node: VNode | null, container: TestElement) => void +}) -export const createApp = createAppAPI(render) +export { render, createApp } // convenience for one-off render validations export function renderToString(vnode: VNode) { diff --git a/packages/runtime-test/src/nodeOps.ts b/packages/runtime-test/src/nodeOps.ts index be98f9fcb..41e2c3967 100644 --- a/packages/runtime-test/src/nodeOps.ts +++ b/packages/runtime-test/src/nodeOps.ts @@ -218,7 +218,7 @@ function nextSibling(node: TestNode): TestNode | null { return parent.children[i + 1] || null } -function querySelector() { +function querySelector(): any { throw new Error('querySelector not supported in test renderer.') }