VUE + TSプロジェクトでは、我々が使用するvue-property-decorator
このライブラリを、script
コードはこのようになります。
<template>
<div id="app">
<HelloWorld msg="this is a msg from app.vue" />
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import HelloWorld from './components/HelloWorld.vue'
@Component({
components: {
HelloWorld
}
})
export default class App extends Vue {}
</script>
参照関係によってコードを見つけることができるvue-property-decorator
に依存して達成するためにvue-class-component
、それは次のプロパティがあります。
-
@成分
-
@Emit
-
@Inject
-
@Provice
-
@Prop
-
@見る
-
@モデル
-
ミックスイン
これらのデコレータがどのように実装されているかを確認するために、ソースコードを見てみましょう。
@成分
Component
でvue-class-component
実現されています。
import Component, { mixins } from 'vue-class-component';
見てみましょうvue-class-component
node_modules / vue-class-component / lib / index.js
ここでComponent
メソッドを定義し、パラメータのoptions
型を判定しますが、関数型でない場合は、関数の層から返さcomponentFactory
れた呼び出し結果を含める必要があります。
import { componentFactory, $internalHooks } from './component';
export { createDecorator, mixins } from './util';
function Component(options) {
// 这里如果是函数类型,说明 options 就是 继承于 Vue 的 class 函数
if (typeof options === 'function') {
return componentFactory(options);
}
return function (Component) {
return componentFactory(Component, options);
};
}
Component.registerHooks = function registerHooks(keys) {
$internalHooks.push(...keys);
};
export default Component;
以下は@Component
、私たちが通常使用するメソッドです。ここで、optionsが関数タイプの場合、optionsがApp
このクラス関数であることを意味し、関数タイプでない場合、オプションは受信{}
構成アイテムであることを意味します。
@Component({
components: {
HelloWorld
}
})
export default class App extends Vue {}
componentFactory
メソッドが何をするか見てください。
node_modules / vue-class-component / lib / component.js
export function componentFactory(Component, options = {}) {
options.name = options.name || Component._componentTag || Component.name;
// prototype props.
const proto = Component.prototype;
// 遍历组件原型对象上的每一个属性,根据属性值的类型,处理 hooks,methods,mixins和computed
Object.getOwnPropertyNames(proto).forEach(function (key) {
if (key === 'constructor') {
return;
}
// hooks
if ($internalHooks.indexOf(key) > -1) {
options[key] = proto[key];
return;
}
// Object.getOwnPropertyDescriptor 返回组件原型对象上自有属性对应的属性描述符
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;
}
node_modules / vue-class-component / lib / data.js
export function collectDataFromConstructor(vm, Component) {
// override _init to prevent to init as Vue instance
const originalInit = Component.prototype._init;
Component.prototype._init = function () {
// proxy to actual vm
const keys = Object.getOwnPropertyNames(vm);
// 2.2.0 compat (props are no longer exposed as self properties)
if (vm.$options.props) {
for (const key in vm.$options.props) {
if (!vm.hasOwnProperty(key)) {
keys.push(key);
}
}
}
keys.forEach(key => {
if (key.charAt(0) !== '_') {
Object.defineProperty(this, key, {
get: () => vm[key],
set: value => { vm[key] = value; },
configurable: true
});
}
});
};
// should be acquired class property values
const data = new Component();
// restore original _init to avoid memory leak (#209)
Component.prototype._init = originalInit;
// create plain data object
const plainData = {};
Object.keys(data).forEach(key => {
if (data[key] !== undefined) {
plainData[key] = data[key];
}
});
if (process.env.NODE_ENV !== 'production') {
if (!(Component.prototype instanceof Vue) && Object.keys(plainData).length > 0) {
warn('Component class must inherit Vue or its descendant class ' +
'when class property is used.');
}
}
return plainData;
}
@Prop
node_modules / vue-property-decorator / lib / vue-property-decorator.js
ここで最も重要なことは、関数、つまり、関数のcreateDecorator
メソッドを呼び出し、現在のクラス(vueコンポーネント)と装飾されたプロパティをパラメーターとして渡すプロパティデコレーターを返すことです。
/**
* decorator of a prop
* @param options the options for the prop
* @return PropertyDecorator | void
*/
export function Prop(options) {
if (options === void 0) { options = {}; }
return function (target, key) {
applyMetadata(options, target, key);
createDecorator(function (componentOptions, k) {
;
(componentOptions.props || (componentOptions.props = {}))[k] = options;
})(target, key);
};
}
以下ではcreateDecorator
、メソッドの実装について見ていきます。
node_modules / vue-class-component / lib / util.js
最初によってtarget
パラメータがあればコンストラクタCTOR電流成分を得ることが決定されtarget
、それが取る関数であるtarget
場合、target
オブジェクトインスタンスの成分が取られますtarget.constructor
。
次に__decorators__
、着信関数をコンストラクターのプロパティに配置しfactory
ます。factory
上記のように、デコレータによって渡されたパラメータはoptions
、コンポーネント構成オプションにマウントされていますprops
。
export function createDecorator(factory) {
return (target, key, index) => {
const Ctor = typeof target === 'function'
? target
: target.constructor;
if (!Ctor.__decorators__) {
Ctor.__decorators__ = [];
}
if (typeof index !== 'number') {
index = undefined;
}
Ctor.__decorators__.push(options => factory(options, key, index));
};
}