Вместо предисловия


Vue используется во всех проектах FunCorp. Мы внимательно следим за развитием фреймворка, постоянно улучшаем процесс разработки и внедряем лучшие практики. И, конечно же, мы не могли пройти мимо и не перевести статью Филиппа Раковски, сооснователя VueStorefront, про новые фичи Vue 3, серьёзно влияющие на написание кода.

image
В прошлый раз мы рассматривали фичи, которые влияют на производительность Vue 3. Нам уже известно, что приложения, написанные на новой версии фреймворка, работают очень быстро, но производительность — не самое важное изменение. Для большинства разработчиков намного важнее то, как Vue 3 повлияет на стиль написания кода.

Как вы уже догадались, во Vue 3 появится много крутых фич. К счастью, команда Vue добавила больше улучшений и дополнений, чем ломающих изменений. Благодаря этому большинство разработчиков, знающих Vue 2, должны быстро освоиться в новом синтаксисе.

Давайте начнём с API, о котором многие из вас могли слышать.

Composition API


Composition API — самая обсуждаемая и упоминаемая фича следующей мажорной версии Vue. Синтаксис Composition API предоставляет абсолютно новый подход к организации и переиспользованию кода.

Сейчас мы создаём компоненты с синтаксисом, который называется Options API. Для того чтобы добавить логику, мы создаём свойства (опции) в объекте компонента, например data, methods, computed и т.д. Основным недостатком данного подхода является то, что это не JavaScript-код как таковой. Вам необходимо точно знать, какие опции доступны в шаблоне и каким будет поведение this. Компилятор Vue преобразует свойства в работающий JavaScript-код за вас. Из-за этой особенности мы не можем в полной мере пользоваться автодополнением или проверкой типов.

Composition API решает эту проблему и даёт возможность использовать механизмы, доступные через опции, с помощью обыкновенных JavaScript-функций.
Команда Vue описывает Composition API как «дополнительный, основанный на функциях API, позволяющий гибко использовать композицию в логике компонента». Код, написанный с помощью нового API, лучше читается, что делает его более лёгким для понимания.

Чтобы разобраться в том, как работает новый синтаксис, рассмотрим пример простого компонента.

<template>
  <button @click="increment">
    Count is: {{ count }}, double is {{ double }}, click to increment.
  </button>
</template>

<script>
import { ref, computed, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)

    function increment() {
      count.value++
    }

    onMounted(() => console.log('component mounted!'))

    return {
      count,
      double,
      increment
    }
  }
}
</script>

Разобьём код на части и разберём, что же здесь происходит.

import { ref, computed, onMounted } from 'vue'

Как я уже упоминал выше, Composition API представляет опции компонента как функции, следовательно, первым делом мы должны импортировать необходимые функции. В этом примере нам нужно создать реактивное свойство с помощью ref, вычисляемое с помощью computed и получить доступ к хуку mounted жизненного цикла с помощью функции onMounted.

Возможно, у вас возникнет вопрос: что это за таинственный метод setup?

export default {
  setup() {}
}

Если коротко, setup — просто функция, которая передаёт свойства и функции в шаблон. Мы описываем все реактивные и вычисляемые свойства, хуки жизненного цикла и всех наблюдателей в функции setup, а затем возвращаем их, чтобы использовать в шаблоне.

К тому, что мы не вернём из setup, доступа в шаблоне не будет.

const count = ref(0)

Реактивное свойство count инициализируем с помощью функции ref. Она принимает примитив или объект и возвращает реактивную ссылку. Переданное значение будет сохранено в свойстве value созданной ссылки. Например, если мы хотим получить доступ к значению count, нам необходимо явно обратиться к count.value.

const double = computed(() => count.value * 2)
 
function increment() {
  count.value++
}

Так мы объявляем вычисляемое свойство double и функцию increment.

onMounted(() => console.log('component mounted!'))

C помощью хука onMounted мы выводим в консоль сообщение после монтирования компонента для демонстрации такой возможности.

