npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@haixing_hu/vue3-class-component

v2.3.0

Published

A JavaScript class decorator for Vue3 components

Downloads

719

Readme

vue3-class-component

npm package License English Document CircleCI Coverage Status

这个库允许您使用类式语法创建您的 Vue 组件。它从 vue-class-component 得到了很多灵感,但有一些显著的区别:

目录

安装方式

yarn add @haixing_hu/vue3-class-component @haixing_hu/typeinfo @haixing_hu/clone

or

npm install @haixing_hu/vue3-class-component @haixing_hu/typeinfo @haixing_hu/clone

注意:这个库依赖于 @haixing_hu/typeinfo@haixing_hu/clone 库,因此您必须同时安装它们。

配置方式

这个库使用了最新的(截止到2023年5月)JavaScript 装饰器第3阶段提案JavaScript 装饰器元数据第3阶段提案,因此您必须配置 Babel, 使用 @babel/plugin-transform-class-properties@babel/plugin-proposal-decorators 插件。

注意: 为了支持 JavaScript 装饰器元数据第3阶段提案, 插件 @babel/plugin-proposal-decorators 的版本号必须至少为 7.23.0

注意: @babel/helpers 在大于 7.23.0 但小于 8.0.0 (尚未发布) 的版本上,有个 严重的 bug:它错误地将装饰在类上的装饰器的上下文中的 kind 属性设置为 'field',而实际上应 设置为 'class'。更多详细信息,请参见 Babelissue #16179issue #16180。 因此,我们需要在 package.json 中强制使用 7.23.0 版本的 @babel/helpers。具体而言, 应该在 package.json中加上下面这段代码:

{
  "resolutions": {
    "@babel/helpers": "7.23.0"
  }
}

使用 webpack 打包

  1. 安装需要的依赖:
    yarn add @haixing_hu/vue3-class-component @haixing_hu/typeinfo @haixing_hu/clone
    yarn add --dev @babel/core @babel/runtime @babel/preset-env 
    yarn add --dev @babel/plugin-proposal-decorators @babel/plugin-transform-class-properties @babel/plugin-transform-runtime
  2. 配置 Babel,使用 @babel/plugin-transform-class-properties@babel/plugin-proposal-decorators 插件。一个可能的 Babel 配置文件 babelrc.json 如下:
    {
      "presets": [
        "@babel/preset-env"
      ],
      "plugins": [
        "@babel/plugin-transform-runtime",
        ["@babel/plugin-proposal-decorators", { "version": "2023-05" }],
        "@babel/plugin-transform-class-properties"
      ]
    }

详细配置过程可以参考:

