В Human Reef наша платформа только что получила полный редизайн UX/UI,
во время этого процесса мы решили реорганизовать наши компоненты Vue. Рефакторинг был основан на нескольких проблемах, например. повторение методов (в основном геттеры/сеттеры и доступ к Vuex) в каждом компоненте.

Что еще более важно, некоторые из этих повторяющихся методов были реализованы совершенно по-разному на платформе. Третьей проблемой была длина файлов некоторых компонентов.

В следующих нескольких разделах мы дадим некоторое представление о том, как мы решили эти проблемы. Во-первых, мы кратко рассмотрим TypeScript и, что более важно, как писать элегантные компоненты с помощью декораторов. Используя эти методы, мы пишем чистые и, что более важно, менее подверженные ошибкам компоненты Vue.

Машинопись

Мы решили писать все наши компоненты на TypeScript. Будучи
ориентированным на типы языком, он позволяет нам писать более чистый и структурированный код. TSLint может выполнять проверку типов и анализировать нашу кодовую базу.

TSLint запускается в процессе разработки с использованием службы, предоставляемой vue-cli-service.

npm run serve

Мы расширили конфигурацию TSLint некоторыми правилами (добавленными в tslint.json), такими как максимальная длина файла и длина строки. Довольно часто мы сталкивались с проблемой длинных строк и/или файлов. Разделение таких компонентов на повторно используемые компоненты с использованием методов, описанных далее, решило эту проблему.

Компоненты TypeScript

Давайте посмотрим на следующий компонент:

<template>
    <button @click="onClick">Click!</button>
</template>
<script lang="ts">
    import Vue from 'vue';
    import Component from 'vue-class-component';
    
    @Component
    export default class MyComponent extends Vue {
      message: string = 'Hello!';
      
      onClick(): void {
        window.alert(this.message);
      }
    }
</script>

Это очень простой компонент, использующий TypeScript. Он не сильно отличается от стандартного компонента vue, но позволяет нашим компонентам иметь типизированные свойства/методы.

Декораторы

С помощью vue-class-component все методы и хуки жизненного цикла могут быть записаны как методы класса. Само по себе это довольно полезно, но когда мы объединяем это с vue-property-decorator, мы можем написать любой prop/watcher/emit/… с помощью декоратора. Более того, мы используем Vuex в качестве управления состоянием, а с помощью vuex-class мы можем написать любой геттер/действие в качестве декоратора.

import Vue from 'vue';
import Component from 'vue-class-component';
    
@Component({
  props: {
    propMessage: string
  }, 
  watch: {
    hello: function (val) {
       // ...
    },
  },
  actions: {
    dummyAction (context) {
      context.commit('dummyAction')
    }
  },
  getters: {
    dummyGetter: state => {
      return state.dummyGetter
    }
  }
})
export default class MyComponent extends Vue {
   method() {
      // some logic
   }
}

В приведенном выше компоненте методы, которые мы пишем в нашем разделе класса (все в MyComponent), будут смешаны с логикой, которую мы пишем в декораторе @Component (вверху файла). Когда в вашем компоненте есть несколько реквизитов и несколько наблюдателей, это очень быстро становится нечитаемым. В нашем приложении использование 10 или более геттеров/экшенов в одном компоненте не является исключением.

import Vue from 'vue';
import {Component, Prop, Watch} from 'vue-property-decorator';
import {Action, Getter} from 'vuex-class';
    
@Component
export default class MyComponent extends Vue {
   
   @Prop()
   propMessage: string;
   
   @Watch('hello')
   hello(val: any) {
      //...
   }
   
    @Getter('dummyGetter')
    dummyGetter: any;
   
    @Action('dummyGetter')
    dummyAction;
   
   // ...
}

Таким образом, мы можем добиться более элегантного компонента, используя правильные декораторы. Хотя это не быстрее и не эффективнее. Фактически, все, что мы сделали, — это переместили код из одного декоратора компонентов в несколько специальных декораторов.

Миксины

Мы комбинируем эти декораторы с миксинами. В vue-class-component включена поддержка миксинов, это позволяет наследовать методы и свойства в компонентах. Обычно использование одного и того же геттера в нескольких компонентах очень громоздко, так как этот геттер приходится копировать и вставлять в каждый компонент.

Мы переместили все наши геттеры/действия в отдельные файлы и наследуем правильный файл в зависимости от потребностей компонента. Поскольку геттеры кэшируются и извлекаются только по требованию, эффективность не связана с количеством унаследованных геттеров. В принципе, наследование слишком большого количества геттеров не проблема.

import Vue from 'vue';
import {Component} from 'vue-property-decorator';
import {Getter} from 'vuex-class';
    
@Component
export default class MyComponent extends Vue {
   
    @Getter('dummyGetter1')
    dummyGetter1: any;
   
    @Getter('dummyGetter2')
    dummyGetter2: any;
   
    @Getter('dummyGetter3')
    dummyGetter3: any;
}

Начнем с вышеуказанного компонента, перепишем его с помощью миксинов как таковых:

import {mixins} from 'vue-class-component';
import {Component} from 'vue-property-decorator';
import DummyMixin from '<path>/DummyMixin.ts'
    
@Component
export default class MyComponent extends mixins(DummyMixin) {
   // ...
}
import Vue from 'vue';
import {Component} from 'vue-property-decorator';
import {Getter} from 'vuex-class';
    
@Component
export default class DummyMixin extends Vue {
   
    @Getter('dummyGetter1')
    dummyGetter1: any;
   
    @Getter('dummyGetter2')
    dummyGetter2: any;
   
    @Getter('dummyGetter3')
    dummyGetter3: any;
    
}

Таким образом, мы можем повторно использовать DummyMixin везде. Мы уменьшили количество строк, посвященных геттерам/действиям Vuex, в каждом компоненте. Рефакторинг и расширение примесей также выполняются быстрее, поскольку изменения напрямую применяются ко всем компонентам, которые их наследуют.

Вывод

Итак, мы пишем компоненты Vue, используя TypeScript. Все наши файлы проверяются на ошибки типа и ошибки lint с помощью TSLint с некоторыми дополнительными правилами.

Устранение необходимости в одном декораторе @Component в начале наших классов достигается с помощью vue-class-component в сочетании с vue-property-decorators.

Чтобы уменьшить размер файла, мы используем примеси, таким образом, мы наследуем часто используемые декораторы. Миксин написан в общем виде и используется везде в нашей кодовой базе.