vue-class-setup 编写 class 风格组合式API

目录
  • 前言
  • 诞生想法
  • 快速开始
  • 最佳实践
  • 多个类实例
  • PassOnTo
  • Watch
  • defineExpose
  • 为什么使用 class ?
  • 最后

前言

我司基于vue-class-component开发的项目有上百个,其中部署的 SSR 服务也接近100个,如此庞大体量的项目一开始的时候还幻想着看看是否要升级Vue3,结果调研一番下来,才发现vue-class-component对Vue3的支持,最后一个版本发布都过去两年了,迟迟还没有发布正式版本。目前基本上处于无人维护的状态,而且升级存在着大量的破坏性更新,对于未来是否还要继续使用Vue3现在还是持保留意见,但是不妨碍我们先把组件库做成Vue2和Vue3通用,于是就有了本文。

在过去的三年里,vue-class-component最大的问题是就是无法正确的校验组件的传参,事件类型,这给我带来了巨大的阴影,在经过一番调研后,惊喜的发现使用defineComponent定义的组件,在Vue2.7和3.x都可以正确的识别类型,所以先计划内部的组件库先做到同时支持Vue2和Vue3,如果后面还要继续采用Vue3就变得容易得多。

于是,回到了开头,调研了一番vue-class-component在Vue3的支持,目前最新的版本是8.0.0-rc.1,结果大失所望,目前基本上处于无人维护的状态,社区内又没有一个能满足我需求的,同时支持Vue2和Vue3的。

诞生想法

鉴于vue-class-component组件目前无法做到正确的组件类型检验,当我惊喜的发现组合式API写出来的代码可以被正确的识别类型时,诞生了一个使用 class 风格来编写组合式API的想法,于是花费一个月的实践,踩遍了所有的坑,终于诞生了vue-class-setup,一个使用 class 风格来编写代码的库,它gzip压缩后,1kb大小。

快速开始

npm install vue-class-setup
<script lang="ts">
import { defineComponent } from 'vue';
import { Setup, Context } from 'vue-class-setup';
// Setup 和 Context 必须一起工作
@Setup
class App extends Context {
    private _value = 0;
    public get text() {
        return String(this._value);
    }
    public set text(text: string) {
        this._value = Number(text);
    }
    public onClick() {
        this._value++;
    }
}
export default defineComponent({
    // 注入类实例的逻辑
    ...App.inject(),
});
</script>
<template>
    <div>
        <p>{{ text }}</p>
        <button @click="onClick()"></button>
    </div>
</template>

尝试多很多种方案,最终采用了上面的形式为最佳实践,它无法做到export default直接导出一个类,必须使用defineComponent 来包装一层,因为它只是一个组合类(API),并非是一个组件。

最佳实践

<script lang="ts">
import { defineComponent } from 'vue';
import { Setup, Define } from 'vue-class-setup';
// 传入组件的 Props 和 Emit,来让组合类获取正确的 `Props` 和 `Emit` 类型
@Setup
class App extends Define<Props, Emit> {
    //  你可以直接这里定义Props的默认值,不需要像 vue-property-decorator 那样使用一个 Prop 装饰器来定义
    public readonly dest = '--';
    // 自动转换成 Vue 的 'computed'
    public get text() {
        return String(this.value);
    }
    public click(evt: MouseEvent) {
        // 发射事件,可以正确的识别类型
        this.$emit('click', evt);
    }
}
/**
 * 这里提供了另外一种在 setup 函数中使用的例子,默认推荐使用 `defineComponent`
 * 如果有多个类实例,也可以在 setup 中实例化类
 * <script lang="ts" setup>
 *      const app = new App();
 * <\/script>
 * <template>
 *      <div>{{ app.text }}</div>
 * </template>
 */
export default defineComponent({
    ...App.inject(),
});
</script>
<script lang="ts" setup>
// 如果在 setup 中定义类型,需要导出一下
export interface Props {
    value: number;
    dest?: string;
}
export interface Emit {
    (event: 'click', evt: MouseEvent): void;
}
// 这里不再需要使用变量来接收,可以利用 Vue 的编译宏来为组件生成正确的 Props 和 Emit
//  const props = defineProps<Props>();
//  const emit = defineEmits<Emit>();
defineProps<Props>(); //
defineEmits<Emit>(); //
// 这种默认值的定义,也不再推荐,而是直接在类上声明
//  withDefaults(defineProps<Props>(), { dest: '--' });
//  @Setup
//  class App extends Define<Props, Emit> {
//      public readonly dest = '--'
//  }
// Setup 装饰器,会在类实例化时,自动 使用 reactive 包装类,
// 如果你在 setup 手动实例化,则不需要再执行一次 reactive
// const app = reactive(new App()); //
// const app = new App();           //
</script>
<template>
    <button class="btn" @click="click($event)">
        <span class="text">{{ text }}</span>
        <span class="props-dest">{{ dest }}</span>
        <span class="props-value">{{ $props.value }}</span>
    </button>