return {
  count,
  double,
  increment
}

Чтобы свойства count и double и метод increment были доступны в шаблоне, возвращаем их из метода setup.

<template>
  <button @click="increment">
    Count is: {{ count }}, double is {{ double }}. Click to increment.
  </button>
</template>

И вуаля! У нас есть доступ к свойствам и методам из setup, точно так же, как если бы они были объявлены через старый Options API.

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

Переиспользование кода с Composition API


Давайте подробнее рассмотрим преимущества нового Composition API, например, для переиспользования кода. Сейчас, если мы хотим использовать какой-то кусок кода в нескольких компонентах, у нас есть два варианта: миксины (mixins) и слоты с ограниченной областью видимости (scoped slots). Оба варианта имеют свои недостатки.

Мы хотим извлечь функциональность счётчика и переиспользовать его в других компонентах. Вот пример, как это может быть сделано с помощью существующего и с помощью нового API.

Для начала рассмотрим реализацию с использованием миксинов.

import CounterMixin from './mixins/counter'
 
export default {
  mixins: [CounterMixin]
}

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

Теперь рассмотрим слоты с ограниченной областью видимости.

<template>
  <Counter v-slot="{ count, increment }">
     {{ count }}
    <button @click="increment">Increment</button> 
  </Counter> 
</template>

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

А теперь рассмотрим реализацию с использованием Composition API.

function useCounter() {
  const count = ref(0)
  function increment () { count.value++ }
 
  return {
    count,
    incrememt
  }
}
 
export default {
  setup () {
    const { count, increment } = useCounter()
    return {
      count,
      increment
    }
  }
}

Выглядит гораздо элегантнее, не так ли? Мы не ограничены ни шаблоном, ни областью видимости и точно знаем, какие свойства счётчика доступны. И благодаря тому, что useCounter — просто функция, которая возвращает данные, в качестве приятного бонуса мы получаем автодополнение кода в редакторе. Здесь нет магии, поэтому редактор может помогать нам с проверкой типов и давать подсказки.

Лучше выглядит и использование сторонних библиотек. Например, если мы хотим использовать Vuex, то можем явно импортировать функцию useStore и не засорять прототип Vue свойством this.$store. Этот подход позволяет избавиться от дополнительных манипуляций в плагинах.

const { commit, dispatch } = useStore()

Если вы хотите узнать больше о Composition API и его применениях, я рекомендую прочитать документ, в котором команда Vue объясняет причины создания нового API и предлагает кейсы, в которых он пригодится. Также есть замечательный репозиторий с примерами использования Composition API от Thorsten Lunborg, одного из членов команды ядра Vue.

Изменения в конфигурировании и монтировании


В новом Vue есть другие важные изменения в том, как мы создаём и конфигурируем наше приложение. Давайте рассмотрим это на примере.

import Vue from 'vue'
import App from './App.vue'
 
Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)
 
new Vue({
  render: h => h(App)
}).$mount('#app')

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

Рассмотрим, как это будет работает во Vue 3.

import { createApp } from 'vue'
import App from './App.vue'
 
const app = createApp(App)
 
app.config.ignoredElements = [/^app-/]
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
 
app.mount('#app')

Как вы уже заметили, конфигурация относится к конкретному инстансу Vue, созданному с помощью createApp.

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

Эти изменения обсуждаются в RFC, и возможно, в будущем реализация будет другой.

Фрагменты


Ещё одна крутая фича, на которую мы можем рассчитывать во Vue 3.
Что такое фрагменты?
В настоящий момент компонент может иметь только один корневой элемент, а это значит, что код ниже работать не будет.

<template>
  <div>Hello</div>
  <div>World</div>
</template>

Причиной является то, что инстанс Vue, скрывающийся за каждым компонентом, может быть прикреплён только к одному элементу DOM. Сейчас существует способ создать компонент с несколькими корневыми элементами: для этого необходимо написать компонент в функциональном стиле, которому не нужен собственный инстанс Vue.