使用 vite 打包

  1. 安装需要的依赖:
    yarn add @haixing_hu/vue3-class-component @haixing_hu/typeinfo @haixing_hu/clone
    yarn add --dev @babel/core @babel/runtime @babel/preset-env 
    yarn add --dev @babel/plugin-proposal-decorators @babel/plugin-transform-class-properties @babel/plugin-transform-runtime
  2. 配置 Babel,使用 @babel/plugin-transform-class-properties@babel/plugin-proposal-decorators 插件。一个可能的 Babel 配置文件 babelrc.json 如下:
    {
      "presets": [
        ["@babel/preset-env", { "modules": false }]
      ],
      "plugins": [
        "@babel/plugin-transform-runtime",
        ["@babel/plugin-proposal-decorators", { "version": "2023-05" }],
        "@babel/plugin-transform-class-properties"
      ]
    }
    注意: 使用 vite 打包需要将 @babel/preset-env 的参数 modules 设置为 false
  3. 配置 vite,修改 vite.config.js 文件,使其支持 Babel。一个可能的 vite.config.js 文件如下:
    import { fileURLToPath, URL } from 'node:url';
    import { defineConfig } from 'vite';
    import vue from '@vitejs/plugin-vue';
    import * as babel from '@babel/core';
        
    // A very simple Vite plugin support babel transpilation
    const babelPlugin = {
      name: 'plugin-babel',
      transform: (src, id) => {
        if (/\.(jsx?|vue)$/.test(id)) {               // the pattern of the file to handle
          return babel.transform(src, {
            filename: id,
            babelrc: true,
          });
        }
      },
    };
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        vue({
          script: {
            babelParserPlugins: ['decorators'],     // must enable decorators support
          },
        }),
        babelPlugin,                                // must after the vue plugin
      ],
      resolve: {
        alias: {
          '@': fileURLToPath(new URL('./src', import.meta.url)),
        },
      },
    });
    注意: 在上面配置文件中我们实现了一个简单的 Vite 插件用于将 vite-plugin-vue 插件处理过的代码通过 babel 转译。虽然有个 vite-plugin-babel 插件声称可以让 vite 支持 babel,但我们发现它无法正确处理 vue 的 SFC 格式 (*.vue格式文件)。仔细研究 它的源码后,我们发现要实现正确的转译,必须在 vite-plugin-vue 插件处理过源码之后再使用 babel 进行转译,因此只需上面非常简单的插件函数即可实现我们需要的功能。作为一个替代选择, 你可以使用 我们的 vite-plugin-babel 插件,下面是一个配置示例:
    import { fileURLToPath, URL } from 'node:url';
    import { defineConfig } from 'vite';
    import vue from '@vitejs/plugin-vue';
    import babel from '@haixing_hu/vite-plugin-babel';
    
    export default defineConfig({
      plugins: [
        vue({
          script: {
            babelParserPlugins: ['decorators'],     // must enable decorators support
          },
        }),
        babel(),
      ],
      resolve: {
        alias: {
          '@': fileURLToPath(new URL('./src', import.meta.url)),
        },
      },
    });

详细配置过程可以参考:

使用示例

<template>
  <div class="hello-page">
    <div class="message">{{ message }}</div>
    <div class="computed-message">{{ computedMessage }}</div>
    <div class="value">{{ value }}</div>
    <input v-model="newMessage">
    <button @click="setMessage(newMessage)">Set Message</button>
  </div>
</template>
<script>
import { Component, toVue } from 'vue3-class-component';

@Component
class HelloPage {
  message = 'hello';

  value = 0;

  newMessage = '';

  mounted() {
    this.value = this.$route.params.value;
  }

  get computedMessage() {
    return this.message + '!';
  }

  setMessage(s) {
    this.message = s;
  }
}

export default toVue(MyComponent); // don't forget calling `toVue`
</script>

上述代码等效于:

<template>
  <div class="hello-page">
    <div class="message">{{ message }}</div>
    <div class="computed-message">{{ computedMessage }}</div>
    <div class="value">{{ value }}</div>
    <input v-model="newMessage">
    <button @click="setMessage(newMessage)">Set Message</button>
  </div>
</template>
<script>
export default {
  name: 'HelloPage',
  data() {
    return {
      message: 'hello',
      value: 0,
      newMessage: '',
    };
  },
  mounted() {
    this.value = this.$route.params.value;
  },
  computed: {
    computedMessage() {
      return this.message + '!';
    },
  },
  methods: {
    setMessage(s) {
      this.message = s;
    },
  },
};
</script>

支持的选项

@Component 装饰器可以与选项参数一起使用,这些选项参数将传递给生成的 Vue 组件的选项。例如:

@Component({
  name: 'Hello',  // override the name of the class
  components: {
    PhoneLink,
  },
  filters: {
    capitalize: (s) => s.toUpperCase(),
  },
})
class HelloPage {
  message = 'hello';

  value = 0;

  newMessage = '';

  mounted() {
    this.value = this.$route.params.value;
  }

  get computedMessage() {
    return this.message + '!';
  }

  setMessage(s) {
    this.message = s;
  }
}

export default toVue(MyComponent); // don't forget calling `toVue`

上述代码等效于:

export default {
  name: 'Hello',
  components: {
    PhoneLink,
  },
  filters: {
    capitalize: (s) => s.toUpperCase(),
  },
  data() {
    return {
      message: 'hello',
      value: 0,
      newMessage: '',
    };
  },
  mounted() {
    this.value = this.$route.params.value;
  },
  computed: {
    computedMessage() {
      return this.message + '!';
    },
  },
  methods: {
    setMessage(s) {
      this.message = s;
    },
  },
};