</template>

多个类实例

在一些复杂的业务时,有时需要多个实例

<script lang="ts">
import { onBeforeMount, onMounted } from 'vue';
import { Setup, Context, PassOnTo } from 'vue-class-setup';
@Setup
class Base extends Context {
    public value = 0;
    public get text() {
        return String(this.value);
    }
    @PassOnTo(onBeforeMount)
    public init() {
        this.value++;
    }
}
@Setup
class Left extends Base {
    public left = 0;
    public get text() {
        return String(`value:${this.value}`);
    }
    public init() {
        super.init();
        this.value++;
    }
    @PassOnTo(onMounted)
    public initLeft() {
        this.left++;
    }
}
@Setup
class Right extends Base {
    public right = 0;
    public init() {
        super.init();
        this.value++;
    }
    @PassOnTo(onMounted)
    public initLeft() {
        this.right++;
    }
}
</script>
<script setup lang="ts">
const left = new Left();
const right = new Right();
</script>
<template>
    <p class="left">{{ left.text }}</p>
    <p class="right">{{ right.text }}</p>
</template>

PassOnTo

在类实例准备就绪后,PassOnTo 装饰器,会将对应的函数,传递给回调,这样我们就可以顺利的和 onMounted 等钩子一起配合使用了

import { onMounted } from 'vue';
@Setup
class App extends Define {
    @PassOnTo(onMounted)
    public onMounted() {}
}

Watch

在使用 vue-property-decoratorWatch 装饰器时,他会接收一个字符串类型,它不能正确的识别类实例是否存在这个字段,但是现在 vue-class-setup 能检查你的类型是否正确,如果传入一个类实例不存在的字段,类型将会报错

<script lang="ts">
import { Setup, Watch, Context } from 'vue-class-setup';
@Setup
class App extends Context {
    public value = 0;
    public immediateValue = 0;
    public onClick() {
        this.value++;
    }
    @Watch('value')
    public watchValue(value: number, oldValue: number) {
        if (value > 100) {
            this.value = 100;
        }
    }
    @Watch('value', { immediate: true })
    public watchImmediateValue(value: number, oldValue: number | undefined) {
        if (typeof oldValue === 'undefined') {
            this.immediateValue = 10;
        } else {
            this.immediateValue++;
        }
    }
}
</script>
<script setup lang="ts">
const app = new App();
</script>
<template>
    <p class="value">{{ app.value }}</p>
    <p class="immediate-value">{{ app.immediateValue }}</p>
    <button @click="app.onClick()">Add</button>
</template>

defineExpose

在一些场景,我们希望可以暴露组件的一些方法和属性,那么就需要使用 defineExpose 编译宏来定义导出了,所以提供了一个.use的类静态方法帮你获取当前注入的类实例

<script lang="ts">
import { defineComponent } from 'vue';
import { Setup, Context } from 'vue-class-setup';
@Setup
class App extends Context {
    private _value = 0;
    public get text() {
        return String(this._value);
    }
    public set text(text: string) {
        this._value = Number(text);
    }
    public addValue() {
        this._value++;
    }
}
export default defineComponent({
    ...App.inject(),
});
</script>
<script lang="ts" setup>
const app = App.use();
defineExpose({
    addValue: app.addValue,
});
</script>
<template>
    <div>
        <p class="text">{{ text }}</p>
        <p class="text-eq">{{ app.text === text }}</p>
        <button @click="addValue"></button>
    </div>
</template>

为什么使用 class ?

其实不太想讨论这个问题,喜欢的自然会喜欢,不喜欢的自然会不喜欢,世上本无路,走的人多了,就有了路。

最后

不管是 选项 API 还是 组合式API,代码都是人写出来的,别人都说 Vue 无法胜任大型项目,但是在我司的实践中经受住了实践,基本上没有产生那种数千行的组件代码。

如果喜欢使用 class 风格来编写代码的,不妨来关注一下

如果你的业务复杂,需要使用 SSR 和微服务架构,不妨也关注一下

以上就是vue-class-setup 编写 class 风格组合式API的详细内容,更多关于vue-class-setup 编写组合式API的资料请关注我们其它相关文章!

(0)

