Идеальное Vue приложение на Typescript

Пока Vue 3 официально еще не вышел, а продакшене в основном 2 версия — я хочу поговорить о типизации и о том, что она все еще не идеальна во Vue. И сегодня мы попробуем создать идеальное приложение с типизацией на typescript сделав упор на code style, пропагандируя vue style guide и прочие обычно не значащие вещи, которые были придуманы умными людьми!

Ремарка

Стоит учитывать, что автор пишет первый свой пост и это пагубно скажется на его качестве и возможно на качестве всего контента на Хабре!

Почему «идеальное»?

Недавно я осознал всю важность стайл-гайдов, очень уперся в качество кода, настройки автоформатирования (eslint) и многие другие вещи, такие как conventional commits, например. Помимо этого я понял, что мне очень нравится, когда мой код подходит под все международные стандарты и принципы, это ли не признание самого себя как неплохого разработчика (риторический вопрос)? Возможно, вы дошли до этого раньше меня и продвинулись еще дальше и увидите в статье недостатки — добро пожаловать в комментарии с объективной критикой.

Какие проблемы с типизацией у Vue?

Версия 2 принесла и популизировала typescript в компонентах и в хранилище (store), но оставила бреши в связи их между собой, store все еще не подсказывает типизацию в компонентах, и это плохо! Следить в ручную можно за чем угодно, но зачем, если можно это исправить и автоматизировать? Чтобы улучшить типизацию мы будем использовать vue-property-decorator для компонентов и vuex-smart-module для прокидывания типов из стора.

Создание проекта и его настройка

Тут все просто, сначала вызываем vue cli и выбираем галочки!

vue create habratest

Наш выбор — vue2, все features, Vue Router History Mode, Vue Class Components, и другие настройки как на скриншоте ниже. Тесты настраиваем по вкусу, в этой статье их касаться не будем.

Vue CLI настройки создания проектаVue CLI настройки создания проекта

Также я рекомендую в графе «Разнести конфиги всего и вся в разные файлы» ставить галочку.

Стили и красота, пока вся тяжесть человеческого прогресса грузится вам в node_modules

В этой статье не будет визуализации и красоты интерфейса, вы можете подключить любую библиотеку внутрь проекта и использовать ее. Мой выбор — vue-bootstrap для легких проектов и quasar для больших. А сегодня красота у нас в коде!

Настройка eslint

Первым делом идем менять настройки! Меняем extends на код ниже — это позволит eslint проверять ваш код более строго — в соответствии с Vue style guide — в соответствии с низшим приоритетом ошибок — recommended.

  extends: [
    'plugin:vue/recommended',
    '@vue/typescript/recommended'
  ],

Это половина победы! Изменение eslint таким образом поможет вам строже соблюдать style guide и в существующем проекте, но будьте осторожны, нередко что-то может пойти не так.

Да, это даже во Vue cli шаблоне исправит пару ошибокДа, это даже во Vue cli шаблоне исправит пару ошибокЧто в eslint можно отлючить

Ничего. Это все поможет вам в написании кода и менять я ничего не рекомендую. Единственное правило, которое можно исключить из проверки — это директива v-html, которая нередко встречается в старой кодовой базе. Но подобное подчеркивание позволит собрать проект и будет лишним напоминанием для вас, так что лучше оставить.

Заодно давайте включим правила для отслеживания одинарных кавычек и удаления лишних точек с запятой — добавим в массив rules в этом же файле следующие строку:

"semi": [2, "never"],
"quotes": [2, "single", { "avoidEscape": true }]

Установка и настройка «правильного» Vuex

npm install vuex-smart-module

Почитать про все библиотеки для типизации Vuex рекомендую тут, возможно другие варианты подойдут вашему проекту больше. Но я выбрал этот модуль, так как он:

  1. От автора vue-property-decorator, который ставится по умолчанию для class-style components;

  2. Максимально близок к обычной версии стора.

Идем src/main.ts и убираем store оттуда. Для типизации нам не нужно определение this.$store на прототипе, мы будем его импортировать.

Создаем в папке store директорию modules и в ней папку habrModule, в ней файлы: index.ts, actions.ts, getters.ts, mutations.ts, state.ts.

9f4aaf705658e7f39771d51bf991a11a

Папка должна выглядеть так, разнесение на модули — важный принцип декомпозиции проекта и поможет вам при увеличении объема кода проекта. © Ваш капитан

(!) Также я не использую в корне стора никаких стейтов и геттеров, если это понадобится — попробуйте придумать модуль для этого, например appSettings