下表列出了Vue选项API中的所有关键词以及它们是否受 @Component 装饰器参数的支持:

| 类别 | 选项 | 是否支持 | 描述 | |-------------|-------------------|-------|------------------------------------------------| | State | data | 否 | 组件的响应式状态应该定义为类字段。 | | State | props | 否 | 组件的属性应该定义为类字段,并使用 @Prop 装饰器标记。 | | State | computed | 否 | 计算属性应该定义为类 getter。 | | State | methods | 否 | 组件的方法应该定义为类方法。 | | State | watch | 否 | 观察者应该定义为类方法,并使用 @Watch 装饰器标记。 | | State | emits | 是 | 由 Vue 组件发出的自定义事件可以在 @Component 的选项中声明。 | | State | expose | 是 | 可公开的公共属性可以在 @Component 的选项中声明。 | | Rendering | template | 是 | 字符串模板可以在 @Component 的选项中声明。 | | Rendering | render | 否 | 渲染函数应该定义为类方法。 | | Rendering | compilerOptions | 是 | 字符串模板的编译器选项可以在 @Component 的选项中声明。 | | Rendering | slot | 是 | Vue 组件的插槽可以在 @Component 的选项中声明。 | | Lifecycle | beforeCreate | 否 | beforeCreate 钩子应该定义为类方法。 | | Lifecycle | created | 否 | created 钩子应该定义为类方法。 | | Lifecycle | beforeMount | 否 | beforeMount 钩子应该定义为类方法。 | | Lifecycle | mounted | 否 | mounted 钩子应该定义为类方法。 | | Lifecycle | beforeUpdate | 否 | beforeUpdate 钩子应该定义为类方法。 | | Lifecycle | updated | 否 | updated 钩子应该定义为类方法。 | | Lifecycle | beforeUnmount | 否 | beforeUnmount 钩子应该定义为类方法。 | | Lifecycle | unmounted | 否 | unmounted 钩子应该定义为类方法。 | | Lifecycle | errorCaptured | 否 | errorCaptured 钩子应该定义为类方法。 | | Lifecycle | renderTracked | 否 | renderTracked 钩子应该定义为类方法。 | | Lifecycle | renderTriggered | 否 | renderTriggered 钩子应该定义为类方法。 | | Lifecycle | activated | 否 | activated 钩子应该定义为类方法。 | | Lifecycle | deactivated | 否 | deactivated 钩子应该定义为类方法。 | | Lifecycle | serverPrefetch | 否 | serverPrefetch 钩子应该定义为类方法。 | | Composition | provide | 否 | provide 属性应该定义为类字段,并使用 @Provide 装饰器标记。 | | Composition | inject | 否 | inject 属性应该定义为类字段,并使用 @Inject 装饰器标记。 | | Composition | mixins | 是 | 可以在 @Component 的选项中声明混入的对象数组。 | | Composition | extends | 是 | 可以在 @Component 的选项中声明要扩展的基础 Vue 组件。 | | Misc | name | 是 | Vue 组件的名称可以在 @Component 的选项中声明;否则将使用装饰的类的类名。 | | Misc | inheritAttrs | 是 | inheritAttrs 可以在 @Component 的选项中声明。 | | Misc | components | 是 | Vue 组件的注册组件可以在 @Component 的选项中声明。 | | Misc | directives | 是 | Vue 组件的注册指令可以在 @Component 的选项中声明。 |

预定义装饰器

这个库为类式 Vue 组件提供了以下常用装饰器:

@Prop 装饰器

@Prop 装饰器应用在类字段上,用于声明 Vue 组件的 props。

例如:

@Component
class MyComponent {
  // 如果新属性定义时有默认值,则无需指定其类型和默认值,系统会自动推断其类型
  @Prop
  message = 'hello';

  @Prop({ type: Number, validator: (v) => (v >= 0) })
  value;

  // 非基本数据类型的默认值 **无需** 用工厂函数包装
  @Prop
  person = {
    id: 1,
    name: 'John',
    age: 32,
    gender: 'MALE',
  };
  
