refactor(types): use stricter settings

fix #847
This commit is contained in:
Evan You 2020-03-23 11:08:22 -04:00
parent b3890a93e3
commit b8c1be18f3
27 changed files with 385 additions and 381 deletions

View File

@ -26,12 +26,12 @@ export interface VOnDirectiveNode extends DirectiveNode {
} }
export const transformOn: DirectiveTransform = ( export const transformOn: DirectiveTransform = (
dir: VOnDirectiveNode, dir,
node, node,
context, context,
augmentor augmentor
) => { ) => {
const { loc, modifiers, arg } = dir const { loc, modifiers, arg } = dir as VOnDirectiveNode
if (!dir.exp && !modifiers.length) { if (!dir.exp && !modifiers.length) {
context.onError(createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc)) context.onError(createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc))
} }
@ -55,7 +55,9 @@ export const transformOn: DirectiveTransform = (
} }
// handler processing // handler processing
let exp: ExpressionNode | undefined = dir.exp let exp: ExpressionNode | undefined = dir.exp as
| SimpleExpressionNode
| undefined
if (exp && !exp.content.trim()) { if (exp && !exp.content.trim()) {
exp = undefined exp = undefined
} }

View File

@ -1,5 +1,4 @@
import { import {
AttributeNode,
createSimpleExpression, createSimpleExpression,
ExpressionNode, ExpressionNode,
NodeTransform, NodeTransform,
@ -42,7 +41,7 @@ export const transformAssetUrl: NodeTransform = (
if ((tag === '*' || node.tag === tag) && node.props.length) { if ((tag === '*' || node.tag === tag) && node.props.length) {
const attributes = options[tag] const attributes = options[tag]
attributes.forEach(item => { attributes.forEach(item => {
node.props.forEach((attr: AttributeNode, index) => { node.props.forEach((attr, index) => {
if (attr.type !== NodeTypes.ATTRIBUTE) return if (attr.type !== NodeTypes.ATTRIBUTE) return
if (attr.name !== item) return if (attr.name !== item) return
if (!attr.value) return if (!attr.value) return

View File

@ -21,7 +21,7 @@ export interface ReactiveEffect<T = any> {
export interface ReactiveEffectOptions { export interface ReactiveEffectOptions {
lazy?: boolean lazy?: boolean
computed?: boolean computed?: boolean
scheduler?: (run: Function) => void scheduler?: (job: () => void) => void
onTrack?: (event: DebuggerEvent) => void onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void
onStop?: () => void onStop?: () => void

View File

@ -10,7 +10,8 @@ import {
withDirectives, withDirectives,
Plugin, Plugin,
ref, ref,
getCurrentInstance getCurrentInstance,
defineComponent
} from '@vue/runtime-test' } from '@vue/runtime-test'
import { mockWarn } from '@vue/shared' import { mockWarn } from '@vue/shared'
@ -18,16 +19,16 @@ describe('api: createApp', () => {
mockWarn() mockWarn()
test('mount', () => { test('mount', () => {
const Comp = { const Comp = defineComponent({
props: { props: {
count: { count: {
default: 0 default: 0
} }
}, },
setup(props: { count: number }) { setup(props) {
return () => props.count return () => props.count
} }
} })
const root1 = nodeOps.createElement('div') const root1 = nodeOps.createElement('div')
createApp(Comp).mount(root1) createApp(Comp).mount(root1)
@ -47,16 +48,16 @@ describe('api: createApp', () => {
}) })
test('unmount', () => { test('unmount', () => {
const Comp = { const Comp = defineComponent({
props: { props: {
count: { count: {
default: 0 default: 0
} }
}, },
setup(props: { count: number }) { setup(props) {
return () => props.count return () => props.count
} }
} })
const root = nodeOps.createElement('div') const root = nodeOps.createElement('div')
const app = createApp(Comp) const app = createApp(Comp)

View File

@ -9,7 +9,8 @@ import {
serializeInner, serializeInner,
serialize, serialize,
VNodeProps, VNodeProps,
KeepAlive KeepAlive,
TestElement
} from '@vue/runtime-test' } from '@vue/runtime-test'
function mount( function mount(
@ -42,13 +43,13 @@ function mockProps(extra: BaseTransitionProps = {}, withKeepAlive = false) {
} }
}), }),
onEnter: jest.fn((el, done) => { onEnter: jest.fn((el, done) => {
cbs.doneEnter[serialize(el)] = done cbs.doneEnter[serialize(el as TestElement)] = done
}), }),
onAfterEnter: jest.fn(), onAfterEnter: jest.fn(),
onEnterCancelled: jest.fn(), onEnterCancelled: jest.fn(),
onBeforeLeave: jest.fn(), onBeforeLeave: jest.fn(),
onLeave: jest.fn((el, done) => { onLeave: jest.fn((el, done) => {
cbs.doneLeave[serialize(el)] = done cbs.doneLeave[serialize(el as TestElement)] = done
}), }),
onAfterLeave: jest.fn(), onAfterLeave: jest.fn(),
onLeaveCancelled: jest.fn(), onLeaveCancelled: jest.fn(),
@ -64,8 +65,10 @@ function assertCalls(
props: BaseTransitionProps, props: BaseTransitionProps,
calls: Record<string, number> calls: Record<string, number>
) { ) {
Object.keys(calls).forEach((key: keyof BaseTransitionProps) => { Object.keys(calls).forEach(key => {
expect(props[key]).toHaveBeenCalledTimes(calls[key]) expect(props[key as keyof BaseTransitionProps]).toHaveBeenCalledTimes(
calls[key]
)
}) })
} }
@ -147,19 +150,19 @@ describe('BaseTransition', () => {
const toggle = ref(true) const toggle = ref(true)
const hooks: VNodeProps = { const hooks: VNodeProps = {
onVnodeBeforeMount(vnode) { onVnodeBeforeMount(vnode) {
vnode.transition!.beforeEnter(vnode.el) vnode.transition!.beforeEnter(vnode.el!)
}, },
onVnodeMounted(vnode) { onVnodeMounted(vnode) {
vnode.transition!.enter(vnode.el) vnode.transition!.enter(vnode.el!)
}, },
onVnodeUpdated(vnode, oldVnode) { onVnodeUpdated(vnode, oldVnode) {
if (oldVnode.props!.id !== vnode.props!.id) { if (oldVnode.props!.id !== vnode.props!.id) {
if (vnode.props!.id) { if (vnode.props!.id) {
vnode.transition!.beforeEnter(vnode.el) vnode.transition!.beforeEnter(vnode.el!)
state.show = true state.show = true
vnode.transition!.enter(vnode.el) vnode.transition!.enter(vnode.el!)
} else { } else {
vnode.transition!.leave(vnode.el, () => { vnode.transition!.leave(vnode.el!, () => {
state.show = false state.show = false
}) })
} }

View File

@ -19,7 +19,9 @@ function mountWithHydration(html: string, render: () => any) {
render render
}) })
return { return {
vnode: app.mount(container).$.subTree, vnode: app.mount(container).$.subTree as VNode<Node, Element> & {
el: Element
},
container container
} }
} }
@ -90,7 +92,7 @@ describe('SSR hydration', () => {
) )
// event handler // event handler
triggerEvent('click', vnode.el.querySelector('.foo')) triggerEvent('click', vnode.el.querySelector('.foo')!)
expect(fn).toHaveBeenCalled() expect(fn).toHaveBeenCalled()
msg.value = 'bar' msg.value = 'bar'
@ -120,7 +122,7 @@ describe('SSR hydration', () => {
const fragment1Children = fragment1.children as VNode[] const fragment1Children = fragment1.children as VNode[]
// first <span> // first <span>
expect(fragment1Children[0].el.tagName).toBe('SPAN') expect(fragment1Children[0].el!.tagName).toBe('SPAN')
expect(fragment1Children[0].el).toBe(vnode.el.childNodes[1]) expect(fragment1Children[0].el).toBe(vnode.el.childNodes[1])
// start fragment 2 // start fragment 2
@ -129,7 +131,7 @@ describe('SSR hydration', () => {
const fragment2Children = fragment2.children as VNode[] const fragment2Children = fragment2.children as VNode[]
// second <span> // second <span>
expect(fragment2Children[0].el.tagName).toBe('SPAN') expect(fragment2Children[0].el!.tagName).toBe('SPAN')
expect(fragment2Children[0].el).toBe(vnode.el.childNodes[3]) expect(fragment2Children[0].el).toBe(vnode.el.childNodes[3])
// end fragment 2 // end fragment 2
@ -139,7 +141,7 @@ describe('SSR hydration', () => {
expect(fragment1.anchor).toBe(vnode.el.childNodes[5]) expect(fragment1.anchor).toBe(vnode.el.childNodes[5])
// event handler // event handler
triggerEvent('click', vnode.el.querySelector('.foo')) triggerEvent('click', vnode.el.querySelector('.foo')!)
expect(fn).toHaveBeenCalled() expect(fn).toHaveBeenCalled()
msg.value = 'bar' msg.value = 'bar'

View File

@ -12,6 +12,7 @@ import { InjectionKey } from './apiInject'
import { isFunction, NO, isObject } from '@vue/shared' import { isFunction, NO, isObject } from '@vue/shared'
import { warn } from './warning' import { warn } from './warning'
import { createVNode, cloneVNode, VNode } from './vnode' import { createVNode, cloneVNode, VNode } from './vnode'
import { RootHydrateFunction } from './hydration'
export interface App<HostElement = any> { export interface App<HostElement = any> {
config: AppConfig config: AppConfig
@ -91,11 +92,11 @@ export type CreateAppFunction<HostElement> = (
rootProps?: Data | null rootProps?: Data | null
) => App<HostElement> ) => App<HostElement>
export function createAppAPI<HostNode, HostElement>( export function createAppAPI<HostElement>(
render: RootRenderFunction<HostNode, HostElement>, render: RootRenderFunction,
hydrate?: (vnode: VNode, container: Element) => void hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> { ): CreateAppFunction<HostElement> {
return function createApp(rootComponent: Component, rootProps = null) { return function createApp(rootComponent, rootProps = null) {
if (rootProps != null && !isObject(rootProps)) { if (rootProps != null && !isObject(rootProps)) {
__DEV__ && warn(`root props passed to app.mount() must be an object.`) __DEV__ && warn(`root props passed to app.mount() must be an object.`)
rootProps = null rootProps = null
@ -107,7 +108,7 @@ export function createAppAPI<HostNode, HostElement>(
let isMounted = false let isMounted = false
const app: App = { const app: App = {
_component: rootComponent, _component: rootComponent as Component,
_props: rootProps, _props: rootProps,
_container: null, _container: null,
_context: context, _context: context,
@ -189,7 +190,7 @@ export function createAppAPI<HostNode, HostElement>(
mount(rootContainer: HostElement, isHydrate?: boolean): any { mount(rootContainer: HostElement, isHydrate?: boolean): any {
if (!isMounted) { if (!isMounted) {
const vnode = createVNode(rootComponent, rootProps) const vnode = createVNode(rootComponent as Component, rootProps)
// store app context on the root VNode. // store app context on the root VNode.
// this will be set on the root instance on initial mount. // this will be set on the root instance on initial mount.
vnode.appContext = context vnode.appContext = context
@ -202,7 +203,7 @@ export function createAppAPI<HostNode, HostElement>(
} }
if (isHydrate && hydrate) { if (isHydrate && hydrate) {
hydrate(vnode, rootContainer as any) hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else { } else {
render(vnode, rootContainer) render(vnode, rootContainer)
} }

View File

@ -89,7 +89,7 @@ export type ComponentOptionsWithoutProps<
D = {}, D = {},
C extends ComputedOptions = {}, C extends ComputedOptions = {},
M extends MethodOptions = {} M extends MethodOptions = {}
> = ComponentOptionsBase<Readonly<Props>, RawBindings, D, C, M> & { > = ComponentOptionsBase<Props, RawBindings, D, C, M> & {
props?: undefined props?: undefined
} & ThisType<ComponentPublicInstance<{}, RawBindings, D, C, M, Readonly<Props>>> } & ThisType<ComponentPublicInstance<{}, RawBindings, D, C, M, Readonly<Props>>>
@ -116,12 +116,9 @@ export type ComponentOptionsWithObjectProps<
} & ThisType<ComponentPublicInstance<Props, RawBindings, D, C, M>> } & ThisType<ComponentPublicInstance<Props, RawBindings, D, C, M>>
export type ComponentOptions = export type ComponentOptions =
| ComponentOptionsWithoutProps | ComponentOptionsWithoutProps<any, any, any, any, any>
| ComponentOptionsWithObjectProps | ComponentOptionsWithObjectProps<any, any, any, any, any>
| ComponentOptionsWithArrayProps | ComponentOptionsWithArrayProps<any, any, any, any, any>
// TODO legacy component definition also supports constructors with .options
type LegacyComponent = ComponentOptions
export type ComputedOptions = Record< export type ComputedOptions = Record<
string, string,
@ -167,7 +164,10 @@ export interface LegacyOptions<
// Limitation: we cannot expose RawBindings on the `this` context for data // Limitation: we cannot expose RawBindings on the `this` context for data
// since that leads to some sort of circular inference and breaks ThisType // since that leads to some sort of circular inference and breaks ThisType
// for the entire component. // for the entire component.
data?: (this: ComponentPublicInstance<Props>) => D data?: (
this: ComponentPublicInstance<Props>,
vm: ComponentPublicInstance<Props>
) => D
computed?: C computed?: C
methods?: M methods?: M
watch?: ComponentWatchOptions watch?: ComponentWatchOptions
@ -175,8 +175,8 @@ export interface LegacyOptions<
inject?: ComponentInjectOptions inject?: ComponentInjectOptions
// composition // composition
mixins?: LegacyComponent[] mixins?: ComponentOptions[]
extends?: LegacyComponent extends?: ComponentOptions
// lifecycle // lifecycle
beforeCreate?(): void beforeCreate?(): void

View File

@ -179,7 +179,7 @@ function doWatch(
getter = () => traverse(baseGetter()) getter = () => traverse(baseGetter())
} }
let cleanup: Function let cleanup: () => void
const onInvalidate: InvalidateCbRegistrator = (fn: () => void) => { const onInvalidate: InvalidateCbRegistrator = (fn: () => void) => {
cleanup = runner.options.onStop = () => { cleanup = runner.options.onStop = () => {
callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP) callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)

View File

@ -63,7 +63,7 @@ export interface ClassComponent {
__vccOpts: ComponentOptions __vccOpts: ComponentOptions
} }
export type Component = ComponentOptions | FunctionalComponent export type Component = ComponentOptions | FunctionalComponent<any>
// A type used in public APIs where a component type is expected. // A type used in public APIs where a component type is expected.
// The constructor type is an artificial type returned by defineComponent(). // The constructor type is an artificial type returned by defineComponent().
@ -100,7 +100,10 @@ export interface SetupContext {
} }
export type RenderFunction = { export type RenderFunction = {
(): VNodeChild (
ctx: ComponentPublicInstance,
cache: ComponentInternalInstance['renderCache']
): VNodeChild
_rc?: boolean // isRuntimeCompiled _rc?: boolean // isRuntimeCompiled
} }

View File

@ -60,7 +60,7 @@ export function renderComponentRoot(
// runtime-compiled render functions using `with` block. // runtime-compiled render functions using `with` block.
const proxyToUse = withProxy || proxy const proxyToUse = withProxy || proxy
result = normalizeVNode( result = normalizeVNode(
instance.render!.call(proxyToUse, proxyToUse, renderCache) instance.render!.call(proxyToUse, proxyToUse!, renderCache)
) )
} else { } else {
// functional // functional

View File

@ -17,8 +17,9 @@ import { toRaw } from '@vue/reactivity'
import { callWithAsyncErrorHandling, ErrorCodes } from '../errorHandling' import { callWithAsyncErrorHandling, ErrorCodes } from '../errorHandling'
import { ShapeFlags } from '@vue/shared' import { ShapeFlags } from '@vue/shared'
import { onBeforeUnmount, onMounted } from '../apiLifecycle' import { onBeforeUnmount, onMounted } from '../apiLifecycle'
import { RendererElement } from '../renderer'
export interface BaseTransitionProps { export interface BaseTransitionProps<HostElement = RendererElement> {
mode?: 'in-out' | 'out-in' | 'default' mode?: 'in-out' | 'out-in' | 'default'
appear?: boolean appear?: boolean
@ -32,25 +33,25 @@ export interface BaseTransitionProps {
// Hooks. Using camel case for easier usage in render functions & JSX. // Hooks. Using camel case for easier usage in render functions & JSX.
// In templates these can be written as @before-enter="xxx" as prop names // In templates these can be written as @before-enter="xxx" as prop names
// are camelized. // are camelized.
onBeforeEnter?: (el: any) => void onBeforeEnter?: (el: HostElement) => void
onEnter?: (el: any, done: () => void) => void onEnter?: (el: HostElement, done: () => void) => void
onAfterEnter?: (el: any) => void onAfterEnter?: (el: HostElement) => void
onEnterCancelled?: (el: any) => void onEnterCancelled?: (el: HostElement) => void
// leave // leave
onBeforeLeave?: (el: any) => void onBeforeLeave?: (el: HostElement) => void
onLeave?: (el: any, done: () => void) => void onLeave?: (el: HostElement, done: () => void) => void
onAfterLeave?: (el: any) => void onAfterLeave?: (el: HostElement) => void
onLeaveCancelled?: (el: any) => void // only fired in persisted mode onLeaveCancelled?: (el: HostElement) => void // only fired in persisted mode
} }
export interface TransitionHooks { export interface TransitionHooks {
persisted: boolean persisted: boolean
beforeEnter(el: object): void beforeEnter(el: RendererElement): void
enter(el: object): void enter(el: RendererElement): void
leave(el: object, remove: () => void): void leave(el: RendererElement, remove: () => void): void
afterLeave?(): void afterLeave?(): void
delayLeave?( delayLeave?(
el: object, el: RendererElement,
earlyRemove: () => void, earlyRemove: () => void,
delayedLeave: () => void delayedLeave: () => void
): void ): void
@ -222,7 +223,7 @@ if (__DEV__) {
// also to avoid inline import() in generated d.ts files // also to avoid inline import() in generated d.ts files
export const BaseTransition = (BaseTransitionImpl as any) as { export const BaseTransition = (BaseTransitionImpl as any) as {
new (): { new (): {
$props: BaseTransitionProps $props: BaseTransitionProps<any>
} }
} }
@ -254,7 +255,7 @@ export function resolveTransitionHooks(
onLeave, onLeave,
onAfterLeave, onAfterLeave,
onLeaveCancelled onLeaveCancelled
}: BaseTransitionProps, }: BaseTransitionProps<any>,
state: TransitionState, state: TransitionState,
instance: ComponentInternalInstance instance: ComponentInternalInstance
): TransitionHooks { ): TransitionHooks {
@ -286,10 +287,10 @@ export function resolveTransitionHooks(
if ( if (
leavingVNode && leavingVNode &&
isSameVNodeType(vnode, leavingVNode) && isSameVNodeType(vnode, leavingVNode) &&
leavingVNode.el._leaveCb leavingVNode.el!._leaveCb
) { ) {
// force early removal (not cancelled) // force early removal (not cancelled)
leavingVNode.el._leaveCb() leavingVNode.el!._leaveCb()
} }
callHook(onBeforeEnter, [el]) callHook(onBeforeEnter, [el])
}, },

View File

@ -17,7 +17,9 @@ import {
RendererInternals, RendererInternals,
queuePostRenderEffect, queuePostRenderEffect,
invokeHooks, invokeHooks,
MoveType MoveType,
RendererElement,
RendererNode
} from '../renderer' } from '../renderer'
import { setTransitionHooks } from './BaseTransition' import { setTransitionHooks } from './BaseTransition'
@ -36,7 +38,11 @@ type Keys = Set<CacheKey>
export interface KeepAliveSink { export interface KeepAliveSink {
renderer: RendererInternals renderer: RendererInternals
parentSuspense: SuspenseBoundary | null parentSuspense: SuspenseBoundary | null
activate: (vnode: VNode, container: object, anchor: object | null) => void activate: (
vnode: VNode,
container: RendererElement,
anchor: RendererNode | null
) => void
deactivate: (vnode: VNode) => void deactivate: (vnode: VNode) => void
} }

View File

@ -1,6 +1,11 @@
import { ComponentInternalInstance } from '../component' import { ComponentInternalInstance } from '../component'
import { SuspenseBoundary } from './Suspense' import { SuspenseBoundary } from './Suspense'
import { RendererInternals, MoveType } from '../renderer' import {
RendererInternals,
MoveType,
RendererElement,
RendererNode
} from '../renderer'
import { VNode, VNodeArrayChildren, VNodeProps } from '../vnode' import { VNode, VNodeArrayChildren, VNodeProps } from '../vnode'
import { isString, ShapeFlags, PatchFlags } from '@vue/shared' import { isString, ShapeFlags, PatchFlags } from '@vue/shared'
import { warn } from '../warning' import { warn } from '../warning'
@ -16,8 +21,8 @@ export const PortalImpl = {
process( process(
n1: VNode | null, n1: VNode | null,
n2: VNode, n2: VNode,
container: object, container: RendererElement,
anchor: object | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,

View File

@ -2,7 +2,13 @@ import { VNode, normalizeVNode, VNodeChild, VNodeProps } from '../vnode'
import { isFunction, isArray, ShapeFlags } from '@vue/shared' import { isFunction, isArray, ShapeFlags } from '@vue/shared'
import { ComponentInternalInstance, handleSetupResult } from '../component' import { ComponentInternalInstance, handleSetupResult } from '../component'
import { Slots } from '../componentSlots' import { Slots } from '../componentSlots'
import { RendererInternals, MoveType, SetupRenderEffectFn } from '../renderer' import {
RendererInternals,
MoveType,
SetupRenderEffectFn,
RendererNode,
RendererElement
} from '../renderer'
import { queuePostFlushCb, queueJob } from '../scheduler' import { queuePostFlushCb, queueJob } from '../scheduler'
import { updateHOCHostEl } from '../componentRenderUtils' import { updateHOCHostEl } from '../componentRenderUtils'
import { pushWarningContext, popWarningContext } from '../warning' import { pushWarningContext, popWarningContext } from '../warning'
@ -27,8 +33,8 @@ export const SuspenseImpl = {
process( process(
n1: VNode | null, n1: VNode | null,
n2: VNode, n2: VNode,
container: object, container: RendererElement,
anchor: object | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
@ -73,8 +79,8 @@ export const Suspense = ((__FEATURE_SUSPENSE__
function mountSuspense( function mountSuspense(
n2: VNode, n2: VNode,
container: object, container: RendererElement,
anchor: object | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
@ -132,8 +138,8 @@ function mountSuspense(
function patchSuspense( function patchSuspense(
n1: VNode, n1: VNode,
n2: VNode, n2: VNode,
container: object, container: RendererElement,
anchor: object | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean, optimized: boolean,
@ -190,21 +196,17 @@ function patchSuspense(
suspense.fallbackTree = fallback suspense.fallbackTree = fallback
} }
export interface SuspenseBoundary< export interface SuspenseBoundary {
HostNode = any, vnode: VNode
HostElement = any, parent: SuspenseBoundary | null
HostVNode = VNode<HostNode, HostElement>
> {
vnode: HostVNode
parent: SuspenseBoundary<HostNode, HostElement> | null
parentComponent: ComponentInternalInstance | null parentComponent: ComponentInternalInstance | null
isSVG: boolean isSVG: boolean
optimized: boolean optimized: boolean
container: HostElement container: RendererElement
hiddenContainer: HostElement hiddenContainer: RendererElement
anchor: HostNode | null anchor: RendererNode | null
subTree: HostVNode subTree: VNode
fallbackTree: HostVNode fallbackTree: VNode
deps: number deps: number
isHydrating: boolean isHydrating: boolean
isResolved: boolean isResolved: boolean
@ -212,30 +214,31 @@ export interface SuspenseBoundary<
effects: Function[] effects: Function[]
resolve(): void resolve(): void
recede(): void recede(): void
move(container: HostElement, anchor: HostNode | null, type: MoveType): void move(
next(): HostNode | null container: RendererElement,
anchor: RendererNode | null,
type: MoveType
): void
next(): RendererNode | null
registerDep( registerDep(
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
setupRenderEffect: SetupRenderEffectFn<HostNode, HostElement> setupRenderEffect: SetupRenderEffectFn
): void
unmount(
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
doRemove?: boolean
): void ): void
unmount(parentSuspense: SuspenseBoundary | null, doRemove?: boolean): void
} }
function createSuspenseBoundary<HostNode, HostElement>( function createSuspenseBoundary(
vnode: VNode<HostNode, HostElement>, vnode: VNode,
parent: SuspenseBoundary<HostNode, HostElement> | null, parent: SuspenseBoundary | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
container: HostElement, container: RendererElement,
hiddenContainer: HostElement, hiddenContainer: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean, optimized: boolean,
rendererInternals: RendererInternals<HostNode, HostElement>, rendererInternals: RendererInternals,
isHydrating = false isHydrating = false
): SuspenseBoundary<HostNode, HostElement> { ): SuspenseBoundary {
const { const {
p: patch, p: patch,
m: move, m: move,
@ -250,7 +253,7 @@ function createSuspenseBoundary<HostNode, HostElement>(
: suspense.fallbackTree : suspense.fallbackTree
const { content, fallback } = normalizeSuspenseChildren(vnode) const { content, fallback } = normalizeSuspenseChildren(vnode)
const suspense: SuspenseBoundary<HostNode, HostElement> = { const suspense: SuspenseBoundary = {
vnode, vnode,
parent, parent,
parentComponent, parentComponent,
@ -430,7 +433,7 @@ function createSuspenseBoundary<HostNode, HostElement>(
// placeholder. // placeholder.
hydratedEl hydratedEl
? parentNode(hydratedEl)! ? parentNode(hydratedEl)!
: parentNode(instance.subTree.el)!, : parentNode(instance.subTree.el!)!,
// anchor will not be used if this is hydration, so only need to // anchor will not be used if this is hydration, so only need to
// consider the comment placeholder case. // consider the comment placeholder case.
hydratedEl ? null : next(instance.subTree), hydratedEl ? null : next(instance.subTree),
@ -484,7 +487,7 @@ function hydrateSuspense(
vnode, vnode,
parentSuspense, parentSuspense,
parentComponent, parentComponent,
node.parentNode, node.parentNode!,
document.createElement('div'), document.createElement('div'),
null, null,
isSVG, isSVG,

View File

@ -13,7 +13,7 @@ export function withCtx(
return function renderFnWithContext() { return function renderFnWithContext() {
const owner = currentRenderingInstance const owner = currentRenderingInstance
setCurrentRenderingInstance(ctx) setCurrentRenderingInstance(ctx)
const res = fn.apply(null, arguments) const res = fn.apply(null, arguments as any)
setCurrentRenderingInstance(owner) setCurrentRenderingInstance(owner)
return res return res
} }

View File

@ -71,21 +71,24 @@ import { invokeDirectiveHook } from './directives'
const __HMR__ = __BUNDLER__ && __DEV__ const __HMR__ = __BUNDLER__ && __DEV__
export interface Renderer<HostNode = any, HostElement = any> { export interface Renderer<HostElement = any> {
render: RootRenderFunction<HostNode, HostElement> render: RootRenderFunction<HostElement>
createApp: CreateAppFunction<HostElement> createApp: CreateAppFunction<HostElement>
} }
export interface HydrationRenderer extends Renderer<Node, Element> { export interface HydrationRenderer extends Renderer<Element> {
hydrate: RootHydrateFunction hydrate: RootHydrateFunction
} }
export type RootRenderFunction<HostNode, HostElement> = ( export type RootRenderFunction<HostElement = RendererElement> = (
vnode: VNode<HostNode, HostElement> | null, vnode: VNode | null,
container: HostElement container: HostElement
) => void ) => void
export interface RendererOptions<HostNode = any, HostElement = any> { export interface RendererOptions<
HostNode = RendererNode,
HostElement = RendererElement
> {
patchProp( patchProp(
el: HostElement, el: HostElement,
key: string, key: string,
@ -94,8 +97,8 @@ export interface RendererOptions<HostNode = any, HostElement = any> {
isSVG?: boolean, isSVG?: boolean,
prevChildren?: VNode<HostNode, HostElement>[], prevChildren?: VNode<HostNode, HostElement>[],
parentComponent?: ComponentInternalInstance | null, parentComponent?: ComponentInternalInstance | null,
parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null, parentSuspense?: SuspenseBoundary | null,
unmountChildren?: UnmountChildrenFn<HostNode, HostElement> unmountChildren?: UnmountChildrenFn
): void ): void
insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void
remove(el: HostNode): void remove(el: HostNode): void
@ -117,115 +120,126 @@ export interface RendererOptions<HostNode = any, HostElement = any> {
): HostElement ): HostElement
} }
// Renderer Node can technically be any object in the context of core renderer
// logic - they are never directly operated on and always passed to the node op
// functions provided via options, so the internal constraint is really just
// a generic object.
export interface RendererNode {
[key: string]: any
}
export interface RendererElement extends RendererNode {}
// An object exposing the internals of a renderer, passed to tree-shakeable // An object exposing the internals of a renderer, passed to tree-shakeable
// features so that they can be decoupled from this file. Keys are shortened // features so that they can be decoupled from this file. Keys are shortened
// to optimize bundle size. // to optimize bundle size.
export interface RendererInternals<HostNode = any, HostElement = any> { export interface RendererInternals<
p: PatchFn<HostNode, HostElement> HostNode = RendererNode,
um: UnmountFn<HostNode, HostElement> HostElement = RendererElement
m: MoveFn<HostNode, HostElement> > {
mt: MountComponentFn<HostNode, HostElement> p: PatchFn
mc: MountChildrenFn<HostNode, HostElement> um: UnmountFn
pc: PatchChildrenFn<HostNode, HostElement> m: MoveFn
pbc: PatchBlockChildrenFn<HostNode, HostElement> mt: MountComponentFn
n: NextFn<HostNode, HostElement> mc: MountChildrenFn
pc: PatchChildrenFn
pbc: PatchBlockChildrenFn
n: NextFn
o: RendererOptions<HostNode, HostElement> o: RendererOptions<HostNode, HostElement>
} }
// These functions are created inside a closure and therefore their types cannot // These functions are created inside a closure and therefore their types cannot
// be directly exported. In order to avoid maintaining function signatures in // be directly exported. In order to avoid maintaining function signatures in
// two places, we declare them once here and use them inside the closure. // two places, we declare them once here and use them inside the closure.
type PatchFn<HostNode, HostElement> = ( type PatchFn = (
n1: VNode<HostNode, HostElement> | null, // null means this is a mount n1: VNode | null, // null means this is a mount
n2: VNode<HostNode, HostElement>, n2: VNode,
container: HostElement, container: RendererElement,
anchor?: HostNode | null, anchor?: RendererNode | null,
parentComponent?: ComponentInternalInstance | null, parentComponent?: ComponentInternalInstance | null,
parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null, parentSuspense?: SuspenseBoundary | null,
isSVG?: boolean, isSVG?: boolean,
optimized?: boolean optimized?: boolean
) => void ) => void
type MountChildrenFn<HostNode, HostElement> = ( type MountChildrenFn = (
children: VNodeArrayChildren<HostNode, HostElement>, children: VNodeArrayChildren,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean, optimized: boolean,
start?: number start?: number
) => void ) => void
type PatchChildrenFn<HostNode, HostElement> = ( type PatchChildrenFn = (
n1: VNode<HostNode, HostElement> | null, n1: VNode | null,
n2: VNode<HostNode, HostElement>, n2: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
optimized?: boolean optimized?: boolean
) => void ) => void
type PatchBlockChildrenFn<HostNode, HostElement> = ( type PatchBlockChildrenFn = (
oldChildren: VNode<HostNode, HostElement>[], oldChildren: VNode[],
newChildren: VNode<HostNode, HostElement>[], newChildren: VNode[],
fallbackContainer: HostElement, fallbackContainer: RendererElement,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean isSVG: boolean
) => void ) => void
type MoveFn<HostNode, HostElement> = ( type MoveFn = (
vnode: VNode<HostNode, HostElement>, vnode: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
type: MoveType, type: MoveType,
parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null parentSuspense?: SuspenseBoundary | null
) => void ) => void
type NextFn<HostNode, HostElement> = ( type NextFn = (vnode: VNode) => RendererNode | null
vnode: VNode<HostNode, HostElement>
) => HostNode | null
type UnmountFn<HostNode, HostElement> = ( type UnmountFn = (
vnode: VNode<HostNode, HostElement>, vnode: VNode,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null, parentSuspense: SuspenseBoundary | null,
doRemove?: boolean doRemove?: boolean
) => void ) => void
type UnmountChildrenFn<HostNode, HostElement> = ( type UnmountChildrenFn = (
children: VNode<HostNode, HostElement>[], children: VNode[],
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null, parentSuspense: SuspenseBoundary | null,
doRemove?: boolean, doRemove?: boolean,
start?: number start?: number
) => void ) => void
export type MountComponentFn<HostNode, HostElement> = ( export type MountComponentFn = (
initialVNode: VNode<HostNode, HostElement>, initialVNode: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean isSVG: boolean
) => void ) => void
type ProcessTextOrCommentFn<HostNode, HostElement> = ( type ProcessTextOrCommentFn = (
n1: VNode<HostNode, HostElement> | null, n1: VNode | null,
n2: VNode<HostNode, HostElement>, n2: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null anchor: RendererNode | null
) => void ) => void
export type SetupRenderEffectFn<HostNode, HostElement> = ( export type SetupRenderEffectFn = (
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
initialVNode: VNode<HostNode, HostElement>, initialVNode: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean isSVG: boolean
) => void ) => void
@ -275,8 +289,8 @@ export const queuePostRenderEffect = __FEATURE_SUSPENSE__
* ``` * ```
*/ */
export function createRenderer< export function createRenderer<
HostNode extends object = any, HostNode = RendererNode,
HostElement extends HostNode = any HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) { >(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options) return baseCreateRenderer<HostNode, HostElement>(options)
} }
@ -287,38 +301,26 @@ export function createRenderer<
export function createHydrationRenderer( export function createHydrationRenderer(
options: RendererOptions<Node, Element> options: RendererOptions<Node, Element>
) { ) {
return baseCreateRenderer<Node, Element>(options, createHydrationFunctions) return baseCreateRenderer(options, createHydrationFunctions)
} }
// overload 1: no hydration // overload 1: no hydration
function baseCreateRenderer< function baseCreateRenderer<
HostNode extends object = any, HostNode = RendererNode,
HostElement extends HostNode = any HostElement = RendererElement
>( >(options: RendererOptions<HostNode, HostElement>): Renderer<HostElement>
options: RendererOptions<HostNode, HostElement>
): Renderer<HostNode, HostElement>
// overload 2: with hydration // overload 2: with hydration
function baseCreateRenderer< function baseCreateRenderer(
HostNode extends object = any, options: RendererOptions<Node, Element>,
HostElement extends HostNode = any
>(
options: RendererOptions<HostNode, HostElement>,
createHydrationFns: typeof createHydrationFunctions createHydrationFns: typeof createHydrationFunctions
): HydrationRenderer ): HydrationRenderer
// implementation // implementation
function baseCreateRenderer< function baseCreateRenderer(
HostNode extends object = any, options: RendererOptions,
HostElement extends HostNode = any
>(
options: RendererOptions<HostNode, HostElement>,
createHydrationFns?: typeof createHydrationFunctions createHydrationFns?: typeof createHydrationFunctions
) { ): any {
type HostVNode = VNode<HostNode, HostElement>
type HostVNodeChildren = VNodeArrayChildren<HostNode, HostElement>
type HostSuspenseBoundary = SuspenseBoundary<HostNode, HostElement>
const { const {
insert: hostInsert, insert: hostInsert,
remove: hostRemove, remove: hostRemove,
@ -337,7 +339,7 @@ function baseCreateRenderer<
// Note: functions inside this closure should use `const xxx = () => {}` // Note: functions inside this closure should use `const xxx = () => {}`
// style in order to prevent being inlined by minifiers. // style in order to prevent being inlined by minifiers.
const patch: PatchFn<HostNode, HostElement> = ( const patch: PatchFn = (
n1, n1,
n2, n2,
container, container,
@ -427,17 +429,12 @@ function baseCreateRenderer<
internals internals
) )
} else if (__DEV__) { } else if (__DEV__) {
warn('Invalid HostVNode type:', type, `(${typeof type})`) warn('Invalid VNode type:', type, `(${typeof type})`)
} }
} }
} }
const processText: ProcessTextOrCommentFn<HostNode, HostElement> = ( const processText: ProcessTextOrCommentFn = (n1, n2, container, anchor) => {
n1,
n2,
container,
anchor
) => {
if (n1 == null) { if (n1 == null) {
hostInsert( hostInsert(
(n2.el = hostCreateText(n2.children as string)), (n2.el = hostCreateText(n2.children as string)),
@ -445,14 +442,14 @@ function baseCreateRenderer<
anchor anchor
) )
} else { } else {
const el = (n2.el = n1.el) as HostNode const el = (n2.el = n1.el!)
if (n2.children !== n1.children) { if (n2.children !== n1.children) {
hostSetText(el, n2.children as string) hostSetText(el, n2.children as string)
} }
} }
} }
const processCommentNode: ProcessTextOrCommentFn<HostNode, HostElement> = ( const processCommentNode: ProcessTextOrCommentFn = (
n1, n1,
n2, n2,
container, container,
@ -471,9 +468,9 @@ function baseCreateRenderer<
} }
const mountStaticNode = ( const mountStaticNode = (
n2: HostVNode, n2: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
isSVG: boolean isSVG: boolean
) => { ) => {
if (n2.el && hostCloneNode !== undefined) { if (n2.el && hostCloneNode !== undefined) {
@ -491,12 +488,12 @@ function baseCreateRenderer<
} }
const processElement = ( const processElement = (
n1: HostVNode | null, n1: VNode | null,
n2: HostVNode, n2: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
) => { ) => {
@ -520,15 +517,15 @@ function baseCreateRenderer<
} }
const mountElement = ( const mountElement = (
vnode: HostVNode, vnode: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
) => { ) => {
let el: HostElement let el: RendererElement
let vnodeHook: VNodeHook | undefined | null let vnodeHook: VNodeHook | undefined | null
const { const {
type, type,
@ -547,7 +544,7 @@ function baseCreateRenderer<
// If a vnode has non-null el, it means it's being reused. // If a vnode has non-null el, it means it's being reused.
// Only static vnodes can be reused, so its mounted DOM nodes should be // Only static vnodes can be reused, so its mounted DOM nodes should be
// exactly the same, and we can simply do a clone here. // exactly the same, and we can simply do a clone here.
el = vnode.el = hostCloneNode(vnode.el) as HostElement el = vnode.el = hostCloneNode(vnode.el)
} else { } else {
el = vnode.el = hostCreateElement(vnode.type as string, isSVG) el = vnode.el = hostCreateElement(vnode.type as string, isSVG)
// props // props
@ -583,7 +580,7 @@ function baseCreateRenderer<
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 HostVNodeChildren, vnode.children as VNodeArrayChildren,
el, el,
null, null,
parentComponent, parentComponent,
@ -611,7 +608,7 @@ function baseCreateRenderer<
} }
} }
const mountChildren: MountChildrenFn<HostNode, HostElement> = ( const mountChildren: MountChildrenFn = (
children, children,
container, container,
anchor, anchor,
@ -623,7 +620,7 @@ function baseCreateRenderer<
) => { ) => {
for (let i = start; i < children.length; i++) { for (let i = start; i < children.length; i++) {
const child = (children[i] = optimized const child = (children[i] = optimized
? cloneIfMounted(children[i] as HostVNode) ? cloneIfMounted(children[i] as VNode)
: normalizeVNode(children[i])) : normalizeVNode(children[i]))
patch( patch(
null, null,
@ -639,14 +636,14 @@ function baseCreateRenderer<
} }
const patchElement = ( const patchElement = (
n1: HostVNode, n1: VNode,
n2: HostVNode, n2: VNode,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
) => { ) => {
const el = (n2.el = n1.el) as HostElement const el = (n2.el = n1.el!)
let { patchFlag, dynamicChildren, dirs } = n2 let { patchFlag, dynamicChildren, dirs } = 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
@ -717,7 +714,7 @@ function baseCreateRenderer<
prev, prev,
next, next,
isSVG, isSVG,
n1.children as HostVNode[], n1.children as VNode[],
parentComponent, parentComponent,
parentSuspense, parentSuspense,
unmountChildren unmountChildren
@ -779,7 +776,7 @@ function baseCreateRenderer<
} }
// The fast path for blocks. // The fast path for blocks.
const patchBlockChildren: PatchBlockChildrenFn<HostNode, HostElement> = ( const patchBlockChildren: PatchBlockChildrenFn = (
oldChildren, oldChildren,
newChildren, newChildren,
fallbackContainer, fallbackContainer,
@ -818,12 +815,12 @@ function baseCreateRenderer<
} }
const patchProps = ( const patchProps = (
el: HostElement, el: RendererElement,
vnode: HostVNode, vnode: VNode,
oldProps: Data, oldProps: Data,
newProps: Data, newProps: Data,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean isSVG: boolean
) => { ) => {
if (oldProps !== newProps) { if (oldProps !== newProps) {
@ -838,7 +835,7 @@ function baseCreateRenderer<
prev, prev,
next, next,
isSVG, isSVG,
vnode.children as HostVNode[], vnode.children as VNode[],
parentComponent, parentComponent,
parentSuspense, parentSuspense,
unmountChildren unmountChildren
@ -854,7 +851,7 @@ function baseCreateRenderer<
oldProps[key], oldProps[key],
null, null,
isSVG, isSVG,
vnode.children as HostVNode[], vnode.children as VNode[],
parentComponent, parentComponent,
parentSuspense, parentSuspense,
unmountChildren unmountChildren
@ -866,12 +863,12 @@ function baseCreateRenderer<
} }
const processFragment = ( const processFragment = (
n1: HostVNode | null, n1: VNode | null,
n2: HostVNode, n2: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
) => { ) => {
@ -897,7 +894,7 @@ function baseCreateRenderer<
// since they are either generated by the compiler, or implicitly created // since they are either generated by the compiler, or implicitly created
// from arrays. // from arrays.
mountChildren( mountChildren(
n2.children as HostVNodeChildren, n2.children as VNodeArrayChildren,
container, container,
fragmentEndAnchor, fragmentEndAnchor,
parentComponent, parentComponent,
@ -937,12 +934,12 @@ function baseCreateRenderer<
} }
const processComponent = ( const processComponent = (
n1: HostVNode | null, n1: VNode | null,
n2: HostVNode, n2: VNode,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
) => { ) => {
@ -1010,7 +1007,7 @@ function baseCreateRenderer<
} }
} }
const mountComponent: MountComponentFn<HostNode, HostElement> = ( const mountComponent: MountComponentFn = (
initialVNode, initialVNode,
container, container,
anchor, anchor,
@ -1073,7 +1070,7 @@ function baseCreateRenderer<
} }
} }
const setupRenderEffect: SetupRenderEffectFn<HostNode, HostElement> = ( const setupRenderEffect: SetupRenderEffectFn = (
instance, instance,
initialVNode, initialVNode,
container, container,
@ -1137,7 +1134,7 @@ function baseCreateRenderer<
} 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: HostVNode) // OR parent calling processComponent (next: VNode)
let { next, bu, u, parent, vnode } = instance let { next, bu, u, parent, vnode } = instance
let vnodeHook: VNodeHook | null | undefined let vnodeHook: VNodeHook | null | undefined
if (__DEV__) { if (__DEV__) {
@ -1170,7 +1167,7 @@ function baseCreateRenderer<
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 as HostNode) as HostElement, hostParentNode(prevTree.el!)!,
// 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,
@ -1203,7 +1200,7 @@ function baseCreateRenderer<
const updateComponentPreRender = ( const updateComponentPreRender = (
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
nextVNode: HostVNode nextVNode: VNode
) => { ) => {
nextVNode.component = instance nextVNode.component = instance
instance.vnode = nextVNode instance.vnode = nextVNode
@ -1212,7 +1209,7 @@ function baseCreateRenderer<
resolveSlots(instance, nextVNode.children) resolveSlots(instance, nextVNode.children)
} }
const patchChildren: PatchChildrenFn<HostNode, HostElement> = ( const patchChildren: PatchChildrenFn = (
n1, n1,
n2, n2,
container, container,
@ -1236,8 +1233,8 @@ function baseCreateRenderer<
// 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 HostVNode[], c1 as VNode[],
c2 as HostVNodeChildren, c2 as VNodeArrayChildren,
container, container,
anchor, anchor,
parentComponent, parentComponent,
@ -1249,8 +1246,8 @@ function baseCreateRenderer<
} else if (patchFlag & PatchFlags.UNKEYED_FRAGMENT) { } else if (patchFlag & PatchFlags.UNKEYED_FRAGMENT) {
// unkeyed // unkeyed
patchUnkeyedChildren( patchUnkeyedChildren(
c1 as HostVNode[], c1 as VNode[],
c2 as HostVNodeChildren, c2 as VNodeArrayChildren,
container, container,
anchor, anchor,
parentComponent, parentComponent,
@ -1266,7 +1263,7 @@ function baseCreateRenderer<
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 HostVNode[], parentComponent, parentSuspense) unmountChildren(c1 as VNode[], parentComponent, parentSuspense)
} }
if (c2 !== c1) { if (c2 !== c1) {
hostSetElementText(container, c2 as string) hostSetElementText(container, c2 as string)
@ -1277,8 +1274,8 @@ function baseCreateRenderer<
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 HostVNode[], c1 as VNode[],
c2 as HostVNodeChildren, c2 as VNodeArrayChildren,
container, container,
anchor, anchor,
parentComponent, parentComponent,
@ -1288,12 +1285,7 @@ function baseCreateRenderer<
) )
} else { } else {
// no new children, just unmount old // no new children, just unmount old
unmountChildren( unmountChildren(c1 as VNode[], parentComponent, parentSuspense, true)
c1 as HostVNode[],
parentComponent,
parentSuspense,
true
)
} }
} else { } else {
// prev children was text OR null // prev children was text OR null
@ -1304,7 +1296,7 @@ function baseCreateRenderer<
// mount new if array // mount new if array
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren( mountChildren(
c2 as HostVNodeChildren, c2 as VNodeArrayChildren,
container, container,
anchor, anchor,
parentComponent, parentComponent,
@ -1318,12 +1310,12 @@ function baseCreateRenderer<
} }
const patchUnkeyedChildren = ( const patchUnkeyedChildren = (
c1: HostVNode[], c1: VNode[],
c2: HostVNodeChildren, c2: VNodeArrayChildren,
container: HostElement, container: RendererElement,
anchor: HostNode | null, anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
) => { ) => {
@ -1335,7 +1327,7 @@ function baseCreateRenderer<
let i let i
for (i = 0; i < commonLength; i++) { for (i = 0; i < commonLength; i++) {
const nextChild = (c2[i] = optimized const nextChild = (c2[i] = optimized
? cloneIfMounted(c2[i] as HostVNode) ? cloneIfMounted(c2[i] as VNode)
: normalizeVNode(c2[i])) : normalizeVNode(c2[i]))
patch( patch(
c1[i], c1[i],
@ -1368,12 +1360,12 @@ function baseCreateRenderer<
// can be all-keyed or mixed // can be all-keyed or mixed
const patchKeyedChildren = ( const patchKeyedChildren = (
c1: HostVNode[], c1: VNode[],
c2: HostVNodeChildren, c2: VNodeArrayChildren,
container: HostElement, container: RendererElement,
parentAnchor: HostNode | null, parentAnchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
isSVG: boolean, isSVG: boolean,
optimized: boolean optimized: boolean
) => { ) => {
@ -1388,7 +1380,7 @@ function baseCreateRenderer<
while (i <= e1 && i <= e2) { while (i <= e1 && i <= e2) {
const n1 = c1[i] const n1 = c1[i]
const n2 = (c2[i] = optimized const n2 = (c2[i] = optimized
? cloneIfMounted(c2[i] as HostVNode) ? cloneIfMounted(c2[i] as VNode)
: normalizeVNode(c2[i])) : normalizeVNode(c2[i]))
if (isSameVNodeType(n1, n2)) { if (isSameVNodeType(n1, n2)) {
patch( patch(
@ -1413,7 +1405,7 @@ function baseCreateRenderer<
while (i <= e1 && i <= e2) { while (i <= e1 && i <= e2) {
const n1 = c1[e1] const n1 = c1[e1]
const n2 = (c2[e2] = optimized const n2 = (c2[e2] = optimized
? cloneIfMounted(c2[e2] as HostVNode) ? cloneIfMounted(c2[e2] as VNode)
: normalizeVNode(c2[e2])) : normalizeVNode(c2[e2]))
if (isSameVNodeType(n1, n2)) { if (isSameVNodeType(n1, n2)) {
patch( patch(
@ -1443,13 +1435,12 @@ function baseCreateRenderer<
if (i > e1) { if (i > e1) {
if (i <= e2) { if (i <= e2) {
const nextPos = e2 + 1 const nextPos = e2 + 1
const anchor = const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor
nextPos < l2 ? (c2[nextPos] as HostVNode).el : parentAnchor
while (i <= e2) { while (i <= e2) {
patch( patch(
null, null,
(c2[i] = optimized (c2[i] = optimized
? cloneIfMounted(c2[i] as HostVNode) ? cloneIfMounted(c2[i] as VNode)
: normalizeVNode(c2[i])), : normalizeVNode(c2[i])),
container, container,
anchor, anchor,
@ -1488,7 +1479,7 @@ function baseCreateRenderer<
const keyToNewIndexMap: Map<string | number, number> = new Map() const keyToNewIndexMap: Map<string | number, number> = new Map()
for (i = s2; i <= e2; i++) { for (i = s2; i <= e2; i++) {
const nextChild = (c2[i] = optimized const nextChild = (c2[i] = optimized
? cloneIfMounted(c2[i] as HostVNode) ? cloneIfMounted(c2[i] as VNode)
: normalizeVNode(c2[i])) : normalizeVNode(c2[i]))
if (nextChild.key != null) { if (nextChild.key != null) {
if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) { if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) {
@ -1533,7 +1524,7 @@ function baseCreateRenderer<
for (j = s2; j <= e2; j++) { for (j = s2; j <= e2; j++) {
if ( if (
newIndexToOldIndexMap[j - s2] === 0 && newIndexToOldIndexMap[j - s2] === 0 &&
isSameVNodeType(prevChild, c2[j] as HostVNode) isSameVNodeType(prevChild, c2[j] as VNode)
) { ) {
newIndex = j newIndex = j
break break
@ -1551,7 +1542,7 @@ function baseCreateRenderer<
} }
patch( patch(
prevChild, prevChild,
c2[newIndex] as HostVNode, c2[newIndex] as VNode,
container, container,
null, null,
parentComponent, parentComponent,
@ -1572,11 +1563,9 @@ function baseCreateRenderer<
// 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 HostVNode const nextChild = c2[nextIndex] as VNode
const anchor = const anchor =
nextIndex + 1 < l2 nextIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor
? (c2[nextIndex + 1] as HostVNode).el
: parentAnchor
if (newIndexToOldIndexMap[i] === 0) { if (newIndexToOldIndexMap[i] === 0) {
// mount new // mount new
patch( patch(
@ -1602,7 +1591,7 @@ function baseCreateRenderer<
} }
} }
const move: MoveFn<HostNode, HostElement> = ( const move: MoveFn = (
vnode, vnode,
container, container,
anchor, anchor,
@ -1619,7 +1608,7 @@ function baseCreateRenderer<
} }
if (vnode.type === Fragment) { if (vnode.type === Fragment) {
hostInsert(vnode.el!, container, anchor) hostInsert(vnode.el!, container, anchor)
const children = vnode.children as HostVNode[] const children = vnode.children as VNode[]
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
move(children[i], container, anchor, type) move(children[i], container, anchor, type)
} }
@ -1657,7 +1646,7 @@ function baseCreateRenderer<
} }
} }
const unmount: UnmountFn<HostNode, HostElement> = ( const unmount: UnmountFn = (
vnode, vnode,
parentComponent, parentComponent,
parentSuspense, parentSuspense,
@ -1696,11 +1685,7 @@ function baseCreateRenderer<
// fast path for block nodes: only need to unmount dynamic children. // fast path for block nodes: only need to unmount dynamic children.
unmountChildren(dynamicChildren, parentComponent, parentSuspense) unmountChildren(dynamicChildren, parentComponent, parentSuspense)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren( unmountChildren(children as VNode[], parentComponent, parentSuspense)
children as HostVNode[],
parentComponent,
parentSuspense
)
} }
if (doRemove) { if (doRemove) {
@ -1717,7 +1702,7 @@ function baseCreateRenderer<
} }
} }
const remove = (vnode: HostVNode) => { const remove = (vnode: VNode) => {
const { type, el, anchor, transition } = vnode const { type, el, anchor, transition } = vnode
if (type === Fragment) { if (type === Fragment) {
removeFragment(el!, anchor!) removeFragment(el!, anchor!)
@ -1748,7 +1733,7 @@ function baseCreateRenderer<
} }
} }
const removeFragment = (cur: HostNode, end: HostNode) => { const removeFragment = (cur: RendererNode, end: RendererNode) => {
// For fragments, directly remove all contained DOM nodes. // For fragments, directly remove all contained DOM nodes.
// (fragment child nodes cannot have transition) // (fragment child nodes cannot have transition)
let next let next
@ -1762,7 +1747,7 @@ function baseCreateRenderer<
const unmountComponent = ( const unmountComponent = (
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
doRemove?: boolean doRemove?: boolean
) => { ) => {
if (__HMR__ && instance.type.__hmrId) { if (__HMR__ && instance.type.__hmrId) {
@ -1819,7 +1804,7 @@ function baseCreateRenderer<
} }
} }
const unmountChildren: UnmountChildrenFn<HostNode, HostElement> = ( const unmountChildren: UnmountChildrenFn = (
children, children,
parentComponent, parentComponent,
parentSuspense, parentSuspense,
@ -1831,7 +1816,7 @@ function baseCreateRenderer<
} }
} }
const getNextHostNode: NextFn<HostNode, HostElement> = vnode => { const getNextHostNode: NextFn = vnode => {
if (vnode.shapeFlag & ShapeFlags.COMPONENT) { if (vnode.shapeFlag & ShapeFlags.COMPONENT) {
return getNextHostNode(vnode.component!.subTree) return getNextHostNode(vnode.component!.subTree)
} }
@ -1845,7 +1830,7 @@ function baseCreateRenderer<
rawRef: VNodeNormalizedRef, rawRef: VNodeNormalizedRef,
oldRawRef: VNodeNormalizedRef | null, oldRawRef: VNodeNormalizedRef | null,
parent: ComponentInternalInstance, parent: ComponentInternalInstance,
value: HostNode | ComponentPublicInstance | null value: RendererNode | ComponentPublicInstance | null
) => { ) => {
const [owner, ref] = rawRef const [owner, ref] = rawRef
if (__DEV__ && !owner) { if (__DEV__ && !owner) {
@ -1887,12 +1872,7 @@ function baseCreateRenderer<
} }
} }
type HostRootElement = HostElement & { _vnode: HostVNode | null } const render: RootRenderFunction = (vnode, container) => {
const render: RootRenderFunction<HostNode, HostElement> = (
vnode,
container: HostRootElement
) => {
if (vnode == null) { if (vnode == null) {
if (container._vnode) { if (container._vnode) {
unmount(container._vnode, null, null, true) unmount(container._vnode, null, null, true)
@ -1904,7 +1884,7 @@ function baseCreateRenderer<
container._vnode = vnode container._vnode = vnode
} }
const internals: RendererInternals<HostNode, HostElement> = { const internals: RendererInternals = {
p: patch, p: patch,
um: unmount, um: unmount,
m: move, m: move,
@ -1928,7 +1908,7 @@ function baseCreateRenderer<
return { return {
render, render,
hydrate, hydrate,
createApp: createAppAPI(render, hydrate) as CreateAppFunction<HostElement> createApp: createAppAPI(render, hydrate)
} }
} }

View File

@ -31,6 +31,7 @@ import { warn } from './warning'
import { currentScopeId } from './helpers/scopeId' import { currentScopeId } from './helpers/scopeId'
import { PortalImpl, isPortal } from './components/Portal' import { PortalImpl, isPortal } from './components/Portal'
import { currentRenderingInstance } from './componentRenderUtils' import { currentRenderingInstance } from './componentRenderUtils'
import { RendererNode, RendererElement } from './renderer'
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as { export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
__isFragment: true __isFragment: true
@ -81,40 +82,31 @@ export interface VNodeProps {
onVnodeUnmounted?: VNodeMountHook | VNodeMountHook[] onVnodeUnmounted?: VNodeMountHook | VNodeMountHook[]
} }
type VNodeChildAtom<HostNode, HostElement> = type VNodeChildAtom = VNode | string | number | boolean | null | void
| VNode<HostNode, HostElement>
export interface VNodeArrayChildren<
HostNode = RendererNode,
HostElement = RendererElement
> extends Array<VNodeArrayChildren | VNodeChildAtom> {}
export type VNodeChild = VNodeChildAtom | VNodeArrayChildren
export type VNodeNormalizedChildren =
| string | string
| number | VNodeArrayChildren
| boolean
| null
| void
export interface VNodeArrayChildren<HostNode = any, HostElement = any>
extends Array<
| VNodeArrayChildren<HostNode, HostElement>
| VNodeChildAtom<HostNode, HostElement>
> {}
export type VNodeChild<HostNode = any, HostElement = any> =
| VNodeChildAtom<HostNode, HostElement>
| VNodeArrayChildren<HostNode, HostElement>
export type VNodeNormalizedChildren<HostNode = any, HostElement = any> =
| string
| VNodeArrayChildren<HostNode, HostElement>
| RawSlots | RawSlots
| null | null
export interface VNode<HostNode = any, HostElement = any> { export interface VNode<HostNode = RendererNode, HostElement = RendererElement> {
_isVNode: true _isVNode: true
type: VNodeTypes type: VNodeTypes
props: VNodeProps | null props: VNodeProps | null
key: string | number | null key: string | number | null
ref: VNodeNormalizedRef | null ref: VNodeNormalizedRef | null
scopeId: string | null // SFC only scopeId: string | null // SFC only
children: VNodeNormalizedChildren<HostNode, HostElement> children: VNodeNormalizedChildren
component: ComponentInternalInstance | null component: ComponentInternalInstance | null
suspense: SuspenseBoundary<HostNode, HostElement> | null suspense: SuspenseBoundary | null
dirs: DirectiveBinding[] | null dirs: DirectiveBinding[] | null
transition: TransitionHooks | null transition: TransitionHooks | null
@ -376,7 +368,7 @@ export function createCommentVNode(
: createVNode(Comment, null, text) : createVNode(Comment, null, text)
} }
export function normalizeVNode<T, U>(child: VNodeChild<T, U>): VNode<T, U> { export function normalizeVNode(child: VNodeChild): VNode {
if (child == null || typeof child === 'boolean') { if (child == null || typeof child === 'boolean') {
// empty placeholder // empty placeholder
return createVNode(Comment) return createVNode(Comment)

View File

@ -13,7 +13,7 @@ import { ErrorCodes } from 'packages/runtime-core/src/errorHandling'
const TRANSITION = 'transition' const TRANSITION = 'transition'
const ANIMATION = 'animation' const ANIMATION = 'animation'
export interface TransitionProps extends BaseTransitionProps { export interface TransitionProps extends BaseTransitionProps<Element> {
name?: string name?: string
type?: typeof TRANSITION | typeof ANIMATION type?: typeof TRANSITION | typeof ANIMATION
css?: boolean css?: boolean
@ -76,7 +76,7 @@ export function resolveTransitionProps({
leaveActiveClass = `${name}-leave-active`, leaveActiveClass = `${name}-leave-active`,
leaveToClass = `${name}-leave-to`, leaveToClass = `${name}-leave-to`,
...baseProps ...baseProps
}: TransitionProps): BaseTransitionProps { }: TransitionProps): BaseTransitionProps<Element> {
if (!css) { if (!css) {
return baseProps return baseProps
} }
@ -94,7 +94,7 @@ export function resolveTransitionProps({
enterToClass = appearToClass enterToClass = appearToClass
} }
type Hook = (el: HTMLElement, done?: () => void) => void type Hook = (el: Element, done?: () => void) => void
const finishEnter: Hook = (el, done) => { const finishEnter: Hook = (el, done) => {
removeTransitionClass(el, enterToClass) removeTransitionClass(el, enterToClass)
@ -124,7 +124,7 @@ export function resolveTransitionProps({
onEnter(el, done) { onEnter(el, done) {
nextFrame(() => { nextFrame(() => {
const resolve = () => finishEnter(el, done) const resolve = () => finishEnter(el, done)
onEnter && callHookWithErrorHandling(onEnter, [el, resolve]) onEnter && callHookWithErrorHandling(onEnter as Hook, [el, resolve])
removeTransitionClass(el, enterFromClass) removeTransitionClass(el, enterFromClass)
addTransitionClass(el, enterToClass) addTransitionClass(el, enterToClass)
if (!(onEnter && onEnter.length > 1)) { if (!(onEnter && onEnter.length > 1)) {
@ -141,7 +141,7 @@ export function resolveTransitionProps({
addTransitionClass(el, leaveFromClass) addTransitionClass(el, leaveFromClass)
nextFrame(() => { nextFrame(() => {
const resolve = () => finishLeave(el, done) const resolve = () => finishLeave(el, done)
onLeave && callHookWithErrorHandling(onLeave, [el, resolve]) onLeave && callHookWithErrorHandling(onLeave as Hook, [el, resolve])
removeTransitionClass(el, leaveFromClass) removeTransitionClass(el, leaveFromClass)
addTransitionClass(el, leaveToClass) addTransitionClass(el, leaveToClass)
if (!(onLeave && onLeave.length > 1)) { if (!(onLeave && onLeave.length > 1)) {
@ -199,17 +199,21 @@ export interface ElementWithTransition extends HTMLElement {
_vtc?: Set<string> _vtc?: Set<string>
} }
export function addTransitionClass(el: ElementWithTransition, cls: string) { export function addTransitionClass(el: Element, cls: string) {
cls.split(/\s+/).forEach(c => c && el.classList.add(c)) cls.split(/\s+/).forEach(c => c && el.classList.add(c))
;(el._vtc || (el._vtc = new Set())).add(cls) ;(
(el as ElementWithTransition)._vtc ||
((el as ElementWithTransition)._vtc = new Set())
).add(cls)
} }
export function removeTransitionClass(el: ElementWithTransition, cls: string) { export function removeTransitionClass(el: Element, cls: string) {
cls.split(/\s+/).forEach(c => c && el.classList.remove(c)) cls.split(/\s+/).forEach(c => c && el.classList.remove(c))
if (el._vtc) { const { _vtc } = el as ElementWithTransition
el._vtc.delete(cls) if (_vtc) {
if (!el._vtc!.size) { _vtc.delete(cls)
el._vtc = undefined if (!_vtc!.size) {
;(el as ElementWithTransition)._vtc = undefined
} }
} }
} }

View File

@ -52,8 +52,8 @@ const TransitionGroupImpl = {
hasMove = hasMove =
hasMove === null hasMove === null
? (hasMove = hasCSSTransform( ? (hasMove = hasCSSTransform(
prevChildren[0].el, prevChildren[0].el as ElementWithTransition,
instance.vnode.el, instance.vnode.el as Node,
moveClass moveClass
)) ))
: hasMove : hasMove
@ -71,17 +71,17 @@ const TransitionGroupImpl = {
forceReflow() forceReflow()
movedChildren.forEach(c => { movedChildren.forEach(c => {
const el = c.el const el = c.el as ElementWithTransition
const style = el.style const style = el.style
addTransitionClass(el, moveClass) addTransitionClass(el, moveClass)
style.transform = style.WebkitTransform = style.transitionDuration = '' style.transform = style.webkitTransform = style.transitionDuration = ''
const cb = (el._moveCb = (e: TransitionEvent) => { const cb = ((el as any)._moveCb = (e: TransitionEvent) => {
if (e && e.target !== el) { if (e && e.target !== el) {
return return
} }
if (!e || /transform$/.test(e.propertyName)) { if (!e || /transform$/.test(e.propertyName)) {
el.removeEventListener('transitionend', cb) el.removeEventListener('transitionend', cb)
el._moveCb = null ;(el as any)._moveCb = null
removeTransitionClass(el, moveClass) removeTransitionClass(el, moveClass)
} }
}) })
@ -120,7 +120,7 @@ const TransitionGroupImpl = {
child, child,
resolveTransitionHooks(child, cssTransitionProps, state, instance) resolveTransitionHooks(child, cssTransitionProps, state, instance)
) )
positionMap.set(child, child.el.getBoundingClientRect()) positionMap.set(child, (child.el as Element).getBoundingClientRect())
} }
} }
@ -145,16 +145,17 @@ if (__DEV__) {
} }
function callPendingCbs(c: VNode) { function callPendingCbs(c: VNode) {
if (c.el._moveCb) { const el = c.el as any
c.el._moveCb() if (el._moveCb) {
el._moveCb()
} }
if (c.el._enterCb) { if (el._enterCb) {
c.el._enterCb() el._enterCb()
} }
} }
function recordPosition(c: VNode) { function recordPosition(c: VNode) {
newPositionMap.set(c, c.el.getBoundingClientRect()) newPositionMap.set(c, (c.el as Element).getBoundingClientRect())
} }
function applyTranslation(c: VNode): VNode | undefined { function applyTranslation(c: VNode): VNode | undefined {
@ -163,8 +164,8 @@ function applyTranslation(c: VNode): VNode | undefined {
const dx = oldPos.left - newPos.left const dx = oldPos.left - newPos.left
const dy = oldPos.top - newPos.top const dy = oldPos.top - newPos.top
if (dx || dy) { if (dx || dy) {
const s = c.el.style const s = (c.el as HTMLElement).style
s.transform = s.WebkitTransform = `translate(${dx}px,${dy}px)` s.transform = s.webkitTransform = `translate(${dx}px,${dy}px)`
s.transitionDuration = '0s' s.transitionDuration = '0s'
return c return c
} }

View File

@ -11,11 +11,11 @@ import { isArray, looseEqual, looseIndexOf } from '@vue/shared'
const getModelAssigner = (vnode: VNode): ((value: any) => void) => const getModelAssigner = (vnode: VNode): ((value: any) => void) =>
vnode.props!['onUpdate:modelValue'] vnode.props!['onUpdate:modelValue']
function onCompositionStart(e: CompositionEvent) { function onCompositionStart(e: Event) {
;(e.target as any).composing = true ;(e.target as any).composing = true
} }
function onCompositionEnd(e: CompositionEvent) { function onCompositionEnd(e: Event) {
const target = e.target as any const target = e.target as any
if (target.composing) { if (target.composing) {
target.composing = false target.composing = false

View File

@ -6,7 +6,7 @@ type KeyedEvent = KeyboardEvent | MouseEvent | TouchEvent
const modifierGuards: Record< const modifierGuards: Record<
string, string,
(e: Event, modifiers?: string[]) => void | boolean (e: Event, modifiers: string[]) => void | boolean
> = { > = {
stop: e => e.stopPropagation(), stop: e => e.stopPropagation(),
prevent: e => e.preventDefault(), prevent: e => e.preventDefault(),
@ -18,7 +18,7 @@ const modifierGuards: Record<
left: e => 'button' in e && (e as MouseEvent).button !== 0, left: e => 'button' in e && (e as MouseEvent).button !== 0,
middle: e => 'button' in e && (e as MouseEvent).button !== 1, middle: e => 'button' in e && (e as MouseEvent).button !== 1,
right: e => 'button' in e && (e as MouseEvent).button !== 2, right: e => 'button' in e && (e as MouseEvent).button !== 2,
exact: (e, modifiers: string[]) => exact: (e, modifiers) =>
systemModifiers.some(m => (e as any)[`${m}Key`] && !modifiers.includes(m)) systemModifiers.some(m => (e as any)[`${m}Key`] && !modifiers.includes(m))
} }

View File

@ -40,7 +40,7 @@ function ensureHydrationRenderer() {
// use explicit type casts here to avoid import() calls in rolled-up d.ts // use explicit type casts here to avoid import() calls in rolled-up d.ts
export const render = ((...args) => { export const render = ((...args) => {
ensureRenderer().render(...args) ensureRenderer().render(...args)
}) as RootRenderFunction<Node, Element> }) as RootRenderFunction<Element>
export const hydrate = ((...args) => { export const hydrate = ((...args) => {
ensureHydrationRenderer().hydrate(...args) ensureHydrationRenderer().hydrate(...args)

View File

@ -4,7 +4,7 @@ import {
RootRenderFunction, RootRenderFunction,
CreateAppFunction CreateAppFunction
} from '@vue/runtime-core' } from '@vue/runtime-core'
import { nodeOps, TestNode, TestElement } from './nodeOps' import { nodeOps, TestElement } from './nodeOps'
import { patchProp } from './patchProp' import { patchProp } from './patchProp'
import { serializeInner } from './serialize' import { serializeInner } from './serialize'
@ -13,7 +13,7 @@ const { render: baseRender, createApp: baseCreateApp } = createRenderer({
...nodeOps ...nodeOps
}) })
export const render = baseRender as RootRenderFunction<TestNode, TestElement> export const render = baseRender as RootRenderFunction<TestElement>
export const createApp = baseCreateApp as CreateAppFunction<TestElement> export const createApp = baseCreateApp as CreateAppFunction<TestElement>
// convenience for one-off render validations // convenience for one-off render validations

View File

@ -42,7 +42,7 @@ export function setupPuppeteer() {
} }
async function value(selector: string) { async function value(selector: string) {
return await page.$eval(selector, (node: HTMLInputElement) => node.value) return await page.$eval(selector, node => (node as HTMLInputElement).value)
} }
async function html(selector: string) { async function html(selector: string) {
@ -58,14 +58,17 @@ export function setupPuppeteer() {
} }
async function isVisible(selector: string) { async function isVisible(selector: string) {
const display = await page.$eval(selector, (node: HTMLElement) => { const display = await page.$eval(selector, node => {
return window.getComputedStyle(node).display return window.getComputedStyle(node).display
}) })
return display !== 'none' return display !== 'none'
} }
async function isChecked(selector: string) { async function isChecked(selector: string) {
return await page.$eval(selector, (node: HTMLInputElement) => node.checked) return await page.$eval(
selector,
node => (node as HTMLInputElement).checked
)
} }
async function isFocused(selector: string) { async function isFocused(selector: string) {
@ -74,13 +77,13 @@ export function setupPuppeteer() {
async function setValue(selector: string, value: string) { async function setValue(selector: string, value: string) {
const el = (await page.$(selector))! const el = (await page.$(selector))!
await el.evaluate((node: HTMLInputElement) => (node.value = '')) await el.evaluate(node => ((node as HTMLInputElement).value = ''))
await el.type(value) await el.type(value)
} }
async function enterValue(selector: string, value: string) { async function enterValue(selector: string, value: string) {
const el = (await page.$(selector))! const el = (await page.$(selector))!
await el.evaluate((node: HTMLInputElement) => (node.value = '')) await el.evaluate(node => ((node as HTMLInputElement).value = ''))
await el.type(value) await el.type(value)
await el.press('Enter') await el.press('Enter')
} }
@ -88,7 +91,7 @@ export function setupPuppeteer() {
async function clearValue(selector: string) { async function clearValue(selector: string) {
return await page.$eval( return await page.$eval(
selector, selector,
(node: HTMLInputElement) => (node.value = '') node => ((node as HTMLInputElement).value = '')
) )
} }

View File

@ -18,7 +18,7 @@ describe('e2e: markdown', () => {
await page().type('textarea', '\n## foo\n\n- bar\n- baz') await page().type('textarea', '\n## foo\n\n- bar\n- baz')
// assert the output is not updated yet because of debounce // assert the output is not updated yet because of debounce
expect(await html('#editor div')).toBe('<h1 id="hello">hello</h1>\n') expect(await html('#editor div')).toBe('<h1 id="hello">hello</h1>\n')
await page().waitFor(30) await page().waitFor(100)
expect(await html('#editor div')).toBe( expect(await html('#editor div')).toBe(
'<h1 id="hello">hello</h1>\n' + '<h1 id="hello">hello</h1>\n' +
'<h2 id="foo">foo</h2>\n' + '<h2 id="foo">foo</h2>\n' +

View File

@ -7,10 +7,8 @@
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"allowJs": false, "allowJs": false,
"strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"strictNullChecks": true,
"noImplicitAny": true,
"noImplicitThis": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"esModuleInterop": true, "esModuleInterop": true,