Почему в Vue 3 Composition API — reactive() будет считаться не нужным

Spread the love

В новом RFC в котором описано Vue Composition API, для создания реактивных переменных было представлены две новые функции ref и reactive. В нем было описаны их преимущества и недостатки, а так же сказано:
На данном этапе мы полагаем, что еще слишком рано называть лучшей практикой использование ref, а не reactive. Мы рекомендуем вам придерживаться стиля, который лучше соответствует вашей ментальной модели, из двух вариантов выше. Мы будем собирать отзывы пользователей в реальном мире и в конечном итоге предоставим более четкие рекомендации по этой теме.

Данная статья описывает одно из мнений, почему функция reactive() может быть не нужной. Само мнение очень интересно, с точки зрения изучения нового API.

Оригинальная статья:  Jason YuThought on Vue 3 Composition API — `reactive()` considered harmful


Спонсор поста RackStore — поставщик широкого спектра IT-услуг на базе сети дата-центров, расположенных в Москве. Размещение серверов в дата-центре (Colocation). Бесперебойное электроснабжение. Круглосуточная охрана и видеонаблюдение, климат-контроль, резервными источниками питания, высокоскоростные каналы связи с безлимитным трафиком. Возможность межоператорских соединений. Техническая поддержка 24/7 и SLA-гарантии. Дата центры Tier2+ и Tier3. Высококвалифицированные специалисты осуществляют круглосуточную техническую поддержку на территории ЦОД


Vue.js выделяется среди других фреймворков своей интуитивной реактивностью. Сomposition API Vue 3 собирается снять некоторые ограничения в Vue 2 и предоставить более явное API.

Краткое введение в Composition API

Есть два способа создания реактивных переменных:

  1. reactive()
  2. ref() / computed()

reactive()

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

Для примера:

// template: {{ state.a }} - {{ state.b }}
const state = reactive({ a: 3 })
// renders: 3 - undefined

state.a = 5
state.b = 'bye'
// renders: 5 - bye

Это работает точно так же, как data в Vue 2. За исключением того, что теперь мы можем добавлять к ним новые свойства, поскольку реактивность реализована с помощью прокси в Vue 3.

Ref

В Vue Composition API так же добавлен ref, который представляет собой просто объект с 1 свойством .value. Мы можем выразить его с помощью Typescript:

interface Ref<A> {
  value: A
}

Есть два способа использования refs:

  1. ref()
    • .value может иметь get/set.
  2. computed()
    • .value будет только для чтения, если не указан setter.

Для примера:

const countRef = ref(0) // { value: 0 }
const countPlusOneRef = computed(() => countRef.value + 1) // { value: 1 }
countRef.value = 5

/*
 * countRef is { value: 5 }
 * countPlusOneRef is { value: 6 } (readonly)
 */

reactive() это плохо; ref это хорошо.

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

Прежде чем я сам не попробовал Composition API, я думал, что reactive() будет API, которое все будут использовать в конечном итоге, так как он не требует лишнего атрибута .value. Удивительно, но после создания нескольких проектов с Composition API, я ни разу не использовал reactive()!

Вот 3 причины почему:

  1. Удобство — ref() позволяет объявить новую реактивную переменную на лету.
  2. Гибкость — ref() разрешает полную замену объекта
  3. Ясность — .value заставляет вас осознавать, то что вы делаете

1. Удобство

Composition api предлагается предоставить способ группировки кода в соответствии с их функцией в компоненте вместо их функции в Vue. Опции Vue Api 2.X группируют код в data, computed, methods, жизненные циклы и т. д. Это делает практически невозможным группирование кода по функциям. Взгляните на следующее изображение, на нем кода закрашен определенным цветом соответствие с решаемой задаче:

Рассмотрим следующие примеры:

const state = reactive({
  count: 0,
  errorMessage: null,
})
setTimeout(() => state.count++, 1000)
watch(state.count, count => {
  if (count > 10) {
    state.errorMessage = 'Larger than 10.'
  }
})

Если мы будем использовать reactive() для хранения нескольких свойств. Легко попасть в ловушку группировки вещей по функциям, а не по свойствам. Скорее всего, вы будете прыгать по базе кода, чтобы изменить этот реактивный объект. Что сделает процесс разработки менее плавным.

const count = ref(0)
setTimeout(() => count.value++, 1000)

const errorMessage = ref(null)
watch(count, count => {
  if (count > 10) {
    errorMessage.value = 'Larger than 10.'
  }
})

С другой стороны, ref() позволяет нам вводить новые переменные на лету. Из приведенного выше примера я представляю переменные только по мере необходимости. Это делает процесс разработки намного более плавным и интуитивно понятным.

2. Гибкость

Сначала я думал, что единственная цель ref() состоит в том, чтобы примитивные значения были реактивными. Но также может быть очень удобно использовать ref() с объектами.

Рассмотрим:

const blogPosts = ref([])
blogPosts.value = await fetchBlogPosts()

Если мы хотим сделать то же самое с reactive, нам нужно вместо этого изменить массив.

const blogPosts = reactive([])
for (const post of (await fetchBlogPosts())) {
  blogPosts.push(post)
}

или с нашим «любимым» Array.prototype.splice()

const blogPosts = reactive([])
blogPosts.splice(0, 0, ...(await fetchBlogPosts()))

Как показано, с ref() проще работать так как в этом случае, вы можете просто заменить весь массив новым. Если это вас не убеждает, представьте, что blogPosts нужно разбить на страницы:

watch(page, page => {
  // remove everything from `blogPosts`
  while (blogPosts.length > 0) {
    blogPosts.pop()
  }

  // add everything from new page
  for (const post of (await fetchBlogPostsOnPage(page))) {
    blogPosts.push(post)
  }
})

или с нашим лучшим другом splice

watch(page, page => {
  blogPosts.splice(0, blogPosts.length, ...(await fetchBlogPostsOnPage(page)))
})

Но если мы используем ref()

watch(page, page => {
  blogPosts.value = await fetchBlogPostsOnPage(page)
})

Что очень гибко для работы.

3. Ясность

reactive() возвращает объект, с которым мы будем взаимодействовать так же, как мы взаимодействуем с другим нереактивным объектом. Это круто, но на практике это может сбить с толку, если мы имеем дело с другими нереактивными объектами.

watch(() => {
  if (human.name === 'Jason') {
    if (!partner.age) {
      partner.age = 30 
    }
  }
})

Мы не можем действительно сказать, будет ли реактивным human или partner. Но если мы отказываемся от использования reactive() и последовательно используем ref(), у нас не будет этой проблемы.

поначалу .value может показаться многословным; но он помогает напомнить нам, что мы имеем дело с реактивностью.

watch(() => {
  if (human.value.name === 'Jason') {
    if (!partner.age) {
      partner.age = 30 
    }
  }
})

Теперь становится очевидным, что human будет реактивным, а не partner.

Заключение

Приведенные выше замечания и мнения носят предварительный характер. Как вы думаете? Вы согласны, что ref() будет доминировать в Vue 3? Или вы думаете, что reactive() будет предпочтительным?

Дай мне знать в комментариях! Я хотел бы услышать больше ваших идей!

Была ли вам полезна эта статья?
[11 / 3.7]

Spread the love
Подписаться
Уведомление о
guest
0 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments