types: cleanup renderer & hydration typing

This commit is contained in:
Evan You 2020-02-14 12:33:32 -05:00
parent 629ee75588
commit 80904e92b8
4 changed files with 88 additions and 57 deletions

View File

@ -14,6 +14,11 @@ import { warn } from './warning'
import { PatchFlags, ShapeFlags, isReservedProp, isOn } from '@vue/shared'
import { RendererOptions } from './renderer'
export type RootHydrateFunction = (
vnode: VNode<Node, Element>,
container: Element
) => void
// Note: hydration is DOM-specific
// But we have to place it in core due to tight coupling with core - splitting
// it out creates a ton of unnecessary complexity.
@ -23,7 +28,7 @@ export function createHydrationFunctions(
mountComponent: any, // TODO
patchProp: RendererOptions['patchProp']
) {
const hydrate = (vnode: VNode, container: Element) => {
const hydrate: RootHydrateFunction = (vnode, container) => {
if (__DEV__ && !container.hasChildNodes()) {
warn(`Attempting to hydrate existing markup but container is empty.`)
return

View File

@ -179,7 +179,13 @@ export {
ComponentOptionsWithArrayProps
} from './apiOptions'
export { ComponentPublicInstance } from './componentProxy'
export { RendererOptions, RootRenderFunction } from './renderer'
export {
Renderer,
HydrationRenderer,
RendererOptions,
RootRenderFunction
} from './renderer'
export { RootHydrateFunction } from './hydration'
export { Slot, Slots } from './componentSlots'
export {
Prop,

View File

@ -62,10 +62,24 @@ import {
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
import { registerHMR, unregisterHMR } from './hmr'
import { createHydrationFunctions } from './hydration'
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
const __HMR__ = __BUNDLER__ && __DEV__
export interface Renderer<HostNode = any, HostElement = any> {
render: RootRenderFunction<HostNode, HostElement>
createApp: CreateAppFunction<HostElement>
}
export interface HydrationRenderer extends Renderer<Node, Element> {
hydrate: RootHydrateFunction
}
export type RootRenderFunction<HostNode, HostElement> = (
vnode: VNode<HostNode, HostElement> | null,
container: HostElement
) => void
export interface RendererOptions<HostNode = any, HostElement = any> {
patchProp(
el: HostElement,
@ -102,41 +116,6 @@ export interface RendererOptions<HostNode = any, HostElement = any> {
): HostElement
}
export type RootRenderFunction<HostNode, HostElement> = (
vnode: VNode<HostNode, HostElement> | null,
dom: HostElement
) => void
// An object exposing the internals of a renderer, passed to tree-shakeable
// features so that they can be decoupled from this file.
export interface RendererInternals<HostNode = any, HostElement = any> {
patch: (
n1: VNode<HostNode, HostElement> | null, // null means this is a mount
n2: VNode<HostNode, HostElement>,
container: HostElement,
anchor?: HostNode | null,
parentComponent?: ComponentInternalInstance | null,
parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null,
isSVG?: boolean,
optimized?: boolean
) => void
unmount: (
vnode: VNode<HostNode, HostElement>,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
doRemove?: boolean
) => void
move: (
vnode: VNode<HostNode, HostElement>,
container: HostElement,
anchor: HostNode | null,
type: MoveType,
parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null
) => void
next: (vnode: VNode<HostNode, HostElement>) => HostNode | null
options: RendererOptions<HostNode, HostElement>
}
export const enum MoveType {
ENTER,
LEAVE,
@ -186,25 +165,36 @@ export function createRenderer<
HostNode extends object = any,
HostElement extends HostNode = any
>(options: RendererOptions<HostNode, HostElement>) {
const res = baseCreateRenderer(options)
return res as typeof res & {
hydrate: undefined
}
return baseCreateRenderer<HostNode, HostElement>(options)
}
// Separate API for creating hydration-enabled renderer.
// Hydration logic is only used when calling this function, making it
// tree-shakable.
export function createHydrationRenderer<
HostNode extends object = any,
HostElement extends HostNode = any
>(options: RendererOptions<HostNode, HostElement>) {
const res = baseCreateRenderer(options, createHydrationFunctions)
return res as typeof res & {
hydrate: ReturnType<typeof createHydrationFunctions>[0]
}
export function createHydrationRenderer(
options: RendererOptions<Node, Element>
) {
return baseCreateRenderer<Node, Element>(options, createHydrationFunctions)
}
// overload 1: no hydration
function baseCreateRenderer<
HostNode extends object = any,
HostElement extends HostNode = any
>(
options: RendererOptions<HostNode, HostElement>
): Renderer<HostNode, HostElement>
// overload 2: with hydration
function baseCreateRenderer<
HostNode extends object = any,
HostElement extends HostNode = any
>(
options: RendererOptions<HostNode, HostElement>,
createHydrationFns: typeof createHydrationFunctions
): HydrationRenderer
// implementation
function baseCreateRenderer<
HostNode extends object = any,
HostElement extends HostNode = any
@ -1862,6 +1852,36 @@ function baseCreateRenderer<
}
}
// An object exposing the internals of a renderer, passed to tree-shakeable
// features so that they can be decoupled from this file.
export interface RendererInternals<HostNode = any, HostElement = any> {
patch: (
n1: VNode<HostNode, HostElement> | null, // null means this is a mount
n2: VNode<HostNode, HostElement>,
container: HostElement,
anchor?: HostNode | null,
parentComponent?: ComponentInternalInstance | null,
parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null,
isSVG?: boolean,
optimized?: boolean
) => void
unmount: (
vnode: VNode<HostNode, HostElement>,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
doRemove?: boolean
) => void
move: (
vnode: VNode<HostNode, HostElement>,
container: HostElement,
anchor: HostNode | null,
type: MoveType,
parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null
) => void
next: (vnode: VNode<HostNode, HostElement>) => HostNode | null
options: RendererOptions<HostNode, HostElement>
}
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
function getSequence(arr: number[]): number[] {
const p = arr.slice()

View File

@ -4,8 +4,10 @@ import {
warn,
RootRenderFunction,
CreateAppFunction,
VNode,
App
Renderer,
HydrationRenderer,
App,
RootHydrateFunction
} from '@vue/runtime-core'
import { nodeOps } from './nodeOps'
import { patchProp } from './patchProp'
@ -19,9 +21,7 @@ const rendererOptions = {
// lazy create the renderer - this makes core renderer logic tree-shakable
// in case the user only imports reactivity utilities from Vue.
let renderer:
| ReturnType<typeof createRenderer>
| ReturnType<typeof createHydrationRenderer>
let renderer: Renderer | HydrationRenderer
let enabledHydration = false
@ -34,7 +34,7 @@ function ensureHydrationRenderer() {
? renderer
: createHydrationRenderer(rendererOptions)
enabledHydration = true
return renderer as ReturnType<typeof createHydrationRenderer>
return renderer as HydrationRenderer
}
// use explicit type casts here to avoid import() calls in rolled-up d.ts
@ -44,7 +44,7 @@ export const render = ((...args) => {
export const hydrate = ((...args) => {
ensureHydrationRenderer().hydrate(...args)
}) as (vnode: VNode, container: Element) => void
}) as RootHydrateFunction
export const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args)