嫩草影院久久99_老司机午夜网站国内精品久久久久久久久_久久夜色精品国产_国产一级做a爰片久久毛片

Vue中使用裝飾器,我是認真的

2020-7-7    seo達人

作為一個曾經(jīng)的Java coder, 當我第一次看到j(luò)s里面的裝飾器(Decorator)的時候,就馬上想到了Java中的注解,當然在實際原理和功能上面,Java的注解和js的裝飾器還是有很大差別的。本文題目是Vue中使用裝飾器,我是認真的,但本文將從裝飾器的概念開發(fā)聊起,一起來看看吧。


通過本文內(nèi)容,你將學(xué)到以下內(nèi)容:


了解什么是裝飾器

在方法使用裝飾器

在class中使用裝飾器

在Vue中使用裝飾器

本文首發(fā)于公眾號【前端有的玩】,不想當咸魚,想要換工作,關(guān)注公眾號,帶你每日一起刷大廠面試題,關(guān)注 === 大廠offer。

什么是裝飾器

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


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


class MyClass {

 follow = debounce(function() {

   console.log('我是子君,關(guān)注我哦')

 }, 100)

}


const myClass = new MyClass()

// 多次調(diào)用只會輸出一次

myClass.follow()

myClass.follow()

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


/**

* 最外層是防抖,否則log會被調(diào)用多次

*/

class MyClass {

 follow = debounce(

   log(function() {

     console.log('我是子君,關(guān)注我哦')

   }),

   100

 )

}

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


class MyClass {

 @debounce(100)

 @log

 follow() {

   console.log('我是子君,關(guān)注我哦')

 }

}

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


在方法上使用裝飾器

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


了解一下屬性描述符

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


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

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


configurable

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


enumerable

表示能不能通過for in或者Object.keys等方式獲取到屬性,我們一般聲明的對象里面這個描述符的值是true,但是對于class類里面的屬性來說,這個值是false


writable

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


value

表示當前屬性的數(shù)據(jù)值,讀取屬性值的時候,從這里讀取;寫入屬性值的時候,會寫到這個位置。


訪問器屬性

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


getter

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


setter

在寫入屬性值的時候調(diào)用這個函數(shù),默認這個函數(shù)為undefined


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


使用Object.defineProperty

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


let obj = {

 name: '子君',

 officialAccounts: '前端有的玩'

}

我希望這個對象里面的用戶名是不能被修改的,用Object.defineProperty該如何定義呢?


Object.defineProperty(obj,'name', {

 // 設(shè)置writable 是 false, 這個屬性將不能被修改

 writable: false

})

// 修改obj.name

obj.name = "君子"

// 打印依然是子君

console.log(obj.name)

通過Object.defineProperty可以去定義或者修改對象屬性的屬性描述符,但是因為數(shù)據(jù)屬性與訪問器屬性是互斥的,所以一次只能修改其中的一類,這一點需要注意。


定義一個防抖裝飾器

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


/**

*@param wait 延遲時長

*/

function debounce(wait) {

 return function(target, name, descriptor) {

   descriptor.value = debounce(descriptor.value, wait)

 }

}

// 使用方式

class MyClass {

 @debounce(100)

 follow() {

   console.log('我是子君,我的公眾號是 【前端有的玩】,關(guān)注有驚喜哦')

 }

}

我們逐行去分析一下代碼


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

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


target: 這個類屬性函數(shù)是在誰上面掛載的,如上例對應(yīng)的是MyClass類

name: 這個類屬性函數(shù)的名稱,對應(yīng)上面的follow

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

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

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


在class上使用裝飾器

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


// 這個是要混入的對象

const methods = {

 logger() {

   console.log('記錄日志')

 }

}


// 這個是一個登陸登出類

class Login{

 login() {}

 logout() {}

}

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


function mixins(obj) {

 return function (target) {

   Object.assign(target.prototype, obj)  

 }

}


// 然后通過裝飾器混入

@mixins(methods)

class Login{

 login() {}

 logout() {}

}

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


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


在Vue中使用裝飾器

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


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

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


 parserOptions: {

   ecmaFeatures:{

     // 支持裝飾器

     legacyDecorators: true

   }

 }

使用裝飾器

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