Оказывается, такая же проблема существует и в React-сообществе, решена она была с помощью виртуального элемента Fragment.

Выглядит это так:

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

Несмотря на то, что Fragment выглядит как обычный DOM-элемент, он виртуальный и не будет создаваться в DOM-дереве. С этим подходом мы можем использовать функциональность одного корневого элемента без создания лишнего элемента в DOM.

Сейчас вы можете использовать фрагменты и во Vue 2, но с помощью библиотеки vue-fragments, а во Vue 3 они будут работать из коробки!

Suspense


Еще одна отличная идея из экосистемы React, которая будет реализована во Vue 3, — это Suspense.

Suspense приостанавливает рендеринг компонента и отображает заглушку до выполнения определённых условий. На конференции Vue London Эван Ю вскользь затронул Suspense и показал API, который мы можем ожидать в будущем. Suspense-компонент будет иметь 2 слота: для контента и для заглушки.

<Suspense>
  <template >
    <Suspended-component />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>

Заглушка будет отображаться до тех пор, пока компонент <Suspended-component/> не будет готов. Компонент Suspense также может ожидать загрузку асинхронного компонента или выполнения каких-то асинхронных действий в setup-функции.

Несколько v-models


v-model — это директива, с помощью которой можно использовать двусторонний биндинг. Мы можем передать реактивное свойство и изменить его внутри компонента.

Нам она хорошо известна по работе с элементами форм.

<input v-model="property" />

Но знали ли вы, что v-model можно использовать с любым компонентом? Под капотом v-model является лишь пробросом параметра value и прослушиванием события input.

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

<input 
  v-bind:value="property"
  v-on:input="property = $event.target.value"
/>

Можно даже изменить названия свойства и события по умолчанию с помощью опции model:

model: {
  prop: 'checked',
  event: 'change'
}

Как видно, директива v-model может быть очень полезным «синтаксическим сахаром», если мы хотим использовать двусторонний биндинг в наших компонентах. К сожалению, на компонент может быть лишь одна v-model.

К счастью, во Vue 3 эта проблема будет решена. Мы сможем передать имя в v-model и использовать столько v-model, сколько необходимо.

Пример использования:

<InviteeForm
  v-model:name="inviteeName"
  v-model:email="inviteeEmail"
/>

Эти изменения обсуждаются в RFC, и возможно, в будущем реализация будет другой.

Portals


Порталы — это компоненты, созданные для рендера контента вне иерархии текущего компонента. Это тоже одна из возможностей, реализованных в React. В документации React порталы описываются следующим образом: «Порталы позволяют рендерить дочерние элементы в DOM-узел, который находится вне DOM-иерархии родительского компонента».

Порталы отлично подходят для реализации таких компонентов, как модальные окна, попапы и всех тех, которые необходимо отобразить поверх страницы.

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

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

Ниже представлен вариант реализации на библиотеке portal-vue, которая добавляет порталы во Vue 2.

<portal to="destination">
  <p>This slot content will be rendered wherever the portal-target with name 'destination'
    is  located.</p>
</portal>
 
<portal-target name="destination">
  <!--
  This component can be located anywhere in your App.
  The slot content of the above portal component wilbe rendered here.
  -->
</portal-target>

А во Vue 3 данная фича будет из коробки.

Новое API пользовательских директив


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

Сейчас объявление пользовательской директивы выглядит так:

const MyDirective = {
  bind(el, binding, vnode, prevVnode) {},
  inserted() {},
  update() {},
  componentUpdated() {},
  unbind() {}
}

А во Vue 3 будет выглядеть так:

const MyDirective = {
  beforeMount(el, binding, vnode, prevVnode) {},
  mounted() {},
  beforeUpdate() {},
  updated() {},
  beforeUnmount() {}, // new
  unmounted() {}
}

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

Этот API так же обсуждается и может измениться в будущем.

Резюме


