Как я начал любить Vue

    Вступление



    Данный пост — это логическое продолжение моего поста/статьи — Как я перестал любить Angular / How I stopped loving Angular.
    Рекомендуется к ознакомлению перед прочтением.


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


    Краткий рекап предыдущего поста:


    Вот краткий список основных проблем, беспокоивших меня в Angular на момент написания предыдущей статьи:


    1. Ужасный роутер
    2. Тяжеловесное и практические бесполезное Dependency Injection (см. ниже)
    3. Крайне спорная система модулей (не используемая ни в одном другом фреймворке)
    4. Множество лишних и мало полезных абстракций, странный API-design
    5. Observable как часть фреймворка
      ...

    DI


    Сразу следует оговориться насчет Dependency Injection: после перехода на Vue, все-таки стоит отметить, что в Angular более удобно мокировать внешние зависимости в юнит тестах.


    Это полностью зависит от кодовой базы и внутренних best-practice, но нередко во Vue мокировать что-то в тестах получается несколько сложнее, нежели в Angular (пример будет ниже).


    Однако я не считаю это серьезным основанием для применения столь чуждого JavaScript'у паттерна на фронт-энде.


    Почему не про реакт


    И еще одна небольшая оговорка, касательно того, почему был выбран именно Vue, а не React.
    Дисклеймер: все пункты ниже ОЧЕНЬ субъективны (поэтому не следует воспринимать их как критику, а лишь как личный взгляд):


    1. Реактивность во Vue просто работает, причем сразу и асинхронно: нет нужды заморачиваться с иммутабельностью и чистыми функциями
      (концепция иммутабельности очень крутая, но зачастую крайне многословная — почти всегда это долго и дорого)
    2. JSX плох сразу по нескольким причинам:
      • Медленная миграция HTML разметки в код приложения — особенно здесь страдают дизайнеры, которым приходится привлекать разработчика иногда для довольно тривиальных вещей
      • Постоянно, даже без особой нужды, приходится дробить все на мелкие компоненты, что отнимает время VS шаблонизация в Angular/Vue – компоненты выносятся по необходимости в любой момент
      • В моей памяти еще свежи воспоминания о том, что кондишeны = боль
    3. Формы адовые – про это позже
    4. сreate-react-app на мой взгляд, на сегодняшний день, является самым мало-функциональным CLI из всех наиболее популярных фреймворков, уж для React давно стоит создать гораздо более мощную тулзу
    5. У нашей команды был довольно богатый опыт работы с Angular — по ряду схожих факторов войти во Vue им было гораздо проще

    Отличия и впечатления после Angular


    Немного про основные отличия и впечатления которые я отметил для себя при переходе на Vue.


    Документация


    Первое с чем вы столкнетесь выбрав UI фреймворк, это, конечно же, документация.


    Не буду повторно разбирать документацию Angular с ее


    Banana in a box

    скажу лишь, что во Vue она значительно более простая и доходчивая.


    Не буду приводить примеры, т.к. все они могут показаться вам субъективными, тогда как начав читать доки, сразу чувствуется, что они писались для людей.


    Также стоит отметить, что доки написаны на 6 языках, хотя хватило бы и английского (считаю, что сейчас любой разработчик должен знать English хотя бы на уровне чтения).


    CLI


    Из моей предыдущей статьи вы могли понять, что раньше я думал что Angular CLI самое лучшее, но, как выяснилось:


    1. Его порой крайне сложно кастомизировать
    2. Angular и CLI разбит на множество пакетов @angular и CLI с разными версиями, что, в свою очередь, приводит к колоссальным проблема при апгрейде версии CLI/angular.
      Что и без того делается весьма не просто


    С другой стороны Vue CLI 3 — самое крутое CLI на сегодня


    1. Кастомизация крайне удобна, любой пакет можно добавить в любой момент, благодаря системе плагинов
    2. Простота и гибкость, благодаря использованию webpack


    Zone.js vs Vue Reactivity


    Angular использует Zone.js для отслеживания изменений, который monkey-патчит для этого стандартные API, вроде setTimeout.


    Это приводит к определенным проблемам:


    1. Сложности с нестандартными API, порой довольно сложно разрешимые
    2. Ужасные стектрейсы
    3. Вообще говоря ИМХО, это хак, причем далеко не самый элегантный

    Vue не нужен Zone.js, отслеживание работатет благодаря превращению всех data/state пропертей в Observer.


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


    На верхнем уровне все тривиально: Vue проходится по всем пропертям через Object.defineProperty (именно по этой причине не поддерживается IE8 и ниже) и добавляет таким образом геттер и сеттер.


    Даже особо нечего добавить...


    Однако, у данного подхода есть подводные камни, но они предельно просто описаны и легки для понимания.
    Причем понимания не столько Vue, сколько самого JS в его основе.


    Во Vue нельзя динамически добавлять новые корневые реактивные свойства в уже существующий экземпляр. Тем не менее, можно добавить реактивное свойство во вложенные объекты, используя метод Vue.set(object, key, value):

    var vm = new Vue({
      data: {
        a: 1
      }
    })
    // теперь vm.a — реактивное поле
    
    vm.b = 2
    // vm.b НЕ реактивно
    
    Vue.set(vm.someObject, 'b', 2)

    Надо также отметить, что с версии 2.6 Vue не будет иметь и этих проблем, т.к. произойдет переход на Proxy, и появится возможность также отслеживать добавление/удаление пропертей.


    Rx и State Management


    В Angular из коробки ядром фреймворка был RxJS, всё — Observable.
    Несмотря на мою любовь к Rx, у меня и у многих возникали вопросы, о том, так ли он нужен внутри Angular'а?


    Особенно на первых порах очень многие просто превращали Observable в промисы через .toPromise().


    Да и вообще идея общей шины данных не самая простая для понимания, вдобавок к сложности самого Angular.


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


    Существует NgRx, но полноценно юзабельным он стал не так уж давно — как результат, у нас даже есть старый проект с кастомной имплементаций Redux-подобного стора.


    А теперь про Vue.


    Мало того, что любой фанат RxJS сможет легко подключить его в любой момент, просто добавив новый пакет.


    Уже даже есть Vue-Rx, позволяющий использовать RxJS Observabl'ы наравне с data.


    Если говорить про State Management, то есть великолепный официальный Vuex.
    В свое время с огромным удивлением обнаружил, что помимо него существует также огромное количество альтернатив.



    Drop In


    Собственно, тут и проявляется основное достоинство (хотя кому-то может показаться и недостатком) Vue — все можно подключить по необходимости.


    Просто добавь:


    1. Воды
    2. RxJS
    3. Vuex
    4. TypeScript
    5. SCSS

    Чего бы вам недоставало, оно всегда интегрируется просто и быстро.


    Роутер


    Причина одной из самых тяжелых психологических травм которую я получил от Angular — роутер.
    Несмотря на то, что переписывался он уже трижды, он все еще ужасен (да, я повторяюсь).


    Не буду описывать все его проблемы подробно, но так как тема наболевшая, кратко:


    1. Нет именованных роутов (!) — одно из самых странных решений, которое можно было принять это убрать именованные роуты, тем самым понизив удобство поддержки сложных приложений в разы
    2. Странная система ивентов, проверять которые нужно по типу,
    3. Превращение параметров роута в Observable — порой с ними крайне неудобно работать если они вам нужны только 1 раз при старте компонента
    4. Для навигации были придуманы команды, очень странное и мало полезное решение, более не используемое ни в одном роутере
    5. Lazy Loading через строковое название модулей, в приложении полностью на TypeScript… без комментариев
      … и т.д.

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


    То есть, даже не будь в нем странных решений вроде команд, количество фич все равно делает его сложным и тяжелым.


    Во Vue роутер предельно простой и рабочий.


    Говоря простой, надо упоминуть, что в свое время я не обнаружил привычного по Angular параметра abstract: true.


    Из коробки нельзя сделать роут без шаблона, но это решается одной строчкой кода — созданием компонента вроде:


    // AbstractRoute.vue
    <template>
      <router-view/>
    </template>

    Плохо ли то, что подобной функциональности нет из коробки?


    И да и нет, ведь с одной стороны важно насколько легко проблема решается, а с другой — сложность и скорость работы самого роутера (в противовес навороченности Angular).


    Кстати, вот классная штука:


        path: `url/sub-url/:id',
        component: MyComponent,
        props: true

    Теперь у компонента MyComponent появится props id, который будет параметром из URL.
    Это элегантное решение, т.к. id сразу будет реактивным и использовать его в компоненте очень удобно (опять же, просто работает).


    Расширение и переиспользование


    С одной стороны все компоненты в Angular — TypeScript классы.
    Несмотря на это, использовать многие фишки TypeScript зачастую либо неудобно, либо невозможно вовсе.


    Это относится в первую очередь к ООП — наследование, абстрактные классы, области видимости.
    Основные проблемы возникают с Dependency Injection и AOT компайлером (упаси вас бог столкнуться с ними).


    Во Vue же — конфигурация инстанса это объект – с ним просто работать и расширять, рефакторить.
    Для переиспользования кода есть мощнейшие штуки — Mixin’ы и Plugin’ы (об этом ниже).


    UI компоненты


    В Enterprise сегменте компоненты обычно имеют готовый дизайн, мокапы и тд. Чаще всего они пишутся с нуля, сразу будучи заточенными под конкретную задачу/продукт/проект.
    Но далеко не всегда у разработчиков есть возможность создавать все с нуля, особенно это касается pet проектов и прототипирования.


    Тут на помощь приходят библиотеки готовые UI компонентов, и для Vue их существует уже великое множество: Element, Vuetify, Quasar, Vue-Material, Muse, iView, итд итд.



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


    Нам также очень нравится основанный на классном CSS фреймворке Bulma набор компонентов Buefy, особенно удобно его использовать в приложениях на Bulma, куда сторонние компоненты подключаются по необходимости.


    В случае Angular — Enterprise-level библиотек компонентов всего пара штук, это в первую очередь Angular Material (Google) и Clarity (VMWare).


    К большому сожалению, темпы развития Clarity в последнее время снизились, что еще более расстраивает в плане перспектив Angular в данном вопросе.




    Реальный опыт и проблемы


    А теперь основные проблемы из реального опыта использования Vue на боевых проектах.


    Слишком много свободы


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


    С одной стороны, то что одно и то же можно сделать множеством разных способов, это очень здорово.


    Но, в реальности, это приводит к тому, что кодовая база становится довольно неконсистентной.


    Утрированный пример: кастомную логику на странице можно сделать объявив некий компонент, причем он может быть как локальным


    const Component = { created() { // logic ... }}
    
    new Vue({
      components: [Component],

    так и глобальным (доступным отовсюду).


    Vue.component('Global', { created() { // logic ... }})

    Можно сделать локальным Mixin который будет реализовывать данную функциональность


    const mixin = { created() { // logic ... }}
    
    new Vue({ mixins: [mixin],

    причем он (примесь? она?..) опять же может быть глобальным.


    Vue.mixin({ created() {// logic ... }})

    В конце концов есть плагины, которые делают почти то же самое, что и глобальные миксины.


    const MyPlugin = {
      install(Vue, options) {
        Vue.mixin({ created() { // logic ... }})
    }

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


    Код ревью необходим


    Несмотря на то, что мы используем Vuex, я отметил для себя, что людям порой достаточно тяжело не использовать data() проперти вместо state.


    Это вопрос скорее скорости — понятно что добавить что-то в data быстрее, но почти всегда получается так, что потом это необходимо будет выносить в state и тратить на это дополнительное время.


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


    Могу предположить что работать с таким кодом через несколько месяцев станет совсем неприятно.


    Юнит тесты


    Также, после Angular, было не очень удобно и очевидно мокировать некоторые вещи в Jest.
    Конкретный пример — local storage. Кто-то решил это нагуглив данный issue на гитхабе.


    Object.defineProperty(window, 'localStorage', {
      value: localStorageMock,
    })

    Мне решение красивым не показалось, но как потом выяснилось, есть и более элегантное


    global.localStorage = localStorageMock;

    Да, это не проблема Vue, но проблема экосистемы в сравнении с Angular.


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


    Cookbook


    Вообще, ребята из Vue знают об этой проблеме и решают ее посредством написания Cookbook с рецептами.
    Фактически это набор готовых решений на популярные задачи.
    Вот например: юнит-тесты,
    валидация и работа с HTTP.


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


    На валидации я остановлюсь позже, но вот работа с HTTP точно описана недостаточно глубоко.


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


    Но так как пресловутого DI у нас нет, а самому создавать инстансы сервисов не очень удобно, хотелось бы иметь подобный паттерн в Cookbook.


    Данную проблему мы по большей части решили выработкой локальных code conventions и best practices. Ссылки в конце статьи


    TypeScript


    Я уже множество раз говорил и писал, о том как крут и полезен TypeScript, но факт в том, что его надо уметь готовить.


    В Angular все завязано на экспериментальные фичи (декораторы), внутренние классы и сотни (тысячи?) излишних абстракций.



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


    Проблемы с TypeScript в Vue


    Однако, на сегодняшний день, использовать TypeScript со Vue не всегда так уж просто, вот ряд проблем с которым мы столкнулись.


    Во-первых они так и не приняли мой Pull Request из-за своих же поломанных тестов =(((


    Шутка, конечно же, это только моя личная боль


    Два подхода


    Основной проблемой TypeScript во Vue я бы назвал то, что есть два разных официальных подхода к его использованию.


    Это Vue.extend (тайпинги идущие в комплекте с Vue из коробки и поддерживаемые наравне с основной библиотекой)


    import Vue from 'vue'
    
    const Component = Vue.extend({
      ...
    })

    и очень схожий с Angular декоратор vue-class-component


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

    Лично мне не нравится class-component, и по ряду причин мы используем Vue.extend:


    1. У class-component есть свои хитрости / недостатки
    2. Хочется как можно меньше уходить от дефолтных ES6 Vue компонентов
      • И использовать код прямо из документации, без необходимости править для TS
    3. Необходимо, чтобы команда понимала как работает Vue без TypeScript
      • Особенно, учитывая что большинство имеет Angular бэкграунд

    Другие проблемы


    Вот еще некоторые проблемы с TypeScript, разной степени печальности:


    1. TSlint из коробки не работает с Vue — до сих пор нельзя запустить для .vue файлов. Вообще есть решения через fork-ts-checker, но все они некрасивые
    2. ESlint для TypeScript еще мягко говоря не супер. И plugin и parser еще в стадии разработки.
      Многие core и vue правила ломаются, но основная проблема в том, что возникают крайне непонятные ошибки.
      Однако, несмотря на это, мы используем именно его, отключив сломанные правила и подкрутив нужные.
      Ссылка на наш конфиг ESlint.
    3. Vuex store не типизирован снаружи, соответственно вызовы this.$store из компонента возможны фактически с любым payload

    Но и Vue.extend(), в свою очередь, имеет некоторые минусы:


    1. Нельзя использовать как класс (ваш капитан), соответственно никаких приватных, статических методов и тд.
    2. Боль c мапперами из Vuex …mapGetters(), …mapState() и тд. При использовании этих мапперов теряется типизация и появляются странные ошибки. Ждем пока зальют решение
    3. Типизация data() пропертей неудобна, т.к. это функция — каждый раз приходится создавать интерфейс описывающий ее возвращаемое значение
    4. Честная типизация props вообще практически невозможна, т.к. необходимо объявлять нативный JS тип, а ожидается обычно TypeScript интерфейс, но есть некрасивое решение с кастингом

    // Тип реального значения приходящего в myObjectProps
    interface MyType = {...}
    
    // Пример типизации data
    interface MyComponentData = {
      someBooleanProp: boolean;
    }
    
    export default Vue.extend({
      data(): MyComponentData { // Указание возвращаемого функцией data значения
        return {
          someBooleanProp: false
        };
      },
      props: {
        myObjectProps: Object as MyType // кастинг к TS интерфейсу
      },

    Формы с Vuex


    Вообще формы это проблема не только Vuex, а почти любого State Management паттерна.
    Все-таки он предполагает односторонний поток данных, а формы подразумевают двусторонний байдинг.


    Vuex предлагает два варианта решения.


    Через привязку value к значению state, а для обновления — событие input с отправкой коммита:


    <input :value="message" @input="updateMessage">

    // ...
    computed: {
      ...mapState({
        message: state => state.obj.message
      })
    },
    methods: {
      updateMessage (e) {
        this.$store.commit('updateMessage', e.target.value)
      }
    }

    Или все же использовать двусторонний байдинг и v-model а получение и коммит осуществлять через геттер и сеттер:


    <input v-model="message">

    // ...
    computed: {
      message: {
        get () {
          return this.$store.state.obj.message
        },
        set (value) {
          this.$store.commit('updateMessage', value)
        }
      }
    }

    Нам второй вариант кажется удобнее и лаконичнее, но все же очень многословным.
    Представьте описывать подобным образом форму с 20ю или более полями?
    Получается огромное количество кода для такой, казалось бы, примитивной задачи.


    Проблема чуть менее актуальна если использовать мапперы, а именно mapGetters() и mapMutations(),
    но, как я писал выше, они в данный момент с TypeScript плохо работают и пришлось искать другое решение.


    Мы написали примитивнейший маппер, который добавляет геттер (геттер из state) и сеттер (коммит мутации):


    static mapTwoWay<T>(getter: string, mutation: string) {
      return {
        get(this: Vue): T {
          return this.$store.getters[getter];
        },
        set(this: Vue, value: T) {
          this.$store.commit(mutation, value);
        }
      };
    }

    Он позволяет сократить количество кода и описывать поля подобным образом:


    stringTags: Util.mapTwoWay<IDatasetExtra[]>(STRING_TAGS, UPDATE_STRING_TAGS)

    И, соответственно, использовать v-model:


    v-model="stringTags"

    Валидация форм


    Что несколько удивило после Angular — из коробки во Vue нет валидации форм. Хотя, казалось бы, фича весьма востребованная.


    Впрочем, это не беда, есть два наиболее популярных решения для этой задачи.


    vee-validate


    Первое — это весьма схожий механизм с template-driven формами в Angular — vee-validate.


    Фактически вы описываете всю логику валидации в HTML.


    <input v-validate="'required|email'">

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


    Плюс это не очень красиво работает с Vuex.


    vuelidate


    Второе решение — Vuelidate. Невероятно элегантный способ валидации почти любых компонентов — валидируется сама модель, а не тот или иной input.


    Самое забавное, что перед тем как обнаружить этот замечательный пакет мы сами уже было начали писать нечто подобное.


    <input v-model="name" @input="$v.name.$touch()">

    import { required, email } from 'vuelidate/lib/validators'
    
    export default {  
      data () {
        return {
          name: ''
        }
      },
      validations: {
        name: {
          required,
          email
        }
      }
    }

    Очень рекомендую при необходимости валидации форм сразу рассматривать Vuelidate — он отлично работает (в том числе с Vuex), легко подключается и кастомизируется.


    Основная фишка/проблема Vue


    Собственно, в этом и состоит основная проблема (?) Vue — это только библиотека, а не навороченный фреймворк all-in-one.


    Из коробки нет:


    • HTTP
    • Валидации
    • i18n
    • … и тд?

    Да, есть официально поддерживаемые:


    • Router
    • State
      Но, все же это отдельные пакеты, а не ядро.

    Однако, при всем этом, во Vue из коробки есть такая крутая встроенная штуки как Animation ???


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


    Это, пожалуй, гораздо более удобный и мощный тулсет, нежели в том же Angular.


    Классный пример из документации:



    Nightwatch и e2e


    Небольшая проблема, снова не связанная напрямую с Vue, но с которой мы столкнулись на реальном проекте.


    Из коробки при генерации проекта для e2e тестирования есть выбор между Nightwatch и Cypress.


    Несмотря на то, что Cypress лично мне видится как самый классный инструмент для e2e тестирования на сегодня, поддержка браузеров отличных от Chrome до сих пор отсутствует.


    Поэтому мы не могли выбрать его для боевого проекта — реальные кастомеры все же используют и другие браузеры.


    Однажды наши тесты начали падать на Linux CI по совершенно необъяснимой причине (на Windows все было ок), при этом ошибки не были информативны.
    Позже удалось выяснить, что проблема связана с хешами (#) в URL'ах.
    А именно такие URL'ы и будут по умолчанию при использовании vue-router.


    К сожалению, это вроде бы проблема селениума или ChromeDriver, а не Nightwatch (еще один повод смотреть в сторону Cypress и TestCafe), но на текущий момент решением будет "обнулять" url перед открытием хешированных:


    .url('data:,')
    .url(client.globals.devServerURL + `/#/my-hashed-url`)

    Что мы отметили


    Скорость разработки


    После Angular и огромного количества boilerplate кода, который для него приходилось писать, работа с Vue кажется очень быстрой и простой.


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


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


    Чего нет


    Чего действительно пока не хватает Vue — достойной альтернативы React Native.
    Ни один из подобных фреймворков для мобильной разработки нельзя с ним сравнить.


    Да, для Vue есть NativeScript Vue, но он, на сегодня, значительно менее мощный.
    Также есть Weex, но использовать его в реальных проектах я бы пока не стал, так как он еще развивается и не слишком стабилен.


    Производительность


    Для себя мы также обратили внимание на шикарную скорость работы фрейморка. Особенно это касается первоначального открытия страницы.




    Более подробный тест производительности можете посмотреть здесь: http://www.stefankrause.net/js-frameworks-benchmark7/table.html
    (помимо "большой тройки" там есть огромное количество других фреймворков и не только).


    И напоследок пара ссылок на мнения более авторитетных товарищей: GitLab,
    CodeShip, Alibaba, Xiaomi.


    Вывод


    Из всего описанного выше мы сделали некоторые выводы.


    В первую очередь — использование TypeScript с Vue на сегодня имеет немало недостатков, но даже несмотря на них, его использование оправдано.


    Мы используем его уже в нескольких проектах, в том числе вот вот выходящих в production.
    Более того, четко очертив для себя проблемы и их решения, мы научились работать с TS так же быстро и удобно во Vue, как это делается на JavaScript.


    Но все же, хотелось бы, чтобы в более свежих версиях, поддержка TypeScript улучшилась.


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


    Так что, "если вы еще кипятите", очень рекомендую хотя бы попробовать Tide Vue и самим почувствовать разницу.


    Наши code conventions


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


    Стиль кода и структура приложения


    Наш собственный cookbook — сейчас содержит только пример сервиса, но будет расширяться (часть информации на локальных ресурсах).


    Это наш Vue seed (TS, кастомные настройки Jest, ESlint и т.д.), в данный момент, до выхода в релиз Vue CLI 3,
    он сгенерирован на предыдущей версии и представляет из себя не темплейт а репозиторий.
    Готовится версия для разворачивания с помощью Vue CLI 3 через шаблон.


    И это все??

    Да. Спасибо за внимание.

    PS: После прочтения может показаться, что у Vue больше минусов чем плюсов — просто не хочется писать очередную хвалебную статью, ведь их и так полон гугл


    PPS: Кстати английский вариант предыдущей статьи был настолько успешен, что у меня даже состоялось прямое общение (видео) с основными виновниками — но работу не предложили =(


    Английская версия: https://medium.com/@igogrek/how-i-started-loving-vue-d41c1c8e77e1

    Поделиться публикацией
    Похожие публикации
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 54
      +3
      Главной (хронической?) болью от ангуляра для меня остались даже не их местами дико монструозные решения, а отношение к разработке.

      Пользователи ангуляра используют фичу Х не по назначению и стреляют себе в ногу? Мы просто уберем фичу Х. Чтоб не говорили, что ангуляр тормозит. Вы использовали её по назначению? Нас не волнует.

      Вы хотите наследование в ангуляре? Вы возмущены тем, что всю нашу документацию мы старательно прикидываемся ООП-комплементарными, а на самом деле нет? Мы за вас очень страдаем, но наследование делать не будем. Потому что это очень сложно™.
        +1
        Если я правильно понял, то наследование компонентов станет доступным после выхода Ivy render. И о какой фиче идет речь?
          0

          Хорошим примером полезных добавленных, но позже безвозвратно убранных фич в Angular будет пресловутый роутер, а именно: именованные роуты (да они были) и, например, route reuse.


          Про второе вообще можно написать отдельный пост — но суть, что раньше это был просто один параматр вроде reuse: false, а теперь нужно имплементировать немаленький класс RouteReuseStrategy

            +1
            Я много смотрел на рассказы об Ivy, но не заметил там ничего на тему наследования. Если говорить предметно, то наследование в ангуляре можно выразить и сейчас (typescript-то наследовать и вовсе никто не помешает, а с декораторами есть определенные решения), просто это, мягко говоря, совсем кривые способы.

            И о какой фиче идет речь?

            К сожалению уже не вспомню, больше чем полгода прошло. Но про роутер хорошо написано. Крайне грустно видеть, что раньше роутер был гораздо более адекватен в применении, чем роутер сейчас.
          0
          Vue.mixin({ created() {// logic… }})

          Я бы сказал это нужно только когда вы делаете фреймворк и хотите экспортировать этот миксин наружу, т.к. он полезен сам по себе. Ну или когда этот миксин действительно используется в большом количестве компонентов.
          Красота миксинов в том, что a) это просто объект и b) его легко использовать в любом количестве компонентов, не загрязняя общий инстанс Vue.


          В конце концов есть плагины, которые делают почти то же самое, что и глобальные миксины.

          А вот это совсем не правда. Миксины просто добавляют свойства и методы в данный компонент, в то время как плагины позволяют манипулировать Vue каким угодно образом. Vuex работает как плагин, а не как миксин, vue-router тоже.

            0
            Эх, я же специально написал что пример утрированный.
            Поверьте, я хорошо понимаю разницу между миксином и плагином, к тому же написал, что и то и другое — очень полезный функционал.

            Поинт в том, что есть степени свободы в сравнении с Angular. Можно также привести пример с computed vs watched property. Опять же, они преследуют разные цели и нужны для разных задач, но новичкам (особенно), не всегда очевидно что использовать и где.
            0
            Производительность
            По вашей ссылке vue прогигрывает ангуляру: 1.38 против 1.28, а в статье наоборот, как так?
            Vue не будет иметь и этих проблем, т.к. произойдет переход на Proxy, и появится возможность также отслеживать добавление/удаление пропертей.
            Переход будет когда все (необходимые) браузеры будут это поддерживать, а не когда будет релиз vue.
              0
              Касательно производительности взята часть таблицы — если брать все результаты, разницы почти не будет, поэтому я и написал, что основное заметное место большей скорости работы Vue — начальная загрузка страницы.
              Но даже так, коэффициэнт который я вижу по ссылке — 1.14, и уж никак не ниже у Angular против Vue. Вы точно сраниваете обычные версии а не с vuex, no-zone и тд?
                +2
                1.28 у ангуляра без использования Zone.js. Что лишает его некоторых фич, ну и всё-таки это «возьмите наш крутой фреймворк и сами доработайте молотком».
                У просто ангуляра всё те же 1.38.
                0
                Для DI можно использовать InversifyJS — совместим со vue-class-component через LazyInjection декоратор — можно инжектировать не через конструктор.

                Не увидел у вас vue-property-decorator и vuex-class в seed. Принципиально не используете?
                  0
                  Про InversifyJS в курсе, но именно и не хочется тащить все это на фронт-энд.
                  Он очень пригодится для Node, но в обычным Vue приложениях считаю слишком большим оверхедом.

                  Вообще, то же самое касается и vue-property-decorator и vuex-class — как раз то место в статье, где описаны причины использования Vue.extend.
                  Все эти вещи добавляют дополнительную сложность и TS специфику там, где изначально она не предусмотрена — больше проблем и сложнее поддержка.
                  Мы стремимся использовать сторонние вещи только по необходимости.

                  ЗЫ: vuex-class использовал на pet проекте, проблемы были, а большой пользы не ощутил
                    0
                    А как же vue-inject?
                    +2
                    Главное отличие Vue от Angular в том, что Angular предназначен только для SPA-приложений. В остальном Vue очень похож на Angular особено в нотации, основанной на классах. Я знаю, что внутри сделано иначе, но для меня, как для разработчика, это не важно, раз я все равно не вижу этого. Если вы будете добавлять, то чего нет из коробки, то, в конечном итоге, получится… Angular. Это мое мнение, хотите — верьте, хотите — нет.
                      +3
                      Angular предназначен только для SPA-приложений

                      А Vue для чего предназначен? оО

                        +2

                        Vue возможно использовать как замену jQuery/Vanilla JavaScript на классических многостраничных сайтах, то есть без клиентского роутинга, и без API. С Angular это не прокатит так как там есть только один единственный корень приложения.

                          0

                          В Angular 1.x точно так же было можно посадить приложение на любой узел в дереве, не знал, что из 2.x это убрали.

                            0
                            Я тоже не знал, а когда узнал, было уже поздно.
                            0
                            Это будет неудобно, но почему «не прокатит»-то? При желании можно и несколько аппликух впихнуть на одну страницу (по крайней мере, пока у них ангулярские зависимости полностью совпадают по версиям), и в многостраничный контекст их вписать, в произвольной N-to-N конфигурации.

                            В js можно примерно всё. Вопрос только в том, скольких усилий это будет стоить в реализации и в поддержке.
                              0

                              Потому что в официальной доке Angular по поводу non-SPA приложений нет ни слова. Есть где-то хак на каком-то форуме с ручным бутстрапом, но сами Google его не используют, как говорится на свой страх и риск. Обещают в шестой версии запилить Angular Elements, но это уже не фреймворк будет.

                                0
                                Конечно. Именно поэтому я и написал, что «будет неудобно». И поддерживать это будет неудобно. И так далее.

                                Но сделать-то можно, и даже без моря усилий.
                        0

                        Вообще они вроде как сделали возможность использовать отдельные компоненты на странице не превращая все в SPA, см. Angular Elements.

                        +1
                        Из коробки нет:
                        HTTP
                        Валидации
                        i18n
                        … и тд?

                        А мне кажется это больше преимущество, нежели недостаток.
                        Как в противовес можно сопоставить python flask и django.
                          0
                          Вы не упомянули Single File Components — на мой взгляд это одна из главных фич, которая заставляет влюбиться во Vue. Любопытно, а почему вы используете Vue.extend а не SFC? По поводу тестирования у Vue есть схожая с React библиотека тестирования (написание тестов очень похоже)
                            0

                            Именно SFC мы и используем (смотри код стайл раздел), Vue.extend необходим для поддержки TypeScript, примерно как здесь.

                              0
                              То есть пропсы не типизированы?
                                0
                                В стринговом описании — нет.
                                В посте есть наш вариант использования с кастингом (после раздела «Другие проблемы»). То есть используется и Vue проверка типа и интерфейс TS.
                                  +1
                                  С кастингом решение совсем никуда не годится, я думал Vue уже научился из коробки делать как-то так Vue.extend<DemoGridProps, DemoGridData>({...}) habr.com/post/351216/#comment_10716424
                              0

                              А чего влюбляться-то? Свалить всё в одну кучу можно и в Реакте и Ангуляре.

                                0
                                А чем удобно все сваливать в один файл? Я бы делал шаблон в этом vue файле, а скрипт и стили подключал обычным образом (link / script теги) — vue поддерживает такой подход. Тогда получились бы трех файловые компоненты, что как по мне удобнее по многим причинам, а на выходе после сборки пускай это будет 1 файл.
                                  0
                                  На самом деле никто вам не запрещает, это отлично поддерживается Vue.

                                  Нам нравится в первую очередь с точки зрения файловой структуры — очень удобно работать с едиными файлами.
                                  С точки зрения IDE работа с .vue файлами очень удобна — подсветка синтаксиса работает, сворачивание, итд.

                                  У нас общие стили вынесены в отдельные обще-проектные стилевые файлы, а компоненты имеют только специфичные им стили. Таким образом стили в .vue довольно небольшие, а то и отсутствуют вовсе.
                                  Примерно так же и для шаблона. Скрипт тег также обычно довольно короткий, так как основная логика находится в store.
                                0
                                PPS: Кстати английский вариант предыдущей статьи был настолько успешен, что у меня даже состоялось прямое общение (видео) с основными виновниками — но работу не предложили =(

                                Статей от товарищей с реальным опытом немного, в основном все по верхам, успешность заслуженная.
                                  0

                                  С ангуляром в принципе все ясно, что, как, для чего, где соломки постелить. Сам запостил им кучу багов и фичреквестов по формам, роутеру, хттп модулю и так далее, но они в развитие модулей не слишком-то и вкладываются, а местами ощущение, что модули пишут как в первый раз. Больше всего меня разозлил автор роутера (а может и всех трех), который параллельно писал по нему книгу и имел совесть продавать ее за деньги. Ну и один раз имел несчатье выбрать родную ангулярную реализацию i18n (адок) (до сих пор по этой теме глухо, я так понимаю к 7й версии сделают).


                                  angular-cli я не пользуюсь, как и react-scripts и даже vue-cli, вебпак или роллап более-менее поддаются настройке и не лишают меня свободы выбора и не добавляют лишних проблем.


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

                                    0
                                    Насчет Tslint: в vscode поддерживается в файлах с расширением .vue
                                    Видимо вы используете WebStorm
                                      0

                                      В VScode оно может и работает, но не через сам пакет линтера — т.к. отдельно он не умеет этого. Соответственно, нельзя запустить на CI — а значит толку от такого линтера немного.

                                        0
                                        Это одна из причин почему удобнее держать скрипты, стили и шаблон в разных файлах.
                                        0
                                        прощу прощения, вот тут написали переход с Angular на Vue с подробным описанием почему не на React. А почему бы например не перейти на RactiveJS аналог Vue, или например RiotJS аналог React только лучше?
                                        Кто нибудь сравнивал эти фреймворки между собой? Я имею в виду RactiveJS vs Vue и React vs RiotJS?
                                          0
                                          Мы рассматривали только наиболее популярные фреймворки — т.к. размер комьюнити очень сильно влияет на качество экосистемы. Больше народу — большу пулл реквестов.
                                          +1
                                          По некоторым пунктам автор излишне оптимистичен.

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

                                          В одной из статей (и выступлений) предлагалось делать автоимпорт компонентов через webpack — мне кажется, это вообще за гранью.

                                          Магия везде — по коду компонента в шторме минимум сложно понять, что откуда прилетает. Мапперы на vuex не навигируются нормально. Магии в связке react/redux — ощутимо меньше.
                                          Маппинг по-разному работает, где-то можно писать имена с неймспейсами, где-то нельзя.

                                          VirtualDOM — вызов создания VNode настолько развесистый, что HOC становится лютой экзотикой. Очень много всего транслировать. Сравните с HOC в реакте.

                                          Отдельная хрень — этот ваш TypeScript. То есть сам по себе он норм (хотя наш проект его не использует). Но! В шторме есть такая замечательная штука — Ctrl+Q, и навигация в код зависимости. До поры-до времени всё было ок, пока в библиотеках массово не стали завозить TS тайпинги. Докблоков в них нет, Ctrl+Q фактически умер, переход на код перебрасывает в тайпинг, и приходится по дереву зависимости руками искать реальный источник. Вот так TS уничтожает существующую экосистему.

                                          Отдельно про TS применительно к Vue. Нормального вывода объектного контекста, как я понимаю, до сих пор нет (не особо слежу, но наличие двух разных способов прикрутить TS говорит за это). Фокусы с модификацией инстанса внешними плагинами жизнь для TS не облегчают — this.$somePlugin.xxx.zzz.ttt. Как с TS нормально уживается this.$refs.xxx — вообще загадка. Про дублирование деклараций пропсов уже решено?

                                          Про внутренности Vue.js — не пинал только ленивый. Эван со товарищи конечно крутой, но недокументированный код — не круто. Back to VDOM — было удивительно узнать, что оно заимствованное из другой библиотеке. Просто как-то не афишируется. Сейчас начинают наконец-то появляться статьи о внутреннем устройстве фреймворка, это радует.

                                          Сервисы для HTTP в статических классах — круто. Есть подход проще — фабричная функция, которая создает «ресурсы», их экспортируем в одной точке. Импорты в местах использования будут включать только необходимое, без классического «банан, гориллу, которая его держит, и все джунгли впридачу».

                                          const resource = (url, method = 'get') => (params = {}) => axios({ url: typeof url === 'function' ? url(params) : url, method, ...params });
                                          
                                          export const resourceUsers = resource('/api/users');
                                          export const resourceUpdateUser = resource(({ id }) => `/api/users/${ id }`, 'patch');
                                          };
                                            –1
                                            А на тайпинг вас перебрасывает из TS-кода или из JS-кода? Если второе, то это уже похоже на баг IDE.

                                            Кстати, только что проверил: сам Typescirpt сохраняет jsdoc при генерации тайпингов, так что ситуация «в тайпингах нет докблоков» возможна только для js-библиотек, и виноваты в ней исключительно авторы тайпингов, а не язык.
                                              +1
                                              Из JS. Во vue.js такое сплошь и рядом — все мапперы из Vuex например. шторм 2018.1.
                                              +1
                                              Структурирование кода — миксины зло. Они затрудняют понимание кода компонента, при не очень строгом код ревью разные миксины используют друг друга, и появляется неописуемая лапша. Правда в том, что это сложно проконтролировать. Проще вообще запретить их использование (как по факту сделано в реакте).

                                              Подпишусь. Миксины это чистая динамика, смотря в код бывает очень сложно отследить откуда что прилетело. По сути это манки патчинг.

                                              Честно говоря, после этой статьи серьезно попробовать Vue c TS желания не прибавилось, а без TS жизнь уже не мила.
                                                0
                                                Не совсем манки патчинг, но всё равно бяка. Манки патчинг в чистом виде — это как плагины ставятся :) Тот же vue-i18n
                                                0

                                                Касательно миксинов не соглашусь — да их нужно грамотно использовать, но в реальности мне этот подход гораздо больше нравится, нежели в Angular (никак). С точки зрения поддержки — мы для себя пока проблем не видим, но если это столь необходимо — class component позволяет делать миксин классом от которого компонент наследуется = рефакторинг и все остальное будет работать.


                                                По поводу магии хотелось бы увидеть конкретный пример. Не сталкивался. Навигация работает в вебшторме отлично с TypeScript.


                                                Со статическими методами классов работать гораздо приятнее с точки зрения TypeScript.


                                                По основным претензиям вижу что TS вам не очень близок и пытаться вас к его использованию склонять не собираюсь, но Ctrl+Q ним особо не нужен, а качество тайпингов зависит от библиотек и тех кто эти тайпинги поддерживает — то есть не все ваши аргументы валидны.

                                                  0
                                                  То есть миксины нативно поддерживаются какой-то IDE? Для той же функциональности я сейчас предлагаю использовать такое:
                                                  import { someReusableMethod } from './mixins';
                                                  
                                                  export default {
                                                    methods: {
                                                      someReusableMethod,
                                                      someOtherMethod() { /*... */ }
                                                    }
                                                  }
                                                  


                                                  И ваш пример с extends mixin() несколько лучше, но классика множественного наследования — если два миксина пересекаются по именам, из какого унаследуется реализация? Как мне понять, из какого места прилетела реализация, если всё, что в компоненте есть — this.someMethod()?

                                                  Сам концепт динамического связывания в рантайме — не очень хорош, если всё, зачем вы его используете — статическое связывание.

                                                  Про магию — в контекст прилетает масса странного, что непонятно откуда берется. Vuelidate -> this.$v, I18N -> this.$t, Vuex -> this.$store, Router -> this.$route, был еще vue-resource -> this.$http. Конкретно в компоненте это и есть магия, когда «ниоткуда» появляется нечто. Впрочем сама модель построения фреймворка такая. Это «easy, not simple». Само конструирование инстанса из частичек конфигурационного объекта, сборка в единое всех этих computed, data, methods, watch — из-за чего с вычислением this такие проблемы — по сути разновидность магии.

                                                  TS — мне нравится типизированный код, и когда язык не вынуждает аннотировать типы на каждый чих. Но (возможно завышенные ожидания) я ожидаю что типизировано будет всё. И ломаться в рантайме по несоответствию типов не будет (будет по минимуму). Пока из того, что я слышу — TS даже в полном «фарше» не может этого обеспечить. Но да, это личное предпочтение.

                                                  Статические методы — это, как мне кажется, притащили из Java, где функций вообще нет. Я для JS считаю это антипаттерном, который только создает бойлерплейт код.

                                                  Качество тайпингов — извините, но минимум сам Vuex вот такой — то есть одна из основных библиотек экосистемы. То, что вам докблоки не нужны — ок, верю, но это не значит, что они никому не нужны. Бегать в онлайн документацию чтобы уточнить порядок аргументов и какие принимаются — надоедает.
                                                    +3

                                                    Про миксины больше спорить смысла нет — это скорее персональные предпочтения.
                                                    Однако, лично мне, плагины и штуки вида this.$v, this.$t итд — нравятся. Даже очень.
                                                    Более того, в случае с TS это отлично поддерживается, и использование плагина без тайпингов сразу приведет к ошибке.


                                                    Касательно качества тайпингов — да, такая проблема сущетсвует, но на нее влиять проще всего, конкретно для Vuelidate они еще не финализированы и мы используем кастомизированную версию.
                                                    А уж добавить доки в тайпинги Vuex — дело одного очень простого и короткого PR'а.


                                                    Это опенсорс, и на некоторые вещи можно влиять самостоятельно.

                                                      0
                                                      Vuex — мы не используем TS, и очень досадно, что комбинация IDE+Vuex при появлении тайпингов стала доставлять неудобства. Простой и короткий PR — это удалить тайпинги? :)
                                                        –1
                                                        Я уже писал выше: проблема на стороне IDE. Пишите разработчикам и не приплетайте язык, он ни в чем не виноват.
                                                          +1
                                                          Не воспринимайте это так всерьез, никто на тайпинги не покушается :)
                                                    0
                                                    Можно еще вопрос оффтопный, а сколько лет вашему проекту на Vue? И какое количество разработчиков? Просто грабли с миксинами у нас выросли после полутора лет, поначалу тоже всё было (казалось) радужно.
                                                      +1
                                                      У нас много проектов на Vue. Самый длительный — около года, благодаря тому, что строгое код ревью, линтер и TS были с первого дня — проблем никаких. Уверен что в части миксинов их и не возникнет, т.к. создаются они только для тех случаев когда действительно нужны.

                                                      Впрочем, я допускаю, что еще через год какие-то неявные вещи могут всплыть, но пока предпосылок к этому нет. Появятся — обязательно напишу новый пост, как есть.
                                                        0
                                                        Прям даже завидно. По тому, что я рассказываю про миксины — у нас оттоптались по граблям по полной. Проекту уже 2.5 года, начинался еще с 1.0 версии.
                                                      0

                                                      Вообще, миксины есть в тайпскрипте с версии 2.2 и их можно использовать с ангуляром (но наверное не нужно), единственная проблема — баг с лайфсайкл методами, компилятор не пока не понимает, что они есть в миксине.

                                                      –1
                                                      То есть сам по себе он норм (хотя наш проект его не использует). Но! В шторме есть такая замечательная штука — Ctrl+Q, и навигация в код зависимости.

                                                      Это проблема JetBrains, а не TypeScript.

                                                      Отдельно про TS применительно к Vue. Нормального вывода объектного контекста, как я понимаю, до сих пор нет.

                                                      Это проблема китайца, а не TypeScript.

                                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                    Самое читаемое