Vue中如何使用裝飾器

今天就跟大家聊聊有關(guān)Vue中如何使用裝飾器,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

成都創(chuàng)新互聯(lián)公司專(zhuān)注于渾源網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供渾源營(yíng)銷(xiāo)型網(wǎng)站建設(shè),渾源網(wǎng)站制作、渾源網(wǎng)頁(yè)設(shè)計(jì)、渾源網(wǎng)站官網(wǎng)定制、微信平臺(tái)小程序開(kāi)發(fā)服務(wù),打造渾源網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供渾源網(wǎng)站排名全網(wǎng)營(yíng)銷(xiāo)落地服務(wù)。

什么是裝飾器

裝飾器是ES2016提出來(lái)的一個(gè)提案,當(dāng)前處于Stage 2階段,關(guān)于裝飾器的體驗(yàn),可以點(diǎn)擊 https://github.com/tc39/proposal-decorators查看詳情。裝飾器是一種與類(lèi)相關(guān)的語(yǔ)法糖,用來(lái)包裝或者修改類(lèi)或者類(lèi)的方法的行為,其實(shí)裝飾器就是設(shè)計(jì)模式中裝飾者模式的一種實(shí)現(xiàn)方式。不過(guò)前面說(shuō)的這些概念太干了,我們用人話(huà)來(lái)翻譯一下,舉一個(gè)例子。

在日常開(kāi)發(fā)寫(xiě)bug過(guò)程中,我們經(jīng)常會(huì)用到防抖和節(jié)流,比如像下面這樣

class MyClass {    follow = debounce(function() {      console.log('我是子君,關(guān)注我哦')    }, 100)  }  const myClass = new MyClass()  // 多次調(diào)用只會(huì)輸出一次  myClass.follow()  myClass.follow()

上面是一個(gè)防抖的例子,我們通過(guò)debounce函數(shù)將另一個(gè)函數(shù)包起來(lái),實(shí)現(xiàn)了防抖的功能,這時(shí)候再有另一個(gè)需求,比如希望在調(diào)用follow函數(shù)前后各打印一段日志,這時(shí)候我們還可以再開(kāi)發(fā)一個(gè)log函數(shù),然后繼續(xù)將follow包裝起來(lái)

/**   * 最外層是防抖,否則log會(huì)被調(diào)用多次   */  class MyClass {    follow = debounce(      log(function() {        console.log('我是子君,關(guān)注我哦')      }),      100    )  }

上面代碼中的debounce和log兩個(gè)函數(shù),本質(zhì)上是兩個(gè)包裝函數(shù),通過(guò)這兩個(gè)函數(shù)對(duì)原函數(shù)的包裝,使原函數(shù)的行為發(fā)生了變化,而js中的裝飾器的原理就是這樣的,我們使用裝飾器對(duì)上面的代碼進(jìn)行改造

class MyClass {    @debounce(100)    @log    follow() {      console.log('我是子君,關(guān)注我哦')    }  }

裝飾器的形式就是 @ + 函數(shù)名,如果有參數(shù)的話(huà),后面的括號(hào)里面可以傳參

在方法上使用裝飾器

裝飾器可以應(yīng)用到class上或者class里面的屬性上面,但一般情況下,應(yīng)用到class屬性上面的場(chǎng)景會(huì)比較多一些,比如像上面我們說(shuō)的log,debounce等等,都一般會(huì)應(yīng)用到類(lèi)屬性上面,接下來(lái)我們一起來(lái)具體看一下如何實(shí)現(xiàn)一個(gè)裝飾器,并應(yīng)用到類(lèi)上面。在實(shí)現(xiàn)裝飾器之前,我們需要先了解一下屬性描述符

了解一下屬性描述符

在我們定義一個(gè)對(duì)象里面的屬性的時(shí)候,其實(shí)這個(gè)屬性上面是有許多屬性描述符的,這些描述符標(biāo)明了這個(gè)屬性能不能修改,能不能枚舉,能不能刪除等等,同時(shí)ECMAScript將這些屬性描述符分為兩類(lèi),分別是數(shù)據(jù)屬性和訪問(wèn)器屬性,并且數(shù)據(jù)屬性與訪問(wèn)器屬性是不能共存的。

