let uid$1 = 0;
function createAppContext() {
return {
app: null,
config: {
isNativeTag: NO,
performance: false,
globalProperties: {},
optionMergeStrategies: {},
isCustomElement: NO,
errorHandler: undefined,
warnHandler: undefined
},
mixins: [],
components: {},
directives: {},
provides: Object.create(null)
};
}
// TODO: å
¥å£
const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args);
{
injectNativeTagCheck(app);
}
const { mount } = app;
app.mount = (containerOrSelector) => { // æ¹åäºåæ¥çmount
const container = normalizeContainer(containerOrSelector);
if (!container)
return;
const component = app._component;
if (!isFunction(component) && !component.render && !component.template) {
component.template = container.innerHTML;
}
// clear content before mounting
container.innerHTML = '';
const proxy = mount(container);
container.removeAttribute('v-cloak');
container.setAttribute('data-v-app', '');
return proxy;
};
return app;
});
function normalizeContainer(container) {
if (isString(container)) {
const res = document.querySelector(container);
if ( !res) {
warn(`Failed to mount app: mount target selector returned null.`);
}
return res;
}
return container;
}
/**
* createRenderer(options) => baseCreateRenderer(options)
* baseCreateRenderer(...) => {render, hydrate, createApp: createAppAPI(render, hydrate)}
*
*/
function createAppAPI(render, hydrate) {
return function createApp(rootComponent, rootProps = null) {
if (rootProps != null && !isObject(rootProps)) {
warn(`root props passed to app.mount() must be an object.`);
rootProps = null;
}
const context = createAppContext();
const installedPlugins = new Set();
let isMounted = false;
const app = (context.app = {
_uid: uid$1++,
_component: rootComponent,
_props: rootProps,
_container: null,
_context: context,
version,
get config() {
return context.config;
},
set config(v) {
{
warn(`app.config cannot be replaced. Modify individual options instead.`);
}
},
use(plugin, ...options) {
if (installedPlugins.has(plugin)) {
warn(`Plugin has already been applied to target app.`);
}
else if (plugin && isFunction(plugin.install)) {
installedPlugins.add(plugin);
plugin.install(app, ...options);
}
else if (isFunction(plugin)) {
installedPlugins.add(plugin);
plugin(app, ...options);
}
else {
warn(`A plugin must either be a function or an object with an "install" ` +
`function.`);
}
return app;
},
mixin(mixin) {
{
if (!context.mixins.includes(mixin)) {
context.mixins.push(mixin);
}
else {
warn('Mixin has already been applied to target app' +
(mixin.name ? `: ${mixin.name}` : ''));
}
}
return app;
},
component(name, component) {
{
validateComponentName(name, context.config);
}
if (!component) {
return context.components[name];
}
if ( context.components[name]) {
warn(`Component "${name}" has already been registered in target app.`);
}
context.components[name] = component;
return app;
},
directive(name, directive) {
{
validateDirectiveName(name);
}
if (!directive) {
return context.directives[name];
}
if ( context.directives[name]) {
warn(`Directive "${name}" has already been registered in target app.`);
}
context.directives[name] = directive;
return app;
},
mount(rootContainer, isHydrate) {
if (!isMounted) {
const vnode = createVNode(rootComponent, rootProps);
// store app context on the root VNode.
// this will be set on the root instance on initial mount.
vnode.appContext = context;
// HMR root reload
{
context.reload = () => {
render(cloneVNode(vnode), rootContainer);
};
}
if (isHydrate && hydrate) {
hydrate(vnode, rootContainer);
}
else {
render(vnode, rootContainer);
}
isMounted = true;
app._container = rootContainer;
rootContainer.__vue_app__ = app;
{
devtoolsInitApp(app, version);
}
return vnode.component.proxy;
}
else {
warn(`App has already been mounted.\n` +
`If you want to remount the same app, move your app creation logic ` +
`into a factory function and create fresh app instances for each ` +
`mount - e.g. \`const createMyApp = () => createApp(App)\``);
}
},
unmount() {
if (isMounted) {
render(null, app._container);
{
devtoolsUnmountApp(app);
}
}
else {
warn(`Cannot unmount an app that is not mounted.`);
}
},
provide(key, value) {
if ( key in context.provides) {
warn(`App already provides property with key "${String(key)}". ` +
`It will be overwritten with the new value.`);
}
// TypeScript doesn't allow symbols as index type
// https://github.com/Microsoft/TypeScript/issues/24587
context.provides[key] = value;
return app;
}
});
return app;
};
}
// implementation
function baseCreateRenderer(options, createHydrationFns) {
const { insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, forcePatchProp: hostForcePatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, setScopeId: hostSetScopeId = NOOP, cloneNode: hostCloneNode, insertStaticContent: hostInsertStaticContent } = options;
// Note: functions inside this closure should use `const xxx = () => {}`
// style in order to prevent being inlined by minifiers.
const patch = (n1, n2, container, anchor = null, parentComponent = null, parentSuspense = null, isSVG = false, optimized = false) => {
// patching & not same type, unmount old tree
if (n1 && !isSameVNodeType(n1, n2)) {
anchor = getNextHostNode(n1);
unmount(n1, parentComponent, parentSuspense, true);
n1 = null;
}
if (n2.patchFlag === -2 /* BAIL */) {
optimized = false;
n2.dynamicChildren = null;
}
const { type, ref, shapeFlag } = n2;
switch (type) {
case Text:
processText(n1, n2, container, anchor);
break;
case Comment:
processCommentNode(n1, n2, container, anchor);
break;
case Static:
if (n1 == null) {
mountStaticNode(n2, container, anchor, isSVG);
}
else {
patchStaticNode(n1, n2, container, isSVG);
}
break;
case Fragment:
processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
break;
default:
if (shapeFlag & 1 /* ELEMENT */) {
processElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
else if (shapeFlag & 6 /* COMPONENT */) {
processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
else if (shapeFlag & 64 /* TELEPORT */) {
type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals);
}
else if ( shapeFlag & 128 /* SUSPENSE */) {
type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals);
}
else {
warn('Invalid VNode type:', type, `(${typeof type})`);
}
}
// set ref
if (ref != null && parentComponent) {
setRef(ref, n1 && n1.ref, parentComponent, parentSuspense, n2);
}
};
const processText = (n1, n2, container, anchor) => {
if (n1 == null) {
hostInsert((n2.el = hostCreateText(n2.children)), container, anchor);
}
else {
const el = (n2.el = n1.el);
if (n2.children !== n1.children) {
hostSetText(el, n2.children);
}
}
};
const processCommentNode = (n1, n2, container, anchor) => {
if (n1 == null) {
hostInsert((n2.el = hostCreateComment(n2.children || '')), container, anchor);
}
else {
// there's no support for dynamic comments
n2.el = n1.el;
}
};
const mountStaticNode = (n2, container, anchor, isSVG) => {
[n2.el, n2.anchor] = hostInsertStaticContent(n2.children, container, anchor, isSVG);
};
/**
* Dev / HMR only
*/
const patchStaticNode = (n1, n2, container, isSVG) => {
// static nodes are only patched during dev for HMR
if (n2.children !== n1.children) {
const anchor = hostNextSibling(n1.anchor);
// remove existing
removeStaticNode(n1);
[n2.el, n2.anchor] = hostInsertStaticContent(n2.children, container, anchor, isSVG);
}
else {
n2.el = n1.el;
n2.anchor = n1.anchor;
}
};
/**
* Dev / HMR only
*/
const moveStaticNode = (vnode, container, anchor) => {
let cur = vnode.el;
const end = vnode.anchor;
while (cur && cur !== end) {
const next = hostNextSibling(cur);
hostInsert(cur, container, anchor);
cur = next;
}
hostInsert(end, container, anchor);
};
/**
* Dev / HMR only
*/
const removeStaticNode = (vnode) => {
let cur = vnode.el;
while (cur && cur !== vnode.anchor) {
const next = hostNextSibling(cur);
hostRemove(cur);
cur = next;
}
hostRemove(vnode.anchor);
};
const processElement = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {
isSVG = isSVG || n2.type === 'svg';
if (n1 == null) {
mountElement(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
else {
patchElement(n1, n2, parentComponent, parentSuspense, isSVG, optimized);
}
};
const mountElement = (vnode, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {
let el;
let vnodeHook;
const { type, props, shapeFlag, transition, scopeId, patchFlag, dirs } = vnode;
{
el = vnode.el = hostCreateElement(vnode.type, isSVG, props && props.is);
// mount children first, since some props may rely on child content
// being already rendered, e.g. `<select value>`
if (shapeFlag & 8 /* TEXT_CHILDREN */) {
hostSetElementText(el, vnode.children);
}
else if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
mountChildren(vnode.children, el, null, parentComponent, parentSuspense, isSVG && type !== 'foreignObject', optimized || !!vnode.dynamicChildren);
}
if (dirs) {
invokeDirectiveHook(vnode, null, parentComponent, 'created');
}
// props
if (props) {
for (const key in props) {
if (!isReservedProp(key)) {
hostPatchProp(el, key, null, props[key], isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
}
}
if ((vnodeHook = props.onVnodeBeforeMount)) {
invokeVNodeHook(vnodeHook, parentComponent, vnode);
}
}
// scopeId
setScopeId(el, scopeId, vnode, parentComponent);
}
{
Object.defineProperty(el, '__vnode', {
value: vnode,
enumerable: false
});
Object.defineProperty(el, '__vueParentComponent', {
value: parentComponent,
enumerable: false
});
}
if (dirs) {
invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount');
}
// #1583 For inside suspense + suspense not resolved case, enter hook should call when suspense resolved
// #1689 For inside suspense + suspense resolved case, just call it
const needCallTransitionHooks = (!parentSuspense || (parentSuspense && !parentSuspense.pendingBranch)) &&
transition &&
!transition.persisted;
if (needCallTransitionHooks) {
transition.beforeEnter(el);
}
hostInsert(el, container, anchor);
if ((vnodeHook = props && props.onVnodeMounted) ||
needCallTransitionHooks ||
dirs) {
queuePostRenderEffect(() => {
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode);
needCallTransitionHooks && transition.enter(el);
dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted');
}, parentSuspense);
}
};
const setScopeId = (el, scopeId, vnode, parentComponent) => {
if (scopeId) {
hostSetScopeId(el, scopeId);
}
if (parentComponent) {
const treeOwnerId = parentComponent.type.__scopeId;
// vnode's own scopeId and the current patched component's scopeId is
// different - this is a slot content node.
if (treeOwnerId && treeOwnerId !== scopeId) {
hostSetScopeId(el, treeOwnerId + '-s');
}
let subTree = parentComponent.subTree;
if ( subTree.type === Fragment) {
subTree =
filterSingleRoot(subTree.children) || subTree;
}
if (vnode === subTree) {
setScopeId(el, parentComponent.vnode.scopeId, parentComponent.vnode, parentComponent.parent);
}
}
};
const mountChildren = (children, container, anchor, parentComponent, parentSuspense, isSVG, optimized, start = 0) => {
for (let i = start; i < children.length; i++) {
const child = (children[i] = optimized
? cloneIfMounted(children[i])
: normalizeVNode(children[i]));
patch(null, child, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
};
const patchElement = (n1, n2, parentComponent, parentSuspense, isSVG, optimized) => {
const el = (n2.el = n1.el);
let { patchFlag, dynamicChildren, dirs } = n2;
// #1426 take the old vnode's patch flag into account since user may clone a
// compiler-generated vnode, which de-opts to FULL_PROPS
patchFlag |= n1.patchFlag & 16 /* FULL_PROPS */;
const oldProps = n1.props || EMPTY_OBJ;
const newProps = n2.props || EMPTY_OBJ;
let vnodeHook;
if ((vnodeHook = newProps.onVnodeBeforeUpdate)) {
invokeVNodeHook(vnodeHook, parentComponent, n2, n1);
}
if (dirs) {
invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate');
}
if ( isHmrUpdating) {
// HMR updated, force full diff
patchFlag = 0;
optimized = false;
dynamicChildren = null;
}
if (patchFlag > 0) {
// the presence of a patchFlag means this element's render code was
// generated by the compiler and can take the fast path.
// in this path old node and new node are guaranteed to have the same shape
// (i.e. at the exact same position in the source template)
if (patchFlag & 16 /* FULL_PROPS */) {
// element props contain dynamic keys, full diff needed
patchProps(el, n2, oldProps, newProps, parentComponent, parentSuspense, isSVG);
}
else {
// class
// this flag is matched when the element has dynamic class bindings.
if (patchFlag & 2 /* CLASS */) {
if (oldProps.class !== newProps.class) {
hostPatchProp(el, 'class', null, newProps.class, isSVG);
}
}
// style
// this flag is matched when the element has dynamic style bindings
if (patchFlag & 4 /* STYLE */) {
hostPatchProp(el, 'style', oldProps.style, newProps.style, isSVG);
}
// props
// This flag is matched when the element has dynamic prop/attr bindings
// other than class and style. The keys of dynamic prop/attrs are saved for
// faster iteration.
// 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
if (patchFlag & 8 /* PROPS */) {
// if the flag is present then dynamicProps must be non-null
const propsToUpdate = n2.dynamicProps;
for (let i = 0; i < propsToUpdate.length; i++) {
const key = propsToUpdate[i];
const prev = oldProps[key];
const next = newProps[key];
if (next !== prev ||
(hostForcePatchProp && hostForcePatchProp(el, key))) {
hostPatchProp(el, key, prev, next, isSVG, n1.children, parentComponent, parentSuspense, unmountChildren);
}
}
}
}
// text
// This flag is matched when the element has only dynamic text children.
if (patchFlag & 1 /* TEXT */) {
if (n1.children !== n2.children) {
hostSetElementText(el, n2.children);
}
}
}
else if (!optimized && dynamicChildren == null) {
// unoptimized, full diff
patchProps(el, n2, oldProps, newProps, parentComponent, parentSuspense, isSVG);
}
const areChildrenSVG = isSVG && n2.type !== 'foreignObject';
if (dynamicChildren) {
patchBlockChildren(n1.dynamicChildren, dynamicChildren, el, parentComponent, parentSuspense, areChildrenSVG);
if ( parentComponent && parentComponent.type.__hmrId) {
traverseStaticChildren(n1, n2);
}
}
else if (!optimized) {
// full diff
patchChildren(n1, n2, el, null, parentComponent, parentSuspense, areChildrenSVG);
}
if ((vnodeHook = newProps.onVnodeUpdated) || dirs) {
queuePostRenderEffect(() => {
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1);
dirs && invokeDirectiveHook(n2, n1, parentComponent, 'updated');
}, parentSuspense);
}
};
// The fast path for blocks.
const patchBlockChildren = (oldChildren, newChildren, fallbackContainer, parentComponent, parentSuspense, isSVG) => {
for (let i = 0; i < newChildren.length; i++) {
const oldVNode = oldChildren[i];
const newVNode = newChildren[i];
// Determine the container (parent element) for the patch.
const container =
// - In the case of a Fragment, we need to provide the actual parent
// of the Fragment itself so it can move its children.
oldVNode.type === Fragment ||
// - In the case of different nodes, there is going to be a replacement
// which also requires the correct parent container
!isSameVNodeType(oldVNode, newVNode) ||
// - In the case of a component, it could contain anything.
oldVNode.shapeFlag & 6 /* COMPONENT */ ||
oldVNode.shapeFlag & 64 /* TELEPORT */
? hostParentNode(oldVNode.el)
: // In other cases, the parent container is not actually used so we
// just pass the block element here to avoid a DOM parentNode call.
fallbackContainer;
patch(oldVNode, newVNode, container, null, parentComponent, parentSuspense, isSVG, true);
}
};
const patchProps = (el, vnode, oldProps, newProps, parentComponent, parentSuspense, isSVG) => {
if (oldProps !== newProps) {
for (const key in newProps) {
if (isReservedProp(key))
continue;
const next = newProps[key];
const prev = oldProps[key];
if (next !== prev ||
(hostForcePatchProp && hostForcePatchProp(el, key))) {
hostPatchProp(el, key, prev, next, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
}
}
if (oldProps !== EMPTY_OBJ) {
for (const key in oldProps) {
if (!isReservedProp(key) && !(key in newProps)) {
hostPatchProp(el, key, oldProps[key], null, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
}
}
}
}
};
const processFragment = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {
const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateText(''));
const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateText(''));
let { patchFlag, dynamicChildren } = n2;
if (patchFlag > 0) {
optimized = true;
}
if ( isHmrUpdating) {
// HMR updated, force full diff
patchFlag = 0;
optimized = false;
dynamicChildren = null;
}
if (n1 == null) {
hostInsert(fragmentStartAnchor, container, anchor);
hostInsert(fragmentEndAnchor, container, anchor);
// a fragment can only have array children
// since they are either generated by the compiler, or implicitly created
// from arrays.
mountChildren(n2.children, container, fragmentEndAnchor, parentComponent, parentSuspense, isSVG, optimized);
}
else {
if (patchFlag > 0 &&
patchFlag & 64 /* STABLE_FRAGMENT */ &&
dynamicChildren) {
// a stable fragment (template root or <template v-for>) doesn't need to
// patch children order, but it may contain dynamicChildren.
patchBlockChildren(n1.dynamicChildren, dynamicChildren, container, parentComponent, parentSuspense, isSVG);
if ( parentComponent && parentComponent.type.__hmrId) {
traverseStaticChildren(n1, n2);
}
else if (
// #2080 if the stable fragment has a key, it's a <template v-for> that may
// get moved around. Make sure all root level vnodes inherit el.
// #2134 or if it's a component root, it may also get moved around
// as the component is being moved.
n2.key != null ||
(parentComponent && n2 === parentComponent.subTree)) {
traverseStaticChildren(n1, n2, true /* shallow */);
}
}
else {
// keyed / unkeyed, or manual fragments.
// for keyed & unkeyed, since they are compiler generated from v-for,
// each child is guaranteed to be a block so the fragment will never
// have dynamicChildren.
patchChildren(n1, n2, container, fragmentEndAnchor, parentComponent, parentSuspense, isSVG, optimized);
}
}
};
const processComponent = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {
if (n1 == null) {
if (n2.shapeFlag & 512 /* COMPONENT_KEPT_ALIVE */) {
parentComponent.ctx.activate(n2, container, anchor, isSVG, optimized);
}
else {
mountComponent(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
}
else {
updateComponent(n1, n2, optimized);
}
};
const mountComponent = (initialVNode, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {
const instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent, parentSuspense));
if ( instance.type.__hmrId) {
registerHMR(instance);
}
{
pushWarningContext(initialVNode);
startMeasure(instance, `mount`);
}
// inject renderer internals for keepAlive
if (isKeepAlive(initialVNode)) {
instance.ctx.renderer = internals;
}
// resolve props and slots for setup context
{
startMeasure(instance, `init`);
}
setupComponent(instance);
{
endMeasure(instance, `init`);
}
// setup() is async. This component relies on async logic to be resolved
// before proceeding
if ( instance.asyncDep) {
parentSuspense && parentSuspense.registerDep(instance, setupRenderEffect);
// Give it a placeholder if this is not hydration
// TODO handle self-defined fallback
if (!initialVNode.el) {
const placeholder = (instance.subTree = createVNode(Comment));
processCommentNode(null, placeholder, container, anchor);
}
return;
}
setupRenderEffect(instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized);
{
popWarningContext();
endMeasure(instance, `mount`);
}
};
const updateComponent = (n1, n2, optimized) => {
const instance = (n2.component = n1.component);
if (shouldUpdateComponent(n1, n2, optimized)) {
if (
instance.asyncDep &&
!instance.asyncResolved) {
// async & still pending - just update props and slots
// since the component's reactive effect for render isn't set-up yet
{
pushWarningContext(n2);
}
updateComponentPreRender(instance, n2, optimized);
{
popWarningContext();
}
return;
}
else {
// normal update
instance.next = n2;
// in case the child component is also queued, remove it to avoid
// double updating the same child component in the same flush.
invalidateJob(instance.update);
// instance.update is the reactive effect runner.
instance.update();
}
}
else {
// no update needed. just copy over properties
n2.component = n1.component;
n2.el = n1.el;
instance.vnode = n2;
}
};
const setupRenderEffect = (instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized) => {
// create reactive effect for rendering
instance.update = effect(function componentEffect() {
if (!instance.isMounted) {
let vnodeHook;
const { el, props } = initialVNode;
const { bm, m, parent } = instance;
// beforeMount hook
if (bm) {
invokeArrayFns(bm);
}
// onVnodeBeforeMount
if ((vnodeHook = props && props.onVnodeBeforeMount)) {
invokeVNodeHook(vnodeHook, parent, initialVNode);
}
// render
{
startMeasure(instance, `render`);
}
const subTree = (instance.subTree = renderComponentRoot(instance));
{
endMeasure(instance, `render`);
}
if (el && hydrateNode) {
{
startMeasure(instance, `hydrate`);
}
// vnode has adopted host node - perform hydration instead of mount.
hydrateNode(initialVNode.el, subTree, instance, parentSuspense);
{
endMeasure(instance, `hydrate`);
}
}
else {
{
startMeasure(instance, `patch`);
}
patch(null, subTree, container, anchor, instance, parentSuspense, isSVG);
{
endMeasure(instance, `patch`);
}
initialVNode.el = subTree.el;
}
// mounted hook
if (m) {
queuePostRenderEffect(m, parentSuspense);
}
// onVnodeMounted
if ((vnodeHook = props && props.onVnodeMounted)) {
queuePostRenderEffect(() => {
invokeVNodeHook(vnodeHook, parent, initialVNode);
}, parentSuspense);
}
// activated hook for keep-alive roots.
// #1742 activated hook must be accessed after first render
// since the hook may be injected by a child keep-alive
const { a } = instance;
if (a &&
initialVNode.shapeFlag & 256 /* COMPONENT_SHOULD_KEEP_ALIVE */) {
queuePostRenderEffect(a, parentSuspense);
}
instance.isMounted = true;
}
else {
// updateComponent
// This is triggered by mutation of component's own state (next: null)
// OR parent calling processComponent (next: VNode)
let { next, bu, u, parent, vnode } = instance;
let originNext = next;
let vnodeHook;
{
pushWarningContext(next || instance.vnode);
}
if (next) {
updateComponentPreRender(instance, next, optimized);
}
else {
next = vnode;
}
next.el = vnode.el;
// beforeUpdate hook
if (bu) {
invokeArrayFns(bu);
}
// onVnodeBeforeUpdate
if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
invokeVNodeHook(vnodeHook, parent, next, vnode);
}
// render
{
startMeasure(instance, `render`);
}
const nextTree = renderComponentRoot(instance);
{
endMeasure(instance, `render`);
}
const prevTree = instance.subTree;
instance.subTree = nextTree;
// reset refs
// only needed if previous patch had refs
if (instance.refs !== EMPTY_OBJ) {
instance.refs = {};
}
{
startMeasure(instance, `patch`);
}
patch(prevTree, nextTree,
// parent may have changed if it's in a teleport
hostParentNode(prevTree.el),
// anchor may have changed if it's in a fragment
getNextHostNode(prevTree), instance, parentSuspense, isSVG);
{
endMeasure(instance, `patch`);
}
next.el = nextTree.el;
if (originNext === null) {
// self-triggered update. In case of HOC, update parent component
// vnode el. HOC is indicated by parent instance's subTree pointing
// to child component's vnode
updateHOCHostEl(instance, nextTree.el);
}
// updated hook
if (u) {
queuePostRenderEffect(u, parentSuspense);
}
// onVnodeUpdated
if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
queuePostRenderEffect(() => {
invokeVNodeHook(vnodeHook, parent, next, vnode);
}, parentSuspense);
}
{
devtoolsComponentUpdated(instance);
}
{
popWarningContext();
}
}
}, createDevEffectOptions(instance) );
};
const updateComponentPreRender = (instance, nextVNode, optimized) => {
nextVNode.component = instance;
const prevProps = instance.vnode.props;
instance.vnode = nextVNode;
instance.next = null;
updateProps(instance, nextVNode.props, prevProps, optimized);
updateSlots(instance, nextVNode.children);
// props update may have triggered pre-flush watchers.
// flush them before the render update.
flushPreFlushCbs(undefined, instance.update);
};
const patchChildren = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized = false) => {
const c1 = n1 && n1.children;
const prevShapeFlag = n1 ? n1.shapeFlag : 0;
const c2 = n2.children;
const { patchFlag, shapeFlag } = n2;
// fast path
if (patchFlag > 0) {
if (patchFlag & 128 /* KEYED_FRAGMENT */) {
// this could be either fully-keyed or mixed (some keyed some not)
// presence of patchFlag means children are guaranteed to be arrays
patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
return;
}
else if (patchFlag & 256 /* UNKEYED_FRAGMENT */) {
// unkeyed
patchUnkeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
return;
}
}
// children has 3 possibilities: text, array or no children.
if (shapeFlag & 8 /* TEXT_CHILDREN */) {
// text children fast path
if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
unmountChildren(c1, parentComponent, parentSuspense);
}
if (c2 !== c1) {
hostSetElementText(container, c2);
}
}
else {
if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
// prev children was array
if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
// two arrays, cannot assume anything, do full diff
patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
else {
// no new children, just unmount old
unmountChildren(c1, parentComponent, parentSuspense, true);
}
}
else {
// prev children was text OR null
// new children is array OR null
if (prevShapeFlag & 8 /* TEXT_CHILDREN */) {
hostSetElementText(container, '');
}
// mount new if array
if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
mountChildren(c2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
}
}
};
const patchUnkeyedChildren = (c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {
c1 = c1 || EMPTY_ARR;
c2 = c2 || EMPTY_ARR;
const oldLength = c1.length;
const newLength = c2.length;
const commonLength = Math.min(oldLength, newLength);
let i;
for (i = 0; i < commonLength; i++) {
const nextChild = (c2[i] = optimized
? cloneIfMounted(c2[i])
: normalizeVNode(c2[i]));
patch(c1[i], nextChild, container, null, parentComponent, parentSuspense, isSVG, optimized);
}
if (oldLength > newLength) {
// remove old
unmountChildren(c1, parentComponent, parentSuspense, true, commonLength);
}
else {
// mount new
mountChildren(c2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, commonLength);
}
};
// can be all-keyed or mixed
const patchKeyedChildren = (c1, c2, container, parentAnchor, parentComponent, parentSuspense, isSVG, optimized) => {
let i = 0;
const l2 = c2.length;
let e1 = c1.length - 1; // prev ending index
let e2 = l2 - 1; // next ending index
// 1. sync from start
// (a b) c
// (a b) d e
while (i <= e1 && i <= e2) {
const n1 = c1[i];
const n2 = (c2[i] = optimized
? cloneIfMounted(c2[i])
: normalizeVNode(c2[i]));
if (isSameVNodeType(n1, n2)) {
patch(n1, n2, container, null, parentComponent, parentSuspense, isSVG, optimized);
}
else {
break;
}
i++;
}
// 2. sync from end
// a (b c)
// d e (b c)
while (i <= e1 && i <= e2) {
const n1 = c1[e1];
const n2 = (c2[e2] = optimized
? cloneIfMounted(c2[e2])
: normalizeVNode(c2[e2]));
if (isSameVNodeType(n1, n2)) {
patch(n1, n2, container, null, parentComponent, parentSuspense, isSVG, optimized);
}
else {
break;
}
e1--;
e2--;
}
// 3. common sequence + mount
// (a b)
// (a b) c
// i = 2, e1 = 1, e2 = 2
// (a b)
// c (a b)
// i = 0, e1 = -1, e2 = 0
if (i > e1) {
if (i <= e2) {
const nextPos = e2 + 1;
const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;
while (i <= e2) {
patch(null, (c2[i] = optimized
? cloneIfMounted(c2[i])
: normalizeVNode(c2[i])), container, anchor, parentComponent, parentSuspense, isSVG);
i++;
}
}
}
// 4. common sequence + unmount
// (a b) c
// (a b)
// i = 2, e1 = 2, e2 = 1
// a (b c)
// (b c)
// i = 0, e1 = 0, e2 = -1
else if (i > e2) {
while (i <= e1) {
unmount(c1[i], parentComponent, parentSuspense, true);
i++;
}
}
// 5. unknown sequence
// [i ... e1 + 1]: a b [c d e] f g
// [i ... e2 + 1]: a b [e d c h] f g
// i = 2, e1 = 4, e2 = 5
else {
const s1 = i; // prev starting index
const s2 = i; // next starting index
// 5.1 build key:index map for newChildren
const keyToNewIndexMap = new Map();
for (i = s2; i <= e2; i++) {
const nextChild = (c2[i] = optimized
? cloneIfMounted(c2[i])
: normalizeVNode(c2[i]));
if (nextChild.key != null) {
if ( keyToNewIndexMap.has(nextChild.key)) {
warn(`Duplicate keys found during update:`, JSON.stringify(nextChild.key), `Make sure keys are unique.`);
}
keyToNewIndexMap.set(nextChild.key, i);
}
}
// 5.2 loop through old children left to be patched and try to patch
// matching nodes & remove nodes that are no longer present
let j;
let patched = 0;
const toBePatched = e2 - s2 + 1;
let moved = false;
// used to track whether any node has moved
let maxNewIndexSoFar = 0;
// works as Map<newIndex, oldIndex>
// Note that oldIndex is offset by +1
// and oldIndex = 0 is a special value indicating the new node has
// no corresponding old node.
// used for determining longest stable subsequence
const newIndexToOldIndexMap = new Array(toBePatched);
for (i = 0; i < toBePatched; i++)
newIndexToOldIndexMap[i] = 0;
for (i = s1; i <= e1; i++) {
const prevChild = c1[i];
if (patched >= toBePatched) {
// all new children have been patched so this can only be a removal
unmount(prevChild, parentComponent, parentSuspense, true);
continue;
}
let newIndex;
if (prevChild.key != null) {
newIndex = keyToNewIndexMap.get(prevChild.key);
}
else {
// key-less node, try to locate a key-less node of the same type
for (j = s2; j <= e2; j++) {
if (newIndexToOldIndexMap[j - s2] === 0 &&
isSameVNodeType(prevChild, c2[j])) {
newIndex = j;
break;
}
}
}
if (newIndex === undefined) {
unmount(prevChild, parentComponent, parentSuspense, true);
}
else {
newIndexToOldIndexMap[newIndex - s2] = i + 1;
if (newIndex >= maxNewIndexSoFar) {
maxNewIndexSoFar = newIndex;
}
else {
moved = true;
}
patch(prevChild, c2[newIndex], container, null, parentComponent, parentSuspense, isSVG, optimized);
patched++;
}
}
// 5.3 move and mount
// generate longest stable subsequence only when nodes have moved
const increasingNewIndexSequence = moved
? getSequence(newIndexToOldIndexMap)
: EMPTY_ARR;
j = increasingNewIndexSequence.length - 1;
// looping backwards so that we can use last patched node as anchor
for (i = toBePatched - 1; i >= 0; i--) {
const nextIndex = s2 + i;
const nextChild = c2[nextIndex];
const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;
if (newIndexToOldIndexMap[i] === 0) {
// mount new
patch(null, nextChild, container, anchor, parentComponent, parentSuspense, isSVG);
}
else if (moved) {
// move if:
// There is no stable subsequence (e.g. a reverse)
// OR current node is not among the stable sequence
if (j < 0 || i !== increasingNewIndexSequence[j]) {
move(nextChild, container, anchor, 2 /* REORDER */);
}
else {
j--;
}
}
}
}
};
const move = (vnode, container, anchor, moveType, parentSuspense = null) => {
const { el, type, transition, children, shapeFlag } = vnode;
if (shapeFlag & 6 /* COMPONENT */) {
move(vnode.component.subTree, container, anchor, moveType);
return;
}
if ( shapeFlag & 128 /* SUSPENSE */) {
vnode.suspense.move(container, anchor, moveType);
return;
}
if (shapeFlag & 64 /* TELEPORT */) {
type.move(vnode, container, anchor, internals);
return;
}
if (type === Fragment) {
hostInsert(el, container, anchor);
for (let i = 0; i < children.length; i++) {
move(children[i], container, anchor, moveType);
}
hostInsert(vnode.anchor, container, anchor);
return;
}
// static node move can only happen when force updating HMR
if ( type === Static) {
moveStaticNode(vnode, container, anchor);
return;
}
// single nodes
const needTransition = moveType !== 2 /* REORDER */ &&
shapeFlag & 1 /* ELEMENT */ &&
transition;
if (needTransition) {
if (moveType === 0 /* ENTER */) {
transition.beforeEnter(el);
hostInsert(el, container, anchor);
queuePostRenderEffect(() => transition.enter(el), parentSuspense);
}
else {
const { leave, delayLeave, afterLeave } = transition;
const remove = () => hostInsert(el, container, anchor);
const performLeave = () => {
leave(el, () => {
remove();
afterLeave && afterLeave();
});
};
if (delayLeave) {
delayLeave(el, remove, performLeave);
}
else {
performLeave();
}
}
}
else {
hostInsert(el, container, anchor);
}
};
const unmount = (vnode, parentComponent, parentSuspense, doRemove = false) => {
const { type, props, ref, children, dynamicChildren, shapeFlag, patchFlag, dirs } = vnode;
// unset ref
if (ref != null && parentComponent) {
setRef(ref, null, parentComponent, parentSuspense, null);
}
if (shapeFlag & 256 /* COMPONENT_SHOULD_KEEP_ALIVE */) {
parentComponent.ctx.deactivate(vnode);
return;
}
const shouldInvokeDirs = shapeFlag & 1 /* ELEMENT */ && dirs;
let vnodeHook;
if ((vnodeHook = props && props.onVnodeBeforeUnmount)) {
invokeVNodeHook(vnodeHook, parentComponent, vnode);
}
if (shapeFlag & 6 /* COMPONENT */) {
unmountComponent(vnode.component, parentSuspense, doRemove);
}
else {
if ( shapeFlag & 128 /* SUSPENSE */) {
vnode.suspense.unmount(parentSuspense, doRemove);
return;
}
if (shouldInvokeDirs) {
invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount');
}
if (dynamicChildren &&
// #1153: fast path should not be taken for non-stable (v-for) fragments
(type !== Fragment ||
(patchFlag > 0 && patchFlag & 64 /* STABLE_FRAGMENT */))) {
// fast path for block nodes: only need to unmount dynamic children.
unmountChildren(dynamicChildren, parentComponent, parentSuspense);
}
else if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
unmountChildren(children, parentComponent, parentSuspense);
}
// an unmounted teleport should always remove its children
if (shapeFlag & 64 /* TELEPORT */) {
vnode.type.remove(vnode, internals);
}
if (doRemove) {
remove(vnode);
}
}
if ((vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) {
queuePostRenderEffect(() => {
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode);
shouldInvokeDirs &&
invokeDirectiveHook(vnode, null, parentComponent, 'unmounted');
}, parentSuspense);
}
};
const remove = vnode => {
const { type, el, anchor, transition } = vnode;
if (type === Fragment) {
removeFragment(el, anchor);
return;
}
if ( type === Static) {
removeStaticNode(vnode);
return;
}
const performRemove = () => {
hostRemove(el);
if (transition && !transition.persisted && transition.afterLeave) {
transition.afterLeave();
}
};
if (vnode.shapeFlag & 1 /* ELEMENT */ &&
transition &&
!transition.persisted) {
const { leave, delayLeave } = transition;
const performLeave = () => leave(el, performRemove);
if (delayLeave) {
delayLeave(vnode.el, performRemove, performLeave);
}
else {
performLeave();
}
}
else {
performRemove();
}
};
const removeFragment = (cur, end) => {
// For fragments, directly remove all contained DOM nodes.
// (fragment child nodes cannot have transition)
let next;
while (cur !== end) {
next = hostNextSibling(cur);
hostRemove(cur);
cur = next;
}
hostRemove(end);
};
const unmountComponent = (instance, parentSuspense, doRemove) => {
if ( instance.type.__hmrId) {
unregisterHMR(instance);
}
const { bum, effects, update, subTree, um } = instance;
// beforeUnmount hook
if (bum) {
invokeArrayFns(bum);
}
if (effects) {
for (let i = 0; i < effects.length; i++) {
stop(effects[i]);
}
}
// update may be null if a component is unmounted before its async
// setup has resolved.
if (update) {
stop(update);
unmount(subTree, instance, parentSuspense, doRemove);
}
// unmounted hook
if (um) {
queuePostRenderEffect(um, parentSuspense);
}
queuePostRenderEffect(() => {
instance.isUnmounted = true;
}, parentSuspense);
// A component with async dep inside a pending suspense is unmounted before
// its async dep resolves. This should remove the dep from the suspense, and
// cause the suspense to resolve immediately if that was the last dep.
if (
parentSuspense &&
parentSuspense.pendingBranch &&
!parentSuspense.isUnmounted &&
instance.asyncDep &&
!instance.asyncResolved &&
instance.suspenseId === parentSuspense.pendingId) {
parentSuspense.deps--;
if (parentSuspense.deps === 0) {
parentSuspense.resolve();
}
}
{
devtoolsComponentRemoved(instance);
}
};
const unmountChildren = (children, parentComponent, parentSuspense, doRemove = false, start = 0) => {
for (let i = start; i < children.length; i++) {
unmount(children[i], parentComponent, parentSuspense, doRemove);
}
};
const getNextHostNode = vnode => {
if (vnode.shapeFlag & 6 /* COMPONENT */) {
return getNextHostNode(vnode.component.subTree);
}
if ( vnode.shapeFlag & 128 /* SUSPENSE */) {
return vnode.suspense.next();
}
return hostNextSibling((vnode.anchor || vnode.el));
};
/**
* #1156
* When a component is HMR-enabled, we need to make sure that all static nodes
* inside a block also inherit the DOM element from the previous tree so that
* HMR updates (which are full updates) can retrieve the element for patching.
*
* #2080
* Inside keyed `template` fragment static children, if a fragment is moved,
* the children will always moved so that need inherit el form previous nodes
* to ensure correct moved position.
*/
const traverseStaticChildren = (n1, n2, shallow = false) => {
const ch1 = n1.children;
const ch2 = n2.children;
if (isArray(ch1) && isArray(ch2)) {
for (let i = 0; i < ch1.length; i++) {
// this is only called in the optimized path so array children are
// guaranteed to be vnodes
const c1 = ch1[i];
const c2 = (ch2[i] = cloneIfMounted(ch2[i]));
if (c2.shapeFlag & 1 /* ELEMENT */ && !c2.dynamicChildren) {
if (c2.patchFlag <= 0 || c2.patchFlag === 32 /* HYDRATE_EVENTS */) {
c2.el = c1.el;
}
if (!shallow)
traverseStaticChildren(c1, c2);
}
if ( c2.type === Comment) {
c2.el = c1.el;
}
}
}
};
const render = (vnode, container) => {
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true);
}
}
else {
patch(container._vnode || null, vnode, container);
}
flushPostFlushCbs();
container._vnode = vnode;
};
const internals = {
p: patch,
um: unmount,
m: move,
r: remove,
mt: mountComponent,
mc: mountChildren,
pc: patchChildren,
pbc: patchBlockChildren,
n: getNextHostNode,
o: options
};
let hydrate;
let hydrateNode;
if (createHydrationFns) {
[hydrate, hydrateNode] = createHydrationFns(internals);
}
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
};
}