  // 多个可能的属性类型,可以表示为一个构造器数组
  @Prop({ type: [Boolean, String] })
  lazy;

  // 如果装饰器的参数是一个函数,它将被认为是新属性的类型
  @Prop(Number)
  value2;

  // 如果装饰器的参数是一个函数数组,它将被认为是新属性的可能的类型
  @Prop([Boolean, String, Number])
  value3;
}

export default toVue(MyComponent);

上述代码等效于:

export default {
  name: 'MyComponent',
  props: {
    message: {
      type: String,
      default: 'hello',
    },
    value: {
      type: Number,
      required: true,
      validator: (v) => {
        return v >= 0
      },
    },
    person: {
      type: Object,
      required: false,
      default: () => ({
        id: 1,
        name: 'John',
        age: 32,
        gender: 'MALE',
      }),
    },
    lazy: {
      type: [Boolean, String],
      required: true,
    },
    value2: {
      type: Number,
      required: true,
    },
    value3: {
      type: [Boolean, String, Number],
      required: true,
    },
  },
};

@Prop 装饰器可以有一个可选参数。@Prop 装饰器的参数是一个包含以下选项的对象:

| 选项 | 类型 | 默认值 | 描述 | |-------------|------------|-------------|--------------------| | type | Function | undefined | Prop 的数据类型,应为构造函数。 | | required | Boolean | false | 指示是否需要传递该 prop。 | | default | any | undefined | 指定 prop 的默认值。 | | validator | Function | undefined | 用于 prop 的自定义验证函数。 |

  • type: 此选项定义了 prop 期望的数据类型,可以是以下之一:StringNumberBooleanArrayObjectDateFunctionSymbol、自定义的类、自定义的构造 函数,或这些类型的数组。在开发模式下,Vue 会验证 prop 的值是否与声明的类型匹配,如果不匹配, 将发出警告。有关更多详情,请参阅属性校验

    如果一个 prop允许多种可能的类型,可以在这个选项中指定一个构造函数数组。例如: { type: [Boolean, String] }

    请注意,具有 Boolean 类型的 prop 在开发和生产模式下都会影响其值的类型转换行为。有关更多 详情,请参阅布尔型强制转换

    如果未指定此选项,则库将从装饰的类字段的初始值中推断出类型。

  • default: 使用此选项为 prop 指定默认值,当它未由父组件传递或具有未定义的值时将会生效。对 象或数组类型的默认值必须使用工厂函数返回。工厂函数还接收原始的 prop 对象作为参数。如果未指定 此选项,则库将自动从装饰的类字段的初始值中推断默认值。

    值得注意的是,Vue 库要求 prop 的非原始类型默认值必须使用工厂函数包装,但我们的库会自动处 理此问题。因此,在声明 prop 时,不需要使用工厂函数包装非原始类型的默认值。

  • required: 使用此选项来指定是否必须传递 prop。在非生产环境中,如果此值为真且未提供 prop, 则会生成控制台警告。

    如果未指定此选项,则库将自动推断装饰的类字段的初始值是否已提供,以确定是否需要 prop。

  • validator: 此选项允许您定义一个自定义验证函数,该函数以 prop 值作为其唯一参数。在开发模 式下,如果此函数返回假值(即验证失败),则会生成控制台警告。

如果 @Prop 装饰器的参数是一个函数,或者一个函数组成的数组,它将被认为是为新 prop 指定的类型。 例如:

@Component
class MyComponent {
  @Prop(Number)
  value1;

  @Prop([Boolean, String, Number])
  value2;
}

如果属性定义时给出了默认值,则无需再指定其类型和默认值,系统会自动推断。例如:

@Component
class MyComponent {
  @Prop
  message = '';

  @Prop
  value = 0;
}

@VModel 装饰器

@VModel 装饰器与 @Prop 装饰器类似,不同之处在于它支持 v-model 绑定。 有关更多详细信息,请参见组件 v-model

例如:

<template>
  <div class="my-component">
    <input v-model="message" />
  </div>
