From 26105034da4fcce7ac883c899d781f016559310d Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 8 Nov 2018 00:38:48 +0800 Subject: switch to vuepress --- node_modules/vue/src/core/vdom/create-component.js | 257 +++++++ node_modules/vue/src/core/vdom/create-element.js | 160 +++++ .../src/core/vdom/create-functional-component.js | 136 ++++ .../vue/src/core/vdom/helpers/extract-props.js | 75 ++ .../core/vdom/helpers/get-first-component-child.js | 15 + node_modules/vue/src/core/vdom/helpers/index.js | 9 + .../src/core/vdom/helpers/is-async-placeholder.js | 5 + .../vue/src/core/vdom/helpers/merge-hook.js | 38 + .../src/core/vdom/helpers/normalize-children.js | 89 +++ .../core/vdom/helpers/resolve-async-component.js | 140 ++++ .../vue/src/core/vdom/helpers/update-listeners.js | 83 +++ .../vue/src/core/vdom/modules/directives.js | 119 ++++ node_modules/vue/src/core/vdom/modules/index.js | 7 + node_modules/vue/src/core/vdom/modules/ref.js | 45 ++ node_modules/vue/src/core/vdom/patch.js | 787 +++++++++++++++++++++ node_modules/vue/src/core/vdom/vnode.js | 108 +++ 16 files changed, 2073 insertions(+) create mode 100644 node_modules/vue/src/core/vdom/create-component.js create mode 100644 node_modules/vue/src/core/vdom/create-element.js create mode 100644 node_modules/vue/src/core/vdom/create-functional-component.js create mode 100644 node_modules/vue/src/core/vdom/helpers/extract-props.js create mode 100644 node_modules/vue/src/core/vdom/helpers/get-first-component-child.js create mode 100644 node_modules/vue/src/core/vdom/helpers/index.js create mode 100644 node_modules/vue/src/core/vdom/helpers/is-async-placeholder.js create mode 100644 node_modules/vue/src/core/vdom/helpers/merge-hook.js create mode 100644 node_modules/vue/src/core/vdom/helpers/normalize-children.js create mode 100644 node_modules/vue/src/core/vdom/helpers/resolve-async-component.js create mode 100644 node_modules/vue/src/core/vdom/helpers/update-listeners.js create mode 100644 node_modules/vue/src/core/vdom/modules/directives.js create mode 100644 node_modules/vue/src/core/vdom/modules/index.js create mode 100644 node_modules/vue/src/core/vdom/modules/ref.js create mode 100644 node_modules/vue/src/core/vdom/patch.js create mode 100644 node_modules/vue/src/core/vdom/vnode.js (limited to 'node_modules/vue/src/core/vdom') diff --git a/node_modules/vue/src/core/vdom/create-component.js b/node_modules/vue/src/core/vdom/create-component.js new file mode 100644 index 00000000..89578224 --- /dev/null +++ b/node_modules/vue/src/core/vdom/create-component.js @@ -0,0 +1,257 @@ +/* @flow */ + +import VNode from './vnode' +import { resolveConstructorOptions } from 'core/instance/init' +import { queueActivatedComponent } from 'core/observer/scheduler' +import { createFunctionalComponent } from './create-functional-component' + +import { + warn, + isDef, + isUndef, + isTrue, + isObject +} from '../util/index' + +import { + resolveAsyncComponent, + createAsyncPlaceholder, + extractPropsFromVNodeData +} from './helpers/index' + +import { + callHook, + activeInstance, + updateChildComponent, + activateChildComponent, + deactivateChildComponent +} from '../instance/lifecycle' + +import { + isRecyclableComponent, + renderRecyclableComponentTemplate +} from 'weex/runtime/recycle-list/render-component-template' + +// inline hooks to be invoked on component VNodes during patch +const componentVNodeHooks = { + init ( + vnode: VNodeWithData, + hydrating: boolean, + parentElm: ?Node, + refElm: ?Node + ): ?boolean { + if ( + vnode.componentInstance && + !vnode.componentInstance._isDestroyed && + vnode.data.keepAlive + ) { + // kept-alive components, treat as a patch + const mountedNode: any = vnode // work around flow + componentVNodeHooks.prepatch(mountedNode, mountedNode) + } else { + const child = vnode.componentInstance = createComponentInstanceForVnode( + vnode, + activeInstance, + parentElm, + refElm + ) + child.$mount(hydrating ? vnode.elm : undefined, hydrating) + } + }, + + prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) { + const options = vnode.componentOptions + const child = vnode.componentInstance = oldVnode.componentInstance + updateChildComponent( + child, + options.propsData, // updated props + options.listeners, // updated listeners + vnode, // new parent vnode + options.children // new children + ) + }, + + insert (vnode: MountedComponentVNode) { + const { context, componentInstance } = vnode + if (!componentInstance._isMounted) { + componentInstance._isMounted = true + callHook(componentInstance, 'mounted') + } + if (vnode.data.keepAlive) { + if (context._isMounted) { + // vue-router#1212 + // During updates, a kept-alive component's child components may + // change, so directly walking the tree here may call activated hooks + // on incorrect children. Instead we push them into a queue which will + // be processed after the whole patch process ended. + queueActivatedComponent(componentInstance) + } else { + activateChildComponent(componentInstance, true /* direct */) + } + } + }, + + destroy (vnode: MountedComponentVNode) { + const { componentInstance } = vnode + if (!componentInstance._isDestroyed) { + if (!vnode.data.keepAlive) { + componentInstance.$destroy() + } else { + deactivateChildComponent(componentInstance, true /* direct */) + } + } + } +} + +const hooksToMerge = Object.keys(componentVNodeHooks) + +export function createComponent ( + Ctor: Class | Function | Object | void, + data: ?VNodeData, + context: Component, + children: ?Array, + tag?: string +): VNode | Array | void { + if (isUndef(Ctor)) { + return + } + + const baseCtor = context.$options._base + + // plain options object: turn it into a constructor + if (isObject(Ctor)) { + Ctor = baseCtor.extend(Ctor) + } + + // if at this stage it's not a constructor or an async component factory, + // reject. + if (typeof Ctor !== 'function') { + if (process.env.NODE_ENV !== 'production') { + warn(`Invalid Component definition: ${String(Ctor)}`, context) + } + return + } + + // async component + let asyncFactory + if (isUndef(Ctor.cid)) { + asyncFactory = Ctor + Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context) + if (Ctor === undefined) { + // return a placeholder node for async component, which is rendered + // as a comment node but preserves all the raw information for the node. + // the information will be used for async server-rendering and hydration. + return createAsyncPlaceholder( + asyncFactory, + data, + context, + children, + tag + ) + } + } + + data = data || {} + + // resolve constructor options in case global mixins are applied after + // component constructor creation + resolveConstructorOptions(Ctor) + + // transform component v-model data into props & events + if (isDef(data.model)) { + transformModel(Ctor.options, data) + } + + // extract props + const propsData = extractPropsFromVNodeData(data, Ctor, tag) + + // functional component + if (isTrue(Ctor.options.functional)) { + return createFunctionalComponent(Ctor, propsData, data, context, children) + } + + // extract listeners, since these needs to be treated as + // child component listeners instead of DOM listeners + const listeners = data.on + // replace with listeners with .native modifier + // so it gets processed during parent component patch. + data.on = data.nativeOn + + if (isTrue(Ctor.options.abstract)) { + // abstract components do not keep anything + // other than props & listeners & slot + + // work around flow + const slot = data.slot + data = {} + if (slot) { + data.slot = slot + } + } + + // install component management hooks onto the placeholder node + installComponentHooks(data) + + // return a placeholder vnode + const name = Ctor.options.name || tag + const vnode = new VNode( + `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`, + data, undefined, undefined, undefined, context, + { Ctor, propsData, listeners, tag, children }, + asyncFactory + ) + + // Weex specific: invoke recycle-list optimized @render function for + // extracting cell-slot template. + // https://github.com/Hanks10100/weex-native-directive/tree/master/component + /* istanbul ignore if */ + if (__WEEX__ && isRecyclableComponent(vnode)) { + return renderRecyclableComponentTemplate(vnode) + } + + return vnode +} + +export function createComponentInstanceForVnode ( + vnode: any, // we know it's MountedComponentVNode but flow doesn't + parent: any, // activeInstance in lifecycle state + parentElm?: ?Node, + refElm?: ?Node +): Component { + const options: InternalComponentOptions = { + _isComponent: true, + parent, + _parentVnode: vnode, + _parentElm: parentElm || null, + _refElm: refElm || null + } + // check inline-template render functions + const inlineTemplate = vnode.data.inlineTemplate + if (isDef(inlineTemplate)) { + options.render = inlineTemplate.render + options.staticRenderFns = inlineTemplate.staticRenderFns + } + return new vnode.componentOptions.Ctor(options) +} + +function installComponentHooks (data: VNodeData) { + const hooks = data.hook || (data.hook = {}) + for (let i = 0; i < hooksToMerge.length; i++) { + const key = hooksToMerge[i] + hooks[key] = componentVNodeHooks[key] + } +} + +// transform component v-model info (value and callback) into +// prop and event handler respectively. +function transformModel (options, data: any) { + const prop = (options.model && options.model.prop) || 'value' + const event = (options.model && options.model.event) || 'input' + ;(data.props || (data.props = {}))[prop] = data.model.value + const on = data.on || (data.on = {}) + if (isDef(on[event])) { + on[event] = [data.model.callback].concat(on[event]) + } else { + on[event] = data.model.callback + } +} diff --git a/node_modules/vue/src/core/vdom/create-element.js b/node_modules/vue/src/core/vdom/create-element.js new file mode 100644 index 00000000..c5cde2c6 --- /dev/null +++ b/node_modules/vue/src/core/vdom/create-element.js @@ -0,0 +1,160 @@ +/* @flow */ + +import config from '../config' +import VNode, { createEmptyVNode } from './vnode' +import { createComponent } from './create-component' +import { traverse } from '../observer/traverse' + +import { + warn, + isDef, + isUndef, + isTrue, + isObject, + isPrimitive, + resolveAsset +} from '../util/index' + +import { + normalizeChildren, + simpleNormalizeChildren +} from './helpers/index' + +const SIMPLE_NORMALIZE = 1 +const ALWAYS_NORMALIZE = 2 + +// wrapper function for providing a more flexible interface +// without getting yelled at by flow +export function createElement ( + context: Component, + tag: any, + data: any, + children: any, + normalizationType: any, + alwaysNormalize: boolean +): VNode | Array { + if (Array.isArray(data) || isPrimitive(data)) { + normalizationType = children + children = data + data = undefined + } + if (isTrue(alwaysNormalize)) { + normalizationType = ALWAYS_NORMALIZE + } + return _createElement(context, tag, data, children, normalizationType) +} + +export function _createElement ( + context: Component, + tag?: string | Class | Function | Object, + data?: VNodeData, + children?: any, + normalizationType?: number +): VNode | Array { + if (isDef(data) && isDef((data: any).__ob__)) { + process.env.NODE_ENV !== 'production' && warn( + `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` + + 'Always create fresh vnode data objects in each render!', + context + ) + return createEmptyVNode() + } + // object syntax in v-bind + if (isDef(data) && isDef(data.is)) { + tag = data.is + } + if (!tag) { + // in case of component :is set to falsy value + return createEmptyVNode() + } + // warn against non-primitive key + if (process.env.NODE_ENV !== 'production' && + isDef(data) && isDef(data.key) && !isPrimitive(data.key) + ) { + if (!__WEEX__ || !('@binding' in data.key)) { + warn( + 'Avoid using non-primitive value as key, ' + + 'use string/number value instead.', + context + ) + } + } + // support single function children as default scoped slot + if (Array.isArray(children) && + typeof children[0] === 'function' + ) { + data = data || {} + data.scopedSlots = { default: children[0] } + children.length = 0 + } + if (normalizationType === ALWAYS_NORMALIZE) { + children = normalizeChildren(children) + } else if (normalizationType === SIMPLE_NORMALIZE) { + children = simpleNormalizeChildren(children) + } + let vnode, ns + if (typeof tag === 'string') { + let Ctor + ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag) + if (config.isReservedTag(tag)) { + // platform built-in elements + vnode = new VNode( + config.parsePlatformTagName(tag), data, children, + undefined, undefined, context + ) + } else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) { + // component + vnode = createComponent(Ctor, data, context, children, tag) + } else { + // unknown or unlisted namespaced elements + // check at runtime because it may get assigned a namespace when its + // parent normalizes children + vnode = new VNode( + tag, data, children, + undefined, undefined, context + ) + } + } else { + // direct component options / constructor + vnode = createComponent(tag, data, context, children) + } + if (Array.isArray(vnode)) { + return vnode + } else if (isDef(vnode)) { + if (isDef(ns)) applyNS(vnode, ns) + if (isDef(data)) registerDeepBindings(data) + return vnode + } else { + return createEmptyVNode() + } +} + +function applyNS (vnode, ns, force) { + vnode.ns = ns + if (vnode.tag === 'foreignObject') { + // use default namespace inside foreignObject + ns = undefined + force = true + } + if (isDef(vnode.children)) { + for (let i = 0, l = vnode.children.length; i < l; i++) { + const child = vnode.children[i] + if (isDef(child.tag) && ( + isUndef(child.ns) || (isTrue(force) && child.tag !== 'svg'))) { + applyNS(child, ns, force) + } + } + } +} + +// ref #5318 +// necessary to ensure parent re-render when deep bindings like :style and +// :class are used on slot nodes +function registerDeepBindings (data) { + if (isObject(data.style)) { + traverse(data.style) + } + if (isObject(data.class)) { + traverse(data.class) + } +} diff --git a/node_modules/vue/src/core/vdom/create-functional-component.js b/node_modules/vue/src/core/vdom/create-functional-component.js new file mode 100644 index 00000000..efed801d --- /dev/null +++ b/node_modules/vue/src/core/vdom/create-functional-component.js @@ -0,0 +1,136 @@ +/* @flow */ + +import VNode, { cloneVNode } from './vnode' +import { createElement } from './create-element' +import { resolveInject } from '../instance/inject' +import { normalizeChildren } from '../vdom/helpers/normalize-children' +import { resolveSlots } from '../instance/render-helpers/resolve-slots' +import { installRenderHelpers } from '../instance/render-helpers/index' + +import { + isDef, + isTrue, + hasOwn, + camelize, + emptyObject, + validateProp +} from '../util/index' + +export function FunctionalRenderContext ( + data: VNodeData, + props: Object, + children: ?Array, + parent: Component, + Ctor: Class +) { + const options = Ctor.options + // ensure the createElement function in functional components + // gets a unique context - this is necessary for correct named slot check + let contextVm + if (hasOwn(parent, '_uid')) { + contextVm = Object.create(parent) + // $flow-disable-line + contextVm._original = parent + } else { + // the context vm passed in is a functional context as well. + // in this case we want to make sure we are able to get a hold to the + // real context instance. + contextVm = parent + // $flow-disable-line + parent = parent._original + } + const isCompiled = isTrue(options._compiled) + const needNormalization = !isCompiled + + this.data = data + this.props = props + this.children = children + this.parent = parent + this.listeners = data.on || emptyObject + this.injections = resolveInject(options.inject, parent) + this.slots = () => resolveSlots(children, parent) + + // support for compiled functional template + if (isCompiled) { + // exposing $options for renderStatic() + this.$options = options + // pre-resolve slots for renderSlot() + this.$slots = this.slots() + this.$scopedSlots = data.scopedSlots || emptyObject + } + + if (options._scopeId) { + this._c = (a, b, c, d) => { + const vnode = createElement(contextVm, a, b, c, d, needNormalization) + if (vnode && !Array.isArray(vnode)) { + vnode.fnScopeId = options._scopeId + vnode.fnContext = parent + } + return vnode + } + } else { + this._c = (a, b, c, d) => createElement(contextVm, a, b, c, d, needNormalization) + } +} + +installRenderHelpers(FunctionalRenderContext.prototype) + +export function createFunctionalComponent ( + Ctor: Class, + propsData: ?Object, + data: VNodeData, + contextVm: Component, + children: ?Array +): VNode | Array | void { + const options = Ctor.options + const props = {} + const propOptions = options.props + if (isDef(propOptions)) { + for (const key in propOptions) { + props[key] = validateProp(key, propOptions, propsData || emptyObject) + } + } else { + if (isDef(data.attrs)) mergeProps(props, data.attrs) + if (isDef(data.props)) mergeProps(props, data.props) + } + + const renderContext = new FunctionalRenderContext( + data, + props, + children, + contextVm, + Ctor + ) + + const vnode = options.render.call(null, renderContext._c, renderContext) + + if (vnode instanceof VNode) { + return cloneAndMarkFunctionalResult(vnode, data, renderContext.parent, options) + } else if (Array.isArray(vnode)) { + const vnodes = normalizeChildren(vnode) || [] + const res = new Array(vnodes.length) + for (let i = 0; i < vnodes.length; i++) { + res[i] = cloneAndMarkFunctionalResult(vnodes[i], data, renderContext.parent, options) + } + return res + } +} + +function cloneAndMarkFunctionalResult (vnode, data, contextVm, options) { + // #7817 clone node before setting fnContext, otherwise if the node is reused + // (e.g. it was from a cached normal slot) the fnContext causes named slots + // that should not be matched to match. + const clone = cloneVNode(vnode) + clone.fnContext = contextVm + clone.fnOptions = options + if (data.slot) { + (clone.data || (clone.data = {})).slot = data.slot + } + return clone +} + +function mergeProps (to, from) { + for (const key in from) { + to[camelize(key)] = from[key] + } +} diff --git a/node_modules/vue/src/core/vdom/helpers/extract-props.js b/node_modules/vue/src/core/vdom/helpers/extract-props.js new file mode 100644 index 00000000..f2a41661 --- /dev/null +++ b/node_modules/vue/src/core/vdom/helpers/extract-props.js @@ -0,0 +1,75 @@ +/* @flow */ + +import { + tip, + hasOwn, + isDef, + isUndef, + hyphenate, + formatComponentName +} from 'core/util/index' + +export function extractPropsFromVNodeData ( + data: VNodeData, + Ctor: Class, + tag?: string +): ?Object { + // we are only extracting raw values here. + // validation and default values are handled in the child + // component itself. + const propOptions = Ctor.options.props + if (isUndef(propOptions)) { + return + } + const res = {} + const { attrs, props } = data + if (isDef(attrs) || isDef(props)) { + for (const key in propOptions) { + const altKey = hyphenate(key) + if (process.env.NODE_ENV !== 'production') { + const keyInLowerCase = key.toLowerCase() + if ( + key !== keyInLowerCase && + attrs && hasOwn(attrs, keyInLowerCase) + ) { + tip( + `Prop "${keyInLowerCase}" is passed to component ` + + `${formatComponentName(tag || Ctor)}, but the declared prop name is` + + ` "${key}". ` + + `Note that HTML attributes are case-insensitive and camelCased ` + + `props need to use their kebab-case equivalents when using in-DOM ` + + `templates. You should probably use "${altKey}" instead of "${key}".` + ) + } + } + checkProp(res, props, key, altKey, true) || + checkProp(res, attrs, key, altKey, false) + } + } + return res +} + +function checkProp ( + res: Object, + hash: ?Object, + key: string, + altKey: string, + preserve: boolean +): boolean { + if (isDef(hash)) { + if (hasOwn(hash, key)) { + res[key] = hash[key] + if (!preserve) { + delete hash[key] + } + return true + } else if (hasOwn(hash, altKey)) { + res[key] = hash[altKey] + if (!preserve) { + delete hash[altKey] + } + return true + } + } + return false +} diff --git a/node_modules/vue/src/core/vdom/helpers/get-first-component-child.js b/node_modules/vue/src/core/vdom/helpers/get-first-component-child.js new file mode 100644 index 00000000..f8649dd8 --- /dev/null +++ b/node_modules/vue/src/core/vdom/helpers/get-first-component-child.js @@ -0,0 +1,15 @@ +/* @flow */ + +import { isDef } from 'shared/util' +import { isAsyncPlaceholder } from './is-async-placeholder' + +export function getFirstComponentChild (children: ?Array): ?VNode { + if (Array.isArray(children)) { + for (let i = 0; i < children.length; i++) { + const c = children[i] + if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { + return c + } + } + } +} diff --git a/node_modules/vue/src/core/vdom/helpers/index.js b/node_modules/vue/src/core/vdom/helpers/index.js new file mode 100644 index 00000000..320cf4c4 --- /dev/null +++ b/node_modules/vue/src/core/vdom/helpers/index.js @@ -0,0 +1,9 @@ +/* @flow */ + +export * from './merge-hook' +export * from './extract-props' +export * from './update-listeners' +export * from './normalize-children' +export * from './resolve-async-component' +export * from './get-first-component-child' +export * from './is-async-placeholder' diff --git a/node_modules/vue/src/core/vdom/helpers/is-async-placeholder.js b/node_modules/vue/src/core/vdom/helpers/is-async-placeholder.js new file mode 100644 index 00000000..6c4a6596 --- /dev/null +++ b/node_modules/vue/src/core/vdom/helpers/is-async-placeholder.js @@ -0,0 +1,5 @@ +/* @flow */ + +export function isAsyncPlaceholder (node: VNode): boolean { + return node.isComment && node.asyncFactory +} diff --git a/node_modules/vue/src/core/vdom/helpers/merge-hook.js b/node_modules/vue/src/core/vdom/helpers/merge-hook.js new file mode 100644 index 00000000..0bb96e8b --- /dev/null +++ b/node_modules/vue/src/core/vdom/helpers/merge-hook.js @@ -0,0 +1,38 @@ +/* @flow */ + +import VNode from '../vnode' +import { createFnInvoker } from './update-listeners' +import { remove, isDef, isUndef, isTrue } from 'shared/util' + +export function mergeVNodeHook (def: Object, hookKey: string, hook: Function) { + if (def instanceof VNode) { + def = def.data.hook || (def.data.hook = {}) + } + let invoker + const oldHook = def[hookKey] + + function wrappedHook () { + hook.apply(this, arguments) + // important: remove merged hook to ensure it's called only once + // and prevent memory leak + remove(invoker.fns, wrappedHook) + } + + if (isUndef(oldHook)) { + // no existing hook + invoker = createFnInvoker([wrappedHook]) + } else { + /* istanbul ignore if */ + if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { + // already a merged invoker + invoker = oldHook + invoker.fns.push(wrappedHook) + } else { + // existing plain hook + invoker = createFnInvoker([oldHook, wrappedHook]) + } + } + + invoker.merged = true + def[hookKey] = invoker +} diff --git a/node_modules/vue/src/core/vdom/helpers/normalize-children.js b/node_modules/vue/src/core/vdom/helpers/normalize-children.js new file mode 100644 index 00000000..a4622ef4 --- /dev/null +++ b/node_modules/vue/src/core/vdom/helpers/normalize-children.js @@ -0,0 +1,89 @@ +/* @flow */ + +import VNode, { createTextVNode } from 'core/vdom/vnode' +import { isFalse, isTrue, isDef, isUndef, isPrimitive } from 'shared/util' + +// The template compiler attempts to minimize the need for normalization by +// statically analyzing the template at compile time. +// +// For plain HTML markup, normalization can be completely skipped because the +// generated render function is guaranteed to return Array. There are +// two cases where extra normalization is needed: + +// 1. When the children contains components - because a functional component +// may return an Array instead of a single root. In this case, just a simple +// normalization is needed - if any child is an Array, we flatten the whole +// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep +// because functional components already normalize their own children. +export function simpleNormalizeChildren (children: any) { + for (let i = 0; i < children.length; i++) { + if (Array.isArray(children[i])) { + return Array.prototype.concat.apply([], children) + } + } + return children +} + +// 2. When the children contains constructs that always generated nested Arrays, +// e.g.