數(shù)據(jù)屬性

數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位置,在這個(gè)位置可以讀取和寫(xiě)入值。數(shù)據(jù)屬性包含了四個(gè)描述符,分別是

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. configurable

表示能不能通過(guò)delete刪除屬性,能否修改屬性的其他描述符特性,或者能否將數(shù)據(jù)屬性修改為訪問(wèn)器屬性。當(dāng)我們通過(guò)let obj = {name: ''}聲明一個(gè)對(duì)象的時(shí)候,這個(gè)對(duì)象里面所有的屬性的configurable描述符的值都是true

    2. enumerable

表示能不能通過(guò)for in或者Object.keys等方式獲取到屬性,我們一般聲明的對(duì)象里面這個(gè)描述符的值是true,但是對(duì)于class類(lèi)里面的屬性來(lái)說(shuō),這個(gè)值是false

    3. writable

表示能否修改屬性的數(shù)據(jù)值,通過(guò)將這個(gè)修改為false,可以實(shí)現(xiàn)屬性只讀的效果。

    4. value

表示當(dāng)前屬性的數(shù)據(jù)值,讀取屬性值的時(shí)候,從這里讀?。粚?xiě)入屬性值的時(shí)候,會(huì)寫(xiě)到這個(gè)位置。

訪問(wèn)器屬性

訪問(wèn)器屬性不包含數(shù)據(jù)值,他們包含了getter與setter兩個(gè)函數(shù),同時(shí)configurable與enumerable是數(shù)據(jù)屬性與訪問(wèn)器屬性共有的兩個(gè)描述符。

    1. getter

在讀取屬性的時(shí)候調(diào)用這個(gè)函數(shù),默認(rèn)這個(gè)函數(shù)為undefined

    2. setter

在寫(xiě)入屬性值的時(shí)候調(diào)用這個(gè)函數(shù),默認(rèn)這個(gè)函數(shù)為undefined

了解了這六個(gè)描述符之后,你可能會(huì)有幾個(gè)疑問(wèn): 我如何去定義修改這些屬性描述符?這些屬性描述符與今天的文章主題有什么關(guān)系?接下來(lái)是揭曉答案的時(shí)候了。

使用Object.defineProperty

了解過(guò)vue2.0雙向綁定原理的同學(xué)一定知道,Vue的雙向綁定就是通過(guò)使用Object.defineProperty去定義數(shù)據(jù)屬性的getter與setter方法來(lái)實(shí)現(xiàn)的,比如下面有一個(gè)對(duì)象

let obj = {    name: '子君',    officialAccounts: '前端有的玩'  }

我希望這個(gè)對(duì)象里面的用戶(hù)名是不能被修改的,用Object.defineProperty該如何定義呢?

Object.defineProperty(obj,'name', {    // 設(shè)置writable 是 false, 這個(gè)屬性將不能被修改    writable: false  })  // 修改obj.name  obj.name = "君子"  // 打印依然是子君  console.log(obj.name)

通過(guò)Object.defineProperty可以去定義或者修改對(duì)象屬性的屬性描述符,但是因?yàn)閿?shù)據(jù)屬性與訪問(wèn)器屬性是互斥的,所以一次只能修改其中的一類(lèi),這一點(diǎn)需要注意。

定義一個(gè)防抖裝飾器

裝飾器本質(zhì)上依然是一個(gè)函數(shù),不過(guò)這個(gè)函數(shù)的參數(shù)是固定的,如下是防抖裝飾器的代碼

/**  *@param wait 延遲時(shí)長(zhǎng)  */  function debounce(wait) {    return function(target, name, descriptor) {      descriptor.value = debounce(descriptor.value, wait)    }  }  // 使用方式  class MyClass {    @debounce(100)    follow() {      console.log('我是子君,我的公眾號(hào)是 【前端有的玩】,關(guān)注有驚喜哦')    }  }