Рядом со значительным нововведением — Composition API — мы можем найти несколько улучшений поменьше. Очевидно, что Vue движется в сторону улучшения опыта разработчика, к упрощению и интуитивизации API. Так же круто видеть, что команда Vue решила добавить в ядро фреймворка много идей, которые уже реализованы в сторонних библиотеках.

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

Комментарии (40)


  1. MaZaAa
    20.11.2019 13:38

    Вот это нормальная тема


  1. Tarik02
    20.11.2019 14:13

    Хмм… это только мне кажется, или у Vue и React такое развитие, что сначала в React сделают крутую фичу, а потом Vue сделают себе то же, но намного круче, как на примере с хуками/Composition API?


    1. indestructable
      20.11.2019 15:51

      А чем эти фичи во Вью круче, чем в Реакте? Не спец по Вью, поэтому интересно.


      1. MaZaAa
        20.11.2019 15:56

        Во Vue это сделано лучше чем в реакте, для этого достаточно пописать на реактовских хуках и всё станет ясно


        1. indestructable
          20.11.2019 17:15

          А не поясните, чем именно? Я много писал на реактовских хуках, вообще не писал на вью, было бы интересно почитать.


          1. MaZaAa
            20.11.2019 17:26

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

            function MyComponent() {
                return () => (
                    <div>Hello world</div>
                );
            }


            1. Tarik02
              20.11.2019 18:45

              А как тогда со стейтом быть? Разве что-то типа как в вью делать через .value?


              1. MaZaAa
                20.11.2019 19:53

                Использовать MobX вместо Redux и вместо локального useState и будет счастье


                1. Tarik02
                  20.11.2019 20:51

                  Один глобальный стейт и компоненты вообще без стейта? Это как так О_о?


                  1. MaZaAa
                    20.11.2019 22:07

                    Нет, локальный стейт тоже будет, просто не useState, а MobX'овский


                    1. Tarik02
                      20.11.2019 23:05

                      Теперь понял. Спасибо.


              1. MaZaAa
                20.11.2019 20:53

                Сейчас это можно реализовать используя такой фокус, но зато все недостатки кривого замыкания улетучиваются и замыкание работает как надо.
                codesandbox.io/s/flamboyant-mclean-tzjfp


                1. rudnevr
                  21.11.2019 07:15

                  Проблема в том, что это изолированно тестировать почти невозможно. (И вообще с хуками геморрой). При этом обычный React+Redux раскладывался на ComponentTest,ActionTest,ReducerTest прямиком, тем и был силен.


    1. SerafimArts
      20.11.2019 22:58

      Хмм… это только мне кажется, или у Vue и React такое развитие, что сначала в React сделают крутую фичу, а потом Vue сделают себе то же, но намного круче, как на примере с хуками/Composition API?

      А в конце концов выясняется что это всё было сто лет назад в эмбере или нокауте =)
      Тот же самый "Composition API" — это почти полная копипаста нокаута.


      Заголовок спойлера

      Vue 3:


      import { ref, computed, onMounted } from 'vue'
      
      export default {
        setup() {
          const count = ref(0)
          const double = computed(() => count.value * 2)
      
          function increment() {
            count.value++
          }
      
          onMounted(() => console.log('component mounted!'))
      
          return {
            count,
            double,
            increment
          }
        }
      }

      Ko (если не путаю):


      import {ko} from "knockout"
      
      export class ExampleController {
          count = ko.observable(0);
          double = ko.computed(() => this.count() * 2, this);
      
          constructor() {
              console.log('component mounted!')
          }
      
          increment () {
              this.count(this.count() + 1);
          }
      }


    1. rudnevr
      21.11.2019 07:22

      это безусловно так, китайская библиотека, копирующая фичи и упрощающая синтаксис. Vue на больших проектах очень трудно использовать уже сейчас (я работал недавно на проекте в 60+ человек), потому что кто-то пишет коммуникацию через VueX, а кто-то через ивенты, и никогда не знаешь, что еще придет в голову и логика расползается повсюду. Тестить это практически невозможно. Кроме того, простота входа привлекает не самых лучших разработчиков. С реактом еще въехать надо, что можно и что нельзя, а тут вперед и с шашкой.


      1. Almatyn
        21.11.2019 11:54

        Китайская библиотека — звучит так, как будто это плохо.
        Вы думаете национальность разработчика это важный фактор выбора?
        К тому-же автор американец и варился в американской среде разработчиков, там китайского и нету ничего.

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

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

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


        1. rudnevr
          21.11.2019 18:30

          отчего же ярлык?:) Я, кажется, конкретно объяснил почему вью сложнее использовать. Слишком много фич. Фичи не сочетаются друг с другом.

          Сравните, например, как выкатываются и обкатываются фичи на реакте.

          Китайский в данном случае подход — скопировать идею без понимания и чтоб как будто просто. Там не один разработчик, а целое коммьюнити такое.

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

          нет, давайте установим правила, а не будем давать джуниорам гранаты


          1. Almatyn
            22.11.2019 07:31
            +1

            Слишком много фич. Фичи не сочетаются друг с другом.

            — это субъективно. На мой взгляд фич ровно столько, чтобы делать проекты. Все сочетается между собой. Я больше скажу, с появлением Vue, лично я начал получать эстетическое удовольствие от работы веб разработчика.

            Китайский в данном случае подход — скопировать идею без понимания и чтоб как будто просто. Там не один разработчик, а целое коммьюнити такое.

            Ого! Вы сильно заблуждаетесь. ИМХО Vue создан человеком, очень хорошо понимающем суть реактивных фреймворков, и с благой целью сделать труд разработчиков проще. И комьюнити отличное. И команда ведет проект в правильном направлении.

            Такое ощущение, что по каким-то личным причинам вы невзлюбили Vue. Ну не понравился — не используйте, зачем же возводить напраслину с уверенным видом. И допускайте иногда мысль, что вы в чем-то неправы, и чего-то еще не знаете.

            нет, давайте установим правила, а не будем давать джуниорам гранаты

            Джуниоры — они такие, могут на любом фреймворке и языке придумать чудеса.
            Код ревью, гайдлайны и т.д. как раз и нужны для предотвращения трагических случаев. Вы думаете самостоятельная группа джуниоров на Реакте не смогут завалить проект? Отлично справятся.


  1. Swartex
    20.11.2019 15:41

    когда ждать Vue 3?


    1. DmitriyTat Автор
      20.11.2019 15:45

      Если верить роадмапе, то в первом квартале 2020.


      1. ferocactus
        21.11.2019 01:22

        Так он ещё не готов? То есть статья на будущее, и применять то, что в ней описывается, пока нельзя? Или можно, но всё ещё может поменяться? Может быть, в статье об этом где-то говорится, но хотелось бы понять прежде, чем потратить время на её изучение.


        1. ferocactus
          21.11.2019 02:42

          Не удержался и изучил, и ещё погуглил: использовать можно, но это, очевидно, не production ready и, да, может измениться, но чем ближе релиз, тем меньше вероятность изменений.


  1. Hardcoin
    20.11.2019 20:31

    Vue 3 крут, если не знать про svelte. Virtual DOM сразу выглядит крайне архаичной идеей (svelte — компилятор и работает без virtual DOM)


    1. neplul
      20.11.2019 21:08

      Ага, а свой кастомный js прям отличная идея?


      1. zxcabs
        20.11.2019 22:00

        sveltejs ничего нового в js не добавляет


      1. Hardcoin
        21.11.2019 13:56

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


        1. zxcabs
          23.11.2019 23:11

          знак $ и: это вполне валидный синтаксис для JS


  1. markmariner
    20.11.2019 23:46
    -1

    const count = ref(0)

    Это точно хороший синтаксис, когда в коде есть волшебные числа?


    1. CoolWolf
      21.11.2019 00:04
      +1

      это всего лишь пример. Здесь инициализируется реактивная переменная-счетчик со стартовым значением 0.


      1. SerafimArts
        21.11.2019 02:51

        А почему примитивные обсерверы в современном JS принято называть "реактивными"?


        P.S. Это немного оффтоп вопрос =)


        1. VolCh
          22.11.2019 07:11

          Это один из паттернов раализации реактивности.


  1. strannik_k
    21.11.2019 01:56

    Я с Vue практически не знаком, но хочу вставить свои 5 копеек.
    Как ни странно, новый вариант с функцией setup для меня выглядит более удобным)
    Другие улучшения в новой версии тоже на мой взгляд есть, но…

    export default {
      mixins: [CounterMixin]
    }

    Самая большая проблема такого подхода — мы ничего не знаем о том, что добавляется в наш компонент. Это затрудняет понимание и может приводить к коллизиям с существующими свойствами и методами.
    Касательно «может приводить к коллизиям с существующими свойствами и методами». А зачем вообще нужно было делать именно смешиванием? Использовались бы вместо миксин объекты с изолированными свойствами и методами, и проблемы бы не было.

    Сейчас, если мы хотим использовать какой-то кусок кода в нескольких компонентах, у нас есть два варианта: миксины (mixins) и слоты с ограниченной областью видимости (scoped slots). Оба варианта имеют свои недостатки.
    А как же директивы? Не для все случаев, но все-таки это довольно удобный способ добавить к компонентам логику, да еще и без необходимости создавать новый компонент. Но возможно в Vue какие-то большие ограничения для директив, я не в курсе.

    API пользовательских директив немного изменится во Vue 3, чтобы больше соответствовать жизненному циклу компонента.
    Так ли было необходимо делать API жизненных циклов разными? Мне кажется для компонентов, миксин и директив можно было сделать одно API.
    Если это вдруг так, то из этого следует еще интересный момент. Стоило ли разделять миксины и директивы? Возможно достаточно было сделать одну общую сущность и добавить опцию, отвечающую за то, где можно использовать эта сущность. Только в коде компонента, только в коде шаблона, в обоих местах.

    Ну и последнее, думаю компоненты будут писаться
    так
    export default {
      setup(props) {
        // куча кода
        useCustomHook1(),
        useCustomHook2()
        return {
        }
      }
    }


    1. faiwer
      21.11.2019 11:44

      У вас во всех примерах хуки подобны миксинам, которые каждый вносят какую-то свою независимую лепту в компонент и с друг другом не связаны даже косвенно. Но по моему опыту работы с React hooks — там порой получаются увесистые деревья из хуков. В 2, 3 и даже 4 уровня вложенности. И вся малина именно в комбинировании их с друг другом. Думаю во Vue получится тоже самое.


      1. strannik_k
        21.11.2019 23:02

        В первых 2-х примерах я не говорю, что так хорошо писать. Я говорю, что скорее всего так будет писать большинство. Ну и как вы написали, вкладывать один хук в другой тоже будут.
        Касательно 3-го примера. Точь в точь так писать не надо, иначе и вправду возвращаемые из хуков поля перемешаются друг с другом. Надо как-то избегать этого.

        которые каждый вносят какую-то свою независимую лепту в компонент и с друг другом не связаны даже косвенно
        С чего вы взяли? Есть много способов связать 2 объекта. Например, теоретически можно сделать в хуках ссылку на компонент, в который они добавлены. Тогда через эту ссылку, можно было бы обращаться к другим хукам.

        И вся малина именно в комбинировании их с друг другом.
        Вот и я отчасти к этому веду в 3-ем примере) А логика в самом компоненте уменьшает возможности комбинирования в целом.

        Но по моему опыту работы с React hooks — там порой получаются увесистые деревья из хуков. В 2, 3 и даже 4 уровня вложенности.
        Здорово, вместо иерархии наследования сделали иерархию из хуков.
        Я думаю именно это в будущем будет называться hooks hell)))
        Может быть вместо добавления новых уровней вложенности, стоило подумать, как уменьшить вложенность за счет вынесения кода по другому и расположения некоторых хуков на одном уровне?


        1. faiwer
          21.11.2019 23:14

          С чего вы взяли? Есть много способов связать 2 объекта. Например, теоретически можно сделать в хуках ссылку на компонент, в который они добавлены. Тогда через эту ссылку, можно было бы обращаться к другим хукам

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


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


          Здорово, вместо иерархии наследования сделали иерархию из хуков.
          Здорово, вместо иерархии наследования сделали иерархию из хуков.
          Я думаю именно это в будущем будет называться hooks hell)))

          Это называется программирование, а не hooks hell :) Разделяй и влавствуй. Дроби сложное на мелкое и делай это мелкое хорошо. Чем-то напоминает философию linux.


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

          Это как тыкать в себя вилкой. Можно то можно, но гарантировано будут только плохие последствия. Зачем делать плохо, когда уже сделано хорошо? :) В случае плоской модели с классами хорошо сделать было необычно сложно. В случае хуков многие вещи стало получаться писать и просто и понятно. Легко дробить, легко объединять, легко делать generic вещи. Если совсем коротко — The Single Responsibility Principle.


          1. strannik_k
            22.11.2019 00:44
            +1

            Пока писал предыдущее сообщение, местами подзабывал, что пишу о функциях. Я не придумываю, я делюсь своим 3-летним опытом работы в геймдеве. Где проблемы с повторным использованием кода успешно решены с помощью композиции на классах за много лет до появления react и vue.
            Я не говорю, что функциями делать плохо, а классами хорошо.
            Схожими подходами можно и там и там пользоваться.

            Касательно всего того, что вы написали во второй части сообщения. Разделять и комбинировать надо тоже с умом. То, что кто-то получил новый инструмент с возможностью писать более качественно код, не значит, что это сразу сделает из него эксперта.
            Мой опыт использования композиции говорит, что 3-4 уровня вложенности пользовательских хуков — это ошибка проектирования. Но, т.к. у меня мало опыта использования композиции именно в виде хуков, я пока не стану это утверждать. Как обычно, годика через 2 будет видно. Когда хуки будут немного проверены временем и станет понятно, как стоит их использовать, а как нет.


            1. faiwer
              22.11.2019 01:17

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

              А стек-трейсы длиннее 3 строк тоже ошибка проектирования? :) По сути это тоже самое.


              1. strannik_k
                23.11.2019 00:39

                Корректнее будет сравнивать не сами стек-трейсы, а то, что они возвращают.
                Где проще разобраться, во вложенных вызовах или в последовательных?

                Я бы с таким сравнивал:
                refactoring.guru/ru/smells/message-chains
                refactoring.guru/ru/replace-nested-conditional-with-guard-clauses

                такой же код понятней
                func1();
                func2(){
                  func3();
                  func4();
                }
                func5() {
                  func6();
                  func7();
                }


                1. faiwer
                  23.11.2019 00:55

                  Где проще разобраться, во вложенных вызовах или в последовательных?

                  1. В последовательных вызовах разобраться проще тогда, когда эти вызовы завязаны друг на друга. Делают одну вещь.
                  2. Во вложенных вызовах проще разобраться тогда, когда этим самым разделяются не связанные с друг другом явным образом вещи. Т.е. углубившись на уровень глубже мы попадаем в п1.

                  Всё тот же Single Responsibility Principle. Это относится не к хукам, это относится к программированию в целом. Т.е. эти вещи НЕ противопоставляются друг другу. Есть однозначная область применения каждому из них.


                  Отсюда вывод, ваше сопоставление этих двух примеров не имеет решительно никакого смысла без явного описания за что отвечает каждая функция и как они друг на друга завязаны. Нужно знать какие задачи они решают и за что ответственны. В каких-то случаях код-ревью не пройдёт 1-ый пример, в каких-то 2-й пример.


  1. JTG
    21.11.2019 11:44

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

    Ну всё, очередной инструмент в экосистеме JS заговорил про "девелопер икспиренс", можно закапывать.