實(shí)現(xiàn)一個(gè)VUE響應(yīng)式屬性裝飾器詳析
前言
使用面向?qū)ο蟮拈_(kāi)發(fā)思想難免會(huì)用到類,既然有了類,那就應(yīng)該有實(shí)例,然而我們使用類的時(shí)候可能需要實(shí)例中的某個(gè)屬性是vue的響應(yīng)屬性,也可能里面的某個(gè)方法也可以被vue的watch監(jiān)聽(tīng)。我就開(kāi)始琢磨如何通過(guò) Composition API
來(lái)實(shí)現(xiàn)這個(gè)類屬性裝飾器
不使用任何的響應(yīng)Api
// TestReactive.ts export class TestReactive { count = 0; loopSetCount() { setInterval(() => { this.count++; }, 1000); } }
// App.vue <template> <div>{{data.count}} </div> </template> <script setup lang="ts"> import { TestReactive } from './TestReactive' const data = new TestReactive() data.loopSetCount() </script> </script>
通過(guò)以上代碼可以看到我調(diào)用 loopSetCount
這個(gè)方法的作用就是想讓data實(shí)例對(duì)象中的count每間隔1s就加1 同時(shí)也想讓template模板中的 {{data.count}}
響應(yīng),但這樣操作他是不會(huì)響應(yīng)的因?yàn)槲覀兊?nbsp;count
并非是 Composition API
提供的響應(yīng)式數(shù)據(jù)。
使用 reactive 實(shí)現(xiàn)
最初的做法很簡(jiǎn)單把實(shí)例裹了一層 reactive
,果不其然view層動(dòng)態(tài)渲染了,但是這樣就會(huì)導(dǎo)致我整個(gè)實(shí)例中所有的方法以及屬性都會(huì)是響應(yīng)式數(shù)據(jù),最終不是想要的效果
const data = reactive(new TestReactive())
使用 ref 實(shí)現(xiàn)
后面我又想了一個(gè)好方法就是把想要響應(yīng)式的屬性賦一個(gè) ref
對(duì)象,這樣也能實(shí)現(xiàn)同樣的效果,但是在賦值的時(shí)候還得必須用到 .value
,感覺(jué)很不優(yōu)雅。
// 去除reactive的包裹 const data = new TestReactive()
// TestReactive.ts import { ref } from "vue"; export class TestReactive { count = ref<number>(0); loopSetCount() { setInterval(() => { this.count.value++; }, 1000); } }
使用裝飾器實(shí)現(xiàn)
實(shí)現(xiàn)Reactive裝飾器
最終又想了一個(gè)方案就是我想要哪個(gè)類屬性實(shí)現(xiàn)vue的響應(yīng),那我只需要用裝飾器裝飾一下這個(gè)屬性,即可讓view層跟著數(shù)據(jù)變化。
我們裝飾的是類屬性首先你得要了解類型裝飾器的規(guī)則,具體請(qǐng)參考TypeScript手冊(cè)
屬性裝飾器聲明在一個(gè)屬性聲明之前(緊靠著屬性聲明)。 屬性裝飾器不能用在聲明文件中(.d.ts),或者任何外部上下文(比如 declare
的類)里。
屬性裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)作函數(shù)被調(diào)用,傳入下列2個(gè)參數(shù):
- 對(duì)于靜態(tài)成員來(lái)說(shuō)是類的構(gòu)造函數(shù),對(duì)于實(shí)例成員是類的原型對(duì)象。
- 成員的名字。
注意 屬性描述符不會(huì)做為參數(shù)傳入屬性裝飾器,這與TypeScript是如何初始化屬性裝飾器的有關(guān)。 因?yàn)槟壳皼](méi)有辦法在定義一個(gè)原型對(duì)象的成員時(shí)描述一個(gè)實(shí)例屬性,并且沒(méi)辦法監(jiān)視或修改一個(gè)屬性的初始化方法。返回值也會(huì)被忽略。因此,屬性描述符只能用來(lái)監(jiān)視類中是否聲明了某個(gè)名字的屬性。
// TestReactive.ts import { ref } from "vue"; function Reactive(target: any, key: string) { const value = ref(); Reflect.defineProperty(target, key, { get() { return value.value; }, set(v) { value.value = v; }, }); } export class TestReactive { @Reactive count = 0; loopSetCount() { setInterval(() => { this.count++; }, 1000); } }
通過(guò)以上代碼使用Reactive裝飾一下這個(gè)屬性頁(yè)面就會(huì)跟著變化,而且在賦值的時(shí)候也不需要通過(guò)this.count.value 去賦值。這樣一來(lái)就解決了我們不優(yōu)雅的問(wèn)題。
實(shí)現(xiàn)Watch裝飾器
緊接著我們可以再實(shí)現(xiàn)一個(gè)Watch的裝飾器專門(mén)用于裝飾方法,通過(guò)傳遞Reactive裝飾的屬性名稱來(lái)監(jiān)聽(tīng),一旦Reactive裝飾的屬性變動(dòng)就會(huì)調(diào)用Watch裝飾的方法。
import { ref, watch, WatchOptions } from "vue"; function Reactive(target: any, key: string) { const value = ref(); Reflect.defineProperty(target, key, { get() { return value.value; }, set(v) { value.value = v; }, }); } function Watch(watchField: string | string[], watchConfig: WatchOptions = {}) { return function (target: any, key: string, descriptor: PropertyDescriptor) { let watchFn: Array<() => any> | (() => any) = []; if (typeof watchField === "string") { watchFn = () => target[watchField]; } else if (Array.isArray(watchField)) { watchFn = watchField.map((field) => () => target[field]); } watch(watchFn, descriptor.value, watchConfig); }; } export class TestReactive { @Reactive count = 0; @Watch("count") doFetch(newValue: number) { console.log(newValue, "最新的數(shù)據(jù)"); } loopSetCount() { setInterval(() => { this.count++; }, 1000); } }
以上是我實(shí)現(xiàn)的一個(gè)思路,當(dāng)我實(shí)現(xiàn)完這個(gè)功能后,同事告訴我 有一個(gè) mobx 的庫(kù)可以像我這樣來(lái)操作數(shù)據(jù)響應(yīng)式,然后我查了一下 mobx for vue
還真找到了一個(gè) mobx-vue 的庫(kù),感覺(jué)可以深度的去學(xué)習(xí)一下。
總結(jié)
響應(yīng)式屬性還是通過(guò)Composition API
來(lái)實(shí)現(xiàn)的,使用裝飾器(Decorators)來(lái)進(jìn)行屬性的操作,讓使用者無(wú)感知,只需要知道哪個(gè)屬性需要響應(yīng)我就給誰(shuí)裝飾。
到此這篇關(guān)于實(shí)現(xiàn)一個(gè)VUE響應(yīng)式屬性裝飾器詳析的文章就介紹到這了,更多相關(guān) VUE響應(yīng)式屬性裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue中使用webuploader做斷點(diǎn)續(xù)傳實(shí)現(xiàn)文件上傳功能
之前做的一個(gè)項(xiàng)目中,由于經(jīng)常上傳幾百兆的壓縮包,導(dǎo)致經(jīng)常上傳失敗,所以就找了webuploader插件做了斷點(diǎn)續(xù)傳,斷點(diǎn)續(xù)傳除了需要前端分片,也需要后臺(tái)去支持,所以做的時(shí)候做好對(duì)接協(xié)調(diào),所以本文就給大家詳細(xì)的介紹一下vue中如何使用webuploader做斷點(diǎn)續(xù)傳2023-07-07Vue3之getCurrentInstance與ts結(jié)合使用的方式
這篇文章主要介紹了Vue3之getCurrentInstance與ts結(jié)合使用的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04vue?動(dòng)態(tài)style?拼接寬度問(wèn)題
這篇文章主要介紹了vue?動(dòng)態(tài)style?拼接寬度問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09vue中使用element ui的input框?qū)崿F(xiàn)模糊搜索的輸入框
這篇文章主要介紹了vue中使用element ui的input框?qū)崿F(xiàn)模糊搜索的輸入框,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-11-11Vue中使用vue2-perfect-scrollbar制作滾動(dòng)條
這篇文章主要介紹了Vue中使用vue2-perfect-scrollbar滾動(dòng)條,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06Vue開(kāi)發(fā)指南之重點(diǎn)知識(shí)梳理
這篇文章主要介紹了Vue開(kāi)發(fā)指南之重點(diǎn)知識(shí)梳理,對(duì)Vue框架感興趣的同學(xué),可以參考下2021-05-05