我們逐行去分析一下代碼

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 首先我們定義了一個(gè) debounce函數(shù),同時(shí)有一個(gè)參數(shù)wait,這個(gè)函數(shù)對(duì)應(yīng)的就是在下面調(diào)用裝飾器時(shí)使用的@debounce(100)

  3. debounce函數(shù)返回了一個(gè)新的函數(shù),這個(gè)函數(shù)即裝飾器的核心,這個(gè)函數(shù)有三個(gè)參數(shù),下面逐一分析

    1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

    2. target: 這個(gè)類(lèi)屬性函數(shù)是在誰(shuí)上面掛載的,如上例對(duì)應(yīng)的是MyClass類(lèi)

    3. name: 這個(gè)類(lèi)屬性函數(shù)的名稱(chēng),對(duì)應(yīng)上面的follow

    4. descriptor: 這個(gè)就是我們前面說(shuō)的屬性描述符,通過(guò)直接descriptor上面的屬性,即可實(shí)現(xiàn)屬性只讀,數(shù)據(jù)重寫(xiě)等功能

    3. 然后第三行 descriptor.value = debounce(descriptor.value, wait), 前面我們已經(jīng)了解到,屬性描述符上面的value對(duì)應(yīng)的是這個(gè)屬性的值,所以我們通過(guò)重寫(xiě)這個(gè)屬性,將其用debounce函數(shù)包裝起來(lái),這樣在函數(shù)調(diào)用follow時(shí)實(shí)際調(diào)用的是包裝后的函數(shù)

通過(guò)上面的三步,我們就實(shí)現(xiàn)了類(lèi)屬性上面可使用的裝飾器,同時(shí)將其應(yīng)用到了類(lèi)屬性上面

在class上使用裝飾器

裝飾器不僅可以應(yīng)用到類(lèi)屬性上面,還可以直接應(yīng)用到類(lèi)上面,比如我希望可以實(shí)現(xiàn)一個(gè)類(lèi)似Vue混入那樣的功能,給一個(gè)類(lèi)混入一些方法屬性,應(yīng)該如何去做呢?

// 這個(gè)是要混入的對(duì)象  const methods = {    logger() {      console.log('記錄日志')    }  }  // 這個(gè)是一個(gè)登陸登出類(lèi)  class Login{    login() {}    logout() {}  }

如何將上面的methods混入到Login中,首先我們先實(shí)現(xiàn)一個(gè)類(lèi)裝飾器

function mixins(obj) {    return function (target) {      Object.assign(target.prototype, obj)      }  }  // 然后通過(guò)裝飾器混入  @mixins(methods)  class Login{    login() {}    logout() {}  }

這樣就實(shí)現(xiàn)了類(lèi)裝飾器。對(duì)于類(lèi)裝飾器,只有一個(gè)參數(shù),即target,對(duì)應(yīng)的就是這個(gè)類(lèi)本身。

了解完裝飾器,我們接下來(lái)看一下如何在Vue中使用裝飾器。

在Vue中使用裝飾器

使用ts開(kāi)發(fā)Vue的同學(xué)一定對(duì)vue-property-decorator不會(huì)感到陌生,這個(gè)插件提供了許多裝飾器,方便大家開(kāi)發(fā)的時(shí)候使用,當(dāng)然本文的中點(diǎn)不是這個(gè)插件。其實(shí)如果我們的項(xiàng)目沒(méi)有使用ts,也是可以使用裝飾器的,怎么用呢?

配置基礎(chǔ)環(huán)境

除了一些老的項(xiàng)目,我們現(xiàn)在一般新建Vue項(xiàng)目的時(shí)候,都會(huì)選擇使用腳手架vue-cli3/4來(lái)新建,這時(shí)候新建的項(xiàng)目已經(jīng)默認(rèn)支持了裝飾器,不需要再配置太多額外的東西,如果你的項(xiàng)目使用了eslint,那么需要給eslint配置以下內(nèi)容。

