国产精品美女久久久浪潮AV,国产精品三级一二三区,久久精品国产一区二区小说 ,依依成人影视国产精品,全部无卡免费的毛片在线看,日本一区二区三深夜不卡,国产精品女同一区二区久久,国产精品夜色一区二区三区

        Vue中使用裝飾器,我是認(rèn)真的

        2020-7-7    seo達(dá)人

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


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


        了解什么是裝飾器

        在方法使用裝飾器

        在class中使用裝飾器

        在Vue中使用裝飾器

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

        什么是裝飾器

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


        在日常開發(fā)寫bug過程中,我們經(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è)防抖的例子,我們通過debounce函數(shù)將另一個(gè)函數(shù)包起來,實(shí)現(xiàn)了防抖的功能,這時(shí)候再有另一個(gè)需求,比如希望在調(diào)用follow函數(shù)前后各打印一段日志,這時(shí)候我們還可以再開發(fā)一個(gè)log函數(shù),然后繼續(xù)將follow包裝起來


        /**

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

        */

        class MyClass {

         follow = debounce(

           log(function() {

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

           }),

           100

         )

        }

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


        class MyClass {

         @debounce(100)

         @log

         follow() {

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

         }

        }

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


        在方法上使用裝飾器

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


        了解一下屬性描述符

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


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

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


        configurable

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


        enumerable

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


        writable

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


        value

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


        訪問器屬性

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


        getter

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


        setter

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


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


        使用Object.defineProperty

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


        let obj = {

         name: '子君',

         officialAccounts: '前端有的玩'

        }

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


        Object.defineProperty(obj,'name', {

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

         writable: false

        })

        // 修改obj.name

        obj.name = "君子"

        // 打印依然是子君

        console.log(obj.name)

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


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

        裝飾器本質(zhì)上依然是一個(gè)函數(shù),不過這個(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)注有驚喜哦')

         }

        }

        我們逐行去分析一下代碼


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

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


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

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

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

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

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


        在class上使用裝飾器

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


        // 這個(gè)是要混入的對(duì)象

        const methods = {

         logger() {

           console.log('記錄日志')

         }

        }


        // 這個(gè)是一個(gè)登陸登出類

        class Login{

         login() {}

         logout() {}

        }

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


        function mixins(obj) {

         return function (target) {

           Object.assign(target.prototype, obj)  

         }

        }


        // 然后通過裝飾器混入

        @mixins(methods)

        class Login{

         login() {}

         logout() {}

        }

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


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


        在Vue中使用裝飾器

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


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

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


         parserOptions: {

           ecmaFeatures:{

             // 支持裝飾器

             legacyDecorators: true

           }

         }

        使用裝飾器

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


        function log() {

         /**

          * @param target 對(duì)應(yīng) methods 這個(gè)對(duì)象

          * @param name 對(duì)應(yī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ì)通過throttle或debounce方法對(duì)要調(diào)用的函數(shù)進(jìn)行包裝,現(xiàn)在就可以使用上文說的內(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é)流開始前。

        * [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): 指定在延遲開始前調(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è)用戶一個(gè)友好的提示,同時(shí)防止用戶繼續(xù)操作,一般會(huì)在請(qǐng)求前顯示一個(gè)loading,然后在請(qǐng)求結(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ù)時(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è)提示框讓用戶確認(rèn)是否刪除,這時(shí)候常規(guī)寫法可能是這樣的


        import { Dialog } from 'vant'


        export default {

         methods: {

           deleteData() {

             Dialog.confirm({

               title: '提示',

               message: '確定要?jiǎng)h除數(shù)據(jù),此操作不可回退。'

             }).then(() => {

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

             })

           }

         }

        }

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


        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è)。


        裝飾器組合使用

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


        export default {

         methods: {

           @confirm()

           @loading()

           async deleteData() {

             await delete()

           }

         }

        }

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

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




        日歷

        鏈接

        個(gè)人資料

        存檔

        主站蜘蛛池模板: 中文无码人妻有码人妻中文字幕| 一本大道东京热无码| 美女网站免费福利视频| 中国老熟妇506070| 国产精品无码久久av不卡| av在线观看| 游戏| 久久久亚洲色| 中文字幕丰满孑伦无码专区 | 国产无遮挡吃胸膜奶免费看| 在线a毛片免费视频观看| 在线观看午夜亚洲一区| 久久久久亚洲av无码麻豆| 亚洲av无码一区二区三区在线| 成人精品视频99在线观看免费| 亚洲国产区男人本色| 亚洲色婷婷一区二区三区| 青青草97国产精品免费观看| 国产精品天堂avav在线| 一本色道久久88—综合亚洲精品| 阳朔县| 蜜桃视频在线观看免费网址入口| 日本亚洲国产一区二区三区 | 777亚洲熟妇色xxxxx| 扒开老女人p大荫蒂视频| 蜜桃视频在线观看免费网址入口| 日本三a级做爰片啊荒野| 盖州市| 亚洲一本之道高清乱码| 欧美亚洲国产suv| 非会员试看60秒体验| 少妇bbbb| 痉挛高潮喷水av无码免费| 在线综合亚洲欧洲综合网站| 出租屋勾搭老熟妇啪啪| 国产精品久久久福利| 国产果冻豆传媒麻婆精东| 日韩人妻无码一区二区三区久久99| 最新中文字幕av无码不卡| 全国最大成人网| 一本一道av中文字幕无码|