export interface ComponentOptions<
V extends Vue,
Data=DefaultData<V>,
Methods=DefaultMethods<V>,
Computed=DefaultComputed,
PropsDef=PropsDefinition<DefaultProps>,
Props=DefaultProps> {
data?: Data;
props?: PropsDef;
propsData?: object;
computed?: Accessors<Computed>;
methods?: Methods;
watch?: Record<string, WatchOptionsWithHandler<any> | WatchHandler<any>>;
el?: Element | string;
template?: string;
// hack is for functional component type inference, should not be used in user code
render?(createElement: CreateElement, hack: RenderContext<Props>): VNode;
renderError?(createElement: CreateElement, err: Error): VNode;
staticRenderFns?: ((createElement: CreateElement) => VNode)[];
beforeCreate?(this: V): void;
created?(): void;
beforeDestroy?(): void;
destroyed?(): void;
beforeMount?(): void;
mounted?(): void;
beforeUpdate?(): void;
updated?(): void;
activated?(): void;
deactivated?(): void;
errorCaptured?(err: Error, vm: Vue, info: string): boolean | void;
serverPrefetch?(this: V): Promise<void>;
directives?: { [key: string]: DirectiveFunction | DirectiveOptions };
components?: { [key: string]: Component<any, any, any, any> | AsyncComponent<any, any, any, any> };
transitions?: { [key: string]: object };
filters?: { [key: string]: Function };
provide?: object | (() => object);
inject?: InjectOptions;
model?: {
prop?: string;
event?: string;
};
parent?: Vue;
mixins?: (ComponentOptions<Vue> | typeof Vue)[];
name?: string;
// TODO: support properly inferred 'extends'
extends?: ComponentOptions<Vue> | typeof Vue;
delimiters?: [string, string];
comments?: boolean;
inheritAttrs?: boolean;
}
1. data——一个函数(返回Vue 实例的数据对象)
-
类型:
Object | Function -
限制:组件的定义只接受
function。 -
详细:
Vue 实例的数据对象。Vue 将会递归将 data 的 property 转换为 getter/setter,从而让 data 的 property 能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个的 key/value 对) :浏览器 API 创建的原生对象,原型上的 property 会被忽略。
一旦观察过,你就无法在根数据对象上添加响应式 property。因此推荐在创建实例之前,就声明所有的根级响应式 property。
实例创建之后,可以通过
vm.$data访问原始数据对象。Vue 实例也代理了 data 对象上所有的 property,因此访问vm.a等价于访问vm.$data.a。以
_或$开头的 property 不会被 Vue 实例代理,因为它们可能和 Vue 内置的 property、API 方法冲突。你可以使用例如vm.$data._property的方式访问这些 property。当一个组件被定义,
data必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果data仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供data函数,每次创建一个新实例后,我们能够调用data函数,从而返回初始数据的一个全新副本数据对象。如果需要,可以通过将
vm.$data传入JSON.parse(JSON.stringify(...))得到深拷贝的原始数据对象。 -
示例:
var data = { a: 1 } // 直接创建一个实例 var vm = new Vue({ data: data }) vm.a // => 1 vm.$data === data // => true // Vue.extend() 中 data 必须是函数 var Component = Vue.extend({ data: function () { return { a: 1 } } })注意,如果你为
dataproperty 使用了箭头函数,则this不会指向这个组件的实例,不过仍然可以将其实例作为函数的第一个参数来访问。data: vm => ({ a: vm.myProp }) -
参考:深入响应式原理
//初始化Vue实例传入的选项数据 function initData (vm: Component) { let data = vm.$options.data // 判断Vue实例的data选项是不是函数,给vm._data赋值 data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} if (!isPlainObject(data)) { data = {} process.env.NODE_ENV !== 'production' && warn( 'data functions should return an object:\n' + 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm ) } // proxy data on instance const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (process.env.NODE_ENV !== 'production') { if (methods && hasOwn(methods, key)) { warn( `Method "${key}" has already been defined as a data property.`, vm ) } } if (props && hasOwn(props, key)) { process.env.NODE_ENV !== 'production' && warn( `The data property "${key}" is already declared as a prop. ` + `Use prop default value instead.`, vm ) } else if (!isReserved(key)) { proxy(vm, `_data`, key) } } // observe data observe(data, true /* asRootData */) }// 如果Vue实例传入一个data函数,调用该data函数 export function getData (data: Function, vm: Component): any { // #7573 disable dep collection when invoking data getters pushTarget() try { return data.call(vm, vm) } catch (e) { handleError(e, vm, `data()`) return {} } finally { popTarget() } }
2. props——一个对象或者数组(用于接收来自父组件的数据)
-
类型:
Array<string> | Object -
详细:
props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
你可以基于对象的语法使用以下选项:
-
type:可以是下列原生构造函数中的一种:String、Number、Boolean、Array、Object、Date、Function、Symbol、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。更多Prop 类型的信息。 -
default:any
为该 prop 指定一个默认值。如果该 prop 没有被传入,则默认使用这个值。对象或数组的默认值必须从一个工厂函数返回。 -
required:Boolean
定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。 -
validator:Function
自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 falsy 的值 (也就是验证失败),一个控制台警告将会被抛出。查阅更多 prop 验证的相关信息。
-
-
示例:
// 简单语法 Vue.component('props-demo-simple', { props: ['size', 'myMessage'] }) // 对象语法,提供验证 Vue.component('props-demo-advanced', { props: { // 检测类型 height: Number, // 检测类型 + 其他验证 age: { type: Number, default: 0, required: true, validator: function (value) { return value >= 0 } } } }) -
参考:Props
``` // Vue实例初始化传入的props选项 function initProps (vm: Component, propsOptions: Object) { const propsData = vm.$options.propsData || {} const props = vm._props = {} // cache prop keys so that future props updates can iterate using Array // instead of dynamic object key enumeration. const keys = vm.$options._propKeys = [] const isRoot = !vm.$parent // root instance props should be converted if (!isRoot) { toggleObserving(false) } for (const key in propsOptions) { keys.push(key) const value = validateProp(key, propsOptions, propsData, vm) /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { const hyphenatedKey = hyphenate(key) if (isReservedAttribute(hyphenatedKey) || config.isReservedAttr(hyphenatedKey)) { warn( `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`, vm ) } defineReactive(props, key, value, () => { if (!isRoot && !isUpdatingChildComponent) { warn( `Avoid mutating a prop directly since the value will be ` + `overwritten whenever the parent component re-renders. ` + `Instead, use a data or computed property based on the prop's ` + `value. Prop being mutated: "${key}"`, vm ) } }) } else { defineReactive(props, key, value) } // static props are already proxied on the component's prototype // during Vue.extend(). We only need to proxy props defined at // instantiation here. if (!(key in vm)) { proxy(vm, `_props`, key) } } toggleObserving(true) } ```
3. propsData——一个对象(创建Vue实例时传递 props)
-
类型:
{ [key: string]: any } -
限制:只用于
new创建的实例中。 -
详细:
创建实例时传递 props。主要作用是方便测试。
-
示例:
var Comp = Vue.extend({ props: ['msg'], template: '<div>{{ msg }}</div>' }) var vm = new Comp({ propsData: { msg: 'hello' } })
4. computed——一个对象(Vue实例的计算属性)
-
类型:
{ [key: string]: Function | { get: Function, set: Function } } -
详细:
计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。
注意如果你为一个计算属性使用了箭头函数,则
this不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。computed: { aDouble: vm => vm.a * 2 }计算属性的结果会被缓存,除非依赖的响应式 property 变化才会重新计算。注意,如果某个依赖 (比如非响应式 property) 在该实例范畴之外,则计算属性是不会被更新的。
function initComputed (vm: Component, computed: Object) { // $flow-disable-line const watchers = vm._computedWatchers = Object.create(null) // computed properties are just getters during SSR const isSSR = isServerRendering() for (const key in computed) { const userDef = computed[key] // 判断传入的计算属性是带get属性的对象还是函数 const getter = typeof userDef === 'function' ? userDef : userDef.get if (process.env.NODE_ENV !== 'production' && getter == null) { warn( `Getter is missing for computed property "${key}".`, vm ) } if (!isSSR) { // create internal watcher for the computed property. watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ) } // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. if (!(key in vm)) { //设置计算属性 defineComputed(vm, key, userDef) } else if (process.env.NODE_ENV !== 'production') { if (key in vm.$data) { warn(`The computed property "${key}" is already defined in data.`, vm) } else if (vm.$options.props && key in vm.$options.props) { warn(`The computed property "${key}" is already defined as a prop.`, vm) } else if (vm.$options.methods && key in vm.$options.methods) { warn(`The computed property "${key}" is already defined as a method.`, vm) } } } } -
示例:
var vm = new Vue({ data: { a: 1 }, computed: { // 仅读取 aDouble: function () { return this.a * 2 }, // 读取和设置 aPlus: { get: function () { return this.a + 1 }, set: function (v) { this.a = v - 1 } } } }) vm.aPlus // => 2 vm.aPlus = 3 vm.a // => 2 vm.aDouble // => 4 -
参考:计算属性
5. methods——一个对象(包含Vue实例方法)
-
类型:
{ [key: string]: Function } -
详细:
methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的
this自动绑定为 Vue 实例。注意,不应该使用箭头函数来定义 method 函数 (例如
plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以this将不会按照期望指向 Vue 实例,this.a将是 undefined。 -
示例:
var vm = new Vue({ data: { a: 1 }, methods: { plus: function () { this.a++ } } }) vm.plus() vm.a // 2 -
参考:事件处理器
6. watch——一个对象(包含需要观察的表达式)
-
类型:
{ [key: string]: string | Function | Object | Array } -
详细:
一个对象,键名是需要观察的表达式,对应值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用
$watch(),遍历 watch 对象的每一个 property。 -
示例:
var vm = new Vue({ data: { a: 1, b: 2, c: 3, d: 4, e: { f: { g: 5 } } }, watch: { a: function (val, oldVal) { console.log('new: %s, old: %s', val, oldVal) }, // 方法名 b: 'someMethod', // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深 c: { handler: function (val, oldVal) { /* ... */ }, deep: true }, // 该回调将会在侦听开始之后被立即调用 d: { handler: 'someMethod', immediate: true }, // 你可以传入回调数组,它们会被逐一调用 e: [ 'handle1', function handle2 (val, oldVal) { /* ... */ }, { handler: function handle3 (val, oldVal) { /* ... */ }, /* ... */ } ], // watch vm.e.f's value: {g: 5} 'e.f': function (val, oldVal) { /* ... */ } } }) vm.a = 2 // => new: 2, old: 1注意,不应该使用箭头函数来定义 watcher 函数 (例如
searchQuery: newValue => this.updateAutocomplete(newValue))。理由是箭头函数绑定了父级作用域的上下文,所以this将不会按照期望指向 Vue 实例,this.updateAutocomplete将是 undefined。