function log() {

 /**

  * @param target 對應(yīng) methods 這個對象

  * @param name 對應(yīng)屬性方法的名稱

  * @param descriptor 對應(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中使用裝飾器還是很簡單的,和在class的屬性上面使用的方式一模一樣,但有一點需要注意,在methods里面的方法上面使用裝飾器,這時候裝飾器的target對應(yīng)的是methods。


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


一些常用的裝飾器

下面小編羅列了幾個小編在項目中常用的幾個裝飾器,方便大家使用


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

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


import { throttle, debounce } from 'lodash'

/**

* 函數(shù)節(jié)流裝飾器

* @param {number} wait 節(jié)流的毫秒

* @param {Object} options 節(jié)流選項對象

* [options.leading=true] (boolean): 指定調(diào)用在節(jié)流開始前。

* [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 選項對象

* [options.leading=false] (boolean): 指定在延遲開始前調(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ù)的時候,為了個用戶一個友好的提示,同時防止用戶繼續(xù)操作,一般會在請求前顯示一個loading,然后在請求結(jié)束之后關(guān)掉loading,一般寫法如下


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)用失敗,且用戶自定義失敗的回調(diào)函數(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. 確認框

當你點擊刪除按鈕的時候,一般都需要彈出一個提示框讓用戶確認是否刪除,這時候常規(guī)寫法可能是這樣的


import { Dialog } from 'vant'


export default {

 methods: {

   deleteData() {

     Dialog.confirm({

       title: '提示',

       message: '確定要刪除數(shù)據(jù),此操作不可回退。'

     }).then(() => {

       console.log('在這里做刪除操作')

     })

   }

 }

}

我們可以把上面確認的過程提出來做成裝飾器,如下代碼


import { Dialog } from 'vant'


/**

* 確認提示框裝飾器

* @param {*} message 提示信息

* @param {*} title 標題

* @param {*} cancelFn 取消回調(diào)函數(shù)

*/

export function confirm(

 message = '確定要刪除數(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)

     }

   }

 }

}

然后再使用確認框的時候,就可以這樣使用了


export default {

 methods: {

   // 可以不傳參,使用默認參數(shù)

   @confirm()

   deleteData() {

     console.log('在這里做刪除操作')

   }

 }

}

是不是瞬間簡單多了,當然還可以繼續(xù)封裝很多很多的裝飾器,因為文章內(nèi)容有限,暫時提供這三個。


裝飾器組合使用

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


export default {

 methods: {

   @confirm()

   @loading()

   async deleteData() {

     await delete()

   }

 }

}

本節(jié)定義的裝飾器,均已應(yīng)用到這個項目中 https://github.com/snowzijun/vue-vant-base, 這是一個基于Vant開發(fā)的開箱即用移動端框架,你只需要fork下來,無需做任何配置就可以直接進行業(yè)務(wù)開發(fā),歡迎使用,喜歡麻煩給一個star。

藍藍設(shè)計m.sdgs6788.com )是一家專注而深入的界面設(shè)計公司,為期望卓越的國內(nèi)外企業(yè)提供卓越的UI界面設(shè)計、BS界面設(shè)計 、 cs界面設(shè)計 、 ipad界面設(shè)計 、 包裝設(shè)計 、 圖標定制 、 用戶體驗 、交互設(shè)計、 網(wǎng)站建設(shè) 平面設(shè)計服務(wù)




日歷

鏈接

個人資料

藍藍設(shè)計的小編 http://m.sdgs6788.com

存檔

