# 深入理解 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
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
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
2
3
4
5
6
7
8
9
10
11
12
componentFactory
函数通过 Vue.extend (opens new window) 创建一个 Vue 子类,然后 return
出去,所以 Component
本质上是对 Vue.extend
的封装。
# 参考
- vue-property-decorator (opens new window)
- vue-class-component (index.ts) (opens new window)
- vue-class-component (component.ts) (opens new window)
# 深入理解编译执行过程
# js 下的编译过程
import App from "path/to/App.vue";
console.log(App);
1
2
3
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
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
2
3
输出
function VueComponent(options) {
this._init(options);
}
1
2
3
2
3
这是一个 Vue
子类,参见 (opens new window),印证了我们上面说的 Component
装饰器本质上是对 Vue.extend
的封装。
# 参考
- Vue.js (opens new window)
- Vue.extend (opens new window)
- Vue.js 技术揭秘 - createElement (opens new window)
# 创建 VNode
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
1
2
3
4
5
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
2
3
4
5
6
7
$createElement
的第一个参数对应 _createElement
的第二个参数,以此类推,参见 (opens new window)
由 tag
参数的类型约束可以看出:
string
: 支持使用原生标签创建 VNodeh('div')
Class<Component>
: 支持通过 Vue 子类创建 VNode,也就是 TS 下的创建过程h(VueComponent)
Object
: 支持通过配置参数创建 VNode,也就是 JS 下的创建过程h({ render: function() {} })
Function
: 参见 (opens new window)