createComponentInstance,
setupStatefulComponent
} from './component'
-import { isString, isArray, EMPTY_OBJ, EMPTY_ARR } from '@vue/shared'
+import {
+ isString,
+ isArray,
+ isFunction,
+ isObject,
+ EMPTY_OBJ,
+ EMPTY_ARR
+} from '@vue/shared'
import { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
import { effect, stop, ReactiveEffectOptions } from '@vue/observer'
setElementText(node: HostNode, text: string): void
parentNode(node: HostNode): HostNode | null
nextSibling(node: HostNode): HostNode | null
+ querySelector(selector: string): HostNode | null
}
export function createRenderer(options: RendererOptions) {
setText: hostSetText,
setElementText: hostSetElementText,
parentNode: hostParentNode,
- nextSibling: hostNextSibling
+ nextSibling: hostNextSibling,
+ querySelector: hostQuerySelector
} = options
function patch(
processFragment(n1, n2, container, anchor, optimized)
break
case Portal:
- // TODO
+ processPortal(n1, n2, container, anchor, optimized)
break
default:
if (isString(type)) {
processElement(n1, n2, container, anchor, optimized)
} else {
+ if (__DEV__ && !isFunction(type) && !isObject(type)) {
+ // TODO warn invalid node type
+ }
processComponent(n1, n2, container, anchor)
}
break
}
}
+ function processPortal(
+ n1: VNode | null,
+ n2: VNode,
+ container: HostNode,
+ anchor?: HostNode,
+ optimized?: boolean
+ ) {
+ const targetSelector = n2.props && n2.props.target
+ if (n1 == null) {
+ const children = n2.children
+ const target = (n2.target = isString(targetSelector)
+ ? hostQuerySelector(targetSelector)
+ : null)
+ if (target != null) {
+ if (isString(children)) {
+ hostSetElementText(target, children)
+ } else if (children != null) {
+ mountChildren(children, target)
+ }
+ } else {
+ // TODO warn missing or invalid target
+ }
+ } else {
+ // update content
+ const target = (n2.target = n1.target)
+ if (n2.patchFlag === TEXT) {
+ hostSetElementText(target, n2.children as string)
+ } else if (!optimized) {
+ patchChildren(n1, n2, target)
+ }
+ // target changed
+ if (targetSelector !== (n1.props && n1.props.target)) {
+ const nextTarget = (n2.target = isString(targetSelector)
+ ? hostQuerySelector(targetSelector)
+ : null)
+ if (nextTarget != null) {
+ // move content
+ const children = n2.children
+ if (isString(children)) {
+ hostSetElementText(target, '')
+ hostSetElementText(nextTarget, children)
+ } else if (children != null) {
+ for (let i = 0; i < children.length; i++) {
+ move(children[i] as VNode, nextTarget, null)
+ }
+ }
+ } else {
+ // TODO warn missing or invalid target
+ }
+ }
+ }
+ // insert an empty node as the placeholder for the portal
+ processEmptyNode(n1, n2, container, anchor)
+ }
+
function processComponent(
n1: VNode | null,
n2: VNode,
patch(
prevTree,
nextTree,
- container || hostParentNode(prevTree.el),
- anchor || getNextHostNode(prevTree)
+ // may have moved
+ hostParentNode(prevTree.el),
+ getNextHostNode(prevTree)
)
if (next != null) {
next.el = nextTree.el
import { RendererOptions } from '@vue/runtime-core'
import { patchProp } from './patchProp'
+const doc = document
const svgNS = 'http://www.w3.org/2000/svg'
export const DOMRendererOptions: RendererOptions = {
},
createElement: (tag: string, isSVG?: boolean): Element =>
- isSVG ? document.createElementNS(svgNS, tag) : document.createElement(tag),
+ isSVG ? doc.createElementNS(svgNS, tag) : doc.createElement(tag),
- createText: (text: string): Text => document.createTextNode(text),
+ createText: (text: string): Text => doc.createTextNode(text),
- createComment: (text: string): Comment => document.createComment(text),
+ createComment: (text: string): Comment => doc.createComment(text),
setText: (node: Text, text: string) => {
node.nodeValue = text
parentNode: (node: Node): Node | null => node.parentNode,
- nextSibling: (node: Node): Node | null => node.nextSibling
+ nextSibling: (node: Node): Node | null => node.nextSibling,
+
+ querySelector: (selector: string): Node | null => doc.querySelector(selector)
}