嫩草影院久久99_老司机午夜网站国内精品久久久久久久久_久久夜色精品国产_国产一级做a爰片久久毛片
<em id="09ttv"></em>
    <sup id="09ttv"><pre id="09ttv"></pre></sup>
    <dd id="09ttv"></dd>

        • 久久久91精品| 国产欧美亚洲一区| 欧美一区二区三区四区在线| 美女脱光内衣内裤视频久久影院| 亚洲一区三区电影在线观看| 母乳一区在线观看| 另类尿喷潮videofree| 国产精品一区二区视频| 亚洲婷婷国产精品电影人久久| 一本色道久久综合精品竹菊| 欧美福利在线| 亚洲国产精品99久久久久久久久| 国产精品嫩草久久久久| 宅男噜噜噜66一区二区| 亚洲一区3d动漫同人无遮挡| 欧美日韩一区二区免费在线观看| 亚洲精品五月天| 中文在线资源观看网站视频免费不卡 | 国产精品久久久久久久久久免费 | 久久狠狠亚洲综合| 国产日本欧美一区二区| 午夜综合激情| 久久免费视频在线观看| 国产一区二区三区黄视频| 羞羞漫画18久久大片| 久久先锋资源| 亚洲国产欧美不卡在线观看| 欧美电影电视剧在线观看| 亚洲三级影院| 亚洲综合导航| 国产亚洲人成网站在线观看| 久久久久国内| 亚洲成在线观看| 一区二区三区免费网站| 国产精品视频999| 久久精品99无色码中文字幕 | 日韩午夜激情电影| 欧美午夜视频在线观看| 午夜视频精品| 欧美大片18| 亚洲午夜黄色| 国产综合自拍| 欧美精品在线视频| 亚洲自拍偷拍网址| 欧美xart系列高清| 在线一区观看| 国内久久精品视频| 欧美日韩第一区日日骚| 亚洲欧美日韩国产另类专区| 免费不卡在线观看| 一区二区三区国产精华| 国产欧美日韩亚洲精品| 免费成人黄色片| 亚洲尤物影院| 亚洲大片一区二区三区| 亚洲欧美一区二区三区在线| 在线日韩欧美| 欧美午夜电影一区| 久久综合色8888| 亚洲影音一区| 最新高清无码专区| 久久青青草综合| 亚洲小少妇裸体bbw| 在线欧美日韩精品| 国产精品每日更新| 农夫在线精品视频免费观看| 亚洲亚洲精品在线观看| 欧美黄色aaaa| 久久久99免费视频| 亚洲视屏在线播放| 亚洲欧洲日本mm| 国产亚洲午夜| 欧美三级视频在线观看| 久久综合激情| 午夜久久电影网| 99天天综合性| 亚洲国产日韩一区| 老司机午夜精品视频| 欧美一级精品大片| 亚洲一区二区在| 亚洲精品在线二区| 亚洲成色www久久网站| 国产区精品视频| 国产精品入口福利| 欧美视频在线观看视频极品| 欧美大尺度在线| 久久一区二区视频| 久久国产精品毛片| 亚洲欧美另类中文字幕| 一区二区高清在线观看| 亚洲国产精品一区| 欧美大片第1页| 美女任你摸久久| 久久五月天婷婷| 久久久久久亚洲综合影院红桃 | 亚洲国产第一| 国产在线精品成人一区二区三区| 国产精品毛片一区二区三区| 国产精品成人国产乱一区| 欧美日韩一区二区三区四区在线观看| 欧美a级一区| 媚黑女一区二区| 老司机67194精品线观看| 久久久久国内| 美女性感视频久久久| 美女尤物久久精品| 农夫在线精品视频免费观看| 免费视频最近日韩| 欧美国产精品v| 欧美精品一区二区三区蜜臀| 欧美成人免费全部| 欧美高清影院| 欧美日韩视频在线一区二区| 欧美日韩视频在线一区二区观看视频| 欧美日韩国产一中文字不卡 | 中文在线不卡| 亚洲一区二区成人| 性欧美xxxx大乳国产app| 欧美一级免费视频| 久久久亚洲精品一区二区三区 | 久久国产精品网站| 久久aⅴ国产欧美74aaa| 久久久国际精品| 理论片一区二区在线| 欧美成人一区二区三区片免费| 欧美韩国日本一区| 国产精品成人在线观看| 国产日产亚洲精品系列| 极品日韩久久| 日韩视频久久| 性色av一区二区三区| 久久久久久日产精品| 欧美国产精品中文字幕| 亚洲日本久久| 亚洲摸下面视频| 久久久久久久久久久一区 | 欧美一区二区视频网站| 久久久久国产精品一区二区| 欧美日本不卡| 国产欧美一区二区三区在线老狼| 狠狠狠色丁香婷婷综合激情| 亚洲国产欧美一区二区三区同亚洲 | 亚洲精品五月天| 亚洲一区二区三区在线视频| 欧美亚洲综合久久| 欧美大学生性色视频| 亚洲精品在线观看视频| 午夜精品免费视频| 老司机免费视频久久| 国产精品成av人在线视午夜片| 国产真实乱偷精品视频免| 亚洲精品一区二区三区福利| 午夜精品视频在线| 欧美成人在线免费观看| 亚洲午夜激情网页| 欧美va天堂| 国产亚洲欧美在线| 中文在线一区| 欧美wwwwww| 午夜精品99久久免费| 欧美黄色影院| 国内精品久久久久久久影视蜜臀 | 国产精品久久久一区麻豆最新章节| 黄色工厂这里只有精品| 亚洲午夜精品国产| 欧美国产日韩视频| 先锋资源久久| 欧美日韩亚洲一区二区三区在线观看| 国内精品伊人久久久久av一坑| 亚洲午夜精品福利| 亚洲韩国青草视频| 久久高清免费观看| 国产精品久久久久久久久久久久久 | 国产精品va在线| 亚洲国产成人精品久久| 欧美在线免费观看| 日韩性生活视频| 免费欧美视频| 狠狠色综合网| 久久国产精品99久久久久久老狼| 亚洲日本一区二区三区| 快播亚洲色图| 国产一区二区三区免费不卡| 亚洲欧美中文另类| 一本色道久久精品| 欧美日韩国产精品一区| 亚洲国产精品嫩草影院| 久久久五月天| 欧美一区三区二区在线观看| 国产精品人人做人人爽| 亚洲一区二区少妇| 日韩亚洲欧美一区| 欧美精品久久99久久在免费线| 亚洲国产精品va在线观看黑人| 久久国产精品一区二区三区四区| 亚洲综合另类| 国产日韩精品一区| 久久精品国产99| 欧美一级精品大片| 黄色另类av|