vue-class-setup?編寫(xiě)?class?風(fēng)格組合式API
前言
我司基于vue-class-component
開(kāi)發(fā)的項(xiàng)目有上百個(gè),其中部署的 SSR 服務(wù)也接近100個(gè),如此龐大體量的項(xiàng)目一開(kāi)始的時(shí)候還幻想著看看是否要升級(jí)Vue3,結(jié)果調(diào)研一番下來(lái),才發(fā)現(xiàn)vue-class-component
對(duì)Vue3的支持,最后一個(gè)版本發(fā)布都過(guò)去兩年了,遲遲還沒(méi)有發(fā)布正式版本。目前基本上處于無(wú)人維護(hù)的狀態(tài),而且升級(jí)存在著大量的破壞性更新,對(duì)于未來(lái)是否還要繼續(xù)使用Vue3現(xiàn)在還是持保留意見(jiàn),但是不妨礙我們先把組件庫(kù)做成Vue2和Vue3通用,于是就有了本文。
在過(guò)去的三年里,vue-class-component
最大的問(wèn)題是就是無(wú)法正確的校驗(yàn)組件的傳參,事件類(lèi)型,這給我?guī)?lái)了巨大的陰影,在經(jīng)過(guò)一番調(diào)研后,驚喜的發(fā)現(xiàn)使用defineComponent
定義的組件,在Vue2.7和3.x都可以正確的識(shí)別類(lèi)型,所以先計(jì)劃內(nèi)部的組件庫(kù)先做到同時(shí)支持Vue2和Vue3,如果后面還要繼續(xù)采用Vue3就變得容易得多。
于是,回到了開(kāi)頭,調(diào)研了一番vue-class-component
在Vue3的支持,目前最新的版本是8.0.0-rc.1,結(jié)果大失所望,目前基本上處于無(wú)人維護(hù)的狀態(tài),社區(qū)內(nèi)又沒(méi)有一個(gè)能滿足我需求的,同時(shí)支持Vue2和Vue3的。
誕生想法
鑒于vue-class-component
組件目前無(wú)法做到正確的組件類(lèi)型檢驗(yàn),當(dāng)我驚喜的發(fā)現(xiàn)組合式API寫(xiě)出來(lái)的代碼可以被正確的識(shí)別類(lèi)型時(shí),誕生了一個(gè)使用 class 風(fēng)格來(lái)編寫(xiě)組合式API的想法,于是花費(fèi)一個(gè)月的實(shí)踐,踩遍了所有的坑,終于誕生了vue-class-setup,一個(gè)使用 class 風(fēng)格來(lái)編寫(xiě)代碼的庫(kù),它gzip壓縮后,1kb大小。
快速開(kāi)始
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({ // 注入類(lèi)實(shí)例的邏輯 ...App.inject(), }); </script> <template> <div> <p>{{ text }}</p> <button @click="onClick()"></button> </div> </template>
嘗試多很多種方案,最終采用了上面的形式為最佳實(shí)踐,它無(wú)法做到export default
直接導(dǎo)出一個(gè)類(lèi),必須使用defineComponent
來(lái)包裝一層,因?yàn)樗皇且粋€(gè)組合類(lèi)(API)
,并非是一個(gè)組件。
最佳實(shí)踐
<script lang="ts"> import { defineComponent } from 'vue'; import { Setup, Define } from 'vue-class-setup'; // 傳入組件的 Props 和 Emit,來(lái)讓組合類(lèi)獲取正確的 `Props` 和 `Emit` 類(lèi)型 @Setup class App extends Define<Props, Emit> { // ? 你可以直接這里定義Props的默認(rèn)值,不需要像 vue-property-decorator 那樣使用一個(gè) Prop 裝飾器來(lái)定義 public readonly dest = '--'; // 自動(dòng)轉(zhuǎn)換成 Vue 的 'computed' public get text() { return String(this.value); } public click(evt: MouseEvent) { // 發(fā)射事件,可以正確的識(shí)別類(lèi)型 this.$emit('click', evt); } } /** * 這里提供了另外一種在 setup 函數(shù)中使用的例子,默認(rèn)推薦使用 `defineComponent` * 如果有多個(gè)類(lèi)實(shí)例,也可以在 setup 中實(shí)例化類(lèi) * <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 中定義類(lèi)型,需要導(dǎo)出一下 export interface Props { value: number; dest?: string; } export interface Emit { (event: 'click', evt: MouseEvent): void; } // 這里不再需要使用變量來(lái)接收,可以利用 Vue 的編譯宏來(lái)為組件生成正確的 Props 和 Emit // ? const props = defineProps<Props>(); // ? const emit = defineEmits<Emit>(); defineProps<Props>(); // ? defineEmits<Emit>(); // ? // 這種默認(rèn)值的定義,也不再推薦,而是直接在類(lèi)上聲明 // ? withDefaults(defineProps<Props>(), { dest: '--' }); // ? @Setup // ? class App extends Define<Props, Emit> { // ? public readonly dest = '--' // ? } // Setup 裝飾器,會(huì)在類(lèi)實(shí)例化時(shí),自動(dòng) 使用 reactive 包裝類(lèi), // 如果你在 setup 手動(dòng)實(shí)例化,則不需要再執(zhí)行一次 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>
多個(gè)類(lèi)實(shí)例
在一些復(fù)雜的業(yè)務(wù)時(shí),有時(shí)需要多個(gè)實(shí)例
<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
在類(lèi)實(shí)例準(zhǔn)備就緒后,PassOnTo 裝飾器,會(huì)將對(duì)應(yīng)的函數(shù),傳遞給回調(diào),這樣我們就可以順利的和 onMounted
等鉤子一起配合使用了
import { onMounted } from 'vue'; @Setup class App extends Define { @PassOnTo(onMounted) public onMounted() {} }
Watch
在使用 vue-property-decorator
的 Watch
裝飾器時(shí),他會(huì)接收一個(gè)字符串類(lèi)型,它不能正確的識(shí)別類(lèi)實(shí)例是否存在這個(gè)字段,但是現(xiàn)在 vue-class-setup 能檢查你的類(lèi)型是否正確,如果傳入一個(gè)類(lèi)實(shí)例不存在的字段,類(lèi)型將會(huì)報(bào)錯(cuò)
<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
在一些場(chǎng)景,我們希望可以暴露組件的一些方法和屬性,那么就需要使用 defineExpose
編譯宏來(lái)定義導(dǎo)出了,所以提供了一個(gè).use
的類(lèi)靜態(tài)方法幫你獲取當(dāng)前注入的類(lèi)實(shí)例
<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 ?
其實(shí)不太想討論這個(gè)問(wèn)題,喜歡的自然會(huì)喜歡,不喜歡的自然會(huì)不喜歡,世上本無(wú)路,走的人多了,就有了路。
最后
不管是 選項(xiàng) API 還是 組合式API,代碼都是人寫(xiě)出來(lái)的,別人都說(shuō) Vue 無(wú)法勝任大型項(xiàng)目,但是在我司的實(shí)踐中經(jīng)受住了實(shí)踐,基本上沒(méi)有產(chǎn)生那種數(shù)千行的組件代碼。
如果喜歡使用 class 風(fēng)格來(lái)編寫(xiě)代碼的,不妨來(lái)關(guān)注一下
如果你的業(yè)務(wù)復(fù)雜,需要使用 SSR 和微服務(wù)架構(gòu),不妨也關(guān)注一下
以上就是vue-class-setup 編寫(xiě) class 風(fēng)格組合式API的詳細(xì)內(nèi)容,更多關(guān)于vue-class-setup 編寫(xiě)組合式API的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue頁(yè)面使用js實(shí)現(xiàn)前端打印功能
這篇文章主要介紹了vue頁(yè)面使用js實(shí)現(xiàn)前端打印功能,文中通過(guò)圖文結(jié)合的方式給大家講解的非常詳細(xì),對(duì)大家實(shí)現(xiàn)打印功能有一定的幫助,需要的朋友可以參考下2024-05-05vue-resource?獲取本地json數(shù)據(jù)404問(wèn)題的解決
這篇文章主要介紹了vue-resource?獲取本地json數(shù)據(jù)404問(wèn)題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10基于Vue.js 2.0實(shí)現(xiàn)百度搜索框效果
這篇文章主要為大家詳細(xì)介紹了基于Vue.js 2.0實(shí)現(xiàn)百度搜索框效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09vue3項(xiàng)目中的el-carousel 輪播圖的使用
Carousel(走馬燈)是一種常見(jiàn)的前端組件,通常用于展示多個(gè)項(xiàng)目(通常是圖片或內(nèi)容塊)的輪播效果,這篇文章主要介紹了vue3項(xiàng)目中的el-carousel 輪播圖的使用,需要的朋友可以參考下2024-02-02vue的style綁定background-image的方式和其他變量數(shù)據(jù)的區(qū)別詳解
今天小編就為大家分享一篇vue的style綁定background-image的方式和其他變量數(shù)據(jù)的區(qū)別詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09vue項(xiàng)目部署到nginx/tomcat服務(wù)器的實(shí)現(xiàn)
這篇文章主要介紹了vue項(xiàng)目部署到nginx/tomcat服務(wù)器的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Nginx部署前端Vue項(xiàng)目的步驟、常見(jiàn)問(wèn)題與解決方案
在現(xiàn)代Web開(kāi)發(fā)中,Vue.js成為前端開(kāi)發(fā)者構(gòu)建單頁(yè)應(yīng)用的熱門(mén)框架,Nginx以其高性能和穩(wěn)定性,成為部署Vue項(xiàng)目的理想選擇,這篇文章主要介紹了Nginx部署前端Vue項(xiàng)目的步驟、常見(jiàn)問(wèn)題與解決方案,需要的朋友可以參考下2024-09-09