</template>
<script>
  import { Component, VModel, toVue } from '@haixing_hu/vue3-class-component';

  @Component
  class MyComponent {
    @VModel({ type: String, validator: (v) => (v.length >= 0) })
    message;
  }

  export default toVue(MyComponent);
</script>

等同于:

<template>
  <div class="my-component">
    <input v-model="message" />
  </div>
</template>
<script>
export default {
  name: 'MyComponent',
  props: {
    modelValue: {
      type: String,
      required: true,
      validator: (v) => {
        return v.length >= 0;
      },
    },
  },
  emits: ['update:modelValue'],
  computed: {
    message: {
      get() {
        return this.modelValue;
      },
      set(value) {
        this.$emit('update:modelValue', value);
      },
    },
  },
};
</script>

上述自定义组件中的@VModel属性可按如下方式使用:

<template>
  <div class="use-my-component">
    <my-component v-model="msg" />
  </div>
</template>

注意:

  • 默认的 v-model 绑定属性名 'modelValue' 不应用作类字段名或类方法名。
  • 为了简化实现,这个库支持多个 v-model 绑定。此外,它也支持 v-model 修饰符, 也不允许更改默认的 v-model 绑定属性名。

类似于 @Prop 装饰器,@VModel 装饰器也可以接受一个可选参数。这个用于 @VModel 装饰器的参数是一个包含附加选项的对象。@VModel 可用的选项与 @Prop 支持的选项完全相同。 有关更多详细信息,请参见 @Prop 装饰器

@Watch 装饰器

@Watch 装饰器应用在类方法上,用于声明 Vue 组件的观察者。

例如:

@Component
class MyComponent {
  value = 123;

  person = {
    id: 1,
    name: 'John',
    age: 32,
    gender: 'MALE',
  };

  @Watch('value')
  onValueChanged(val, oldVal) {
    console.log(`The value is changed from ${oldVal} to ${val}.`);
  }

  @Watch('person', { deep: true })
  onPersonChanged(val, oldVal) {
    console.log(`The person is changed from ${oldVal} to ${val}.`);
  }
}

export default toVue(MyComponent);

上述代码等效于:

export default {
  name: 'MyComponent',
  data() {
    return {
      value: 123,
    };
  },
  watch: {
    value(val, oldVal) {
      console.log(`The value is changed from ${oldVal} to ${val}.`);
    },
    person: {
      deep: true,
      handler(val, oldVal) {
        console.log(`The person is changed from ${oldVal} to ${val}.`);
      },
    },
  },
};

@Watch 装饰器可以带有一个或两个参数。@Watch 装饰器的第一个参数是被监视的状态或属性的路径, 第二个可选参数是一个包含以下选项的对象:

| 选项 | 类型 | 默认值 | 描述 | |-------------|-----------|---------|------------------------------------------------| | deep | Boolean | false | 观察者是否应深度遍历源对象(如果它是对象或数组)。 | | immediate | Boolean | false | 观察者是否应在创建后立即调用。 | | flush | String | 'pre' | 观察者的刷新时机。可以是 'pre''post''sync' 中的一个。 |

  • deep: 如果源对象是对象或数组,强制深度遍历,以便在发生深层变化时触发回调。 请参阅深度观察者
  • immediate: 在观察者创建时立即触发回调。第一次调用时旧值将为 undefined。 请参阅急切观察者
  • flush: 调整回调的刷新时机。可以是 'pre''post''sync' 中的一个。 请参阅回调刷新时机watchEffect()

注意:vue-property-decorator 中的 @Watch 装饰器不同,此库中的 @Watch 装饰器不支持使用多个观察处理程序同时监视相同的状态或属性。因为这不是常见用例,所以我们决定简 化@Watch装饰器的实现。

@Provide 装饰器

@Provide 装饰器应用在类字段上,用于声明可以由子组件注入的提供的值。

例如:

const myInjectedKey = Symbol('myInjectedKey');

@Component
class AncestorComponent {
  @Provide
  message = 'hello';

  @Provide({key: myInjectedKey, reactive: true})
  @Prop
  value = 123;

  @Provide({ reactive: true })
  person = {
    id: 1,
    name: 'John',
    age: 32,
    gender: 'MALE',
  };
}

