wip: add types to refactored runtime-core
This commit is contained in:
parent
3cded86b98
commit
b3f8b5ae0a
@ -1,21 +1,54 @@
|
|||||||
// TODO:
|
// TODO:
|
||||||
// - app context
|
|
||||||
// - component
|
// - component
|
||||||
// - lifecycle
|
// - lifecycle
|
||||||
|
// - app context
|
||||||
|
// - svg
|
||||||
// - refs
|
// - refs
|
||||||
// - reused nodes
|
|
||||||
// - hydration
|
// - hydration
|
||||||
|
// - warning context
|
||||||
|
// - parent chain
|
||||||
|
// - reused nodes (warning)
|
||||||
|
|
||||||
import { Text, Fragment, Empty, createVNode } from './h.js'
|
import {
|
||||||
|
Text,
|
||||||
|
Fragment,
|
||||||
|
Empty,
|
||||||
|
createVNode,
|
||||||
|
VNode,
|
||||||
|
VNodeChildren
|
||||||
|
} from './h.js'
|
||||||
import { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
|
import { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
|
||||||
|
|
||||||
const emptyArr: any[] = []
|
const emptyArr: any[] = []
|
||||||
const emptyObj = {}
|
const emptyObj: { [key: string]: any } = {}
|
||||||
|
|
||||||
const isSameType = (n1, n2) => n1.type === n2.type && n1.key === n2.key
|
function isSameType(n1: VNode, n2: VNode): boolean {
|
||||||
|
return n1.type === n2.type && n1.key === n2.key
|
||||||
|
}
|
||||||
|
|
||||||
export function createRenderer(hostConfig) {
|
export type HostNode = any
|
||||||
|
|
||||||
|
export interface RendererOptions {
|
||||||
|
patchProp(
|
||||||
|
el: HostNode,
|
||||||
|
key: string,
|
||||||
|
value: any,
|
||||||
|
oldValue: any,
|
||||||
|
isSVG: boolean,
|
||||||
|
prevChildren?: VNode[],
|
||||||
|
unmountChildren?: (children: VNode[]) => void
|
||||||
|
): void
|
||||||
|
insert(el: HostNode, parent: HostNode, anchor?: HostNode): void
|
||||||
|
remove(el: HostNode): void
|
||||||
|
createElement(type: string): HostNode
|
||||||
|
createText(text: string): HostNode
|
||||||
|
createComment(text: string): HostNode
|
||||||
|
setText(node: HostNode, text: string): void
|
||||||
|
setElementText(node: HostNode, text: string): void
|
||||||
|
nextSibling(node: HostNode): HostNode | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRenderer(options: RendererOptions) {
|
||||||
const {
|
const {
|
||||||
insert,
|
insert,
|
||||||
remove,
|
remove,
|
||||||
@ -26,9 +59,15 @@ export function createRenderer(hostConfig) {
|
|||||||
setText: hostSetText,
|
setText: hostSetText,
|
||||||
setElementText: hostSetElementText,
|
setElementText: hostSetElementText,
|
||||||
nextSibling: hostNextSibling
|
nextSibling: hostNextSibling
|
||||||
} = hostConfig
|
} = options
|
||||||
|
|
||||||
function patch(n1, n2, container, anchor, optimized) {
|
function patch(
|
||||||
|
n1: VNode | null, // null means this is a mount
|
||||||
|
n2: VNode,
|
||||||
|
container: HostNode,
|
||||||
|
anchor?: HostNode,
|
||||||
|
optimized?: boolean
|
||||||
|
) {
|
||||||
// patching & not same type, unmount old tree
|
// patching & not same type, unmount old tree
|
||||||
if (n1 != null && !isSameType(n1, n2)) {
|
if (n1 != null && !isSameType(n1, n2)) {
|
||||||
anchor = hostNextSibling(n1.el)
|
anchor = hostNextSibling(n1.el)
|
||||||
@ -50,18 +89,28 @@ export function createRenderer(hostConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processText(n1, n2, container, anchor) {
|
function processText(
|
||||||
|
n1: VNode | null,
|
||||||
|
n2: VNode,
|
||||||
|
container: HostNode,
|
||||||
|
anchor?: HostNode
|
||||||
|
) {
|
||||||
if (n1 == null) {
|
if (n1 == null) {
|
||||||
insert((n2.el = hostCreateText(n2.children)), container, anchor)
|
insert((n2.el = hostCreateText(n2.children as string)), container, anchor)
|
||||||
} else {
|
} else {
|
||||||
const el = (n2.el = n1.el)
|
const el = (n2.el = n1.el)
|
||||||
if (n2.children !== n1.children) {
|
if (n2.children !== n1.children) {
|
||||||
hostSetText(el, n2, children)
|
hostSetText(el, n2.children as string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processEmptyNode(n1, n2, container, anchor) {
|
function processEmptyNode(
|
||||||
|
n1: VNode | null,
|
||||||
|
n2: VNode,
|
||||||
|
container: HostNode,
|
||||||
|
anchor?: HostNode
|
||||||
|
) {
|
||||||
if (n1 == null) {
|
if (n1 == null) {
|
||||||
insert((n2.el = hostCreateComment('')), container, anchor)
|
insert((n2.el = hostCreateComment('')), container, anchor)
|
||||||
} else {
|
} else {
|
||||||
@ -69,51 +118,65 @@ export function createRenderer(hostConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processElement(n1, n2, container, anchor, optimized) {
|
function processElement(
|
||||||
|
n1: VNode | null,
|
||||||
|
n2: VNode,
|
||||||
|
container: HostNode,
|
||||||
|
anchor?: HostNode,
|
||||||
|
optimized?: boolean
|
||||||
|
) {
|
||||||
// mount
|
// mount
|
||||||
if (n1 == null) {
|
if (n1 == null) {
|
||||||
mountElement(n2, container, anchor)
|
mountElement(n2, container, anchor)
|
||||||
} else {
|
} else {
|
||||||
patchElement(n1, n2, container, optimized)
|
patchElement(n1, n2, optimized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mountElement(vnode, container, anchor) {
|
function mountElement(vnode: VNode, container: HostNode, anchor?: HostNode) {
|
||||||
const el = (vnode.el = hostCreateElement(vnode.type))
|
const el = (vnode.el = hostCreateElement(vnode.type as string))
|
||||||
if (vnode.props != null) {
|
if (vnode.props != null) {
|
||||||
for (const key in vnode.props) {
|
for (const key in vnode.props) {
|
||||||
hostPatchProp(el, key, vnode.props[key], null)
|
hostPatchProp(el, key, vnode.props[key], null, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof vnode.children === 'string') {
|
if (typeof vnode.children === 'string') {
|
||||||
hostSetElementText(el, vnode.children)
|
hostSetElementText(el, vnode.children)
|
||||||
} else {
|
} else if (vnode.children != null) {
|
||||||
mountChildren(vnode.children, el)
|
mountChildren(vnode.children, el)
|
||||||
}
|
}
|
||||||
insert(el, container, anchor)
|
insert(el, container, anchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
function mountChildren(children, container, anchor, start = 0) {
|
function mountChildren(
|
||||||
|
children: VNodeChildren,
|
||||||
|
container: HostNode,
|
||||||
|
anchor?: HostNode,
|
||||||
|
start: number = 0
|
||||||
|
) {
|
||||||
for (let i = start; i < children.length; i++) {
|
for (let i = start; i < children.length; i++) {
|
||||||
const child = (children[i] = normalizeChild(children[i]))
|
const child = (children[i] = normalizeChild(children[i]))
|
||||||
patch(null, child, container, anchor)
|
patch(null, child, container, anchor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeChild(child) {
|
function normalizeChild(child: any): VNode {
|
||||||
// empty placeholder
|
|
||||||
if (child == null) {
|
if (child == null) {
|
||||||
|
// empty placeholder
|
||||||
return createVNode(Empty)
|
return createVNode(Empty)
|
||||||
} else if (typeof child === 'string' || typeof child === 'number') {
|
|
||||||
return createVNode(Text, null, child + '')
|
|
||||||
} else if (Array.isArray(child)) {
|
} else if (Array.isArray(child)) {
|
||||||
|
// fragment
|
||||||
return createVNode(Fragment, null, child)
|
return createVNode(Fragment, null, child)
|
||||||
|
} else if (typeof child === 'object') {
|
||||||
|
// already vnode
|
||||||
|
return child as VNode
|
||||||
} else {
|
} else {
|
||||||
return child
|
// primitive types
|
||||||
|
return createVNode(Text, null, child + '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchElement(n1, n2, container, optimized) {
|
function patchElement(n1: VNode, n2: VNode, optimized?: boolean) {
|
||||||
const el = (n2.el = n1.el)
|
const el = (n2.el = n1.el)
|
||||||
const { patchFlag, dynamicChildren } = n2
|
const { patchFlag, dynamicChildren } = n2
|
||||||
const oldProps = (n1 && n1.props) || emptyObj
|
const oldProps = (n1 && n1.props) || emptyObj
|
||||||
@ -138,7 +201,7 @@ export function createRenderer(hostConfig) {
|
|||||||
// this flag is matched when the element has dynamic style bindings
|
// this flag is matched when the element has dynamic style bindings
|
||||||
// TODO separate static and dynamic styles?
|
// TODO separate static and dynamic styles?
|
||||||
if (patchFlag & STYLE) {
|
if (patchFlag & STYLE) {
|
||||||
setStyles(el.style, oldProps.style, newProps.style)
|
hostPatchProp(el, 'style', oldProps.style, newProps.style, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// props
|
// props
|
||||||
@ -148,13 +211,22 @@ export function createRenderer(hostConfig) {
|
|||||||
// Note dynamic keys like :[foo]="bar" will cause this optimization to
|
// Note dynamic keys like :[foo]="bar" will cause this optimization to
|
||||||
// bail out and go through a full diff because we need to unset the old key
|
// bail out and go through a full diff because we need to unset the old key
|
||||||
if (patchFlag & PROPS) {
|
if (patchFlag & PROPS) {
|
||||||
const propsToUpdate = n2.dynamicProps
|
// if the flag is present then dynamicProps must be non-null
|
||||||
|
const propsToUpdate = n2.dynamicProps as string[]
|
||||||
for (let i = 0; i < propsToUpdate.length; i++) {
|
for (let i = 0; i < propsToUpdate.length; i++) {
|
||||||
const key = propsToUpdate[i]
|
const key = propsToUpdate[i]
|
||||||
const prev = oldProps[key]
|
const prev = oldProps[key]
|
||||||
const next = newProps[key]
|
const next = newProps[key]
|
||||||
if (prev !== next) {
|
if (prev !== next) {
|
||||||
hostPatchProp(el, key, next, prev)
|
hostPatchProp(
|
||||||
|
el,
|
||||||
|
key,
|
||||||
|
next,
|
||||||
|
prev,
|
||||||
|
false,
|
||||||
|
n1.children as VNode[],
|
||||||
|
unmountChildren
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,18 +236,18 @@ export function createRenderer(hostConfig) {
|
|||||||
// this flag is terminal (i.e. skips children diffing).
|
// this flag is terminal (i.e. skips children diffing).
|
||||||
if (patchFlag & TEXT) {
|
if (patchFlag & TEXT) {
|
||||||
if (n1.children !== n2.children) {
|
if (n1.children !== n2.children) {
|
||||||
hostSetElementText(el, n2.children)
|
hostSetElementText(el, n2.children as string)
|
||||||
}
|
}
|
||||||
return // terminal
|
return // terminal
|
||||||
}
|
}
|
||||||
} else if (!optimized) {
|
} else if (!optimized) {
|
||||||
// unoptimized, full diff
|
// unoptimized, full diff
|
||||||
patchProps(el, oldProps, newProps)
|
patchProps(el, n2, oldProps, newProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dynamicChildren != null) {
|
if (dynamicChildren != null) {
|
||||||
// children fast path
|
// children fast path
|
||||||
const olddynamicChildren = n1.dynamicChildren
|
const olddynamicChildren = n1.dynamicChildren as VNode[]
|
||||||
for (let i = 0; i < dynamicChildren.length; i++) {
|
for (let i = 0; i < dynamicChildren.length; i++) {
|
||||||
patch(olddynamicChildren[i], dynamicChildren[i], el, null, true)
|
patch(olddynamicChildren[i], dynamicChildren[i], el, null, true)
|
||||||
}
|
}
|
||||||
@ -185,36 +257,70 @@ export function createRenderer(hostConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchProps(el, oldProps, newProps) {
|
function patchProps(
|
||||||
|
el: HostNode,
|
||||||
|
vnode: VNode,
|
||||||
|
oldProps: any,
|
||||||
|
newProps: any
|
||||||
|
) {
|
||||||
if (oldProps !== newProps) {
|
if (oldProps !== newProps) {
|
||||||
for (const key in newProps) {
|
for (const key in newProps) {
|
||||||
const next = newProps[key]
|
const next = newProps[key]
|
||||||
const prev = oldProps[key]
|
const prev = oldProps[key]
|
||||||
if (next !== prev) {
|
if (next !== prev) {
|
||||||
hostPatchProp(el, key, next, prev)
|
hostPatchProp(
|
||||||
|
el,
|
||||||
|
key,
|
||||||
|
next,
|
||||||
|
prev,
|
||||||
|
false,
|
||||||
|
vnode.children as VNode[],
|
||||||
|
unmountChildren
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oldProps !== emptyObj) {
|
if (oldProps !== emptyObj) {
|
||||||
for (const key in oldProps) {
|
for (const key in oldProps) {
|
||||||
if (!(key in newProps)) {
|
if (!(key in newProps)) {
|
||||||
hostPatchProp(el, key, null, null)
|
hostPatchProp(
|
||||||
|
el,
|
||||||
|
key,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
vnode.children as VNode[],
|
||||||
|
unmountChildren
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processFragment(n1, n2, container, anchor, optimized) {
|
function processFragment(
|
||||||
|
n1: VNode | null,
|
||||||
|
n2: VNode,
|
||||||
|
container: HostNode,
|
||||||
|
anchor?: HostNode,
|
||||||
|
optimized?: boolean
|
||||||
|
) {
|
||||||
const fragmentAnchor = (n2.el = n1 ? n1.el : document.createComment(''))
|
const fragmentAnchor = (n2.el = n1 ? n1.el : document.createComment(''))
|
||||||
if (n1 == null) {
|
if (n1 == null) {
|
||||||
insert(fragmentAnchor, container, anchor)
|
insert(fragmentAnchor, container, anchor)
|
||||||
mountChildren(n2.children, container, fragmentAnchor)
|
// a fragment can only have array children
|
||||||
|
mountChildren(n2.children as VNodeChildren, container, fragmentAnchor)
|
||||||
} else {
|
} else {
|
||||||
patchChildren(n1, n2, container, fragmentAnchor, optimized)
|
patchChildren(n1, n2, container, fragmentAnchor, optimized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchChildren(n1, n2, container, anchor, optimized) {
|
function patchChildren(
|
||||||
|
n1: VNode | null,
|
||||||
|
n2: VNode,
|
||||||
|
container: HostNode,
|
||||||
|
anchor?: HostNode,
|
||||||
|
optimized?: boolean
|
||||||
|
) {
|
||||||
const c1 = n1 && n1.children
|
const c1 = n1 && n1.children
|
||||||
const c2 = n2.children
|
const c2 = n2.children
|
||||||
|
|
||||||
@ -223,11 +329,24 @@ export function createRenderer(hostConfig) {
|
|||||||
if (patchFlag != null) {
|
if (patchFlag != null) {
|
||||||
if (patchFlag & KEYED) {
|
if (patchFlag & KEYED) {
|
||||||
// this could be either fully-keyed or mixed (some keyed some not)
|
// this could be either fully-keyed or mixed (some keyed some not)
|
||||||
patchKeyedChildren(c1, c2, container, anchor, optimized)
|
// presence of patchFlag means children are guaranteed to be arrays
|
||||||
|
patchKeyedChildren(
|
||||||
|
c1 as VNode[],
|
||||||
|
c2 as VNodeChildren,
|
||||||
|
container,
|
||||||
|
anchor,
|
||||||
|
optimized
|
||||||
|
)
|
||||||
return
|
return
|
||||||
} else if (patchFlag & UNKEYED) {
|
} else if (patchFlag & UNKEYED) {
|
||||||
// unkeyed
|
// unkeyed
|
||||||
patchUnkeyedChildren(c1, c2, container, anchor, optimized)
|
patchUnkeyedChildren(
|
||||||
|
c1 as VNode[],
|
||||||
|
c2 as VNodeChildren,
|
||||||
|
container,
|
||||||
|
anchor,
|
||||||
|
optimized
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,21 +354,34 @@ export function createRenderer(hostConfig) {
|
|||||||
if (typeof c2 === 'string') {
|
if (typeof c2 === 'string') {
|
||||||
// text children fast path
|
// text children fast path
|
||||||
if (Array.isArray(c1)) {
|
if (Array.isArray(c1)) {
|
||||||
unmountChildren(c1, false)
|
unmountChildren(c1 as VNode[])
|
||||||
}
|
}
|
||||||
hostSetElementText(container, c2)
|
hostSetElementText(container, c2)
|
||||||
} else {
|
} else {
|
||||||
if (typeof c1 === 'string') {
|
if (typeof c1 === 'string') {
|
||||||
hostSetElementText('')
|
hostSetElementText(container, '')
|
||||||
|
if (c2 != null) {
|
||||||
mountChildren(c2, container, anchor)
|
mountChildren(c2, container, anchor)
|
||||||
} else {
|
}
|
||||||
|
} else if (Array.isArray(c1)) {
|
||||||
|
if (Array.isArray(c2)) {
|
||||||
// two arrays, cannot assume anything, do full diff
|
// two arrays, cannot assume anything, do full diff
|
||||||
patchKeyedChildren(c1, c2, container, anchor, optimized)
|
patchKeyedChildren(c1 as VNode[], c2, container, anchor, optimized)
|
||||||
|
} else {
|
||||||
|
// c2 is null in this case
|
||||||
|
unmountChildren(c1 as VNode[], 0, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchUnkeyedChildren(c1, c2, container, anchor, optimized) {
|
function patchUnkeyedChildren(
|
||||||
|
c1: VNode[],
|
||||||
|
c2: VNodeChildren,
|
||||||
|
container: HostNode,
|
||||||
|
anchor?: HostNode,
|
||||||
|
optimized?: boolean
|
||||||
|
) {
|
||||||
c1 = c1 || emptyArr
|
c1 = c1 || emptyArr
|
||||||
c2 = c2 || emptyArr
|
c2 = c2 || emptyArr
|
||||||
const oldLength = c1.length
|
const oldLength = c1.length
|
||||||
@ -270,30 +402,43 @@ export function createRenderer(hostConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// can be all-keyed or mixed
|
// can be all-keyed or mixed
|
||||||
function patchKeyedChildren(c1, c2, container, anchor, optimized) {
|
function patchKeyedChildren(
|
||||||
|
c1: VNode[],
|
||||||
|
c2: VNodeChildren,
|
||||||
|
container: HostNode,
|
||||||
|
anchor?: HostNode,
|
||||||
|
optimized?: boolean
|
||||||
|
) {
|
||||||
// TODO
|
// TODO
|
||||||
patchUnkeyedChildren(c1, c2, container, anchor, optimized)
|
patchUnkeyedChildren(c1, c2, container, anchor, optimized)
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmount(vnode, doRemove) {
|
function unmount(vnode: VNode, doRemove?: boolean) {
|
||||||
|
if (vnode.dynamicChildren != null) {
|
||||||
|
unmountChildren(vnode.dynamicChildren)
|
||||||
|
} else if (Array.isArray(vnode.children)) {
|
||||||
|
unmountChildren(vnode.children as VNode[])
|
||||||
|
}
|
||||||
if (doRemove) {
|
if (doRemove) {
|
||||||
if (vnode.type === Fragment) {
|
if (vnode.type === Fragment) {
|
||||||
unmountChildren(vnode.children, 0, doRemove)
|
// raw VNodeChildren is normalized to VNode[] when the VNode is patched
|
||||||
|
unmountChildren(vnode.children as VNode[], 0, doRemove)
|
||||||
}
|
}
|
||||||
remove(vnode.el)
|
remove(vnode.el)
|
||||||
}
|
}
|
||||||
if (Array.isArray(vnode.children)) {
|
|
||||||
unmountChildren(vnode.children)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmountChildren(children, start = 0, doRemove) {
|
function unmountChildren(
|
||||||
|
children: VNode[],
|
||||||
|
start: number = 0,
|
||||||
|
doRemove?: boolean
|
||||||
|
) {
|
||||||
for (let i = start; i < children.length; i++) {
|
for (let i = start; i < children.length; i++) {
|
||||||
unmount(children[i], doRemove)
|
unmount(children[i], doRemove)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return function render(vnode, dom) {
|
return function render(vnode: VNode, dom: HostNode): VNode {
|
||||||
patch(dom._vnode, vnode, dom)
|
patch(dom._vnode, vnode, dom)
|
||||||
return (dom._vnode = vnode)
|
return (dom._vnode = vnode)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ export type VNodeChild = VNode | string | number | null
|
|||||||
export interface VNodeChildren extends Array<VNodeChildren | VNodeChild> {}
|
export interface VNodeChildren extends Array<VNodeChildren | VNodeChild> {}
|
||||||
|
|
||||||
export interface VNode {
|
export interface VNode {
|
||||||
|
el: any
|
||||||
type: VNodeTypes
|
type: VNodeTypes
|
||||||
props: { [key: string]: any } | null
|
props: { [key: string]: any } | null
|
||||||
key: string | number | null
|
key: string | number | null
|
||||||
@ -58,6 +59,7 @@ export function createVNode(
|
|||||||
dynamicProps: string[] | null = null
|
dynamicProps: string[] | null = null
|
||||||
): VNode {
|
): VNode {
|
||||||
const vnode: VNode = {
|
const vnode: VNode = {
|
||||||
|
el: null,
|
||||||
type,
|
type,
|
||||||
props,
|
props,
|
||||||
key: props && props.key,
|
key: props && props.key,
|
||||||
@ -81,4 +83,5 @@ function trackDynamicNode(vnode: VNode) {
|
|||||||
|
|
||||||
export function cloneVNode(vnode: VNode): VNode {
|
export function cloneVNode(vnode: VNode): VNode {
|
||||||
// TODO
|
// TODO
|
||||||
|
return vnode
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
|
export {
|
||||||
|
VNode,
|
||||||
|
openBlock,
|
||||||
|
createBlock,
|
||||||
|
createVNode,
|
||||||
|
Fragment,
|
||||||
|
Text,
|
||||||
|
Empty
|
||||||
|
} from './h'
|
||||||
|
export { createRenderer, RendererOptions } from './createRenderer'
|
||||||
|
export * from '@vue/observer'
|
||||||
export { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
|
export { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
|
||||||
|
|
||||||
export { openBlock, createBlock, createVNode, Fragment, Text, Empty } from './h'
|
|
||||||
|
|
||||||
export { createRenderer } from './createRenderer'
|
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
import { createRenderer, Component } from '@vue/runtime-core'
|
import { createRenderer, VNode } from '@vue/runtime-core'
|
||||||
import { nodeOps } from './nodeOps'
|
import { DOMRendererOptions } from './rendererOptions'
|
||||||
import { patchData } from './patchData'
|
|
||||||
|
|
||||||
const { render: _render } = createRenderer({
|
export const render = createRenderer(DOMRendererOptions) as (
|
||||||
nodeOps,
|
vnode: VNode | null,
|
||||||
patchData
|
|
||||||
})
|
|
||||||
|
|
||||||
type publicRender = (
|
|
||||||
node: {} | null,
|
|
||||||
container: HTMLElement
|
container: HTMLElement
|
||||||
) => Promise<Component | null>
|
) => VNode
|
||||||
export const render = _render as publicRender
|
|
||||||
|
|
||||||
// re-export everything from core
|
// re-export everything from core
|
||||||
// h, Component, observer API, nextTick, flags & types
|
// h, Component, observer API, nextTick, flags & types
|
||||||
|
@ -1,18 +1,12 @@
|
|||||||
import { VNode, ChildrenFlags } from '@vue/runtime-core'
|
|
||||||
|
|
||||||
export function patchDOMProp(
|
export function patchDOMProp(
|
||||||
el: any,
|
el: any,
|
||||||
key: string,
|
key: string,
|
||||||
value: any,
|
value: any,
|
||||||
prevVNode: VNode,
|
prevChildren: any,
|
||||||
unmountChildren: any
|
unmountChildren: any
|
||||||
) {
|
) {
|
||||||
if (key === 'innerHTML' || key === 'textContent') {
|
if ((key === 'innerHTML' || key === 'textContent') && prevChildren != null) {
|
||||||
if (prevVNode && prevVNode.children) {
|
unmountChildren(prevChildren)
|
||||||
unmountChildren(prevVNode.children, prevVNode.childFlags)
|
|
||||||
prevVNode.children = null
|
|
||||||
prevVNode.childFlags = ChildrenFlags.NO_CHILDREN
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
el[key] = value
|
el[key] = value
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { isString } from '@vue/shared'
|
import { isString } from '@vue/shared'
|
||||||
|
|
||||||
export function patchStyle(el: any, prev: any, next: any, data: any) {
|
export function patchStyle(el: any, prev: any, next: any) {
|
||||||
const { style } = el
|
const { style } = el
|
||||||
if (!next) {
|
if (!next) {
|
||||||
el.removeAttribute('style')
|
el.removeAttribute('style')
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import { NodeOps } from '@vue/runtime-core'
|
|
||||||
|
|
||||||
const svgNS = 'http://www.w3.org/2000/svg'
|
|
||||||
|
|
||||||
export const nodeOps: NodeOps = {
|
|
||||||
createElement: (tag: string, isSVG?: boolean): Element =>
|
|
||||||
isSVG ? document.createElementNS(svgNS, tag) : document.createElement(tag),
|
|
||||||
|
|
||||||
createText: (text: string): Text => document.createTextNode(text),
|
|
||||||
|
|
||||||
setText: (node: Text, text: string) => {
|
|
||||||
node.nodeValue = text
|
|
||||||
},
|
|
||||||
|
|
||||||
appendChild: (parent: Node, child: Node) => {
|
|
||||||
parent.appendChild(child)
|
|
||||||
},
|
|
||||||
|
|
||||||
insertBefore: (parent: Node, child: Node, ref: Node) => {
|
|
||||||
parent.insertBefore(child, ref)
|
|
||||||
},
|
|
||||||
|
|
||||||
removeChild: (parent: Node, child: Node) => {
|
|
||||||
parent.removeChild(child)
|
|
||||||
},
|
|
||||||
|
|
||||||
clearContent: (node: Node) => {
|
|
||||||
node.textContent = ''
|
|
||||||
},
|
|
||||||
|
|
||||||
parentNode: (node: Node): Node | null => node.parentNode,
|
|
||||||
|
|
||||||
nextSibling: (node: Node): Node | null => node.nextSibling,
|
|
||||||
|
|
||||||
querySelector: (selector: string): Node | null =>
|
|
||||||
document.querySelector(selector)
|
|
||||||
}
|
|
@ -1,24 +1,19 @@
|
|||||||
import { VNode } from '@vue/runtime-core'
|
|
||||||
import { patchClass } from './modules/class'
|
import { patchClass } from './modules/class'
|
||||||
import { patchStyle } from './modules/style'
|
import { patchStyle } from './modules/style'
|
||||||
import { patchAttr } from './modules/attrs'
|
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'
|
||||||
|
|
||||||
// value, checked, selected & muted
|
export function patchProp(
|
||||||
// plus anything with upperCase letter in it are always patched as properties
|
|
||||||
const domPropsReplaceRE = /^domProps/
|
|
||||||
|
|
||||||
export function patchData(
|
|
||||||
el: Element,
|
el: Element,
|
||||||
key: string,
|
key: string,
|
||||||
prevValue: any,
|
prevValue: any,
|
||||||
nextValue: any,
|
nextValue: any,
|
||||||
prevVNode: VNode,
|
|
||||||
nextVNode: VNode,
|
|
||||||
isSVG: boolean,
|
isSVG: boolean,
|
||||||
unmountChildren: any
|
prevChildren?: VNode[],
|
||||||
|
unmountChildren?: any
|
||||||
) {
|
) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
// special
|
// special
|
||||||
@ -26,19 +21,13 @@ export function patchData(
|
|||||||
patchClass(el, nextValue, isSVG)
|
patchClass(el, nextValue, isSVG)
|
||||||
break
|
break
|
||||||
case 'style':
|
case 'style':
|
||||||
patchStyle(el, prevValue, nextValue, nextVNode.data)
|
patchStyle(el, prevValue, nextValue)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
if (isOn(key)) {
|
if (isOn(key)) {
|
||||||
patchEvent(el, key.slice(2).toLowerCase(), prevValue, nextValue)
|
patchEvent(el, key.slice(2).toLowerCase(), prevValue, nextValue)
|
||||||
} else if (key in el) {
|
} else if (key in el) {
|
||||||
patchDOMProp(
|
patchDOMProp(el, key, nextValue, prevChildren, unmountChildren)
|
||||||
el,
|
|
||||||
key.replace(domPropsReplaceRE, '').toLowerCase(),
|
|
||||||
nextValue,
|
|
||||||
prevVNode,
|
|
||||||
unmountChildren
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
patchAttr(el, key, nextValue, isSVG)
|
patchAttr(el, key, nextValue, isSVG)
|
||||||
}
|
}
|
40
packages/runtime-dom/src/rendererOptions.ts
Normal file
40
packages/runtime-dom/src/rendererOptions.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { RendererOptions } from '@vue/runtime-core'
|
||||||
|
import { patchProp } from './patchProp'
|
||||||
|
|
||||||
|
const svgNS = 'http://www.w3.org/2000/svg'
|
||||||
|
|
||||||
|
export const DOMRendererOptions: RendererOptions = {
|
||||||
|
patchProp,
|
||||||
|
|
||||||
|
insert: (parent: Node, child: Node, anchor?: Node) => {
|
||||||
|
if (anchor != null) {
|
||||||
|
parent.insertBefore(child, anchor)
|
||||||
|
} else {
|
||||||
|
parent.appendChild(child)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: (child: Node) => {
|
||||||
|
const parent = child.parentNode
|
||||||
|
if (parent != null) {
|
||||||
|
parent.removeChild(child)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
createElement: (tag: string, isSVG?: boolean): Element =>
|
||||||
|
isSVG ? document.createElementNS(svgNS, tag) : document.createElement(tag),
|
||||||
|
|
||||||
|
createText: (text: string): Text => document.createTextNode(text),
|
||||||
|
|
||||||
|
createComment: (text: string): Comment => document.createComment(text),
|
||||||
|
|
||||||
|
setText: (node: Text, text: string) => {
|
||||||
|
node.nodeValue = text
|
||||||
|
},
|
||||||
|
|
||||||
|
setElementText: (el: HTMLElement, text: string) => {
|
||||||
|
el.textContent = text
|
||||||
|
},
|
||||||
|
|
||||||
|
nextSibling: (node: Node): Node | null => node.nextSibling
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user