Файл src/store/modules/habrModule/state.ts:

export default class HabrState {
  value = 'hello';
}

Файл src/store/modules/habrModule/getters.ts:

import { Getters } from 'vuex-smart-module'
import HabrState from './state'

export default class HabrGetters extends Getters {
  /**
   * Параметризированный greeting, не кэшируется Vuex
   * @param name 
   * @example module.getters.greeting("Habr!")
   */
  greeting(name: string): string {
    return this.state.value + ', ' + name
  }

  /**
   * Не параметризированный greeting, кэшируется Vuex
   * @example module.getters.greetingDefault
   */
  get greetingDefault(): string {
    return this.getters.greeting('Habr!')
  }
}

Файл src/store/modules/habrModule/index.ts:

import { Module } from 'vuex-smart-module'
import getters from './getters'
import state from './state'

const habr = new Module({
  state: state,
  getters: getters,
})

export default habr

Мы получили маленький, но гордый модуль! Осталось зарегистрировать его в store

Файл src/store/index.ts

import Vue from 'vue'
import Vuex from 'vuex'

import { Module, createStore } from 'vuex-smart-module'
import habr from './modules/habrModule'

Vue.use(Vuex)

const root = new Module({
  modules: {
    habr,
  },
})

const store = createStore(root)

export default store

export const habrModule = habr.context(store)

Другие файлы модуля оставим пустыми, но вы можете их заполнять по необходимости, вся документация есть на гитхаб у модуля.

Чтобы использовать модуль — нужно импортировать его из стора. Далее он будет типизированным.

Пример компонента



И подсказочки будут работать!

И подсказочки работают!И подсказочки работают!

И вроде бы почти закончили! Осталось только…

Рефакторинг

  1. Идем в папке src/views и переименовываем там файлы в HomeView.vue и AboutView.vue, затем в src/components и переименуем там файл в HelloComponent.vue. Также дописываем им name в аннотацию компонента.

  2. Идем в src/router/index.ts и делаем импорт HomeView.vue динамическим — чтобы было одинаково все и везде, это рекомендация vue style guide.

  3. Поправляем поломавшиеся импорты.

  4. Запускаем автоматический линтер через npm run lint.

  5. Довольные идем пить кофе и отдыхать.

О чем в статье ни слова

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

  1. Component-naming — Vue имеет некоторые правила касающиеся названия компонентов, которые не отслеживаются eslint.

    1. Например компоненты, которые используются лишь раз — следует называть c префиксом The, к примеру TheNavigationComponent.vue — так бы я назвал панель навигации, которая лежала бы в корне и больше никуда не импортировалась.

    2. Мой принцип названия и разделения между views/components: если компонент импортируется только в роутер — он View, иначе — Component (с опциональным The), суффиксы позволяют выполнять другую рекомендацию — названия компонентов не должны быть из одного слова (Navigation.vue — ALERT!), потому что могут совпадать с названиями html тегов по умолчанию (текущими и будущими).

    3. Названия компонентов в темплейтах: vs Vue разрешает и то и другое, но я рекомендую CamelCase для удобства (например — подсвечивается большинством ide).

  2. Разбиение хранилища на модули — опционально по стайл гайдам, но я приверженец делать его всегда.

  3. Динамические импорты компонентов — это по умолчанию, всегда так делайте (это Components: () => import(path) ), webpack умный и такие импорты в 90% случаях уменьшат вам время полной загрузки страницы, а в оставшихся 10% — ничего не поменяют, так что оно того стоит. Это просто, одинаково и удобно.

  4. Всегда в компонентах прописывайте имя (даже с учетом того, что оно опционально).

  5. Старайтесь названия файлов компонентов согласовывать с названием классов (и именами внутри).

  6. Вызовы к api — ТОЛЬКО из store, об этом вам скажет любой Vue разработчик.

От всего остальное вас должен спасти eslint и голова, но style guide почитайте! :)

Конец!

Я надеюсь вам понравилось, качество кода и стайл гайды — это интересно, а также порождает продуктивные дискуссии в командах, обсуждайте такие вещи, это принесет удовлетворение и иногда поднимет самооценку! Но без негатива!

Гитхаб получившегося приложения: github

Также все это получится с небольшими доработками запустить и на Vue 3, у меня получилось, но так как я в нем не до конца еще уверен и разобрался — статья про уходящий Vue 2.

P. S.

Буду рад любому развернутому фидбеку!

Было бы вам интересно почитать нечто подобное для «идеального» тестирования Vue приложений?

© Habrahabr.ru