# 深入理解 Component 装饰器(对比 JS)

Vue 通过 装饰器 实现 class-style 语法。

下面通过源码看一下 Component 究竟是什么和做了什么:

# 导入 Component 装饰器

import { Component, Vue } from "vue-property-decorator";
1

# vue-property-decorator

vue-property-decorator 中的 Component 直接引用自 vue-class-component详情 (opens new window)


 






 

import Vue, { PropOptions, WatchOptions } from "vue";
import Component, { createDecorator, mixins } from "vue-class-component";
import { InjectKey } from "vue/types/options";

export type Constructor = {
  new (...args: any[]): any;
};

export { Component, Vue, mixins as Mixins };
1
2
3
4
5
6
7
8
9

# vue-class-component

# Component 定义







 
 
 
 
 
 
 
 
 
 







import Vue, { ComponentOptions } from "vue";
import { VueClass } from "./declarations";
import { componentFactory, $internalHooks } from "./component";

export { createDecorator, VueDecorator, mixins } from "./util";

function Component<V extends Vue>(options: ComponentOptions<V> & ThisType<V>): <VC extends VueClass<V>>(target: VC) => VC;
function Component<VC extends VueClass<Vue>>(target: VC): VC;
function Component(options: ComponentOptions<Vue> | VueClass<Vue>): any {
  if (typeof options === "function") {
    return componentFactory(options);
  }
  return function(Component: VueClass<Vue>) {
    return componentFactory(Component, options);
  };
}

Component.registerHooks = function registerHooks(keys: string[]): void {
  $internalHooks.push(...keys);
};

export default Component;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

这里使用了 TS 里的 函数重载,允许 @Component@Component(options) 两种写法。

# componentFactory 定义







 






export function componentFactory(Component: VueClass<Vue>, options: ComponentOptions<Vue> = {}): VueClass<Vue> {
  // ...

  // find super
  const superProto = Object.getPrototypeOf(Component.prototype);
  const Super = superProto instanceof Vue ? (superProto.constructor as VueClass<Vue>) : Vue;
  const Extended = Super.extend(options);

  // ...

  return Extended;
}
1
2
3
4
5
6
7
8
9
10
11
12

componentFactory 函数通过 Vue.extend (opens new window) 创建一个 Vue 子类,然后 return 出去,所以 Component 本质上是对 Vue.extend 的封装。

# 参考

# 深入理解编译执行过程

# js 下的编译过程

import App from "path/to/App.vue";

console.log(App);
1
2
3

输出

{
  "name": "app",
  "components": {},
  "data": function() {},
  "computed": {},
  "watch": {},
  "methods": {},
  "beforeCreate": function() {},
  "created": function() {},
  "beforeDestroy": function() {},
  "render": function() {}
  // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13

输出结果是一个包含组件选项的对象。

# ts 下的编译过程

import App from "path/to/App.vue";

console.log(App);
1
2
3

输出

function VueComponent(options) {
  this._init(options);
}
1
2
3

这是一个 Vue 子类,参见 (opens new window),印证了我们上面说的 Component 装饰器本质上是对 Vue.extend 的封装。

# 参考

# 创建 VNode




 


new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount("#app");
1
2
3
4
5

render 函数中的 h 其实是 $createElement 函数, 参见 (opens new window)

Vue.js 利用 createElement 方法创建 VNode,参见 (opens new window)



 





export function _createElement(
  context: Component,
  tag?: string | Class<Component> | Function | Object,
  data?: VNodeData,
  children?: any,
  normalizationType?: number,
): VNode | Array<VNode> {}
1
2
3
4
5
6
7

$createElement 的第一个参数对应 _createElement 的第二个参数,以此类推,参见 (opens new window)

tag 参数的类型约束可以看出:

  • string: 支持使用原生标签创建 VNode h('div')
  • Class<Component>: 支持通过 Vue 子类创建 VNode,也就是 TS 下的创建过程 h(VueComponent)
  • Object: 支持通过配置参数创建 VNode,也就是 JS 下的创建过程 h({ render: function() {} })
  • Function: 参见 (opens new window)