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

@ -9,8 +9,7 @@ For full exposed APIs, see `src/index.ts`. You can also run `yarn build runtime-
``` ts ``` ts
import { createRenderer, createAppAPI } from '@vue/runtime-core' import { createRenderer, createAppAPI } from '@vue/runtime-core'
// low-level render method const { render, createApp } = createRenderer({
export const render = createRenderer({
pathcProp, pathcProp,
insert, insert,
remove, 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' export * from '@vue/runtime-core'
``` ```

View File

@ -2,13 +2,13 @@ import { Component, Data, ComponentInternalInstance } from './component'
import { ComponentOptions } from './componentOptions' import { ComponentOptions } from './componentOptions'
import { ComponentPublicInstance } from './componentPublicInstanceProxy' import { ComponentPublicInstance } from './componentPublicInstanceProxy'
import { Directive } from './directives' import { Directive } from './directives'
import { HostNode, RootRenderFunction } from './createRenderer' import { RootRenderFunction } from './createRenderer'
import { InjectionKey } from './apiInject' import { InjectionKey } from './apiInject'
import { isFunction } from '@vue/shared' import { isFunction } from '@vue/shared'
import { warn } from './warning' import { warn } from './warning'
import { createVNode } from './vnode' import { createVNode } from './vnode'
export interface App { export interface App<HostElement = any> {
config: AppConfig config: AppConfig
use(plugin: Plugin, options?: any): this use(plugin: Plugin, options?: any): this
mixin(mixin: ComponentOptions): this mixin(mixin: ComponentOptions): this
@ -18,7 +18,7 @@ export interface App {
directive(name: string, directive: Directive): this directive(name: string, directive: Directive): this
mount( mount(
rootComponent: Component, rootComponent: Component,
rootContainer: string | HostNode, rootContainer: HostElement,
rootProps?: Data rootProps?: Data
): ComponentPublicInstance ): ComponentPublicInstance
provide<T>(key: InjectionKey<T> | string, value: T): void provide<T>(key: InjectionKey<T> | string, value: T): void
@ -70,7 +70,9 @@ export function createAppContext(): AppContext {
} }
} }
export function createAppAPI(render: RootRenderFunction): () => App { export function createAppAPI<HostNode, HostElement>(
render: RootRenderFunction<HostNode, HostElement>
): () => App<HostElement> {
return function createApp(): App { return function createApp(): App {
const context = createAppContext() 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) { if (!isMounted) {
const vnode = createVNode(rootComponent, rootProps) const vnode = createVNode(rootComponent, rootProps)
// store app context on the root VNode. // store app context on the root VNode.

View File

@ -38,6 +38,8 @@ import { PatchFlags } from './patchFlags'
import { ShapeFlags } from './shapeFlags' import { ShapeFlags } from './shapeFlags'
import { pushWarningContext, popWarningContext, warn } from './warning' import { pushWarningContext, popWarningContext, warn } from './warning'
import { invokeDirectiveHook } from './directives' import { invokeDirectiveHook } from './directives'
import { ComponentPublicInstance } from './componentPublicInstanceProxy'
import { App, createAppAPI } from './apiApp'
const prodEffectOptions = { const prodEffectOptions = {
scheduler: queueJob scheduler: queueJob
@ -67,40 +69,64 @@ function invokeHooks(hooks: Function[], arg?: any) {
} }
} }
export type HostNode = any export interface RendererOptions<HostNode = any, HostElement = any> {
export interface RendererOptions {
patchProp( patchProp(
el: HostNode, el: HostElement,
key: string, key: string,
value: any, value: any,
oldValue: any, oldValue: any,
isSVG: boolean, isSVG: boolean,
prevChildren?: VNode[], prevChildren?: VNode<HostNode, HostElement>[],
parentComponent?: ComponentInternalInstance | null, parentComponent?: ComponentInternalInstance | null,
unmountChildren?: ( unmountChildren?: (
children: VNode[], children: VNode<HostNode, HostElement>[],
parentComponent: ComponentInternalInstance | null parentComponent: ComponentInternalInstance | null
) => void ) => void
): void ): void
insert(el: HostNode, parent: HostNode, anchor?: HostNode): void insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void
remove(el: HostNode): void remove(el: HostNode): void
createElement(type: string, isSVG?: boolean): HostNode createElement(type: string, isSVG?: boolean): HostElement
createText(text: string): HostNode createText(text: string): HostNode
createComment(text: string): HostNode createComment(text: string): HostNode
setText(node: HostNode, text: string): void setText(node: HostNode, text: string): void
setElementText(node: HostNode, text: string): void setElementText(node: HostElement, text: string): void
parentNode(node: HostNode): HostNode | null parentNode(node: HostNode): HostNode | null
nextSibling(node: HostNode): HostNode | null nextSibling(node: HostNode): HostNode | null
querySelector(selector: string): HostNode | null querySelector(selector: string): HostElement | null
} }
export type RootRenderFunction = ( export type RootRenderFunction<HostNode, HostElement> = (
vnode: VNode | null, vnode: VNode<HostNode, HostElement> | null,
dom: HostNode | string dom: HostElement | string
) => void ) => 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 { const {
insert: hostInsert, insert: hostInsert,
remove: hostRemove, remove: hostRemove,
@ -116,10 +142,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} = options } = options
function patch( function patch(
n1: VNode | null, // null means this is a mount n1: HostVNode | null, // null means this is a mount
n2: VNode, n2: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode = null, anchor: HostNode | null = null,
parentComponent: ComponentInternalInstance | null = null, parentComponent: ComponentInternalInstance | null = null,
isSVG: boolean = false, isSVG: boolean = false,
optimized: boolean = false optimized: boolean = false
@ -183,16 +209,16 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
optimized optimized
) )
} else if (__DEV__) { } else if (__DEV__) {
warn('Invalid VNode type:', n2.type, `(${typeof n2.type})`) warn('Invalid HostVNode type:', n2.type, `(${typeof n2.type})`)
} }
} }
} }
function processText( function processText(
n1: VNode | null, n1: HostVNode | null,
n2: VNode, n2: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode anchor: HostNode | null
) { ) {
if (n1 == null) { if (n1 == null) {
hostInsert( hostInsert(
@ -201,7 +227,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
anchor anchor
) )
} else { } else {
const el = (n2.el = n1.el) const el = (n2.el = n1.el) as HostNode
if (n2.children !== n1.children) { if (n2.children !== n1.children) {
hostSetText(el, n2.children as string) hostSetText(el, n2.children as string)
} }
@ -209,10 +235,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function processEmptyNode( function processEmptyNode(
n1: VNode | null, n1: HostVNode | null,
n2: VNode, n2: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode anchor: HostNode | null
) { ) {
if (n1 == null) { if (n1 == null) {
hostInsert((n2.el = hostCreateComment('')), container, anchor) hostInsert((n2.el = hostCreateComment('')), container, anchor)
@ -222,10 +248,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function processElement( function processElement(
n1: VNode | null, n1: HostVNode | null,
n2: VNode, n2: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode, anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
@ -241,9 +267,9 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function mountElement( function mountElement(
vnode: VNode, vnode: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode, anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean isSVG: boolean
) { ) {
@ -264,7 +290,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
hostSetElementText(el, vnode.children as string) hostSetElementText(el, vnode.children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren( mountChildren(
vnode.children as VNodeChildren, vnode.children as HostVNodeChildren,
el, el,
null, null,
parentComponent, parentComponent,
@ -280,9 +306,9 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function mountChildren( function mountChildren(
children: VNodeChildren, children: HostVNodeChildren,
container: HostNode, container: HostElement,
anchor: HostNode, anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
start: number = 0 start: number = 0
@ -294,13 +320,13 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function patchElement( function patchElement(
n1: VNode, n1: HostVNode,
n2: VNode, n2: HostVNode,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
) { ) {
const el = (n2.el = n1.el) const el = (n2.el = n1.el) as HostElement
const { patchFlag, dynamicChildren } = n2 const { patchFlag, dynamicChildren } = n2
const oldProps = (n1 && n1.props) || EMPTY_OBJ const oldProps = (n1 && n1.props) || EMPTY_OBJ
const newProps = n2.props || EMPTY_OBJ const newProps = n2.props || EMPTY_OBJ
@ -353,7 +379,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
next, next,
prev, prev,
isSVG, isSVG,
n1.children as VNode[], n1.children as HostVNode[],
parentComponent, parentComponent,
unmountChildren unmountChildren
) )
@ -378,7 +404,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
if (dynamicChildren != null) { if (dynamicChildren != null) {
// children fast path // children fast path
const olddynamicChildren = n1.dynamicChildren as VNode[] const olddynamicChildren = n1.dynamicChildren as HostVNode[]
for (let i = 0; i < dynamicChildren.length; i++) { for (let i = 0; i < dynamicChildren.length; i++) {
patch( patch(
olddynamicChildren[i], olddynamicChildren[i],
@ -403,8 +429,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function patchProps( function patchProps(
el: HostNode, el: HostElement,
vnode: VNode, vnode: HostVNode,
oldProps: any, oldProps: any,
newProps: any, newProps: any,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
@ -422,7 +448,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
next, next,
prev, prev,
isSVG, isSVG,
vnode.children as VNode[], vnode.children as HostVNode[],
parentComponent, parentComponent,
unmountChildren unmountChildren
) )
@ -438,7 +464,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
null, null,
null, null,
isSVG, isSVG,
vnode.children as VNode[], vnode.children as HostVNode[],
parentComponent, parentComponent,
unmountChildren unmountChildren
) )
@ -449,24 +475,26 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function processFragment( function processFragment(
n1: VNode | null, n1: HostVNode | null,
n2: VNode, n2: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode, anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
optimized: 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 const fragmentEndAnchor = (n2.anchor = n1
? n1.anchor ? n1.anchor
: hostCreateComment('')) : hostCreateComment('')) as HostNode
if (n1 == null) { if (n1 == null) {
hostInsert(fragmentStartAnchor, container, anchor) hostInsert(fragmentStartAnchor, container, anchor)
hostInsert(fragmentEndAnchor, container, anchor) hostInsert(fragmentEndAnchor, container, anchor)
// a fragment can only have array children // a fragment can only have array children
mountChildren( mountChildren(
n2.children as VNodeChildren, n2.children as HostVNodeChildren,
container, container,
fragmentEndAnchor, fragmentEndAnchor,
parentComponent, parentComponent,
@ -486,10 +514,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function processPortal( function processPortal(
n1: VNode | null, n1: HostVNode | null,
n2: VNode, n2: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode, anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
@ -505,7 +533,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
hostSetElementText(target, children as string) hostSetElementText(target, children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren( mountChildren(
children as VNodeChildren, children as HostVNodeChildren,
target, target,
null, null,
parentComponent, parentComponent,
@ -517,7 +545,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
} else { } else {
// update content // update content
const target = (n2.target = n1.target) const target = (n2.target = n1.target) as HostElement
if (patchFlag === PatchFlags.TEXT) { if (patchFlag === PatchFlags.TEXT) {
hostSetElementText(target, children as string) hostSetElementText(target, children as string)
} else if (!optimized) { } else if (!optimized) {
@ -534,8 +562,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
hostSetElementText(target, '') hostSetElementText(target, '')
hostSetElementText(nextTarget, children as string) hostSetElementText(nextTarget, children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
for (let i = 0; i < (children as VNode[]).length; i++) { for (let i = 0; i < (children as HostVNode[]).length; i++) {
move((children as VNode[])[i], nextTarget, null) move((children as HostVNode[])[i], nextTarget, null)
} }
} }
} else if (__DEV__) { } else if (__DEV__) {
@ -548,10 +576,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function processComponent( function processComponent(
n1: VNode | null, n1: HostVNode | null,
n2: VNode, n2: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode, anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
@ -580,9 +608,9 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function mountComponent( function mountComponent(
initialVNode: VNode, initialVNode: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode, anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean isSVG: boolean
) { ) {
@ -624,7 +652,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} else { } else {
// updateComponent // updateComponent
// This is triggered by mutation of component's own state (next: null) // 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 const { next } = instance
if (__DEV__) { if (__DEV__) {
@ -654,7 +682,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
prevTree, prevTree,
nextTree, nextTree,
// parent may have changed if it's in a portal // 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 // anchor may have changed if it's in a fragment
getNextHostNode(prevTree), getNextHostNode(prevTree),
instance, instance,
@ -689,10 +717,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function patchChildren( function patchChildren(
n1: VNode | null, n1: HostVNode | null,
n2: VNode, n2: HostVNode,
container: HostNode, container: HostElement,
anchor: HostNode, anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean = false 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) // this could be either fully-keyed or mixed (some keyed some not)
// presence of patchFlag means children are guaranteed to be arrays // presence of patchFlag means children are guaranteed to be arrays
patchKeyedChildren( patchKeyedChildren(
c1 as VNode[], c1 as HostVNode[],
c2 as VNodeChildren, c2 as HostVNodeChildren,
container, container,
anchor, anchor,
parentComponent, parentComponent,
@ -720,8 +748,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} else if (patchFlag & PatchFlags.UNKEYED) { } else if (patchFlag & PatchFlags.UNKEYED) {
// unkeyed // unkeyed
patchUnkeyedChildren( patchUnkeyedChildren(
c1 as VNode[], c1 as HostVNode[],
c2 as VNodeChildren, c2 as HostVNodeChildren,
container, container,
anchor, anchor,
parentComponent, parentComponent,
@ -735,7 +763,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) { if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
// text children fast path // text children fast path
if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) { if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren(c1 as VNode[], parentComponent) unmountChildren(c1 as HostVNode[], parentComponent)
} }
if (c2 !== c1) { if (c2 !== c1) {
hostSetElementText(container, c2 as string) hostSetElementText(container, c2 as string)
@ -745,7 +773,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
hostSetElementText(container, '') hostSetElementText(container, '')
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren( mountChildren(
c2 as VNodeChildren, c2 as HostVNodeChildren,
container, container,
anchor, anchor,
parentComponent, parentComponent,
@ -756,8 +784,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
// two arrays, cannot assume anything, do full diff // two arrays, cannot assume anything, do full diff
patchKeyedChildren( patchKeyedChildren(
c1 as VNode[], c1 as HostVNode[],
c2 as VNodeChildren, c2 as HostVNodeChildren,
container, container,
anchor, anchor,
parentComponent, parentComponent,
@ -766,17 +794,17 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
) )
} else { } else {
// c2 is null in this case // c2 is null in this case
unmountChildren(c1 as VNode[], parentComponent, true) unmountChildren(c1 as HostVNode[], parentComponent, true)
} }
} }
} }
} }
function patchUnkeyedChildren( function patchUnkeyedChildren(
c1: VNode[], c1: HostVNode[],
c2: VNodeChildren, c2: HostVNodeChildren,
container: HostNode, container: HostElement,
anchor: HostNode, anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
@ -810,10 +838,10 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
// can be all-keyed or mixed // can be all-keyed or mixed
function patchKeyedChildren( function patchKeyedChildren(
c1: VNode[], c1: HostVNode[],
c2: VNodeChildren, c2: HostVNodeChildren,
container: HostNode, container: HostElement,
parentAnchor: HostNode, parentAnchor: HostNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
@ -878,7 +906,8 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
if (i > e1) { if (i > e1) {
if (i <= e2) { if (i <= e2) {
const nextPos = e2 + 1 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) { while (i <= e2) {
patch( patch(
null, null,
@ -960,7 +989,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} else { } else {
// key-less node, try to locate a key-less node of the same type // key-less node, try to locate a key-less node of the same type
for (j = s2; j <= e2; j++) { for (j = s2; j <= e2; j++) {
if (isSameType(prevChild, c2[j] as VNode)) { if (isSameType(prevChild, c2[j] as HostVNode)) {
newIndex = j newIndex = j
break break
} }
@ -977,7 +1006,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
patch( patch(
prevChild, prevChild,
c2[newIndex] as VNode, c2[newIndex] as HostVNode,
container, container,
null, null,
parentComponent, parentComponent,
@ -997,9 +1026,11 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
// looping backwards so that we can use last patched node as anchor // looping backwards so that we can use last patched node as anchor
for (i = toBePatched - 1; i >= 0; i--) { for (i = toBePatched - 1; i >= 0; i--) {
const nextIndex = s2 + i const nextIndex = s2 + i
const nextChild = c2[nextIndex] as VNode const nextChild = c2[nextIndex] as HostVNode
const anchor = 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) { if (newIndexToOldIndexMap[i] === 0) {
// mount new // mount new
patch(null, nextChild, container, anchor, parentComponent, isSVG) 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) { if (vnode.component !== null) {
move(vnode.component.subTree, container, anchor) move(vnode.component.subTree, container, anchor)
return return
} }
if (vnode.type === Fragment) { if (vnode.type === Fragment) {
hostInsert(vnode.el, container, anchor) hostInsert(vnode.el as HostNode, container, anchor)
const children = vnode.children as VNode[] const children = vnode.children as HostVNode[]
for (let i = 0; i < children.length; i++) { 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 { } else {
hostInsert(vnode.el, container, anchor) hostInsert(vnode.el as HostNode, container, anchor)
} }
} }
function unmount( function unmount(
vnode: VNode, vnode: HostVNode,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
doRemove?: boolean doRemove?: boolean
) { ) {
@ -1069,14 +1104,14 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
unmountChildren(dynamicChildren, parentComponent, shouldRemoveChildren) unmountChildren(dynamicChildren, parentComponent, shouldRemoveChildren)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren( unmountChildren(
children as VNode[], children as HostVNode[],
parentComponent, parentComponent,
shouldRemoveChildren shouldRemoveChildren
) )
} }
if (doRemove) { if (doRemove) {
hostRemove(vnode.el) hostRemove(vnode.el as HostNode)
if (anchor != null) hostRemove(anchor) if (anchor != null) hostRemove(anchor)
} }
@ -1110,7 +1145,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
function unmountChildren( function unmountChildren(
children: VNode[], children: HostVNode[],
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
doRemove?: boolean, doRemove?: boolean,
start: number = 0 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 return vnode.component === null
? hostNextSibling(vnode.anchor || vnode.el) ? hostNextSibling((vnode.anchor || vnode.el) as HostNode)
: getNextHostNode(vnode.component.subTree) : getNextHostNode(vnode.component.subTree)
} }
@ -1130,7 +1165,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
ref: string | Function | Ref<any>, ref: string | Function | Ref<any>,
oldRef: string | Function | Ref<any> | null, oldRef: string | Function | Ref<any> | null,
parent: ComponentInternalInstance, parent: ComponentInternalInstance,
value: HostNode | ComponentInternalInstance | null value: HostNode | ComponentPublicInstance | null
) { ) {
const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs
const renderContext = toRaw(parent.renderContext) const renderContext = toRaw(parent.renderContext)
@ -1163,39 +1198,33 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
} }
return function render(vnode: VNode | null, dom: HostNode | string) { function render(vnode: HostVNode | null, rawContainer: HostElement | string) {
if (isString(dom)) { let container: any = rawContainer
if (isFunction(hostQuerySelector)) { if (isString(container)) {
dom = hostQuerySelector(dom) container = hostQuerySelector(container)
if (!dom) { if (!container) {
if (__DEV__) {
warn(
`Failed to locate root container: ` +
`querySelector returned null.`
)
}
return
}
} else {
if (__DEV__) { if (__DEV__) {
warn( warn(
`Failed to locate root container: ` + `Failed to locate root container: ` + `querySelector returned null.`
`target platform does not support querySelector.`
) )
} }
return return
} }
} }
if (vnode == null) { if (vnode == null) {
debugger if (container._vnode) {
if (dom._vnode) { unmount(container._vnode, null, true)
unmount(dom._vnode, null, true)
} }
} else { } else {
patch(dom._vnode, vnode, dom) patch(container._vnode || null, vnode, container)
} }
flushPostFlushCbs() flushPostFlushCbs()
dom._vnode = vnode container._vnode = vnode
}
return {
render,
createApp: createAppAPI(render)
} }
} }

View File

@ -17,7 +17,6 @@ import { warn } from './warning'
import { ComponentInternalInstance } from './component' import { ComponentInternalInstance } from './component'
import { currentRenderingInstance } from './componentRenderUtils' import { currentRenderingInstance } from './componentRenderUtils'
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling' import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
import { HostNode } from './createRenderer'
import { ComponentPublicInstance } from './componentPublicInstanceProxy' import { ComponentPublicInstance } from './componentPublicInstanceProxy'
export interface DirectiveBinding { export interface DirectiveBinding {
@ -29,7 +28,7 @@ export interface DirectiveBinding {
} }
export type DirectiveHook = ( export type DirectiveHook = (
el: HostNode, el: any,
binding: DirectiveBinding, binding: DirectiveBinding,
vnode: VNode, vnode: VNode,
prevVNode: VNode | null prevVNode: VNode | null

View File

@ -28,7 +28,6 @@ export { PublicShapeFlags as ShapeFlags } from './shapeFlags'
export { getCurrentInstance } from './component' export { getCurrentInstance } from './component'
// For custom renderers // For custom renderers
export { createAppAPI } from './apiApp'
export { createRenderer } from './createRenderer' export { createRenderer } from './createRenderer'
export { export {
handleError, handleError,

View File

@ -7,17 +7,16 @@ import {
extend extend
} from '@vue/shared' } from '@vue/shared'
import { ComponentInternalInstance, Data, SetupProxySymbol } from './component' import { ComponentInternalInstance, Data, SetupProxySymbol } from './component'
import { HostNode } from './createRenderer'
import { RawSlots } from './componentSlots' import { RawSlots } from './componentSlots'
import { PatchFlags } from './patchFlags' import { PatchFlags } from './patchFlags'
import { ShapeFlags } from './shapeFlags' import { ShapeFlags } from './shapeFlags'
import { isReactive } from '@vue/reactivity' import { isReactive } from '@vue/reactivity'
import { AppContext } from './apiApp' import { AppContext } from './apiApp'
export const Fragment = Symbol('Fragment') export const Fragment = __DEV__ ? Symbol('Fragment') : Symbol()
export const Text = Symbol('Text') export const Text = __DEV__ ? Symbol('Text') : Symbol()
export const Empty = Symbol('Empty') export const Empty = __DEV__ ? Symbol('Empty') : Symbol()
export const Portal = Symbol('Portal') export const Portal = __DEV__ ? Symbol('Portal') : Symbol()
export type VNodeTypes = export type VNodeTypes =
| string | string
@ -28,24 +27,42 @@ export type VNodeTypes =
| typeof Text | typeof Text
| typeof Empty | typeof Empty
type VNodeChildAtom = VNode | string | number | boolean | null | void type VNodeChildAtom<HostNode, HostElement> =
export interface VNodeChildren extends Array<VNodeChildren | VNodeChildAtom> {} | VNode<HostNode, HostElement>
export type VNodeChild = VNodeChildAtom | VNodeChildren | string
| number
| boolean
| null
| void
export type NormalizedChildren = string | VNodeChildren | RawSlots | null export interface VNodeChildren<HostNode = any, HostElement = any>
extends Array<
| VNodeChildren<HostNode, HostElement>
| VNodeChildAtom<HostNode, HostElement>
> {}
export interface VNode { export type VNodeChild<HostNode = any, HostElement = any> =
| VNodeChildAtom<HostNode, HostElement>
| VNodeChildren<HostNode, HostElement>
export type NormalizedChildren<HostNode, HostElement> =
| string
| VNodeChildren<HostNode, HostElement>
| RawSlots
| null
export interface VNode<HostNode = any, HostElement = any> {
type: VNodeTypes type: VNodeTypes
props: Record<any, any> | null props: Record<any, any> | null
key: string | number | null key: string | number | null
ref: string | Function | null ref: string | Function | null
children: NormalizedChildren children: NormalizedChildren<HostNode, HostElement>
component: ComponentInternalInstance | null component: ComponentInternalInstance | null
// DOM // DOM
el: HostNode | null el: HostNode | null
anchor: HostNode | null // fragment anchor anchor: HostNode | null // fragment anchor
target: HostNode | null // portal target target: HostElement | null // portal target
// optimization only // optimization only
shapeFlag: number shapeFlag: number
@ -209,7 +226,7 @@ export function cloneVNode(vnode: VNode): VNode {
} }
} }
export function normalizeVNode(child: VNodeChild): VNode { export function normalizeVNode(child: VNodeChild<any, any>): VNode {
if (child == null) { if (child == null) {
// empty placeholder // empty placeholder
return createVNode(Empty) return createVNode(Empty)
@ -241,7 +258,7 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
children = isString(children) ? children : children + '' children = isString(children) ? children : children + ''
type = ShapeFlags.TEXT_CHILDREN type = ShapeFlags.TEXT_CHILDREN
} }
vnode.children = children as NormalizedChildren vnode.children = children as NormalizedChildren<any, any>
vnode.shapeFlag |= type vnode.shapeFlag |= type
} }

View File

@ -1,14 +1,18 @@
import { createRenderer, VNode, createAppAPI } from '@vue/runtime-core' import { createRenderer } from '@vue/runtime-core'
import { nodeOps } from './nodeOps' import { nodeOps } from './nodeOps'
import { patchProp } from './patchProp' import { patchProp } from './patchProp'
export const render = createRenderer({ const { render, createApp } = createRenderer<Node, Element>({
patchProp, patchProp,
...nodeOps ...nodeOps
}) as (vnode: VNode | null, container: HTMLElement) => void })
export const createApp = createAppAPI(render) export { render, createApp }
// re-export everything from core // re-export everything from core
// h, Component, reactivity API, nextTick, flags & types // h, Component, reactivity API, nextTick, flags & types
export * from '@vue/runtime-core' export * from '@vue/runtime-core'
export interface ComponentPublicInstance {
$el: Element
}

View File

@ -45,7 +45,7 @@ export function patchEvent(
name: string, name: string,
prevValue: EventValue | null, prevValue: EventValue | null,
nextValue: EventValue | null, nextValue: EventValue | null,
instance: ComponentInternalInstance | null instance: ComponentInternalInstance | null = null
) { ) {
const invoker = prevValue && prevValue.invoker const invoker = prevValue && prevValue.invoker
if (nextValue) { if (nextValue) {

View File

@ -36,5 +36,6 @@ export const nodeOps = {
nextSibling: (node: Node): Node | null => node.nextSibling, nextSibling: (node: Node): Node | null => node.nextSibling,
querySelector: (selector: string): Node | null => doc.querySelector(selector) querySelector: (selector: string): Element | null =>
doc.querySelector(selector)
} }

View File

@ -4,7 +4,7 @@ import { patchAttr } from './modules/attrs'
import { patchDOMProp } from './modules/props' import { patchDOMProp } from './modules/props'
import { patchEvent } from './modules/events' import { patchEvent } from './modules/events'
import { isOn } from '@vue/shared' import { isOn } from '@vue/shared'
import { VNode } from '@vue/runtime-core' import { VNode, ComponentInternalInstance } from '@vue/runtime-core'
export function patchProp( export function patchProp(
el: Element, el: Element,
@ -13,7 +13,7 @@ export function patchProp(
prevValue: any, prevValue: any,
isSVG: boolean, isSVG: boolean,
prevChildren?: VNode[], prevChildren?: VNode[],
parentComponent?: any, parentComponent?: ComponentInternalInstance,
unmountChildren?: any unmountChildren?: any
) { ) {
switch (key) { switch (key) {

View File

@ -1,14 +1,14 @@
import { createRenderer, VNode, createAppAPI } from '@vue/runtime-core' import { createRenderer, VNode } from '@vue/runtime-core'
import { nodeOps, TestElement } from './nodeOps' import { nodeOps, TestNode, TestElement } from './nodeOps'
import { patchProp } from './patchProp' import { patchProp } from './patchProp'
import { serializeInner } from './serialize' import { serializeInner } from './serialize'
export const render = createRenderer({ const { render, createApp } = createRenderer<TestNode, TestElement>({
patchProp, patchProp,
...nodeOps ...nodeOps
}) as (node: VNode | null, container: TestElement) => void })
export const createApp = createAppAPI(render) export { render, createApp }
// convenience for one-off render validations // convenience for one-off render validations
export function renderToString(vnode: VNode) { export function renderToString(vnode: VNode) {

View File

@ -218,7 +218,7 @@ function nextSibling(node: TestNode): TestNode | null {
return parent.children[i + 1] || null return parent.children[i + 1] || null
} }
function querySelector() { function querySelector(): any {
throw new Error('querySelector not supported in test renderer.') throw new Error('querySelector not supported in test renderer.')
} }