Vue-property-decorator source code reading

In vue + ts project, we will use vue-property-decoratorthis library, scriptthe code becomes this:

<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>

Code by reference relationship can be found vue-property-decoratorto achieve is dependent on vue-class-component, it has the following properties:

  • @Component

  • @Emit

  • @Inject

  • @Provice

  • @Prop

  • @Watch

  • @Model

  • Mixins

Let's take a look at the source code to see how these decorators are implemented.

@Component

ComponentIs vue-class-componentrealized in.

import Component, { mixins } from 'vue-class-component';

Let's take a lookvue-class-component

node_modules/vue-class-component/lib/index.js

The Componentmethod is defined here , and the parameter optionstype is judged . If it is not a function type, it is necessary to include componentFactorythe call result returned by a layer of function .

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;

The following is @Componentthe method we usually use . Here, if options is a function type, it means that options is Appthis class function, if it is not a function type, it means that options are incoming {}configuration items.

@Component({
  components: {
    HelloWorld
  }
})
export default class App extends Vue {}

Look at componentFactorywhat the method does.

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

The most important thing here is to return a function, that is, a property decorator, which createDecoratorcalls the method in the function , passing in the current class (vue component) and the decorated property as parameters.

/**
 * 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);
    };
}

Below we look at createDecoratorthe implementation of the method.

node_modules/vue-class-component/lib/util.js

First, by targetobtaining the constructor Ctor of the current component, determine if the parameter targetis a function, then take it target, if it targetis an instance object of the component, take it target.constructor;

Then __decorators__put the incoming function into the properties of the constructor factory. factoryAs you can see above, the parameters passed by the decorator are optionsmounted on the component configuration 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));
    };
}

Guess you like

Origin www.cnblogs.com/dora-zc/p/12687156.html