Использование декораторов в Vue, я серьезно

JavaScript Vue.js

Есть много вещей, которые нужно сделать при запуске продукта, и тестирование продуктов нельзя отделить друг от друга. Хью спросил, сколько жуков осталось, и круги под глазами были как лак и грязь.

как бывшийJava coder, когда я впервые увиделjsдекоратор внутри(Decorator), я сразу подумал оJavaАннотации в , конечно же, по фактическому принципу и функции,Javaаннотации иjsДекораторы совсем другие. Название этой статьиVue中使用装饰器,我是认真的, но эта статья начнётся с разработки концепции декораторов, давайте посмотрим.

Из этой статьи вы узнаете следующее:

  1. Узнайте, что такое декоратор
  2. Использование декораторов в методах
  3. существуетclassиспользование декораторов в
  4. существуетVueиспользование декораторов в

Эта статья была впервые опубликована на официальном аккаунте [Некоторые играют на фронтенде]. Если вы не хотите быть соленой рыбой, но хотите сменить работу, обратите внимание на официальный аккаунт и возьмите вас, чтобы почистить вопросы интервью крупных заводов каждый день.=== Дачангoffer.

что такое декоратор

декораторES2016Предложенное предложение, которое в настоящее время находится вStage 2Стадия, про опыт декоратора, можно нажатьGitHub.com/Job39/предложения…проверьте детали. Декоратор — это синтаксический сахар, связанный с классом, который используется для переноса или изменения поведения класса или метода класса.Фактически декоратор — это реализация шаблона декоратора в шаблоне проектирования. Однако приведенные выше понятия слишком сухие, переведем их человеческими словами и приведем пример.

Пишите в ежедневном развитии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Эти дескрипторы атрибутов делятся на две категории, а именно атрибуты данных и атрибуты доступа, и атрибуты данных и атрибуты доступа не могут сосуществовать.

Атрибут данных

Атрибут данных содержит расположение значения данных, где значение может быть прочитано и записано. Атрибут данных содержит четыре дескриптора, которые

  1. configurable

    Указывает, может ли он пройтиdeleteУдалить свойство, можно изменить другие свойства дескриптора свойства или изменить свойство данных на свойство средства доступа. когда мы проходимlet obj = {name: ''}При объявлении объекта все свойства в объектеconfigurableЗначение дескриптораtrue

  2. enumerable

    Указывает, может ли он пройтиfor inилиObject.keysи другие методы для получения атрибута, значение этого дескриптора в объекте, который мы обычно объявляем, равноtrue, но дляclassДля свойств в классе значение равноfalse

  3. writable

    Указывает, можно ли изменить значение данных атрибута, изменив его наfalse, можно добиться эффекта атрибута только для чтения.

  4. value

    Представляет значение данных текущего атрибута, при чтении значения атрибута считывается отсюда, при записи значений атрибута оно будет записано.

свойство доступа

Свойства аксессора не содержат значений данных, они содержатgetterа такжеsetterдве функции одновременноconfigurableа такжеenumerableдва дескриптора, совместно используемые атрибутами данных и атрибутами доступа.

  1. getter

    Эта функция вызывается при чтении свойств, по умолчанию эта функцияundefined

  2. 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('我是子君,我的公众号是 【前端有的玩】,关注有惊喜哦')
  }
}

Разберем код построчно

  1. Сначала мы определяемdebounceфункция с одним параметромwait, эта функция соответствует той, что используется при вызове декоратора ниже@debounce(100)
  2. debounceФункция возвращает новую функцию, которая является ядром декоратора, эта функция имеет три параметра, которые будут анализироваться один за другим.
    1. target: Эта функция является атрибутом класса, установленным сверху, что соответствует приведенному выше примеру.MyClassДобрый
    2. name: Имя этой функции атрибута класса, соответствующее приведенному вышеfollow
    3. descriptor: это дескриптор атрибута, о котором мы упоминали ранее, непосредственноdescriptorВышеупомянутые свойства могут реализовывать свойства только для чтения, перезапись данных и другие функции.
  3. затем третья строка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

При загрузке данных, чтобы предоставить удобную подсказку и предотвратить дальнейшую работу пользователей, перед запросом обычно отображается загрузка, а затем загрузка отключается после завершения запроса.Общий метод записи выглядит следующим образом.

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()
    }
  }
}

Декораторы, определенные в этом разделе, были применены к этому проекту.GitHub.com/snow Zijun/V…, который основан наVantРазработан готовый мобильный фреймворк, вам нужно толькоforkВниз, вы можете напрямую заниматься развитием бизнеса без какой-либо конфигурации, добро пожаловать в использование, если вы хотите беспокоить вас, чтобы датьstar.

Меня зовут Zijun, и я сегодня так много написал.Эта статья была впервые опубликована на официальном аккаунте [Something Plays at the Front End].Добро пожаловать, обратите внимание.

Эпилог

Не истощайте свое вдохновение и воображение, не будьте рабом своих моделей. - Винсент Ван Гог