export default toVue(AncestorComponent);

上述代码等效于:

import { computed } from 'vue'

export default {
  name: 'AncestorComponent',
  props: {
    value: {
      type: Number,
      default: 123,
      required: false,
    },
  },
  data() {
    return {
      message: 'hello',
    };
  },
  provide() {
    return {
      message: this.message,                        // non-reactive
      [myInjectedKey]: computed(() => this.value),  // reactive
      person: computed(() => this.person),          // reactive
    };
  },
};

@Provide@Inject 装饰器一起使用,允许祖先组件作为所有后代组件的依赖注入器,无论组件 层次结构有多深,只要它们在同一父级链中。有关详细信息,请参阅Provide / Inject

@Provide 装饰器可以带有一个可选参数。可选参数是一个包含以下选项的对象:

| 选项 | 类型 | 默认值 | 描述 | |------------|--------------------|-------------|-------------| | key | String \| Symbol | undefined | 提供值的键。 | | reactive | Boolean | false | 提供值是否是响应式的。 |

  • key: 子组件使用键来定位要注入的正确值。键可以是字符串或符号。有关更多详情,请参阅使用符号键。 如果未指定此选项,则将使用由 @Provide 装饰器装饰的字段的名称作为键。
  • reactive: 指示提供的值是否是响应式的。默认情况下,提供的值不是响应式的,即在祖先组件中更 改提供的值不会影响后代组件中注入的值。如果将此选项设置为 true,则提供的值将变为响应式。 有关更多详情,请参阅使用响应式

注意: vue-property-decorator 提供了 @Provide@ProvideReactive 装饰器, 分别用于声明非响应式和响应式的提供的值。但此库通过提供一个带有可选 reactive 选项的 @Provide 装饰器来简化实现。由于提供的值通常是非响应式的,我们决定将 reactive 选项的默认 值设置为 false

@Inject 装饰器

@Inject 装饰器应用在类字段上,用于声明被注入的值。

例如:

@Component
class DescendantComponent {
  @Inject
  message;

  @Inject({from: myInjectedKey, default: 0})
  injectedValue;

  // non-primitive default value DO NOT need to be wrapped by a factory function
  @Inject({ default: {id: 0, name: 'unknown'} })
  person;
}

export default toVue(DescendantComponent);

上述代码等效于:

export default {
  name: 'DescendantComponent',
  inject: {
    message: {              // non-reactive
      from: 'message',
      default: undefined,
    },
    injectedValue: {        // reactive, since the provided `myInjectedKey` is reactive
      from: myInjectedKey,
      default: 0,
    },
    person: {               // reactive, since the provided `person` is reactive
      from: 'person',
      default: () => ({id: 0, name: 'unknown'}),
    },
  },
};

@Provide@Inject 装饰器一起使用,允许祖先组件作为所有后代组件的依赖注入器,无论组件 层次结构有多深,只要它们在同一父级链中。有关详细信息,请参阅Provide / Inject

@Inject 装饰器可以带有一个可选参数。可选参数是一个包含以下选项的对象:

| 选项 | 类型 | 默认值 | 描述 | |-----------|--------------------|-------------|-------------| | from | String \| Symbol | undefined | 要注入的源提供值的键。 | | default | any | undefined | 被注入值的默认值。 |

  • from: 此选项的值指定要注入的提供值的键。键可以是字符串或符号。有关更多详情,请参阅使用符号键。 如果未指定此选项,则将使用由 @Inject 装饰器装饰的字段的名称作为键。
  • default: 被注入属性的默认值。请注意,类似于 @Prop 装饰器的 default 选项,此库将自 动将非原始类型的默认值转换为工厂函数。

注意: 如果提供的值是非响应式的,则相应的注入值也是非响应式的。如果提供的值是响应式的, 则相应的注入值也是响应式的。有关更多详情,请参阅使用响应式

注意: vue-property-decorator 提供了 @Inject@InjectReactive 装饰器, 分别用于声明非响应式和响应式的注入值。但此库通过提供只有一个 @Inject 装饰器来简化实现, 并且注入值的响应性由提供值的响应性决定。

