import Vue from 'vue'; import { copyReflectionMetadata, reflectionIsSupported } from './reflect'; import { collectDataFromConstructor } from './data'; import { hasProto, isPrimitive, warn } from './util'; export const $internalHooks = [ 'data', 'beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeDestroy', 'destroyed', 'beforeUpdate', 'updated', 'activated', 'deactivated', 'render', 'errorCaptured', 'serverPrefetch' // 2.6 ]; export function componentFactory(Component, options = {}) { options.name = options.name || Component._componentTag || Component.name; // prototype props. const proto = Component.prototype; Object.getOwnPropertyNames(proto).forEach(function (key) { if (key === 'constructor') { return; } // hooks if ($internalHooks.indexOf(key) > -1) { options[key] = proto[key]; return; } const descriptor = Object.getOwnPropertyDescriptor(proto, key); if (descriptor.value !== void 0) { // methods if (typeof descriptor.value === 'function') { (options.methods || (options.methods = {}))[key] = descriptor.value; } else { // typescript decorated data (options.mixins || (options.mixins = [])).push({ data() { return { [key]: descriptor.value }; } }); } } else if (descriptor.get || descriptor.set) { // computed properties (options.computed || (options.computed = {}))[key] = { get: descriptor.get, set: descriptor.set }; } }); (options.mixins || (options.mixins = [])).push({ data() { return collectDataFromConstructor(this, Component); } }); // decorate options const decorators = Component.__decorators__; if (decorators) { decorators.forEach(fn => fn(options)); delete Component.__decorators__; } // find super const superProto = Object.getPrototypeOf(Component.prototype); const Super = superProto instanceof Vue ? superProto.constructor : Vue; const Extended = Super.extend(options); forwardStaticMembers(Extended, Component, Super); if (reflectionIsSupported()) { copyReflectionMetadata(Extended, Component); } return Extended; } const reservedPropertyNames = [ // Unique id 'cid', // Super Vue constructor 'super', // Component options that will be used by the component 'options', 'superOptions', 'extendOptions', 'sealedOptions', // Private assets 'component', 'directive', 'filter' ]; const shouldIgnore = { prototype: true, arguments: true, callee: true, caller: true }; function forwardStaticMembers(Extended, Original, Super) { // We have to use getOwnPropertyNames since Babel registers methods as non-enumerable Object.getOwnPropertyNames(Original).forEach(key => { // Skip the properties that should not be overwritten if (shouldIgnore[key]) { return; } // Some browsers does not allow reconfigure built-in properties const extendedDescriptor = Object.getOwnPropertyDescriptor(Extended, key); if (extendedDescriptor && !extendedDescriptor.configurable) { return; } const descriptor = Object.getOwnPropertyDescriptor(Original, key); // If the user agent does not support `__proto__` or its family (IE <= 10), // the sub class properties may be inherited properties from the super class in TypeScript. // We need to exclude such properties to prevent to overwrite // the component options object which stored on the extended constructor (See #192). // If the value is a referenced value (object or function), // we can check equality of them and exclude it if they have the same reference. // If it is a primitive value, it will be forwarded for safety. if (!hasProto) { // Only `cid` is explicitly exluded from property forwarding // because we cannot detect whether it is a inherited property or not // on the no `__proto__` environment even though the property is reserved. if (key === 'cid') { return; } const superDescriptor = Object.getOwnPropertyDescriptor(Super, key); if (!isPrimitive(descriptor.value) && superDescriptor && superDescriptor.value === descriptor.value) { return; } } // Warn if the users manually declare reserved properties if (process.env.NODE_ENV !== 'production' && reservedPropertyNames.indexOf(key) >= 0) { warn(`Static property name '${key}' declared on class '${Original.name}' ` + 'conflicts with reserved property name of Vue internal. ' + 'It may cause unexpected behavior of the component. Consider renaming the property.'); } Object.defineProperty(Extended, key, descriptor); }); }