parserOptions: {     ecmaFeatures:{       // 支持裝飾器       legacyDecorators: true     }   }

使用裝飾器

雖然Vue的組件,我們一般書(shū)寫(xiě)的時(shí)候export出去的是一個(gè)對(duì)象,但是這個(gè)并不影響我們直接在組件中使用裝飾器,比如就拿上例中的log舉例。

function log() {    /**     * @param target 對(duì)應(yīng) methods 這個(gè)對(duì)象     * @param name 對(duì)應(yīng)屬性方法的名稱(chēng)     * @param descriptor 對(duì)應(yīng)屬性方法的修飾符     */    return function(target, name, descriptor) {      console.log(target, name, descriptor)      const fn = descriptor.value      descriptor.value = function(...rest) {        console.log(`這是調(diào)用方法【${name}】前打印的日志`)        fn.call(this, ...rest)        console.log(`這是調(diào)用方法【${name}】后打印的日志`)      }    }  }  export default {    created() {      this.getData()    },    methods: {      @log()      getData() {        console.log('獲取數(shù)據(jù)')      }    }  }

看了上面的代碼,是不是發(fā)現(xiàn)在Vue中使用裝飾器還是很簡(jiǎn)單的,和在class的屬性上面使用的方式一模一樣,但有一點(diǎn)需要注意,在methods里面的方法上面使用裝飾器,這時(shí)候裝飾器的target對(duì)應(yīng)的是methods。

除了在methods上面可以使用裝飾器之外,你也可以在生命周期鉤子函數(shù)上面使用裝飾器,這時(shí)候target對(duì)應(yīng)的是整個(gè)組件對(duì)象。

一些常用的裝飾器

下面小編羅列了幾個(gè)小編在項(xiàng)目中常用的幾個(gè)裝飾器,方便大家使用

1. 函數(shù)節(jié)流與防抖

函數(shù)節(jié)流與防抖應(yīng)用場(chǎng)景是比較廣的,一般使用時(shí)候會(huì)通過(guò)throttle或debounce方法對(duì)要調(diào)用的函數(shù)進(jìn)行包裝,現(xiàn)在就可以使用上文說(shuō)的內(nèi)容將這兩個(gè)函數(shù)封裝成裝飾器, 防抖節(jié)流使用的是lodash提供的方法,大家也可以自行實(shí)現(xiàn)節(jié)流防抖函數(shù)哦

import { throttle, debounce } from 'lodash'  /**   * 函數(shù)節(jié)流裝飾器   * @param {number} wait 節(jié)流的毫秒   * @param {Object} options 節(jié)流選項(xiàng)對(duì)象   * [options.leading=true] (boolean): 指定調(diào)用在節(jié)流開(kāi)始前。   * [options.trailing=true] (boolean): 指定調(diào)用在節(jié)流結(jié)束后。   */  export const throttle =  function(wait, options = {}) {    return function(target, name, descriptor) {      descriptor.value = throttle(descriptor.value, wait, options)    }  }  /**   * 函數(shù)防抖裝飾器   * @param {number} wait 需要延遲的毫秒數(shù)。   * @param {Object} options 選項(xiàng)對(duì)象   * [options.leading=false] (boolean): 指定在延遲開(kāi)始前調(diào)用。   * [options.maxWait] (number): 設(shè)置 func 允許被延遲的最大值。   * [options.trailing=true] (boolean): 指定在延遲結(jié)束后調(diào)用。   */  export const debounce = function(wait, options = {}) {    return function(target, name, descriptor) {      descriptor.value = debounce(descriptor.value, wait, options)    }  }

封裝完之后,在組件中使用

import {debounce} from '@/decorator'  export default {    methods:{      @debounce(100)      resize(){}    }  }

2. loading

在加載數(shù)據(jù)的時(shí)候,為了個(gè)用戶(hù)一個(gè)友好的提示,同時(shí)防止用戶(hù)繼續(xù)操作,一般會(huì)在請(qǐng)求前顯示一個(gè)loading,然后在請(qǐng)求結(jié)束之后關(guān)掉loading,一般寫(xiě)法如下

export default {    methods:{      async getData() {        const loading = Toast.loading()        try{          const data = await loadData()          // 其他操作        }catch(error){          // 異常處理          Toast.fail('加載失敗');        }finally{          loading.clear()        }        }    }  }

我們可以把上面的loading的邏輯使用裝飾器重新封裝,如下代碼

import { Toast } from 'vant'  /**   * loading 裝飾器   * @param {*} message 提示信息   * @param {function} errorFn 異常處理邏輯   */  export const loading =  function(message = '加載中...', errorFn = function() {}) {    return function(target, name, descriptor) {      const fn = descriptor.value      descriptor.value = async function(...rest) {        const loading = Toast.loading({          message: message,          forbidClick: true        })        try {          return await fn.call(this, ...rest)        } catch (error) {          // 在調(diào)用失敗,且用戶(hù)自定義失敗的回調(diào)函數(shù)時(shí),則執(zhí)行          errorFn && errorFn.call(this, error, ...rest)          console.error(error)        } finally {          loading.clear()        }      }    }  }

然后改造上面的組件代碼

export default {    methods:{      @loading('加載中')      async getData() {        try{          const data = await loadData()          // 其他操作        }catch(error){          // 異常處理          Toast.fail('加載失敗');        }        }    }  }

3. 確認(rèn)框

當(dāng)你點(diǎn)擊刪除按鈕的時(shí)候,一般都需要彈出一個(gè)提示框讓用戶(hù)確認(rèn)是否刪除,這時(shí)候常規(guī)寫(xiě)法可能是這樣的

import { Dialog } from 'vant'  export default {    methods: {      deleteData() {        Dialog.confirm({          title: '提示',          message: '確定要?jiǎng)h除數(shù)據(jù),此操作不可回退。'        }).then(() => {          console.log('在這里做刪除操作')        })      }    }  }

我們可以把上面確認(rèn)的過(guò)程提出來(lái)做成裝飾器,如下代碼

import { Dialog } from 'vant'  /**   * 確認(rèn)提示框裝飾器   * @param {*} message 提示信息   * @param {*} title 標(biāo)題   * @param {*} cancelFn 取消回調(diào)函數(shù)   */  export function confirm(    message = '確定要?jiǎng)h除數(shù)據(jù),此操作不可回退。',    title = '提示',    cancelFn = function() {}  ) {    return function(target, name, descriptor) {      const originFn = descriptor.value      descriptor.value = async function(...rest) {        try {          await Dialog.confirm({            message,            title: title          })          originFn.apply(this, rest)        } catch (error) {          cancelFn && cancelFn(error)        }      }    }  }

然后再使用確認(rèn)框的時(shí)候,就可以這樣使用了

export default {    methods: {      // 可以不傳參,使用默認(rèn)參數(shù)      @confirm()      deleteData() {        console.log('在這里做刪除操作')      }   }  }

是不是瞬間簡(jiǎn)單多了,當(dāng)然還可以繼續(xù)封裝很多很多的裝飾器,因?yàn)槲恼聝?nèi)容有限,暫時(shí)提供這三個(gè)。

裝飾器組合使用

在上面我們將類(lèi)屬性上面使用裝飾器的時(shí)候,說(shuō)道裝飾器可以組合使用,在Vue組件上面使用也是一樣的,比如我們希望在確認(rèn)刪除之后,調(diào)用接口時(shí)候出現(xiàn)loading,就可以這樣寫(xiě)(一定要注意順序)

export default {    methods: {      @confirm()      @loading()      async deleteData() {        await delete()      }    }  }

看完上述內(nèi)容,你們對(duì)Vue中如何使用裝飾器有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。

分享名稱(chēng):Vue中如何使用裝飾器
鏈接地址:http://bm7419.com/article38/jdidpp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、外貿(mào)建站、軟件開(kāi)發(fā)、網(wǎng)站制作App開(kāi)發(fā)、定制開(kāi)發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)