@Raw 装饰器

@Raw 装饰器标记在类字段上,用于声明该字段应被视为原始值,这意味着它不应被包装在响应式代理中。 当你想要将非响应式属性注入到 Vue 组件中时,这个装饰器非常有用。这个装饰器的作用有点类似于 composition API 中的 markRaw() 函数。

例如

@Component
class MyComponent {

  message = 'hello';

  @Raw
  rawValue = 123;

  @Raw
  rawObject = {
    id: 1,
    name: 'John',
    age: 32,
    gender: 'MALE',
  };

  @Raw
  client = new Client({
    url: '/graphql',
  });

  created() {
    console.log(this.rawValue);  
    console.log(this.rawObject);
    this.client.fetch();
  }
}

上述代码等效于:

export default {
  name: 'MyComponent',
  data() {
    return {
      message: 'hello',
    };
  },
  created() {
    console.log(this.rawValue);
    console.log(this.rawObject);
    this.client.fetch();
  },
  mixins: [{
    created() {
      this.rawValue = 123;
      this.rawObject = {
        id: 1,
        name: 'John',
        age: 32,
        gender: 'MALE',
      };
      this.client = new Client({
        url: '/graphql',
      });
    },
  }],
};

注意,直接在data()中返回一个被markRaw()函数所标记的对象属性,是无效的。因为markRaw()函数 只能在 compositional API 的 setup() 函数中使用。因此,我们在mixins中注入一个created() 生命周期钩子函数来初始化这些非响应式的属性。

自定义装饰器

此库提供了一个 createDecorator() 函数,用于创建自定义装饰器。该函数接受一个回调函数作为参 数,并返回一个装饰器函数。回调函数将使用以下参数进行调用:

  • Class:被装饰类的构造函数。
  • instance:被装饰类的默认构造实例。此默认实例可用于获取被装饰类的所有实例字段。
  • target:被装饰的目标值,可以是类方法、getter 或 setter。请注意,如果被装饰的目标是类字 段,此参数将始终为 undefined
  • context:包含有关被装饰目标的信息的上下文对象,如 JavaScript 装饰器第3阶段提案JavaScript 装饰器元数据第3阶段提案 中所述。
  • options:Vue 组件选项对象。对此对象的更改将影响提供的组件。该对象包含了 Vue 组件选项对象应具备的所有属性, 还额外包含一个名为 fields 的属性,它是一个包含了 Vue 组件的所有响应式状态的对象; 也就是说, 它是 Vue 组件的 data() 函数返回的对象。修改 optionsfields 属性允许您更改 Vue 组件的 data() 函数返回的响应式状态。

此库将调用回调函数,以便给它一个机会来修改 Vue 组件选项。回调函数的返回值将被忽略。

createDecorator() 函数将返回一个装饰器函数,该函数接受以下两个参数:

以下是一个示例用法:

const Log = createDecorator((Class, instance, target, context, options) => {
  if (context?.kind !== 'method') {
    throw new Error('The @Log decorator can only be used to decorate a class method.');
  }
  const methodName = context.name;
  const originalMethod = options.methods[methodName];
  options.methods[methodName] = function (...args) {
    console.log(`${Class.name}.${methodName}: ${args.join(', ')}`);
    return originalMethod.apply(this, args);
  };
});

上面的示例演示了如何创建一个 @Log 装饰器,该装饰器可用于记录类方法的参数。例如:

@Component
class HelloPage {
  message = 'hello';

  value = 0;

  newMessage = '';

  @Log
  mounted() {
    this.value = this.$route.params.value;
  }

  get computedMessage() {
    return this.message + '!';
  }

  @Log
  setMessage(s) {
    this.message = s;
  }
}

export default toVue(MyComponent);

注意: 上述的 @Log 装饰器不能应用于组件类的 getter 或 setter。

贡献

如果你发现任何问题或有改进建议,欢迎提交 issue 或者 PR 到本项目的 GitHub 仓库

许可

vue3-class-component 采用 Apache 2.0 许可证。详细信息请查阅 LICENSE 文件。