UNPKG

@vue/runtime-core

Version:
1,382 lines (1,367 loc) 169 kB
import { pauseTracking, resumeTracking, isRef, toRaw, isReactive, unlock, lock, effect, stop, reactive, shallowReadonly, computed as computed$1 } from '@vue/reactivity'; export { effect, isReactive, isReadonly, isRef, markNonReactive, markReadonly, reactive, readonly, ref, toRaw, toRefs } from '@vue/reactivity'; // Make a map and return a function for checking if a key // is in that map. // // IMPORTANT: all calls of this function must be prefixed with /*#__PURE__*/ // So that rollup can tree-shake them if necessary. function makeMap(str, expectsLowerCase) { const map = Object.create(null); const list = str.split(','); for (let i = 0; i < list.length; i++) { map[list[i]] = true; } return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val]; } // Patch flags are optimization hints generated by the compiler. // when a block with dynamicChildren is encountered during diff, the algorithm // enters "optimized mode". In this mode, we know that the vdom is produced by // a render function generated by the compiler, so the algorithm only needs to // handle updates explicitly marked by these patch flags. // runtime object for public consumption const PublicPatchFlags = { TEXT: 1 /* TEXT */, CLASS: 2 /* CLASS */, STYLE: 4 /* STYLE */, PROPS: 8 /* PROPS */, NEED_PATCH: 32 /* NEED_PATCH */, FULL_PROPS: 16 /* FULL_PROPS */, KEYED_FRAGMENT: 128 /* KEYED_FRAGMENT */, UNKEYED_FRAGMENT: 256 /* UNKEYED_FRAGMENT */, DYNAMIC_SLOTS: 512 /* DYNAMIC_SLOTS */, BAIL: -1 /* BAIL */ }; const EMPTY_OBJ = (process.env.NODE_ENV !== 'production') ? Object.freeze({}) : {}; const EMPTY_ARR = []; const NOOP = () => { }; /** * Always return false. */ const NO = () => false; const extend = (a, b) => { for (const key in b) { a[key] = b[key]; } return a; }; const hasOwnProperty = Object.prototype.hasOwnProperty; const hasOwn = (val, key) => hasOwnProperty.call(val, key); const isArray = Array.isArray; const isFunction = (val) => typeof val === 'function'; const isString = (val) => typeof val === 'string'; const isObject = (val) => val !== null && typeof val === 'object'; function isPromise(val) { return isObject(val) && isFunction(val.then) && isFunction(val.catch); } const objectToString = Object.prototype.toString; const toTypeString = (value) => objectToString.call(value); function toRawType(value) { return toTypeString(value).slice(8, -1); } const isPlainObject = (val) => toTypeString(val) === '[object Object]'; const isReservedProp = /*#__PURE__*/ makeMap('key,ref,' + 'onVnodeBeforeMount,onVnodeMounted,' + 'onVnodeBeforeUpdate,onVnodeUpdated,' + 'onVnodeBeforeUnmount,onVnodeUnmounted'); function cacheStringFunction(fn) { const cache = Object.create(null); return ((str) => { const hit = cache[str]; return hit || (cache[str] = fn(str)); }); } const camelizeRE = /-(\w)/g; const camelize = cacheStringFunction((str) => { return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')); }); const hyphenateRE = /\B([A-Z])/g; const hyphenate = cacheStringFunction((str) => { return str.replace(hyphenateRE, '-$1').toLowerCase(); }); const capitalize = cacheStringFunction((str) => { return str.charAt(0).toUpperCase() + str.slice(1); }); // compare whether a value has changed, accounting for NaN. const hasChanged = (value, oldValue) => value !== oldValue && (value === value || oldValue === oldValue); const ErrorTypeStrings = { ["bc" /* BEFORE_CREATE */]: 'beforeCreate hook', ["c" /* CREATED */]: 'created hook', ["bm" /* BEFORE_MOUNT */]: 'beforeMount hook', ["m" /* MOUNTED */]: 'mounted hook', ["bu" /* BEFORE_UPDATE */]: 'beforeUpdate hook', ["u" /* UPDATED */]: 'updated', ["bum" /* BEFORE_UNMOUNT */]: 'beforeUnmount hook', ["um" /* UNMOUNTED */]: 'unmounted hook', ["a" /* ACTIVATED */]: 'activated hook', ["da" /* DEACTIVATED */]: 'deactivated hook', ["ec" /* ERROR_CAPTURED */]: 'errorCaptured hook', ["rtc" /* RENDER_TRACKED */]: 'renderTracked hook', ["rtg" /* RENDER_TRIGGERED */]: 'renderTriggered hook', [0 /* SETUP_FUNCTION */]: 'setup function', [1 /* RENDER_FUNCTION */]: 'render function', [2 /* WATCH_GETTER */]: 'watcher getter', [3 /* WATCH_CALLBACK */]: 'watcher callback', [4 /* WATCH_CLEANUP */]: 'watcher cleanup function', [5 /* NATIVE_EVENT_HANDLER */]: 'native event handler', [6 /* COMPONENT_EVENT_HANDLER */]: 'component event handler', [7 /* DIRECTIVE_HOOK */]: 'directive hook', [8 /* TRANSITION_HOOK */]: 'transition hook', [9 /* APP_ERROR_HANDLER */]: 'app errorHandler', [10 /* APP_WARN_HANDLER */]: 'app warnHandler', [11 /* FUNCTION_REF */]: 'ref function', [12 /* SCHEDULER */]: 'scheduler flush. This is likely a Vue internals bug. ' + 'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue' }; function callWithErrorHandling(fn, instance, type, args) { let res; try { res = args ? fn(...args) : fn(); } catch (err) { handleError(err, instance, type); } return res; } function callWithAsyncErrorHandling(fn, instance, type, args) { if (isFunction(fn)) { const res = callWithErrorHandling(fn, instance, type, args); if (res != null && !res._isVue && isPromise(res)) { res.catch((err) => { handleError(err, instance, type); }); } return res; } for (let i = 0; i < fn.length; i++) { callWithAsyncErrorHandling(fn[i], instance, type, args); } } function handleError(err, instance, type) { const contextVNode = instance ? instance.vnode : null; if (instance) { let cur = instance.parent; // the exposed instance is the render proxy to keep it consistent with 2.x const exposedInstance = instance.proxy; // in production the hook receives only the error code const errorInfo = (process.env.NODE_ENV !== 'production') ? ErrorTypeStrings[type] : type; while (cur) { const errorCapturedHooks = cur.ec; if (errorCapturedHooks !== null) { for (let i = 0; i < errorCapturedHooks.length; i++) { if (errorCapturedHooks[i](err, exposedInstance, errorInfo)) { return; } } } cur = cur.parent; } // app-level handling const appErrorHandler = instance.appContext.config.errorHandler; if (appErrorHandler) { callWithErrorHandling(appErrorHandler, null, 9 /* APP_ERROR_HANDLER */, [err, exposedInstance, errorInfo]); return; } } logError(err, type, contextVNode); } // Test-only toggle for testing the unhandled warning behavior let forceRecover = false; function logError(err, type, contextVNode) { // default behavior is crash in prod & test, recover in dev. if ((process.env.NODE_ENV !== 'production') && (forceRecover || !(process.env.NODE_ENV === 'test'))) { const info = ErrorTypeStrings[type]; if (contextVNode) { pushWarningContext(contextVNode); } warn(`Unhandled error${info ? ` during execution of ${info}` : ``}`); console.error(err); if (contextVNode) { popWarningContext(); } } else { throw err; } } const stack = []; function pushWarningContext(vnode) { stack.push(vnode); } function popWarningContext() { stack.pop(); } function warn(msg, ...args) { // avoid props formatting or warn handler tracking deps that might be mutated // during patch, leading to infinite recursion. pauseTracking(); const instance = stack.length ? stack[stack.length - 1].component : null; const appWarnHandler = instance && instance.appContext.config.warnHandler; const trace = getComponentTrace(); if (appWarnHandler) { callWithErrorHandling(appWarnHandler, instance, 10 /* APP_WARN_HANDLER */, [ msg + args.join(''), instance && instance.proxy, trace .map(({ vnode }) => `at <${formatComponentName(vnode)}>`) .join('\n'), trace ]); } else { const warnArgs = [`[Vue warn]: ${msg}`, ...args]; if (trace.length && // avoid spamming console during tests !(process.env.NODE_ENV === 'test')) { warnArgs.push(`\n`, ...formatTrace(trace)); } console.warn(...warnArgs); } resumeTracking(); } function getComponentTrace() { let currentVNode = stack[stack.length - 1]; if (!currentVNode) { return []; } // we can't just use the stack because it will be incomplete during updates // that did not start from the root. Re-construct the parent chain using // instance parent pointers. const normalizedStack = []; while (currentVNode) { const last = normalizedStack[0]; if (last && last.vnode === currentVNode) { last.recurseCount++; } else { normalizedStack.push({ vnode: currentVNode, recurseCount: 0 }); } const parentInstance = currentVNode.component .parent; currentVNode = parentInstance && parentInstance.vnode; } return normalizedStack; } function formatTrace(trace) { const logs = []; trace.forEach((entry, i) => { logs.push(...(i === 0 ? [] : [`\n`]), ...formatTraceEntry(entry)); }); return logs; } function formatTraceEntry({ vnode, recurseCount }) { const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``; const open = ` at <${formatComponentName(vnode)}`; const close = `>` + postfix; const rootLabel = vnode.component.parent == null ? `(Root)` : ``; return vnode.props ? [open, ...formatProps(vnode.props), close, rootLabel] : [open + close, rootLabel]; } const classifyRE = /(?:^|[-_])(\w)/g; const classify = (str) => str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, ''); function formatComponentName(vnode, file) { const Component = vnode.type; let name = isFunction(Component) ? Component.displayName || Component.name : Component.name; if (!name && file) { const match = file.match(/([^/\\]+)\.vue$/); if (match) { name = match[1]; } } return name ? classify(name) : 'Anonymous'; } function formatProps(props) { const res = []; const keys = Object.keys(props); keys.slice(0, 3).forEach(key => { res.push(...formatProp(key, props[key])); }); if (keys.length > 3) { res.push(` ...`); } return res; } function formatProp(key, value, raw) { if (isString(value)) { value = JSON.stringify(value); return raw ? value : [`${key}=${value}`]; } else if (typeof value === 'number' || typeof value === 'boolean' || value == null) { return raw ? value : [`${key}=${value}`]; } else if (isRef(value)) { value = formatProp(key, toRaw(value.value), true); return raw ? value : [`${key}=Ref<`, value, `>`]; } else if (isFunction(value)) { return [`${key}=fn${value.name ? `<${value.name}>` : ``}`]; } else { value = toRaw(value); return raw ? value : [`${key}=`, value]; } } // SFC scoped style ID management. // These are only used in esm-bundler builds, but since exports cannot be // conditional, we can only drop inner implementations in non-bundler builds. let currentScopeId = null; const scopeIdStack = []; function pushScopeId(id) { { scopeIdStack.push((currentScopeId = id)); } } function popScopeId() { { scopeIdStack.pop(); currentScopeId = scopeIdStack[scopeIdStack.length - 1] || null; } } function withScopeId(id) { { return ((fn) => { return function () { pushScopeId(id); const res = fn.apply(this, arguments); popScopeId(); return res; }; }); } } const Fragment = Symbol((process.env.NODE_ENV !== 'production') ? 'Fragment' : undefined); const Portal = Symbol((process.env.NODE_ENV !== 'production') ? 'Portal' : undefined); const Text = Symbol((process.env.NODE_ENV !== 'production') ? 'Text' : undefined); const Comment = Symbol((process.env.NODE_ENV !== 'production') ? 'Comment' : undefined); // Since v-if and v-for are the two possible ways node structure can dynamically // change, once we consider v-if branches and each v-for fragment a block, we // can divide a template into nested blocks, and within each block the node // structure would be stable. This allows us to skip most children diffing // and only worry about the dynamic nodes (indicated by patch flags). const blockStack = []; let currentBlock = null; // Open a block. // This must be called before `createBlock`. It cannot be part of `createBlock` // because the children of the block are evaluated before `createBlock` itself // is called. The generated code typically looks like this: // // function render() { // return (openBlock(),createBlock('div', null, [...])) // } // // disableTracking is true when creating a fragment block, since a fragment // always diffs its children. function openBlock(disableTracking) { blockStack.push((currentBlock = disableTracking ? null : [])); } // Whether we should be tracking dynamic child nodes inside a block. // Only tracks when this value is > 0 // We are not using a simple boolean because this value may need to be // incremented/decremented by nested usage of v-once (see below) let shouldTrack = 1; // Block tracking sometimes needs to be disabled, for example during the // creation of a tree that needs to be cached by v-once. The compiler generates // code like this: // _cache[1] || ( // setBlockTracking(-1), // _cache[1] = createVNode(...), // setBlockTracking(1), // _cache[1] // ) function setBlockTracking(value) { shouldTrack += value; } // Create a block root vnode. Takes the same exact arguments as `createVNode`. // A block root keeps track of dynamic nodes within the block in the // `dynamicChildren` array. function createBlock(type, props, children, patchFlag, dynamicProps) { // avoid a block with patchFlag tracking itself shouldTrack--; const vnode = createVNode(type, props, children, patchFlag, dynamicProps); shouldTrack++; // save current block children on the block vnode vnode.dynamicChildren = currentBlock || EMPTY_ARR; // close block blockStack.pop(); currentBlock = blockStack[blockStack.length - 1] || null; // a block is always going to be patched, so track it as a child of its // parent block if (currentBlock !== null) { currentBlock.push(vnode); } return vnode; } function isVNode(value) { return value ? value._isVNode === true : false; } function isSameVNodeType(n1, n2) { if ( (process.env.NODE_ENV !== 'production') && n2.shapeFlag & 6 /* COMPONENT */ && n2.type.__hmrUpdated) { // HMR only: if the component has been hot-updated, force a reload. return false; } return n1.type === n2.type && n1.key === n2.key; } function createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null) { if ((process.env.NODE_ENV !== 'production') && !type) { warn(`Invalid vnode type when creating vnode: ${type}.`); type = Comment; } // class & style normalization. if (props !== null) { // for reactive or proxy objects, we need to clone it to enable mutation. if (isReactive(props) || SetupProxySymbol in props) { props = extend({}, props); } let { class: klass, style } = props; if (klass != null && !isString(klass)) { props.class = normalizeClass(klass); } if (style != null) { // reactive state objects need to be cloned since they are likely to be // mutated if (isReactive(style) && !isArray(style)) { style = extend({}, style); } props.style = normalizeStyle(style); } } // encode the vnode type information into a bitmap const shapeFlag = isString(type) ? 1 /* ELEMENT */ : type.__isSuspense === true ? 64 /* SUSPENSE */ : isObject(type) ? 4 /* STATEFUL_COMPONENT */ : isFunction(type) ? 2 /* FUNCTIONAL_COMPONENT */ : 0; const vnode = { _isVNode: true, type, props, key: (props !== null && props.key) || null, ref: (props !== null && props.ref) || null, scopeId: currentScopeId, children: null, component: null, suspense: null, dirs: null, transition: null, el: null, anchor: null, target: null, shapeFlag, patchFlag, dynamicProps, dynamicChildren: null, appContext: null }; normalizeChildren(vnode, children); // presence of a patch flag indicates this node needs patching on updates. // component nodes also should always be patched, because even if the // component doesn't need to update, it needs to persist the instance on to // the next vnode so that it can be properly unmounted later. if (shouldTrack > 0 && currentBlock !== null && (patchFlag > 0 || shapeFlag & 4 /* STATEFUL_COMPONENT */ || shapeFlag & 2 /* FUNCTIONAL_COMPONENT */)) { currentBlock.push(vnode); } return vnode; } function cloneVNode(vnode, extraProps) { // This is intentionally NOT using spread or extend to avoid the runtime // key enumeration cost. return { _isVNode: true, type: vnode.type, props: extraProps ? vnode.props ? mergeProps(vnode.props, extraProps) : extraProps : vnode.props, key: vnode.key, ref: vnode.ref, scopeId: vnode.scopeId, children: vnode.children, target: vnode.target, shapeFlag: vnode.shapeFlag, patchFlag: vnode.patchFlag, dynamicProps: vnode.dynamicProps, dynamicChildren: vnode.dynamicChildren, appContext: vnode.appContext, dirs: vnode.dirs, transition: vnode.transition, // These should technically only be non-null on mounted VNodes. However, // they *should* be copied for kept-alive vnodes. So we just always copy // them since them being non-null during a mount doesn't affect the logic as // they will simply be overwritten. component: vnode.component, suspense: vnode.suspense, el: vnode.el, anchor: vnode.anchor }; } function createTextVNode(text = ' ', flag = 0) { return createVNode(Text, null, text, flag); } function createCommentVNode(text = '', // when used as the v-else branch, the comment node must be created as a // block to ensure correct updates. asBlock = false) { return asBlock ? createBlock(Comment, null, text) : createVNode(Comment, null, text); } function normalizeVNode(child) { if (child == null) { // empty placeholder return createVNode(Comment); } else if (isArray(child)) { // fragment return createVNode(Fragment, null, child); } else if (typeof child === 'object') { // already vnode, this should be the most common since compiled templates // always produce all-vnode children arrays return child.el === null ? child : cloneVNode(child); } else { // primitive types return createVNode(Text, null, String(child)); } } // optimized normalization for template-compiled render fns function cloneIfMounted(child) { return child.el === null ? child : cloneVNode(child); } function normalizeChildren(vnode, children) { let type = 0; if (children == null) { children = null; } else if (isArray(children)) { type = 16 /* ARRAY_CHILDREN */; } else if (typeof children === 'object') { type = 32 /* SLOTS_CHILDREN */; } else if (isFunction(children)) { children = { default: children }; type = 32 /* SLOTS_CHILDREN */; } else { children = String(children); type = 8 /* TEXT_CHILDREN */; } vnode.children = children; vnode.shapeFlag |= type; } function normalizeStyle(value) { if (isArray(value)) { const res = {}; for (let i = 0; i < value.length; i++) { const normalized = normalizeStyle(value[i]); if (normalized) { for (const key in normalized) { res[key] = normalized[key]; } } } return res; } else if (isObject(value)) { return value; } } function normalizeClass(value) { let res = ''; if (isString(value)) { res = value; } else if (isArray(value)) { for (let i = 0; i < value.length; i++) { res += normalizeClass(value[i]) + ' '; } } else if (isObject(value)) { for (const name in value) { if (value[name]) { res += name + ' '; } } } return res.trim(); } const handlersRE = /^on|^vnode/; function mergeProps(...args) { const ret = {}; extend(ret, args[0]); for (let i = 1; i < args.length; i++) { const toMerge = args[i]; for (const key in toMerge) { if (key === 'class') { ret.class = normalizeClass([ret.class, toMerge.class]); } else if (key === 'style') { ret.style = normalizeStyle([ret.style, toMerge.style]); } else if (handlersRE.test(key)) { // on*, vnode* const existing = ret[key]; ret[key] = existing ? [].concat(existing, toMerge[key]) : toMerge[key]; } else { ret[key] = toMerge[key]; } } } return ret; } const queue = []; const postFlushCbs = []; const p = Promise.resolve(); let isFlushing = false; let isFlushPending = false; const RECURSION_LIMIT = 100; function nextTick(fn) { return fn ? p.then(fn) : p; } function queueJob(job) { if (!queue.includes(job)) { queue.push(job); queueFlush(); } } function queuePostFlushCb(cb) { if (!isArray(cb)) { postFlushCbs.push(cb); } else { postFlushCbs.push(...cb); } queueFlush(); } function queueFlush() { if (!isFlushing && !isFlushPending) { isFlushPending = true; nextTick(flushJobs); } } const dedupe = (cbs) => [...new Set(cbs)]; function flushPostFlushCbs(seen) { if (postFlushCbs.length) { const cbs = dedupe(postFlushCbs); postFlushCbs.length = 0; if ((process.env.NODE_ENV !== 'production')) { seen = seen || new Map(); } for (let i = 0; i < cbs.length; i++) { if ((process.env.NODE_ENV !== 'production')) { checkRecursiveUpdates(seen, cbs[i]); } cbs[i](); } } } function flushJobs(seen) { isFlushPending = false; isFlushing = true; let job; if ((process.env.NODE_ENV !== 'production')) { seen = seen || new Map(); } while ((job = queue.shift())) { if ((process.env.NODE_ENV !== 'production')) { checkRecursiveUpdates(seen, job); } callWithErrorHandling(job, null, 12 /* SCHEDULER */); } flushPostFlushCbs(seen); isFlushing = false; // some postFlushCb queued jobs! // keep flushing until it drains. if (queue.length || postFlushCbs.length) { flushJobs(seen); } } function checkRecursiveUpdates(seen, fn) { if (!seen.has(fn)) { seen.set(fn, 1); } else { const count = seen.get(fn); if (count > RECURSION_LIMIT) { throw new Error('Maximum recursive updates exceeded. ' + "You may have code that is mutating state in your component's " + 'render function or updated hook or watcher source function.'); } else { seen.set(fn, count + 1); } } } // mark the current rendering instance for asset resolution (e.g. // resolveComponent, resolveDirective) during render let currentRenderingInstance = null; // dev only flag to track whether $attrs was used during render. // If $attrs was used during render then the warning for failed attrs // fallthrough can be suppressed. let accessedAttrs = false; function markAttrsAccessed() { accessedAttrs = true; } function renderComponentRoot(instance) { const { type: Component, vnode, proxy, withProxy, props, slots, attrs, emit } = instance; let result; currentRenderingInstance = instance; if ((process.env.NODE_ENV !== 'production')) { accessedAttrs = false; } try { if (vnode.shapeFlag & 4 /* STATEFUL_COMPONENT */) { result = normalizeVNode(instance.render.call(withProxy || proxy)); } else { // functional const render = Component; result = normalizeVNode(render.length > 1 ? render(props, { attrs, slots, emit }) : render(props, null /* we know it doesn't need it */)); } // attr merging if (Component.props != null && Component.inheritAttrs !== false && attrs !== EMPTY_OBJ && Object.keys(attrs).length) { if (result.shapeFlag & 1 /* ELEMENT */ || result.shapeFlag & 6 /* COMPONENT */) { result = cloneVNode(result, attrs); } else if ((process.env.NODE_ENV !== 'production') && !accessedAttrs && result.type !== Comment) { warn(`Extraneous non-props attributes (${Object.keys(attrs).join(',')}) ` + `were passed to component but could not be automatically inherited ` + `because component renders fragment or text root nodes.`); } } // inherit transition data if (vnode.transition != null) { if ((process.env.NODE_ENV !== 'production') && !(result.shapeFlag & 6 /* COMPONENT */) && !(result.shapeFlag & 1 /* ELEMENT */) && result.type !== Comment) { warn(`Component inside <Transition> renders non-element root node ` + `that cannot be animated.`); } result.transition = vnode.transition; } } catch (err) { handleError(err, instance, 1 /* RENDER_FUNCTION */); result = createVNode(Comment); } currentRenderingInstance = null; return result; } function shouldUpdateComponent(prevVNode, nextVNode, parentComponent, optimized) { const { props: prevProps, children: prevChildren } = prevVNode; const { props: nextProps, children: nextChildren, patchFlag } = nextVNode; // Parent component's render function was hot-updated. Since this may have // caused the child component's slots content to have changed, we need to // force the child to update as well. if ( (process.env.NODE_ENV !== 'production') && (prevChildren || nextChildren) && parentComponent && parentComponent.renderUpdated) { return true; } if (patchFlag > 0) { if (patchFlag & 512 /* DYNAMIC_SLOTS */) { // slot content that references values that might have changed, // e.g. in a v-for return true; } if (patchFlag & 16 /* FULL_PROPS */) { // presence of this flag indicates props are always non-null return hasPropsChanged(prevProps, nextProps); } else if (patchFlag & 8 /* PROPS */) { const dynamicProps = nextVNode.dynamicProps; for (let i = 0; i < dynamicProps.length; i++) { const key = dynamicProps[i]; if (nextProps[key] !== prevProps[key]) { return true; } } } } else if (!optimized) { // this path is only taken by manually written render functions // so presence of any children leads to a forced update if (prevChildren != null || nextChildren != null) { if (nextChildren == null || !nextChildren.$stable) { return true; } } if (prevProps === nextProps) { return false; } if (prevProps === null) { return nextProps !== null; } if (nextProps === null) { return true; } return hasPropsChanged(prevProps, nextProps); } return false; } function hasPropsChanged(prevProps, nextProps) { const nextKeys = Object.keys(nextProps); if (nextKeys.length !== Object.keys(prevProps).length) { return true; } for (let i = 0; i < nextKeys.length; i++) { const key = nextKeys[i]; if (nextProps[key] !== prevProps[key]) { return true; } } return false; } function updateHOCHostEl({ vnode, parent }, el // HostNode ) { while (parent && parent.subTree === vnode) { (vnode = parent.vnode).el = el; parent = parent.parent; } } // resolve raw VNode data. // - filter out reserved keys (key, ref, slots) // - extract class and style into $attrs (to be merged onto child // component root) // - for the rest: // - if has declared props: put declared ones in `props`, the rest in `attrs` // - else: everything goes in `props`. function resolveProps(instance, rawProps, _options) { const hasDeclaredProps = _options != null; if (!rawProps && !hasDeclaredProps) { return; } const { 0: options, 1: needCastKeys } = normalizePropsOptions(_options); const props = {}; let attrs = void 0; // update the instance propsProxy (passed to setup()) to trigger potential // changes const propsProxy = instance.propsProxy; const setProp = propsProxy ? (key, val) => { props[key] = val; propsProxy[key] = val; } : (key, val) => { props[key] = val; }; // allow mutation of propsProxy (which is readonly by default) unlock(); if (rawProps != null) { for (const key in rawProps) { // key, ref are reserved and never passed down if (key === 'key' || key === 'ref') continue; // prop option names are camelized during normalization, so to support // kebab -> camel conversion here we need to camelize the key. const camelKey = camelize(key); if (hasDeclaredProps && !hasOwn(options, camelKey)) { (attrs || (attrs = {}))[key] = rawProps[key]; } else { setProp(camelKey, rawProps[key]); } } } if (hasDeclaredProps) { // set default values & cast booleans for (let i = 0; i < needCastKeys.length; i++) { const key = needCastKeys[i]; let opt = options[key]; if (opt == null) continue; const isAbsent = !hasOwn(props, key); const hasDefault = hasOwn(opt, 'default'); const currentValue = props[key]; // default values if (hasDefault && currentValue === undefined) { const defaultValue = opt.default; setProp(key, isFunction(defaultValue) ? defaultValue() : defaultValue); } // boolean casting if (opt[0 /* shouldCast */]) { if (isAbsent && !hasDefault) { setProp(key, false); } else if (opt[1 /* shouldCastTrue */] && (currentValue === '' || currentValue === hyphenate(key))) { setProp(key, true); } } } // validation if ((process.env.NODE_ENV !== 'production') && rawProps) { for (const key in options) { let opt = options[key]; if (opt == null) continue; let rawValue; if (!(key in rawProps) && hyphenate(key) in rawProps) { rawValue = rawProps[hyphenate(key)]; } else { rawValue = rawProps[key]; } validateProp(key, toRaw(rawValue), opt, !hasOwn(props, key)); } } } else { // if component has no declared props, $attrs === $props attrs = props; } // in case of dynamic props, check if we need to delete keys from // the props proxy const { patchFlag } = instance.vnode; if (propsProxy !== null && (patchFlag === 0 || patchFlag & 16 /* FULL_PROPS */)) { const rawInitialProps = toRaw(propsProxy); for (const key in rawInitialProps) { if (!hasOwn(props, key)) { delete propsProxy[key]; } } } // lock readonly lock(); instance.props = props; instance.attrs = options ? attrs || EMPTY_OBJ : props; } const normalizationMap = new WeakMap(); function normalizePropsOptions(raw) { if (!raw) { return []; } if (normalizationMap.has(raw)) { return normalizationMap.get(raw); } const options = {}; const needCastKeys = []; if (isArray(raw)) { for (let i = 0; i < raw.length; i++) { if ((process.env.NODE_ENV !== 'production') && !isString(raw[i])) { warn(`props must be strings when using array syntax.`, raw[i]); } const normalizedKey = camelize(raw[i]); if (normalizedKey[0] !== '$') { options[normalizedKey] = EMPTY_OBJ; } else if ((process.env.NODE_ENV !== 'production')) { warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`); } } } else { if ((process.env.NODE_ENV !== 'production') && !isObject(raw)) { warn(`invalid props options`, raw); } for (const key in raw) { const normalizedKey = camelize(key); if (normalizedKey[0] !== '$') { const opt = raw[key]; const prop = (options[normalizedKey] = isArray(opt) || isFunction(opt) ? { type: opt } : opt); if (prop != null) { const booleanIndex = getTypeIndex(Boolean, prop.type); const stringIndex = getTypeIndex(String, prop.type); prop[0 /* shouldCast */] = booleanIndex > -1; prop[1 /* shouldCastTrue */] = booleanIndex < stringIndex; // if the prop needs boolean casting or default value if (booleanIndex > -1 || hasOwn(prop, 'default')) { needCastKeys.push(normalizedKey); } } } else if ((process.env.NODE_ENV !== 'production')) { warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`); } } } const normalized = [options, needCastKeys]; normalizationMap.set(raw, normalized); return normalized; } // use function string name to check type constructors // so that it works across vms / iframes. function getType(ctor) { const match = ctor && ctor.toString().match(/^\s*function (\w+)/); return match ? match[1] : ''; } function isSameType(a, b) { return getType(a) === getType(b); } function getTypeIndex(type, expectedTypes) { if (isArray(expectedTypes)) { for (let i = 0, len = expectedTypes.length; i < len; i++) { if (isSameType(expectedTypes[i], type)) { return i; } } } else if (isObject(expectedTypes)) { return isSameType(expectedTypes, type) ? 0 : -1; } return -1; } function validateProp(name, value, prop, isAbsent) { const { type, required, validator } = prop; // required! if (required && isAbsent) { warn('Missing required prop: "' + name + '"'); return; } // missing but optional if (value == null && !prop.required) { return; } // type check if (type != null && type !== true) { let isValid = false; const types = isArray(type) ? type : [type]; const expectedTypes = []; // value is valid as long as one of the specified types match for (let i = 0; i < types.length && !isValid; i++) { const { valid, expectedType } = assertType(value, types[i]); expectedTypes.push(expectedType || ''); isValid = valid; } if (!isValid) { warn(getInvalidTypeMessage(name, value, expectedTypes)); return; } } // custom validator if (validator && !validator(value)) { warn('Invalid prop: custom validator check failed for prop "' + name + '".'); } } const isSimpleType = /*#__PURE__*/ makeMap('String,Number,Boolean,Function,Symbol'); function assertType(value, type) { let valid; const expectedType = getType(type); if (isSimpleType(expectedType)) { const t = typeof value; valid = t === expectedType.toLowerCase(); // for primitive wrapper objects if (!valid && t === 'object') { valid = value instanceof type; } } else if (expectedType === 'Object') { valid = toRawType(value) === 'Object'; } else if (expectedType === 'Array') { valid = isArray(value); } else { valid = value instanceof type; } return { valid, expectedType }; } function getInvalidTypeMessage(name, value, expectedTypes) { let message = `Invalid prop: type check failed for prop "${name}".` + ` Expected ${expectedTypes.map(capitalize).join(', ')}`; const expectedType = expectedTypes[0]; const receivedType = toRawType(value); const expectedValue = styleValue(value, expectedType); const receivedValue = styleValue(value, receivedType); // check if we need to specify expected value if (expectedTypes.length === 1 && isExplicable(expectedType) && !isBoolean(expectedType, receivedType)) { message += ` with value ${expectedValue}`; } message += `, got ${receivedType} `; // check if we need to specify received value if (isExplicable(receivedType)) { message += `with value ${receivedValue}.`; } return message; } function styleValue(value, type) { if (type === 'String') { return `"${value}"`; } else if (type === 'Number') { return `${Number(value)}`; } else { return `${value}`; } } function isExplicable(type) { const explicitTypes = ['string', 'number', 'boolean']; return explicitTypes.some(elem => type.toLowerCase() === elem); } function isBoolean(...args) { return args.some(elem => elem.toLowerCase() === 'boolean'); } const normalizeSlotValue = (value) => isArray(value) ? value.map(normalizeVNode) : [normalizeVNode(value)]; const normalizeSlot = (key, rawSlot) => (props) => { if ((process.env.NODE_ENV !== 'production') && currentInstance != null) { warn(`Slot "${key}" invoked outside of the render function: ` + `this will not track dependencies used in the slot. ` + `Invoke the slot function inside the render function instead.`); } return normalizeSlotValue(rawSlot(props)); }; function resolveSlots(instance, children) { let slots; if (instance.vnode.shapeFlag & 32 /* SLOTS_CHILDREN */) { const rawSlots = children; if (rawSlots._compiled) { // pre-normalized slots object generated by compiler slots = children; } else { slots = {}; for (const key in rawSlots) { if (key === '$stable') continue; const value = rawSlots[key]; if (isFunction(value)) { slots[key] = normalizeSlot(key, value); } else if (value != null) { if ((process.env.NODE_ENV !== 'production')) { warn(`Non-function value encountered for slot "${key}". ` + `Prefer function slots for better performance.`); } const normalized = normalizeSlotValue(value); slots[key] = () => normalized; } } } } else if (children !== null) { // non slot object children (direct value) passed to a component if ((process.env.NODE_ENV !== 'production') && !isKeepAlive(instance.vnode)) { warn(`Non-function value encountered for default slot. ` + `Prefer function slots for better performance.`); } const normalized = normalizeSlotValue(children); slots = { default: () => normalized }; } instance.slots = slots || EMPTY_OBJ; } /** Runtime helper for applying directives to a vnode. Example usage: const comp = resolveComponent('comp') const foo = resolveDirective('foo') const bar = resolveDirective('bar') return withDirectives(h(comp), [ [foo, this.x], [bar, this.y] ]) */ const isBuiltInDirective = /*#__PURE__*/ makeMap('bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text'); function validateDirectiveName(name) { if (isBuiltInDirective(name)) { warn('Do not use built-in directive ids as custom directive id: ' + name); } } const directiveToVnodeHooksMap = /*#__PURE__*/ [ 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeUnmount', 'unmounted' ].reduce((map, key) => { const vnodeKey = `onVnode` + key[0].toUpperCase() + key.slice(1); const vnodeHook = (vnode, prevVnode) => { const bindings = vnode.dirs; const prevBindings = prevVnode ? prevVnode.dirs : EMPTY_ARR; for (let i = 0; i < bindings.length; i++) { const binding = bindings[i]; const hook = binding.dir[key]; if (hook != null) { if (prevVnode != null) { binding.oldValue = prevBindings[i].value; } hook(vnode.el, binding, vnode, prevVnode); } } }; map[key] = [vnodeKey, vnodeHook]; return map; }, {}); function withDirectives(vnode, directives) { const internalInstance = currentRenderingInstance; if (internalInstance === null) { (process.env.NODE_ENV !== 'production') && warn(`withDirectives can only be used inside render functions.`); return vnode; } const instance = internalInstance.proxy; const props = vnode.props || (vnode.props = {}); const bindings = vnode.dirs || (vnode.dirs = new Array(directives.length)); const injected = {}; for (let i = 0; i < directives.length; i++) { let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]; if (isFunction(dir)) { dir = { mounted: dir, updated: dir }; } bindings[i] = { dir, instance, value, oldValue: void 0, arg, modifiers }; // inject onVnodeXXX hooks for (const key in dir) { if (!injected[key]) { const { 0: hookName, 1: hook } = directiveToVnodeHooksMap[key]; const existing = props[hookName]; props[hookName] = existing ? [].concat(existing, hook) : hook; injected[key] = true; } } } return vnode; } function invokeDirectiveHook(hook, instance, vnode, prevVNode = null) { callWithAsyncErrorHandling(hook, instance, 7 /* DIRECTIVE_HOOK */, [ vnode, prevVNode ]); } function createAppContext() { return { config: { devtools: true, performance: false, isNativeTag: NO, isCustomElement: NO, errorHandler: undefined, warnHandler: undefined }, mixins: [], components: {}, directives: {}, provides: {} }; } function createAppAPI(render) { return function createApp() { const context = createAppContext(); const installedPlugins = new Set(); let isMounted = false; const app = { get config() { return context.config; }, set config(v) { if ((process.env.NODE_ENV !== 'production')) { warn(`app.config cannot be replaced. Modify individual options instead.`); } }, use(plugin, ...options) { if (installedPlugins.has(plugin)) { (process.env.NODE_ENV !== 'production') && warn(`Plugin has already been applied to target app.`); } else if (isFunction(plugin)) { installedPlugins.add(plugin); plugin(app, ...options); } else if (plugin && isFunction(plugin.install)) { installedPlugins.add(plugin); plugin.install(app, ...options); } else if ((process.env.NODE_ENV !== 'production')) { warn(`A plugin must either be a function or an object with an "install" ` + `function.`); } return app; }, mixin(mixin) { if ((process.env.NODE_ENV !== 'production') && !true) { warn('Mixins are only available in builds supporting Options API'); } if (!context.mixins.includes(mixin)) { context.mixins.push(mixin); } else if ((process.env.NODE_ENV !== 'production')) { warn('Mixin has already been applied to target app' + (mixin.name ? `: ${mixin.name}` : '')); } return app; }, component(name, component) { if ((process.env.NODE_ENV !== 'production')) { validateComponentName(name, context.config); } if (!component) { return context.components[name]; } if ((process.env.NODE_ENV !== 'production') && context.components[name]) { warn(`Component "${name}" has already been registered in target app.`); } context.components[name] = component; return app; }, directive(name, directive) { if ((process.env.NODE_ENV !== 'production')) { validateDirectiveName(name); } if (!directive) { return context.directives[name]; } if ((process.env.NODE_ENV !== 'production') && context.directives[name]) { warn(`Directive "${name}" has already been registered in target app.`); } context.directives[name] = directive; return app; }, mount(rootComponent, rootContainer, rootProps) { if (!isMounted) { if (rootProps != null && !isObject(rootProps)) { (process.env.NODE_ENV !== 'production') && warn(`root props pa