types: use stricter HostNode typings

This commit is contained in:
Evan You
2019-09-06 16:58:32 -04:00
parent 360f3b4f37
commit 3904678306
12 changed files with 228 additions and 171 deletions

View File

@@ -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<HostNode = any, HostElement = any> {
patchProp(
el: HostNode,
el: HostElement,
key: string,
value: any,
oldValue: any,
isSVG: boolean,
prevChildren?: VNode[],
prevChildren?: VNode<HostNode, HostElement>[],
parentComponent?: ComponentInternalInstance | null,
unmountChildren?: (
children: VNode[],
children: VNode<HostNode, HostElement>[],
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<HostNode, HostElement> = (
vnode: VNode<HostNode, HostElement> | 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<Node, Element>({
* patchProp,
* ...nodeOps
* })
* ```
*/
export function createRenderer<
HostNode extends object = any,
HostElement extends HostNode = any
>(
options: RendererOptions<HostNode, HostElement>
): {
render: RootRenderFunction<HostNode, HostElement>
createApp: () => App<HostElement>
} {
type HostVNode = VNode<HostNode, HostElement>
type HostVNodeChildren = VNodeChildren<HostNode, HostElement>
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<any>,
oldRef: string | Function | Ref<any> | 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)
}
}