Vue-property-decoratorソースコードの読み取り

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

  • @見る

  • @モデル

  • ミックスイン

これらのデコレータがどのように実装されているかを確認するために、ソースコードを見てみましょう。

@成分

Componentvue-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));
    };
}

おすすめ

転載: www.cnblogs.com/dora-zc/p/12687156.html