国产精品爱久久久久久久小说,女人扒开腿让男人桶到爽 ,亚洲欧美国产双大乳头,国产成人精品综合久久久久,国产精品制服丝袜无码,免费无码精品黄av电影,黑色丝袜无码中中文字幕,乱熟女高潮一区二区在线

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

            2020-7-7    seo達人

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


            通過本文內容,你將學到以下內容:


            了解什么是裝飾器

            在方法使用裝飾器

            在class中使用裝飾器

            在Vue中使用裝飾器

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

            什么是裝飾器

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


            在日常開發寫bug過程中,我們經常會用到防抖和節流,比如像下面這樣


            class MyClass {

             follow = debounce(function() {

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

             }, 100)

            }


            const myClass = new MyClass()

            // 多次調用只會輸出一次

            myClass.follow()

            myClass.follow()

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


            /**

            * 最外層是防抖,否則log會被調用多次

            */

            class MyClass {

             follow = debounce(

               log(function() {

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

               }),

               100

             )

            }

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


            class MyClass {

             @debounce(100)

             @log

             follow() {

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

             }

            }

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


            在方法上使用裝飾器

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


            了解一下屬性描述符

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


            數據屬性

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


            configurable

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


            enumerable

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


            writable

            表示能否修改屬性的數據值,通過將這個修改為false,可以實現屬性只讀的效果。


            value

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


            訪問器屬性

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


            getter

            在讀取屬性的時候調用這個函數,默認這個函數為undefined


            setter

            在寫入屬性值的時候調用這個函數,默認這個函數為undefined


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


            使用Object.defineProperty

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


            let obj = {

             name: '子君',

             officialAccounts: '前端有的玩'

            }

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


            Object.defineProperty(obj,'name', {

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

             writable: false

            })

            // 修改obj.name

            obj.name = "君子"

            // 打印依然是子君

            console.log(obj.name)

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


            定義一個防抖裝飾器

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


            /**

            *@param wait 延遲時長

            */

            function debounce(wait) {

             return function(target, name, descriptor) {

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

             }

            }

            // 使用方式

            class MyClass {

             @debounce(100)

             follow() {

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

             }

            }

            我們逐行去分析一下代碼


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

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


            target: 這個類屬性函數是在誰上面掛載的,如上例對應的是MyClass類

            name: 這個類屬性函數的名稱,對應上面的follow

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

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

            通過上面的三步,我們就實現了類屬性上面可使用的裝飾器,同時將其應用到了類屬性上面


            在class上使用裝飾器

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


            // 這個是要混入的對象

            const methods = {

             logger() {

               console.log('記錄日志')

             }

            }


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

            class Login{

             login() {}

             logout() {}

            }

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


            function mixins(obj) {

             return function (target) {

               Object.assign(target.prototype, obj)  

             }

            }


            // 然后通過裝飾器混入

            @mixins(methods)

            class Login{

             login() {}

             logout() {}

            }

            這樣就實現了類裝飾器。對于類裝飾器,只有一個參數,即target,對應的就是這個類本身。


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


            在Vue中使用裝飾器

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


            配置基礎環境

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


             parserOptions: {

               ecmaFeatures:{

                 // 支持裝飾器

                 legacyDecorators: true

               }

             }

            使用裝飾器

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


            function log() {

             /**

              * @param target 對應 methods 這個對象

              * @param name 對應屬性方法的名稱

              * @param descriptor 對應屬性方法的修飾符

              */

             return function(target, name, descriptor) {

               console.log(target, name, descriptor)

               const fn = descriptor.value

               descriptor.value = function(...rest) {

                 console.log(`這是調用方法【${name}】前打印的日志`)

                 fn.call(this, ...rest)

                 console.log(`這是調用方法【${name}】后打印的日志`)

               }

             }

            }


            export default {

             created() {

               this.getData()

             },

             methods: {

               @log()

               getData() {

                 console.log('獲取數據')

               }

             }

            }

            看了上面的代碼,是不是發現在Vue中使用裝飾器還是很簡單的,和在class的屬性上面使用的方式一模一樣,但有一點需要注意,在methods里面的方法上面使用裝飾器,這時候裝飾器的target對應的是methods。


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


            一些常用的裝飾器

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


            1. 函數節流與防抖

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


            import { throttle, debounce } from 'lodash'

            /**

            * 函數節流裝飾器

            * @param {number} wait 節流的毫秒

            * @param {Object} options 節流選項對象

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

            * [options.trailing=true] (boolean): 指定調用在節流結束后。

            */

            export const throttle =  function(wait, options = {}) {

             return function(target, name, descriptor) {

               descriptor.value = throttle(descriptor.value, wait, options)

             }

            }


            /**

            * 函數防抖裝飾器

            * @param {number} wait 需要延遲的毫秒數。

            * @param {Object} options 選項對象

            * [options.leading=false] (boolean): 指定在延遲開始前調用。

            * [options.maxWait] (number): 設置 func 允許被延遲的最大值。

            * [options.trailing=true] (boolean): 指定在延遲結束后調用。

            */

            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

            在加載數據的時候,為了個用戶一個友好的提示,同時防止用戶繼續操作,一般會在請求前顯示一個loading,然后在請求結束之后關掉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) {

                   // 在調用失敗,且用戶自定義失敗的回調函數時,則執行

                   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. 確認框

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


            import { Dialog } from 'vant'


            export default {

             methods: {

               deleteData() {

                 Dialog.confirm({

                   title: '提示',

                   message: '確定要刪除數據,此操作不可回退。'

                 }).then(() => {

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

                 })

               }

             }

            }

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


            import { Dialog } from 'vant'


            /**

            * 確認提示框裝飾器

            * @param {*} message 提示信息

            * @param {*} title 標題

            * @param {*} cancelFn 取消回調函數

            */

            export function confirm(

             message = '確定要刪除數據,此操作不可回退。',

             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: {

               // 可以不傳參,使用默認參數

               @confirm()

               deleteData() {

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

               }

             }

            }

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


            裝飾器組合使用

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


            export default {

             methods: {

               @confirm()

               @loading()

               async deleteData() {

                 await delete()

               }

             }

            }

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

            藍藍設計www.dzxscac.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務




            日歷

            鏈接

            個人資料

            藍藍設計的小編 http://www.dzxscac.cn

            存檔

            主站蜘蛛池模板: 中文字幕免费不卡二区| 黄色成人免费视频| 日本精品一区二区三区四区| 国产无套抽出白浆来| 福利一区在线| 国产在线精品一区二区在线看| 大帝av在线一区二区三区| 激情久久五月天| 国产精品麻豆中文字幕| 在线观看国产成人swag| 第一福利丝瓜av导航| 欧美36p| 在线va| 国产中文字幕精品在线| 国产成人无码精品久久久小说| 天天插天天狠天天透| 精品成人一区二区三区久久精品| 怡红院免费的全部视频| 国产成人麻豆亚洲综合精品| 欧美www在线观看| 亚洲伊人久久精品影院| 国产av永久精品无码| 欧美亚洲国产一区| 国产精品精品一区二区三| 黑人猛挺进小莹的体内视频| 99久久久成人国产精品| 国产精品午夜影院| 国产99久一区二区三区a片| 精品国产一卡2卡3卡4卡新区| 亚洲精品一二三四区| 精品视频久久久久久| 熟女人妻aⅴ一区二区三区60路 | 黄页网站视频免费大全| 精品欧美| 黄色美女片| 日韩精品久久无码中文字幕| 色综合久久婷婷88| 亚洲激情中文字幕| 国产在线观看成人| 18禁超污无遮挡无码免费网站国产| 亚洲国产欧美在线人成人|