相关推荐

  • 浅谈vue2的$refs在vue3组合式API中的替代方法

    如果你有过vue2的项目开发经验,那么对$refs就很熟悉了.由于vue3的断崖式的升级,在vue3中如何使用$refs呢?想必有遇到过类似的问题,我也有一样的疑惑.通过搜索引擎和github,基本掌握如何使用$refs.在vue3中使用组合式API的函数ref来代替静态或者动态html元素的应用. 最近业余在学习vue3项目<蜡笔(Crayon)管理模板:Vue3 + Vuex4 + Ant Design2>开发,这两天迭代推进了一点,实现了chart图表组件,写文章的时候发现提交代码的co

  • WebStorm无法正确识别Vue3组合式API的解决方案

    1 问题描述 Vue3的组合式API无法在WebStorm中正确识别,表现为defineComponent等无法被识别: 2 尝试方案 猜想这种问题的原因是无法正确识别对应的Vue3库,笔者相信WebStorm是可以做到的,只是某些配置没有正确,笔者尝试过在设置的Libraries中勾选上node_modules,但是没有效果. 3 解决办法 最终的解决办法参考了此处(图源): 选择node_modules文件夹,右键选择Mark Directory as,最后选择Not Excluded即可.

  • vue3.0组合式api的使用小结

    目录 1.setup使用 2.生命周期函数 3.vuex 4.toRef介绍 5.ref介绍 6.组件传值 7.shallowRef和shallowReactive 8.watchEffect 9.watch侦听器 10.computed 11.reactive介绍 12.toRefs介绍 vue2开发缺点和vue3开发优点 vue2代码冗余,杂乱 vue3则可以把相关的功能代码抽离分割在一起,方便开发者快速阅读 1.setup使用 setup函数是 Composition API(组合API)

  • Vue3中的组合式 API示例详解

    目录 为什么要有组合式 API? 更好的逻辑复用 更灵活的代码组织 更好的类型推导 生产包体积更小 与选项式 API 的关系 组合式 API 是否覆盖了所有场景? 可以同时使用两种 API 吗? 选项式 API 会被废弃吗? 组合式 API 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue 组件.它是一个概括性的术语,涵盖了以下方面的 API: 响应性 API:例如ref()和reactive(),使我们可以直接创建响应式状态.计算属性和侦听器. 生命周期钩子:例如o

  • vue-class-setup 编写 class 风格组合式API

    目录 前言 诞生想法 快速开始 最佳实践 多个类实例 PassOnTo Watch defineExpose 为什么使用 class ? 最后 前言 我司基于vue-class-component开发的项目有上百个,其中部署的 SSR 服务也接近100个,如此庞大体量的项目一开始的时候还幻想着看看是否要升级Vue3,结果调研一番下来,才发现vue-class-component对Vue3的支持,最后一个版本发布都过去两年了,迟迟还没有发布正式版本.目前基本上处于无人维护的状态,而且升级存在着大量

  • vue组合式API浅显入门示例详解

    目录 正文 组合式API setup 变量声明 目前比起选项式API的优点 生命周期 正文 在react推出了hook之后,vue也在vue3里面添加了组合式API,鉴于这个时间间隔,我有理由认为组合式api和hook还是有一些关系的.不过在我具体的使用中,我并没发现这两个方法太多的相同点,不过鉴于vue自动发布之后就与react之间那剪不断理还乱的量子力学关系,估计会有很多人会问及这俩之间的异同.我还没到能分辨出异同的地步,就简单的介绍一下vue的组合式API吧. 组合式API 在vue3.0

  • VUE脚手架框架编写简洁的登录界面的实现

    目录 前言 安装Vue脚手架 创建登录界面的文件--login.Vue 配置路由-- router.js 配置main.js 编写登录界面--Login.Vue 总结 前言 一个好的前端开发项目,都是一个团队负责一个部分进行通力合作的.简单的一个系统网站一般包含登录.主体.各个模块功能这三个大部分,现在我们写的登录界面,我们一般编写这样的登录界面可以说有一定的固定套路. 安装Vue脚手架 1.我们一Windows 10为例子,安装Vue 3脚手架前,我们先要配置node js环境: 2.接下来,

  • 解决vue.js在编写过程中出现空格不规范报错的问题

    找到build文件夹下面的webpack.base.conf.js文件. 然后打开该文件,找到图下这段代码,把他注释掉. 注释掉之后,再进行子页面等编写的时候,空格不规范的情况下也不会再报错啦.因为这个报错对于初学者来说实在头大.哈哈O(∩_∩)O哈哈~ 我标注的这些地方,原本是有严格的空格规范要求的,这些报错真是另人烦躁呀o(╥﹏╥)o 反正我把这个问题解决了,特别开心哒哒哒~~~ 以上这篇解决vue.js在编写过程中出现空格不规范报错的问题就是小编分享给大家的全部内容了,希望能给大家一个参考

  • Yii2框架制作RESTful风格的API快速入门教程

    先给大家说下什么是REST restful REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移. 它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一. 他在论文中提到:"我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强.性能好.适宜通信的架构.REST指的是一组架构约束条件和原则." 如

  • vue 使用外部JS与调用原生API操作示例

    本文实例讲述了vue 使用外部JS与调用原生API操作.分享给大家供大家参考,具体如下: vue 使用外部JS 概要 在开发时我们会经常需要使用到外部的JS,这样我们需要引入外部js,然后进行使用. 实现方法 我们在开发的过程中需要使用到 sha256 将用户的密码进行加密传输. 我们对js进行一点点改造. function sha256_digest(data) { sha256_init(); sha256_update(data, data.length); sha256_final();

随机推荐